public IFile SaveFile(IEnumerable <char> name, Stream data) { lock (this.FileManager.SyncRoot) { var realName = name.AsString(); if (realName == null) { throw new ArgumentNullException("realName"); } realName = realName.Trim(); if (realName == string.Empty) { throw new ArgumentException("realName"); } if (data == null) { throw new ArgumentNullException("data"); } if (data.CanRead == false) { throw new ArgumentException("data"); } var deleteMaskFileOnFailure = false; var maskedFile = this.GetNextMaskedFile(); try { var meta = this.TryGetMetaData(); if (meta == null) { // current meta data could not be read or was not found meta = new XDocument(new XDeclaration("1.0", Encoding.UTF8.WebName, "yes")); } var directoryElement = meta.Root; if (directoryElement == null) { // root element does not exist directoryElement = new XElement(_XML_TAG_METAFILE_ROOT); meta.Add(directoryElement); } var fileListElement = directoryElement.Elements(_XML_TAG_METAFILE_FILELIST).SingleOrDefault(); if (fileListElement == null) { // file list element does not exist fileListElement = new XElement(_XML_TAG_METAFILE_FILELIST); directoryElement.Add(fileListElement); } // try find existing entry var fileElement = CollectionHelper.SingleOrDefault(fileListElement.Elements(_XML_TAG_METAFILE_FILE), e => { var realNameNameAttrib = e.Attribute(_XML_ATTRIB_METAFILE_REALNAME); if (realNameNameAttrib != null) { return((realNameNameAttrib.Value ?? string.Empty).ToLower().Trim() == realName.ToLower().Trim()); } return(false); }); if (fileElement == null) { // no entry found => create new one using (var s = new FileStream(maskedFile.FullName, FileMode.CreateNew, FileAccess.ReadWrite)) { /* create 0 byte file */ } deleteMaskFileOnFailure = true; fileElement = new XElement(_XML_TAG_METAFILE_FILE); fileElement.SetAttributeValue(_XML_ATTRIB_METAFILE_REALNAME, realName); fileElement.SetAttributeValue(_XML_ATTRIB_METAFILE_MASKEDNAME, maskedFile.Name); fileListElement.Add(fileElement); } else { var maskedNameAttrib = fileElement.Attribute(_XML_ATTRIB_METAFILE_MASKEDNAME); if (maskedNameAttrib != null && string.IsNullOrWhiteSpace(maskedNameAttrib.Value) == false) { maskedFile = new FileInfo(Path.Combine(this.LocalPath, maskedNameAttrib.Value.Trim())); } } Rijndael alg; Rfc2898DeriveBytes pdb; var pwd = new byte[48]; try { // create encrypted file using (var fs = new FileStream(maskedFile.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) { // make file empty fs.SetLength(0); // generate password { var rng = new RNGCryptoServiceProvider(); rng.GetBytes(pwd); fileElement.SetAttributeValue(_XML_ATTRIB_METAFILE_PASSWORD, Convert.ToBase64String(pwd)); pdb = new Rfc2898DeriveBytes(pwd, CryptoHelper.DEFAULT_CRYTO_SALT, CryptoHelper.DEFAULT_ITERATIONS); } alg = Rijndael.Create(); alg.Key = pdb.GetBytes(32); alg.IV = pdb.GetBytes(16); using (var cs = new CryptoStream(fs, alg.CreateEncryptor(), CryptoStreamMode.Write)) { data.CopyTo(cs); cs.Flush(); cs.Close(); } } // file size try { fileElement.SetAttributeValue(_XML_ATTRIB_METAFILE_SIZE, data.Length); } catch { // ignore } // update meta file this.UpdateMetaData(meta); var secPwd = CryptoHelper.ToSecureString(fileElement.Attribute(_XML_ATTRIB_METAFILE_PASSWORD).Value); if (secPwd != null) { secPwd.MakeReadOnly(); } var result = new CloudPrincipalFile(); result.Directory = this; result.FileManager = this.FileManager; result.LocalPath = maskedFile.FullName; result.Password = secPwd; result.Xml = fileElement; result.SetName(realName); // store file size var sizeAttrib = fileElement.Attribute(_XML_ATTRIB_METAFILE_SIZE); if (sizeAttrib != null && string.IsNullOrWhiteSpace(sizeAttrib.Value) == false) { result.Size = GlobalConverter.Current.ChangeType <long>(sizeAttrib.Value.Trim(), CultureInfo.InvariantCulture); } // try update timestamps try { maskedFile.Refresh(); var actions = new Action <CloudPrincipalFile, FileInfo>[] { (cloudFile, localFile) => cloudFile.CreationTime = localFile.CreationTimeUtc, (cloudFile, localFile) => cloudFile.WriteTime = localFile.LastWriteTimeUtc, }; actions.ForAll(ctx => ctx.Item(result, maskedFile), throwExceptions: false); } catch { // ignore errors here } return(result); } finally { alg = null; pwd = null; pdb = null; } } catch { if (deleteMaskFileOnFailure) { if (maskedFile != null) { maskedFile.Refresh(); if (maskedFile.Exists) { maskedFile.Delete(); maskedFile.Refresh(); } } } throw; } } }
public IEnumerable <IFile> GetFiles() { var meta = this.TryGetMetaData(); if (meta == null) { yield break; } var dir = new DirectoryInfo(this.LocalPath); foreach (var grp in meta.XPathSelectElements("//" + _XML_TAG_METAFILE_ROOT + "/" + _XML_TAG_METAFILE_FILELIST + "/" + _XML_TAG_METAFILE_FILE) .Select(fe => { string maskedName = null; string realName; var maskedNameAttrib = fe.Attribute(_XML_ATTRIB_METAFILE_MASKEDNAME); if (maskedNameAttrib != null) { maskedName = maskedNameAttrib.Value; } var realNameAttrib = fe.Attribute(_XML_ATTRIB_METAFILE_REALNAME); if (realNameAttrib == null || string.IsNullOrWhiteSpace(realNameAttrib.Value)) { realName = maskedName; } else { realName = realNameAttrib.Value.Trim(); } return(new { MaskedName = maskedName, RealName = realName, XmlElement = fe, }); }).Where(i => string.IsNullOrWhiteSpace(i.MaskedName) == false && string.IsNullOrWhiteSpace(i.RealName) == false) .GroupBy(i => i.MaskedName.ToLower().Trim())) { var item = grp.Last(); var maskedFile = new FileInfo(Path.Combine(dir.FullName, item.MaskedName)); if (maskedFile.Exists == false) { continue; } SecureString pwd = null; var passwordAttrib = item.XmlElement.Attribute(_XML_ATTRIB_METAFILE_PASSWORD); if (passwordAttrib != null && string.IsNullOrWhiteSpace(passwordAttrib.Value) == false) { pwd = CryptoHelper.ToSecureString(passwordAttrib.Value); if (pwd != null) { pwd.MakeReadOnly(); } } var newFile = new CloudPrincipalFile() { Directory = this, FileManager = this.FileManager, LocalPath = maskedFile.FullName, Password = pwd, Xml = item.XmlElement, }; newFile.SetName(item.RealName); // try get file size var sizeAttrib = item.XmlElement.Attribute(_XML_ATTRIB_METAFILE_SIZE); if (sizeAttrib != null && string.IsNullOrWhiteSpace(sizeAttrib.Value) == false) { newFile.Size = GlobalConverter.Current .ChangeType <long>(sizeAttrib.Value.Trim(), CultureInfo.InvariantCulture); } newFile.RefreshTimestamps(); yield return(newFile); } }