Beispiel #1
0
        /// <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);
            }
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        /// <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);
            }
Beispiel #5
0
 /// <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);
     }
 }
Beispiel #6
0
 /// <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);
     }
 }
Beispiel #7
0
        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);
            }
        }
Beispiel #8
0
        /// <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));
        }
Beispiel #9
0
        /// <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));
                    }
                }
            }
        }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        /// <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);
            }
        }
Beispiel #12
0
 private string GetLiteralPath(Path parentPath, ExFatFilesystemEntry entry)
 {
     return(GetLiteralPath(parentPath.ToLiteral(PathSeparators[0]), entry));
 }
Beispiel #13
0
 /// <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);
 }
Beispiel #14
0
 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;
 }
Beispiel #16
0
 private Stream OpenData(ExFatFilesystemEntry fileEntry, FileAccess access)
 {
     return(_partition.OpenDataStream(fileEntry.DataDescriptor, access, d => UpdateEntry(fileEntry, access, d)));
 }
Beispiel #17
0
        private string GetLiteralPath(string literalParentPath, ExFatFilesystemEntry entry)
        {
            var fileName = entry.MetaEntry.ExtensionsFileName;

            return(GetLiteralPath(literalParentPath, fileName));
        }