public virtual FileRecord CreateFile(MftSegmentReference parentDirectory, string fileName, bool isDirectory) { // Worst case scenrario: the MFT might be full and the parent directory index requires multiple splits if (NumberOfFreeClusters < 24) { throw new DiskFullException(); } FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); m_mftLock.AcquireWriterLock(Timeout.Infinite); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); if (parentDirectoryIndex.ContainsFileName(fileName)) { m_mftLock.ReleaseWriterLock(); throw new AlreadyExistsException(); } List <FileNameRecord> fileNameRecords = IndexHelper.GenerateFileNameRecords(parentDirectory, fileName, isDirectory, m_generateDosNames, parentDirectoryIndex); uint transactionID = m_logClient.AllocateTransactionID(); FileRecord fileRecord = m_mft.CreateFile(fileNameRecords, transactionID); // Update parent directory index foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.AddEntry(fileRecord.BaseSegmentReference, fileNameRecord.GetBytes()); } m_logClient.WriteForgetTransactionRecord(transactionID); m_logClient.WriteRestartRecord(this.MajorVersion, true); m_mftLock.ReleaseWriterLock(); return(fileRecord); }
public virtual KeyValuePairList <MftSegmentReference, FileNameRecord> GetFileNameRecordsInDirectory(MftSegmentReference directoryReference) { KeyValuePairList <MftSegmentReference, FileNameRecord> result; lock (m_mftLock) { FileRecord directoryRecord = GetFileRecord(directoryReference); if (!directoryRecord.IsDirectory) { throw new ArgumentException("directoryReference belongs to a file record which is not a directory"); } IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); result = indexData.GetAllFileNameRecords(); } for (int index = 0; index < result.Count; index++) { bool isMetaFile = (result[index].Key.SegmentNumber < MasterFileTable.FirstUserSegmentNumber); if (result[index].Value.Flags == FileNameFlags.DOS || isMetaFile) { // The same FileRecord can have multiple FileNameRecord entries, each with its own namespace result.RemoveAt(index); index--; } } return(result); }
public virtual void DeleteFile(FileRecord fileRecord) { MftSegmentReference parentDirectory = fileRecord.ParentDirectoryReference; FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); // Update parent directory index List <FileNameRecord> fileNameRecords = fileRecord.FileNameRecords; foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.RemoveEntry(fileNameRecord.GetBytes()); } // Deallocate all data clusters foreach (AttributeRecord atttributeRecord in fileRecord.Attributes) { if (atttributeRecord is NonResidentAttributeRecord) { NonResidentAttributeData attributeData = new NonResidentAttributeData(this, fileRecord, (NonResidentAttributeRecord)atttributeRecord); attributeData.Truncate(0); } } m_mft.DeleteFile(fileRecord); }
public virtual KeyValuePairList <MftSegmentReference, FileNameRecord> GetFileNameRecordsInDirectory(MftSegmentReference directoryReference) { FileRecord directoryRecord = GetFileRecord(directoryReference); KeyValuePairList <MftSegmentReference, FileNameRecord> result = null; if (directoryRecord != null && directoryRecord.IsDirectory) { m_mftLock.AcquireReaderLock(Timeout.Infinite); IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); result = indexData.GetAllFileNameRecords(); m_mftLock.ReleaseReaderLock(); for (int index = 0; index < result.Count; index++) { bool isMetaFile = (result[index].Key.SegmentNumber < MasterFileTable.FirstUserSegmentNumber); if (result[index].Value.Flags == FileNameFlags.DOS || isMetaFile) { // The same FileRecord can have multiple FileNameRecord entries, each with its own namespace result.RemoveAt(index); index--; } } } return(result); }
public virtual void DeleteFile(FileRecord fileRecord) { MftSegmentReference parentDirectory = fileRecord.ParentDirectoryReference; FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); m_mftLock.AcquireWriterLock(Timeout.Infinite); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); uint transactionID = m_logClient.AllocateTransactionID(); // Update parent directory index List <FileNameRecord> fileNameRecords = fileRecord.FileNameRecords; foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.RemoveEntry(fileNameRecord.GetBytes()); } // Deallocate all data clusters foreach (AttributeRecord atttributeRecord in fileRecord.Attributes) { if (atttributeRecord is NonResidentAttributeRecord) { NonResidentAttributeData attributeData = new NonResidentAttributeData(this, fileRecord, (NonResidentAttributeRecord)atttributeRecord); attributeData.Truncate(0); } } m_mft.DeleteFile(fileRecord, transactionID); m_logClient.WriteForgetTransactionRecord(transactionID); m_logClient.WriteRestartRecord(this.MajorVersion, true); m_mftLock.ReleaseWriterLock(); }
public virtual FileRecord CreateFile(MftSegmentReference parentDirectory, string fileName, bool isDirectory) { // Worst case scenrario: the MFT might be full and the parent directory index requires multiple splits if (NumberOfFreeClusters < 24) { throw new DiskFullException(); } FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); if (parentDirectoryIndex.ContainsFileName(fileName)) { throw new AlreadyExistsException(); } List <FileNameRecord> fileNameRecords = IndexHelper.GenerateFileNameRecords(parentDirectory, fileName, isDirectory, m_generateDosNames, parentDirectoryIndex); FileRecord fileRecord = m_mft.CreateFile(fileNameRecords); // Update parent directory index foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.AddEntry(fileRecord.BaseSegmentReference, fileNameRecord.GetBytes()); } return(fileRecord); }
public virtual void MoveFile(FileRecord fileRecord, MftSegmentReference newParentDirectory, string newFileName) { // Worst case scenrario: the new parent directory index requires multiple splits if (NumberOfFreeClusters < 4) { throw new DiskFullException(); } FileRecord oldParentDirectoryRecord = GetFileRecord(fileRecord.ParentDirectoryReference); IndexData oldParentDirectoryIndex = new IndexData(this, oldParentDirectoryRecord, AttributeType.FileName); IndexData newParentDirectoryIndex; if (fileRecord.ParentDirectoryReference == newParentDirectory) { newParentDirectoryIndex = oldParentDirectoryIndex; } else { FileRecord newParentDirectoryRecord = GetFileRecord(newParentDirectory); newParentDirectoryIndex = new IndexData(this, newParentDirectoryRecord, AttributeType.FileName); if (newParentDirectoryIndex.ContainsFileName(newFileName)) { throw new AlreadyExistsException(); } } List <FileNameRecord> fileNameRecords = fileRecord.FileNameRecords; foreach (FileNameRecord fileNameRecord in fileNameRecords) { oldParentDirectoryIndex.RemoveEntry(fileNameRecord.GetBytes()); } DateTime creationTime = fileRecord.FileNameRecord.CreationTime; DateTime modificationTime = fileRecord.FileNameRecord.ModificationTime; DateTime mftModificationTime = fileRecord.FileNameRecord.MftModificationTime; DateTime lastAccessTime = fileRecord.FileNameRecord.LastAccessTime; ulong allocatedLength = fileRecord.FileNameRecord.AllocatedLength; ulong fileSize = fileRecord.FileNameRecord.FileSize; FileAttributes fileAttributes = fileRecord.FileNameRecord.FileAttributes; ushort packedEASize = fileRecord.FileNameRecord.PackedEASize; fileNameRecords = IndexHelper.GenerateFileNameRecords(newParentDirectory, newFileName, fileRecord.IsDirectory, m_generateDosNames, newParentDirectoryIndex, creationTime, modificationTime, mftModificationTime, lastAccessTime, allocatedLength, fileSize, fileAttributes, packedEASize); fileRecord.RemoveAttributeRecords(AttributeType.FileName, String.Empty); foreach (FileNameRecord fileNameRecord in fileNameRecords) { FileNameAttributeRecord fileNameAttribute = (FileNameAttributeRecord)fileRecord.CreateAttributeRecord(AttributeType.FileName, String.Empty); fileNameAttribute.IsIndexed = true; fileNameAttribute.Record = fileNameRecord; } UpdateFileRecord(fileRecord); foreach (FileNameRecord fileNameRecord in fileNameRecords) { newParentDirectoryIndex.AddEntry(fileRecord.BaseSegmentReference, fileNameRecord.GetBytes()); } }
public virtual FileRecord GetFileRecord(string path) { if (path != String.Empty && !path.StartsWith(@"\")) { throw new InvalidPathException(String.Format("The path '{0}' is invalid", path)); } if (path.EndsWith(@"\")) { path = path.Substring(0, path.Length - 1); } if (path == String.Empty) { return(GetFileRecord(MasterFileTable.RootDirSegmentReference)); } string[] components = path.Substring(1).Split('\\'); MftSegmentReference directoryReference = MasterFileTable.RootDirSegmentReference; lock (m_mftLock) { for (int index = 0; index < components.Length; index++) { FileRecord directoryRecord = GetFileRecord(directoryReference); if (index < components.Length - 1) { if (!directoryRecord.IsDirectory) { throw new InvalidPathException(String.Format("The path '{0}' is invalid", path)); } IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); directoryReference = indexData.FindFileNameRecordSegmentReference(components[index]); if (directoryReference == null) { throw new DirectoryNotFoundException(String.Format("Could not find part of the path '{0}'", path)); } } else // Last component { IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); MftSegmentReference fileReference = indexData.FindFileNameRecordSegmentReference(components[index]); if (fileReference == null) { throw new FileNotFoundException(String.Format("Could not find file '{0}'", path)); } FileRecord fileRecord = GetFileRecord(fileReference); if (!fileRecord.IsMetaFile) { return(fileRecord); } } } } // We should never get here throw new InvalidPathException(); }
public virtual FileRecord GetFileRecord(string path) { if (path != String.Empty && !path.StartsWith(@"\")) { throw new ArgumentException("Invalid path"); } if (path.EndsWith(@"\")) { path = path.Substring(0, path.Length - 1); } if (path == String.Empty) { return(GetFileRecord(MasterFileTable.RootDirSegmentReference)); } string[] components = path.Substring(1).Split('\\'); MftSegmentReference directoryReference = MasterFileTable.RootDirSegmentReference; for (int index = 0; index < components.Length; index++) { FileRecord directoryRecord = GetFileRecord(directoryReference); if (index < components.Length - 1) { if (!directoryRecord.IsDirectory) { return(null); } IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); directoryReference = indexData.FindFileNameRecordSegmentReference(components[index]); if (directoryReference == null) { return(null); } } else // Last component { IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); MftSegmentReference fileReference = indexData.FindFileNameRecordSegmentReference(components[index]); if (fileReference == null) { return(null); } FileRecord fileRecord = GetFileRecord(fileReference); if (fileRecord != null && !fileRecord.IsMetaFile) { return(fileRecord); } } } return(null); }
internal void UpdateDirectoryIndex(MftSegmentReference parentDirectory, List <FileNameRecord> fileNameRecords) { lock (m_mftLock) { FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.UpdateFileNameRecord(fileNameRecord); } } }
private void UpdateFileNameRecords() { List <FileNameRecord> fileNameRecords = m_fileRecord.FileNameRecords; foreach (FileNameRecord fileNameRecord in fileNameRecords) { fileNameRecord.AllocatedLength = this.Data.AllocatedLength; fileNameRecord.FileSize = this.Data.Length; } m_volume.UpdateFileRecord(m_fileRecord); // Update directory index MftSegmentReference parentDirectory = m_fileRecord.ParentDirectoryReference; FileRecord parentDirectoryRecord = m_volume.GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(m_volume, parentDirectoryRecord, AttributeType.FileName); foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.UpdateFileNameRecord(fileNameRecord); } }
public override void Delete(string path) { FileRecord fileRecord = m_volume.GetFileRecord(path); if (fileRecord != null) { if (fileRecord.IsDirectory) { IndexData directoryIndex = new IndexData(m_volume, fileRecord, AttributeType.FileName); if (!directoryIndex.IsEmpty) { throw new DirectoryNotEmptyException(); } } m_volume.DeleteFile(fileRecord); } else { throw new FileNotFoundException(); } }
public virtual void DeleteFile(FileRecord fileRecord) { lock (m_mftLock) { MftSegmentReference parentDirectory = fileRecord.ParentDirectoryReference; FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); if (fileRecord.IsDirectory) { IndexData directoryIndex = new IndexData(this, fileRecord, AttributeType.FileName); if (!directoryIndex.IsEmpty) { throw new DirectoryNotEmptyException(); } } uint transactionID = m_logClient.AllocateTransactionID(); // Update parent directory index List <FileNameRecord> fileNameRecords = fileRecord.FileNameRecords; foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.RemoveEntry(fileNameRecord.GetBytes()); } // Deallocate all data clusters foreach (AttributeRecord atttributeRecord in fileRecord.Attributes) { if (atttributeRecord is NonResidentAttributeRecord) { NonResidentAttributeData attributeData = new NonResidentAttributeData(this, fileRecord, (NonResidentAttributeRecord)atttributeRecord); attributeData.Truncate(0); } } m_mft.DeleteFile(fileRecord, transactionID); m_logClient.WriteForgetTransactionRecord(transactionID); m_logClient.WriteRestartRecord(true); } }
public static string GenerateDosName(IndexData parentDirectoryIndex, string fileName) { string nameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); string extension = Path.GetExtension(fileName); nameWithoutExtension = ConvertToDosCharacters(nameWithoutExtension).ToUpper(); if (extension.Length > 0) { extension = "." + ConvertToDosCharacters(extension.Substring(1)).ToUpper(); } if (nameWithoutExtension.Length > 8) { nameWithoutExtension = nameWithoutExtension.Substring(0, 6) + "~1"; } if (extension.Length > 4) { extension = extension.Substring(0, 4); } int index = 1; string shortNameWithoutExtension; while (index <= 0xFFFFFF) { string suffix = "~" + index.ToString("X"); int nameCharsToKeep = Math.Min(nameWithoutExtension.Length, 8 - suffix.Length); shortNameWithoutExtension = nameWithoutExtension.Substring(0, nameCharsToKeep) + suffix; string dosFileName = shortNameWithoutExtension + extension; if (!parentDirectoryIndex.ContainsFileName(dosFileName)) { return(dosFileName); } index++; } // Extremely unlikely that we will ever get here throw new NotSupportedException(String.Format("Could not find an available DOS name for '{0}'", fileName)); }
public virtual FileRecord CreateFile(MftSegmentReference parentDirectory, string fileName, bool isDirectory) { if (fileName.Length > FileNameRecord.MaxFileNameLength) { throw new InvalidNameException(); } // Worst case scenrario: the MFT might be full and the parent directory index requires multiple splits. // We assume IndexData.ExtendGranularity is bigger than or equal to the number of splits. if (NumberOfFreeClusters < m_mft.NumberOfClustersRequiredToExtend + NumberOfClustersRequiredToExtendIndex) { throw new DiskFullException(); } lock (m_mftLock) { FileRecord parentDirectoryRecord = GetFileRecord(parentDirectory); IndexData parentDirectoryIndex = new IndexData(this, parentDirectoryRecord, AttributeType.FileName); if (parentDirectoryIndex.ContainsFileName(fileName)) { throw new AlreadyExistsException(); } List <FileNameRecord> fileNameRecords = IndexHelper.GenerateFileNameRecords(parentDirectory, fileName, isDirectory, GenerateDosNames, parentDirectoryIndex); uint transactionID = m_logClient.AllocateTransactionID(); FileRecord fileRecord = m_mft.CreateFile(fileNameRecords, transactionID); // Update parent directory index foreach (FileNameRecord fileNameRecord in fileNameRecords) { parentDirectoryIndex.AddEntry(fileRecord.BaseSegmentReference, fileNameRecord.GetBytes()); } m_logClient.WriteForgetTransactionRecord(transactionID); m_logClient.WriteRestartRecord(true); return(fileRecord); } }
public static List <FileNameRecord> GenerateFileNameRecords(MftSegmentReference parentDirectory, string fileName, bool isDirectory, bool generateDosName, IndexData parentDirectoryIndex, DateTime creationTime, DateTime modificationTime, DateTime mftModificationTime, DateTime lastAccessTime, ulong allocatedLength, ulong fileSize, FileAttributes fileAttributes, ushort packedEASize) { FileNameRecord fileNameRecord = new FileNameRecord(parentDirectory, fileName, isDirectory, creationTime); fileNameRecord.ParentDirectory = parentDirectory; fileNameRecord.CreationTime = creationTime; fileNameRecord.ModificationTime = modificationTime; fileNameRecord.MftModificationTime = mftModificationTime; fileNameRecord.LastAccessTime = lastAccessTime; fileNameRecord.AllocatedLength = allocatedLength; fileNameRecord.FileSize = fileSize; fileNameRecord.FileAttributes = fileAttributes; fileNameRecord.PackedEASize = packedEASize; fileNameRecord.IsDirectory = isDirectory; fileNameRecord.FileName = fileName; bool createDosOnlyRecord = false; if (generateDosName) { fileNameRecord.Flags = FileNameFlags.Win32; if (DosFileNameHelper.IsValidDosFileName(fileName)) { fileNameRecord.Flags |= FileNameFlags.DOS; } else { createDosOnlyRecord = true; } } else { // This is similar to Windows 8.1, When FileNameFlags.Win32 is set, Windows Server 2003's CHKDSK expects to find a record with FileNameFlags.DOS set. fileNameRecord.Flags = FileNameFlags.POSIX; } List <FileNameRecord> result = new List <FileNameRecord>(); result.Add(fileNameRecord); if (createDosOnlyRecord) { string dosFileName = DosFileNameHelper.GenerateDosName(parentDirectoryIndex, fileName); FileNameRecord dosOnlyRecord = new FileNameRecord(parentDirectory, dosFileName, isDirectory, creationTime); dosOnlyRecord.Flags = FileNameFlags.DOS; result.Add(dosOnlyRecord); } return(result); }
public static List <FileNameRecord> GenerateFileNameRecords(MftSegmentReference parentDirectory, string fileName, bool isDirectory, bool generateDosName, IndexData parentDirectoryIndex) { DateTime creationTime = DateTime.Now; return(GenerateFileNameRecords(parentDirectory, fileName, isDirectory, generateDosName, parentDirectoryIndex, creationTime, creationTime, creationTime, creationTime, 0, 0, 0, 0)); }
public virtual void MoveFile(FileRecord fileRecord, MftSegmentReference newParentDirectory, string newFileName) { // Worst case scenrario: the new parent directory index requires multiple splits. // We assume IndexData.ExtendGranularity is bigger than or equal to the number of splits. if (NumberOfFreeClusters < NumberOfClustersRequiredToExtendIndex) { throw new DiskFullException(); } lock (m_mftLock) { FileRecord oldParentDirectoryRecord = GetFileRecord(fileRecord.ParentDirectoryReference); IndexData oldParentDirectoryIndex = new IndexData(this, oldParentDirectoryRecord, AttributeType.FileName); IndexData newParentDirectoryIndex; if (fileRecord.ParentDirectoryReference == newParentDirectory) { newParentDirectoryIndex = oldParentDirectoryIndex; } else { FileRecord newParentDirectoryRecord = GetFileRecord(newParentDirectory); newParentDirectoryIndex = new IndexData(this, newParentDirectoryRecord, AttributeType.FileName); } if (newParentDirectoryIndex.ContainsFileName(newFileName)) { throw new AlreadyExistsException(); } List <FileNameRecord> fileNameRecords = fileRecord.FileNameRecords; uint transactionID = m_logClient.AllocateTransactionID(); foreach (FileNameRecord fileNameRecord in fileNameRecords) { oldParentDirectoryIndex.RemoveEntry(fileNameRecord.GetBytes()); } // Windows will not update the dates and FileAttributes in $File_Name as often as their counterparts in $STANDARD_INFORMATION. DateTime creationTime = fileRecord.StandardInformation.CreationTime; DateTime modificationTime = fileRecord.StandardInformation.ModificationTime; DateTime mftModificationTime = fileRecord.StandardInformation.MftModificationTime; DateTime lastAccessTime = fileRecord.StandardInformation.LastAccessTime; ulong allocatedLength = fileRecord.FileNameRecord.AllocatedLength; FileAttributes fileAttributes = fileRecord.StandardInformation.FileAttributes; ushort packedEASize = fileRecord.FileNameRecord.PackedEASize; // Windows NTFS v5.1 driver does not usually update the value of the FileSize field belonging to the FileNameRecords that are stored in the FileRecord. // The driver does update the value during a rename, which is inconsistent file creation and is likely to be incidental rather than intentional. // We will set the value to 0 to be consistent with file creation. fileNameRecords = IndexHelper.GenerateFileNameRecords(newParentDirectory, newFileName, fileRecord.IsDirectory, GenerateDosNames, newParentDirectoryIndex, creationTime, modificationTime, mftModificationTime, lastAccessTime, allocatedLength, 0, fileAttributes, packedEASize); fileRecord.RemoveAttributeRecords(AttributeType.FileName, String.Empty); foreach (FileNameRecord fileNameRecord in fileNameRecords) { FileNameAttributeRecord fileNameAttribute = (FileNameAttributeRecord)fileRecord.CreateAttributeRecord(AttributeType.FileName, String.Empty); fileNameAttribute.IsIndexed = true; fileNameAttribute.Record = fileNameRecord; } UpdateFileRecord(fileRecord, transactionID); foreach (FileNameRecord fileNameRecord in fileNameRecords) { if (!fileRecord.IsDirectory) { fileNameRecord.FileSize = fileRecord.DataRecord.DataLength; } newParentDirectoryIndex.AddEntry(fileRecord.BaseSegmentReference, fileNameRecord.GetBytes()); } m_logClient.WriteForgetTransactionRecord(transactionID); m_logClient.WriteRestartRecord(true); } }