/// <summary> /// Moves the specified entry from source to target. /// </summary> /// <param name="source">The source.</param> /// <param name="targetDirectory">The target directory.</param> /// <param name="targetName">Name of the target.</param> public void Move(ExFatFilesystemEntry source, ExFatFilesystemEntry targetDirectory, string targetName = null) { if (!(source.MetaEntry.Primary is FileExFatDirectoryEntry sourceFile)) { throw new InvalidOperationException(); } lock (_entryLock) { // create new entry, similar to source targetName = targetName ?? source.Name; var newEntry = CreateEntry(targetDirectory, targetName, source.Attributes); var newFile = (FileExFatDirectoryEntry)newEntry.MetaEntry.Primary; newFile.CreationTimeStamp.Value = sourceFile.CreationTimeStamp.Value; newFile.Creation10msIncrement.Value = sourceFile.Creation10msIncrement.Value; newFile.CreationTimeZoneOffset.Value = sourceFile.CreationTimeZoneOffset.Value; newFile.LastWriteTimeStamp.Value = sourceFile.LastWriteTimeStamp.Value; newFile.LastWrite10msIncrement.Value = sourceFile.LastWrite10msIncrement.Value; newFile.LastWriteTimeZoneOffset.Value = sourceFile.LastWriteTimeZoneOffset.Value; newFile.LastAccessTimeStamp.Value = sourceFile.LastAccessTimeStamp.Value; newFile.LastAccessTimeZoneOffset.Value = sourceFile.LastAccessTimeZoneOffset.Value; newEntry.MetaEntry.SecondaryStreamExtension.DataDescriptor = source.MetaEntry.SecondaryStreamExtension.DataDescriptor; newEntry.MetaEntry.SecondaryStreamExtension.ValidDataLength.Value = source.MetaEntry.SecondaryStreamExtension.ValidDataLength.Value; // add it to target directory var newDataDescriptor = _partition.AddEntry(targetDirectory.DataDescriptor, newEntry.MetaEntry); UpdateEntry(targetDirectory, FileAccess.ReadWrite, newDataDescriptor); // and mark previous as deleted foreach (var e in source.MetaEntry.Entries) { e.EntryType.Value &= ~ExFatDirectoryEntryType.InUse; } Update(source); } }
/// <summary> /// Finds a child with given name. /// </summary> /// <param name="directoryEntry">The directory entry.</param> /// <param name="name">The name.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException"></exception> public ExFatFilesystemEntry FindChild(ExFatFilesystemEntry directoryEntry, string name) { if (directoryEntry == null) { throw new ArgumentNullException(nameof(directoryEntry)); } if (!directoryEntry.IsDirectory) { throw new InvalidOperationException(); } var nameHash = _partition.ComputeNameHash(name); lock (_entryLock) { foreach (var metaEntry in _partition.GetMetaEntries(directoryEntry.DataDescriptor)) { var streamExtension = metaEntry.SecondaryStreamExtension; // keep only file entries if (streamExtension != null && streamExtension.NameHash.Value == nameHash && metaEntry.ExtensionsFileName == name) { return(new ExFatFilesystemEntry(directoryEntry.DataDescriptor, metaEntry)); } } } return(null); }
/// <summary> /// Opens the specified entry. /// </summary> /// <param name="fileEntry">The entry.</param> /// <param name="access">The access.</param> /// <returns></returns> /// <exception cref="InvalidOperationException"></exception> public Stream OpenFile(ExFatFilesystemEntry fileEntry, FileAccess access) { if (fileEntry.IsDirectory) { throw new InvalidOperationException(); } return(OpenData(fileEntry, access)); }
public Node NewChild(Path path, ExFatFilesystemEntry entry) { var child = new Node(entry, _filesystem) { _parent = this }; lock (_children) _children[path.Name] = child; return(child); }
/// <summary> /// Deletes the specified entry. /// </summary> /// <param name="entry">The entry.</param> public void Delete(ExFatFilesystemEntry entry) { lock (_entryLock) { _partition.Free(entry.DataDescriptor); foreach (var e in entry.MetaEntry.Entries) { e.EntryType.Value &= ~ExFatDirectoryEntryType.InUse; } Update(entry); } }
/// <summary> /// Deletes the tree for specified entry. /// </summary> /// <param name="entry">The entry.</param> public void DeleteTree(ExFatFilesystemEntry entry) { lock (_entryLock) { if (entry.IsDirectory) { foreach (var childEntry in EnumerateFileSystemEntries(entry)) { DeleteTree(childEntry); } } Delete(entry); } }
private void UpdateEntry(ExFatFilesystemEntry entry, FileAccess fileAccess, DataDescriptor dataDescriptor) { if (entry?.MetaEntry == null) { return; } DateTimeOffset?now = null; var file = (FileExFatDirectoryEntry)entry.MetaEntry.Primary; // if file was open for reading and the flag is set, the entry is updated if (fileAccess.HasAny(FileAccess.Read) && _options.HasAny(ExFatOptions.UpdateLastAccessTime)) { now = DateTimeOffset.Now; file.LastAccessDateTimeOffset.Value = now.Value; } // when it was open for writing, its characteristics may have changed, so we update them if (fileAccess.HasAny(FileAccess.Write)) { now = now ?? DateTimeOffset.Now; file.FileAttributes.Value |= ExFatFileAttributes.Archive; file.LastWriteDateTimeOffset.Value = now.Value; var stream = entry.MetaEntry.SecondaryStreamExtension; if (dataDescriptor.Contiguous) { stream.GeneralSecondaryFlags.Value |= ExFatGeneralSecondaryFlags.NoFatChain; } else { stream.GeneralSecondaryFlags.Value &= ~ExFatGeneralSecondaryFlags.NoFatChain; } stream.FirstCluster.Value = (UInt32)dataDescriptor.FirstCluster.Value; stream.ValidDataLength.Value = dataDescriptor.LogicalLength; stream.DataLength.Value = dataDescriptor.PhysicalLength; } // now has value only if it was used before, so we spare a flag :) if (now.HasValue) { Update(entry); } }
/// <summary> /// Creates the file. /// </summary> /// <param name="parentDirectory">The parent directory.</param> /// <param name="fileName">Name of the file.</param> /// <returns></returns> public Stream CreateFile(ExFatFilesystemEntry parentDirectory, string fileName) { if (!parentDirectory.IsDirectory) { throw new InvalidOperationException(); } var existingFile = FindChild(parentDirectory, fileName); if (existingFile != null) { var stream = OpenData(existingFile, FileAccess.ReadWrite); stream.SetLength(0); return(stream); } var fileEntry = CreateEntry(parentDirectory, fileName, FileAttributes.Archive); var updatedDataDescriptor = _partition.AddEntry(parentDirectory.DataDescriptor, fileEntry.MetaEntry); UpdateEntry(parentDirectory, FileAccess.Write, updatedDataDescriptor); return(OpenData(fileEntry, FileAccess.ReadWrite)); }
/// <summary> /// Enumerates the file system entries. /// </summary> /// <param name="directoryEntry">The directory entry.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException"></exception> public IEnumerable <ExFatFilesystemEntry> EnumerateFileSystemEntries(ExFatFilesystemEntry directoryEntry) { if (directoryEntry == null) { throw new ArgumentNullException(nameof(directoryEntry)); } if (!directoryEntry.IsDirectory) { throw new InvalidOperationException(); } lock (_entryLock) { foreach (var metaEntry in _partition.GetMetaEntries(directoryEntry.DataDescriptor)) { // keep only file entries if (metaEntry.Primary is FileExFatDirectoryEntry) { yield return(new ExFatFilesystemEntry(directoryEntry.DataDescriptor, metaEntry)); } } } }
/// <summary> /// Creates a <see cref="ExFatFilesystemEntry" />. /// </summary> /// <param name="parent">The parent.</param> /// <param name="name">The name.</param> /// <param name="attributes">The attributes.</param> /// <returns></returns> private ExFatFilesystemEntry CreateEntry(ExFatFilesystemEntry parent, string name, FileAttributes attributes) { var now = DateTimeOffset.Now; var entries = new List <ExFatDirectoryEntry> { new FileExFatDirectoryEntry(new Buffer(new byte[32])) { EntryType = { Value = ExFatDirectoryEntryType.File }, FileAttributes = { Value = (ExFatFileAttributes)attributes }, CreationDateTimeOffset = { Value = now }, LastWriteDateTimeOffset = { Value = now }, LastAccessDateTimeOffset = { Value = now }, }, new StreamExtensionExFatDirectoryEntry(new Buffer(new byte[32])) { FirstCluster = { Value = 0 }, EntryType = { Value = ExFatDirectoryEntryType.Stream }, GeneralSecondaryFlags = { Value = ExFatGeneralSecondaryFlags.ClusterAllocationPossible }, NameLength = { Value = (byte)name.Length }, NameHash = { Value = _partition.ComputeNameHash(name) }, } }; for (int nameIndex = 0; nameIndex < name.Length; nameIndex += 15) { var namePart = name.Substring(nameIndex, Math.Min(15, name.Length - nameIndex)); entries.Add(new FileNameExtensionExFatDirectoryEntry(new Buffer(new byte[32])) { EntryType = { Value = ExFatDirectoryEntryType.FileName }, FileName = { Value = namePart } }); } var metaEntry = new ExFatMetaDirectoryEntry(entries); var entry = new ExFatFilesystemEntry(parent.DataDescriptor, metaEntry); return(entry); }
/// <summary> /// Creates a directory or returns the existing. /// </summary> /// <param name="parentDirectoryEntry">The parent directory.</param> /// <param name="directoryName">Name of the directory.</param> /// <returns></returns> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="IOException"></exception> public ExFatFilesystemEntry CreateDirectory(ExFatFilesystemEntry parentDirectoryEntry, string directoryName) { if (parentDirectoryEntry == null) { throw new ArgumentNullException(nameof(parentDirectoryEntry)); } if (!parentDirectoryEntry.IsDirectory) { throw new InvalidOperationException(); } lock (_entryLock) { var existingEntry = FindChild(parentDirectoryEntry, directoryName); if (existingEntry != null) { if (!existingEntry.IsDirectory) { throw new IOException(); } return(existingEntry); } var directoryEntry = CreateEntry(parentDirectoryEntry, directoryName, FileAttributes.Directory); var updatedDataDescriptor = _partition.AddEntry(parentDirectoryEntry.DataDescriptor, directoryEntry.MetaEntry); using (var directoryStream = OpenData(directoryEntry, FileAccess.ReadWrite)) { // at least one empty entry, otherwise CHKDSK doesn't understand (the dumbass) var empty = new byte[32]; directoryStream.Write(empty, 0, empty.Length); } UpdateEntry(parentDirectoryEntry, FileAccess.Write, updatedDataDescriptor); return(directoryEntry); } }
private string GetLiteralPath(Path parentPath, ExFatFilesystemEntry entry) { return(GetLiteralPath(parentPath.ToLiteral(PathSeparators[0]), entry)); }
/// <summary> /// Writes entry to partition (after changes). /// </summary> /// <param name="entry">The entry.</param> public void Update(ExFatFilesystemEntry entry) { lock (_entryLock) _partition.UpdateEntry(entry.ParentDataDescriptor, entry.MetaEntry); }
public Node(ExFatFilesystemEntry entry, ExFatPathFilesystem filesystem) { _filesystem = filesystem; _generation = _filesystem.GetNextGeneration(); Entry = entry; }
/// <summary> /// Initializes a new instance of the <see cref="ExFatEntryInformation"/> class. /// </summary> /// <param name="entryFilesystem">The entry filesystem.</param> /// <param name="entry">The entry.</param> /// <param name="cleanPath">The path.</param> internal ExFatEntryInformation(ExFatEntryFilesystem entryFilesystem, ExFatFilesystemEntry entry, string cleanPath) { Path = cleanPath; _entryFilesystem = entryFilesystem; _entry = entry; }
private Stream OpenData(ExFatFilesystemEntry fileEntry, FileAccess access) { return(_partition.OpenDataStream(fileEntry.DataDescriptor, access, d => UpdateEntry(fileEntry, access, d))); }
private string GetLiteralPath(string literalParentPath, ExFatFilesystemEntry entry) { var fileName = entry.MetaEntry.ExtensionsFileName; return(GetLiteralPath(literalParentPath, fileName)); }