我需要一种方法来一些文件簇插入到一个文件中插入一些数据。
通常情况下,我也只是读取整个文件,并将其与变化进行再次写回,但文件的大小数千兆字节,并且需要花费30分钟只是读取文件和它再次回信。
簇的大小不打扰我。 我基本上可以写出零到我的插入集群的结束,它仍然会在这个文件格式工作。
我将如何使用Windows文件API(或其他机构)来修改一个文件的文件分配表,将在文件中指定点的一个或多个未使用的集群?
我需要一种方法来一些文件簇插入到一个文件中插入一些数据。
通常情况下,我也只是读取整个文件,并将其与变化进行再次写回,但文件的大小数千兆字节,并且需要花费30分钟只是读取文件和它再次回信。
簇的大小不打扰我。 我基本上可以写出零到我的插入集群的结束,它仍然会在这个文件格式工作。
我将如何使用Windows文件API(或其他机构)来修改一个文件的文件分配表,将在文件中指定点的一个或多个未使用的集群?
[编辑:]
等等 - 我会说:“这是不可行的,至少不是通过修改MFT,无疼痛的很多”; 第一关,NTFS的MFT结构本身并不是100%的“开放”,所以我开始钻研逆向工程领地,它具有法律后果我没有心情对付我。 此外,在.NET这样做是基于很多猜测的映射和编组结构的超繁琐的过程(不要让我开始的事实,大多数的MFT结构以奇怪的方式被压缩)。 短篇小说,而我确实学到了非常多的关于NTFS如何“工作”,我没有接近解决这一问题。
[/编辑]
唉...... SOOO多废话编组站....
这让我觉得“有趣”,所以我才不得不在这个问题闲逛......它仍然是一个“回答进行中”,但想张贴我什么都来帮助其他人在未来的东西了。 :)
另外,我有一个粗糙感,这将是更容易在FAT32,但鉴于我只有NTFS一起工作......
所以- 很多 pinvoking和编组的,所以让我们从这里开始和向后工作:
正如人们所猜测的,标准的.NET文件/ IO API是不会帮助你很多在这里-我们需要的设备级别访问:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool ReadFile(
SafeFileHandle hFile, // handle to file
byte[] pBuffer, // data buffer, should be fixed
int NumberOfBytesToRead, // number of bytes to read
IntPtr pNumberOfBytesRead, // number of bytes read, provide NULL here
ref NativeOverlapped lpOverlapped // should be fixed, if not null
);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetFilePointerEx(
SafeFileHandle hFile,
long liDistanceToMove,
out long lpNewFilePointer,
SeekOrigin dwMoveMethod);
我们将使用正是如此这些讨厌的Win32野兽:
// To the metal, baby!
using (var fileHandle = NativeMethods.CreateFile(
// Magic "give me the device" syntax
@"\\.\c:",
// MUST explicitly provide both of these, not ReadWrite
FileAccess.Read | FileAccess.Write,
// MUST explicitly provide both of these, not ReadWrite
FileShare.Write | FileShare.Read,
IntPtr.Zero,
FileMode.Open,
FileAttributes.Normal,
IntPtr.Zero))
{
if (fileHandle.IsInvalid)
{
// Doh!
throw new Win32Exception();
}
else
{
// Boot sector ~ 512 bytes long
byte[] buffer = new byte[512];
NativeOverlapped overlapped = new NativeOverlapped();
NativeMethods.ReadFile(fileHandle, buffer, buffer.Length, IntPtr.Zero, ref overlapped);
// Pin it so we can transmogrify it into a FAT structure
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
// note, I've got an NTFS drive, change yours to suit
var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(),
typeof(BootSector_NTFS));
哇,哇喔-到底什么是BootSector_NTFS
? 这是一个字节映射struct
适合亲如我能估计到NTFS结构是什么样子(FAT32包括在内):
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=0)]
public struct JumpBoot
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U1, SizeConst=3)]
public byte[] BS_jmpBoot;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string BS_OEMName;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 0, Size = 90)]
public struct BootSector_NTFS
{
[FieldOffset(0)]
public JumpBoot JumpBoot;
[FieldOffset(0xb)]
public short BytesPerSector;
[FieldOffset(0xd)]
public byte SectorsPerCluster;
[FieldOffset(0xe)]
public short ReservedSectorCount;
[FieldOffset(0x10)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] Reserved0_MUSTBEZEROs;
[FieldOffset(0x15)]
public byte BPB_Media;
[FieldOffset(0x16)]
public short Reserved1_MUSTBEZERO;
[FieldOffset(0x18)]
public short SectorsPerTrack;
[FieldOffset(0x1A)]
public short HeadCount;
[FieldOffset(0x1c)]
public int HiddenSectorCount;
[FieldOffset(0x20)]
public int LargeSectors;
[FieldOffset(0x24)]
public int Reserved6;
[FieldOffset(0x28)]
public long TotalSectors;
[FieldOffset(0x30)]
public long MftClusterNumber;
[FieldOffset(0x38)]
public long MftMirrorClusterNumber;
[FieldOffset(0x40)]
public byte ClustersPerMftRecord;
[FieldOffset(0x41)]
public byte Reserved7;
[FieldOffset(0x42)]
public short Reserved8;
[FieldOffset(0x44)]
public byte ClustersPerIndexBuffer;
[FieldOffset(0x45)]
public byte Reserved9;
[FieldOffset(0x46)]
public short ReservedA;
[FieldOffset(0x48)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] SerialNumber;
[FieldOffset(0x50)]
public int Checksum;
[FieldOffset(0x54)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1AA)]
public byte[] BootupCode;
[FieldOffset(0x1FE)]
public ushort EndOfSectorMarker;
public long GetMftAbsoluteIndex(int recordIndex = 0)
{
return (BytesPerSector * SectorsPerCluster * MftClusterNumber) + (GetMftEntrySize() * recordIndex);
}
public long GetMftEntrySize()
{
return (BytesPerSector * SectorsPerCluster * ClustersPerMftRecord);
}
}
// Note: dont have fat32, so can't verify all these...they *should* work, tho
// refs:
// http://www.pjrc.com/tech/8051/ide/fat32.html
// http://msdn.microsoft.com/en-US/windows/hardware/gg463084
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto, Pack=0, Size=90)]
public struct BootSector_FAT32
{
[FieldOffset(0)]
public JumpBoot JumpBoot;
[FieldOffset(11)]
public short BPB_BytsPerSec;
[FieldOffset(13)]
public byte BPB_SecPerClus;
[FieldOffset(14)]
public short BPB_RsvdSecCnt;
[FieldOffset(16)]
public byte BPB_NumFATs;
[FieldOffset(17)]
public short BPB_RootEntCnt;
[FieldOffset(19)]
public short BPB_TotSec16;
[FieldOffset(21)]
public byte BPB_Media;
[FieldOffset(22)]
public short BPB_FATSz16;
[FieldOffset(24)]
public short BPB_SecPerTrk;
[FieldOffset(26)]
public short BPB_NumHeads;
[FieldOffset(28)]
public int BPB_HiddSec;
[FieldOffset(32)]
public int BPB_TotSec32;
[FieldOffset(36)]
public FAT32 FAT;
}
[StructLayout(LayoutKind.Sequential)]
public struct FAT32
{
public int BPB_FATSz32;
public short BPB_ExtFlags;
public short BPB_FSVer;
public int BPB_RootClus;
public short BPB_FSInfo;
public short BPB_BkBootSec;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=12)]
public byte[] BPB_Reserved;
public byte BS_DrvNum;
public byte BS_Reserved1;
public byte BS_BootSig;
public int BS_VolID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=11)]
public string BS_VolLab;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string BS_FilSysType;
}
所以,现在我们可以映射回一个整体mess'o'bytes这个结构:
// Pin it so we can transmogrify it into a FAT structure
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
// note, I've got an NTFS drive, change yours to suit
var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(),
typeof(BootSector_NTFS));
Console.WriteLine(
"I think that the Master File Table is at absolute position:{0}, sector:{1}",
bootSector.GetMftAbsoluteIndex(),
bootSector.GetMftAbsoluteIndex() / bootSector.BytesPerSector);
其中在这一点上的输出:
I think that the Master File Table is at
absolute position:3221225472, sector:6291456
让我们确认,快速使用OEM支持工具nfi.exe
:
C:\tools\OEMTools\nfi>nfi c:
NTFS File Sector Information Utility.
Copyright (C) Microsoft Corporation 1999. All rights reserved.
File 0
Master File Table ($Mft)
$STANDARD_INFORMATION (resident)
$FILE_NAME (resident)
$DATA (nonresident)
logical sectors 6291456-6487039 (0x600000-0x62fbff)
logical sectors 366267960-369153591 (0x15d4ce38-0x1600d637)
$BITMAP (nonresident)
logical sectors 6291448-6291455 (0x5ffff8-0x5fffff)
logical sectors 7273984-7274367 (0x6efe00-0x6eff7f)
酷,看起来我们在正确的轨道上前进...!
// If you've got LinqPad, uncomment this to look at boot sector
bootSector.Dump();
Console.WriteLine("Jumping to Master File Table...");
long lpNewFilePointer;
if (!NativeMethods.SetFilePointerEx(
fileHandle,
bootSector.GetMftAbsoluteIndex(),
out lpNewFilePointer,
SeekOrigin.Begin))
{
throw new Win32Exception();
}
Console.WriteLine("Position now: {0}", lpNewFilePointer);
// Read in one MFT entry
byte[] mft_buffer = new byte[bootSector.GetMftEntrySize()];
Console.WriteLine("Reading $MFT entry...calculated size: 0x{0}",
bootSector.GetMftEntrySize().ToString("X"));
var seekIndex = bootSector.GetMftAbsoluteIndex();
overlapped.OffsetHigh = (int)(seekIndex >> 32);
overlapped.OffsetLow = (int)seekIndex;
NativeMethods.ReadFile(
fileHandle,
mft_buffer,
mft_buffer.Length,
IntPtr.Zero,
ref overlapped);
// Pin it for transmogrification
var mft_handle = GCHandle.Alloc(mft_buffer, GCHandleType.Pinned);
try
{
var mftRecords = (MFTSystemRecords)Marshal.PtrToStructure(
mft_handle.AddrOfPinnedObject(),
typeof(MFTSystemRecords));
mftRecords.Dump();
}
finally
{
// make sure we clean up
mft_handle.Free();
}
}
finally
{
// make sure we clean up
handle.Free();
}
哎呀,更天然结构来讨论 - 这样的MFT被布置成使得所述第一16个左右条目是“固定”:
[StructLayout(LayoutKind.Sequential)]
public struct MFTSystemRecords
{
public MFTRecord Mft;
public MFTRecord MftMirror;
public MFTRecord LogFile;
public MFTRecord Volume;
public MFTRecord AttributeDefs;
public MFTRecord RootFile;
public MFTRecord ClusterBitmap;
public MFTRecord BootSector;
public MFTRecord BadClusterFile;
public MFTRecord SecurityFile;
public MFTRecord UpcaseTable;
public MFTRecord ExtensionFile;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public MFTRecord[] MftReserved;
public MFTRecord MftFileExt;
}
其中MFTRecord
是:
[StructLayout(LayoutKind.Sequential, Size = 1024)]
public struct MFTRecord
{
const int BASE_RECORD_SIZE = 48;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string Type;
public short UsaOffset;
public short UsaCount;
public long Lsn; /* $LogFile sequence number for this record. Changed every time the record is modified. */
public short SequenceNumber; /* # of times this record has been reused */
public short LinkCount; /* Number of hard links, i.e. the number of directory entries referencing this record. */
public short AttributeOffset; /* Byte offset to the first attribute in this mft record from the start of the mft record. */
public short MftRecordFlags;
public int BytesInUse;
public int BytesAllocated;
public long BaseFileRecord;
public short NextAttributeNumber;
public short Reserved;
public int MftRecordNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 976)]
public byte[] Data;
public byte[] SetData
{
get
{
return this.Data
.Skip(AttributeOffset - BASE_RECORD_SIZE)
.Take(BytesInUse - BASE_RECORD_SIZE)
.ToArray();
}
}
public MftAttribute[] Attributes
{
get
{
var idx = 0;
var ret = new List<MftAttribute>();
while (idx < SetData.Length)
{
var attr = MftAttribute.FromBytes(SetData.Skip(idx).ToArray());
ret.Add(attr);
idx += attr.Attribute.Length;
// A special "END" attribute denotes the end of the list
if (attr.Attribute.AttributeType == MftAttributeType.AT_END) break;
}
return ret.ToArray();
}
}
}
而且......这里就是我有始无终现在; 主要是因为我想要吃晚饭和这样。 我会回来到这一点,但是!
参考文献(部分为我自己的记忆,部分以帮助其他研究者)
全码转储a'following:
所有的本地映射我呆滞以上(由于张贴大小限制,不是一个完整的翻版):
public enum MftRecordFlags : ushort
{
MFT_RECORD_IN_USE = 0x0001,
MFT_RECORD_IS_DIRECTORY = 0x0002,
MFT_RECORD_IN_EXTEND = 0x0004,
MFT_RECORD_IS_VIEW_INDEX = 0x0008,
MFT_REC_SPACE_FILLER = 0xffff
}
public enum MftAttributeType : uint
{
AT_UNUSED = 0,
AT_STANDARD_INFORMATION = 0x10,
AT_ATTRIBUTE_LIST = 0x20,
AT_FILENAME = 0x30,
AT_OBJECT_ID = 0x40,
AT_SECURITY_DESCRIPTOR = 0x50,
AT_VOLUME_NAME = 0x60,
AT_VOLUME_INFORMATION = 0x70,
AT_DATA = 0x80,
AT_INDEX_ROOT = 0x90,
AT_INDEX_ALLOCATION = 0xa0,
AT_BITMAP = 0xb0,
AT_REPARSE_POINT = 0xc0,
AT_EA_INFORMATION = 0xd0,
AT_EA = 0xe0,
AT_PROPERTY_SET = 0xf0,
AT_LOGGED_UTILITY_STREAM = 0x100,
AT_FIRST_USER_DEFINED_ATTRIBUTE = 0x1000,
AT_END = 0xffffffff
}
public enum MftAttributeDefFlags : byte
{
ATTR_DEF_INDEXABLE = 0x02, /* Attribute can be indexed. */
ATTR_DEF_MULTIPLE = 0x04, /* Attribute type can be present multiple times in the mft records of an inode. */
ATTR_DEF_NOT_ZERO = 0x08, /* Attribute value must contain at least one non-zero byte. */
ATTR_DEF_INDEXED_UNIQUE = 0x10, /* Attribute must be indexed and the attribute value must be unique for the attribute type in all of the mft records of an inode. */
ATTR_DEF_NAMED_UNIQUE = 0x20, /* Attribute must be named and the name must be unique for the attribute type in all of the mft records of an inode. */
ATTR_DEF_RESIDENT = 0x40, /* Attribute must be resident. */
ATTR_DEF_ALWAYS_LOG = 0x80, /* Always log modifications to this attribute, regardless of whether it is resident or
non-resident. Without this, only log modifications if the attribute is resident. */
}
[StructLayout(LayoutKind.Explicit)]
public struct MftInternalAttribute
{
[FieldOffset(0)]
public MftAttributeType AttributeType;
[FieldOffset(4)]
public int Length;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.Bool)]
public bool NonResident;
[FieldOffset(9)]
public byte NameLength;
[FieldOffset(10)]
public short NameOffset;
[FieldOffset(12)]
public int AttributeFlags;
[FieldOffset(14)]
public short Instance;
[FieldOffset(16)]
public ResidentAttribute ResidentAttribute;
[FieldOffset(16)]
public NonResidentAttribute NonResidentAttribute;
}
[StructLayout(LayoutKind.Sequential)]
public struct ResidentAttribute
{
public int ValueLength;
public short ValueOffset;
public byte ResidentAttributeFlags;
public byte Reserved;
public override string ToString()
{
return string.Format("{0}:{1}:{2}:{3}", ValueLength, ValueOffset, ResidentAttributeFlags, Reserved);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct NonResidentAttribute
{
public long LowestVcn;
public long HighestVcn;
public short MappingPairsOffset;
public byte CompressionUnit;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] Reserved;
public long AllocatedSize;
public long DataSize;
public long InitializedSize;
public long CompressedSize;
public override string ToString()
{
return string.Format("{0}:{1}:{2}:{3}:{4}:{5}:{6}:{7}", LowestVcn, HighestVcn, MappingPairsOffset, CompressionUnit, AllocatedSize, DataSize, InitializedSize, CompressedSize);
}
}
public struct MftAttribute
{
public MftInternalAttribute Attribute;
[field: NonSerialized]
public string Name;
[field: NonSerialized]
public byte[] Data;
[field: NonSerialized]
public object Payload;
public static MftAttribute FromBytes(byte[] buffer)
{
var hnd = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
var attr = (MftInternalAttribute)Marshal.PtrToStructure(hnd.AddrOfPinnedObject(), typeof(MftInternalAttribute));
var ret = new MftAttribute() { Attribute = attr };
ret.Data = buffer.Skip(Marshal.SizeOf(attr)).Take(attr.Length).ToArray();
if (ret.Attribute.AttributeType == MftAttributeType.AT_STANDARD_INFORMATION)
{
var payloadHnd = GCHandle.Alloc(ret.Data, GCHandleType.Pinned);
try
{
var payload = (MftStandardInformation)Marshal.PtrToStructure(payloadHnd.AddrOfPinnedObject(), typeof(MftStandardInformation));
ret.Payload = payload;
}
finally
{
payloadHnd.Free();
}
}
return ret;
}
finally
{
hnd.Free();
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MftStandardInformation
{
public ulong CreationTime;
public ulong LastDataChangeTime;
public ulong LastMftChangeTime;
public ulong LastAccessTime;
public int FileAttributes;
public int MaximumVersions;
public int VersionNumber;
public int ClassId;
public int OwnerId;
public int SecurityId;
public long QuotaChanged;
public long Usn;
}
// Note: dont have fat32, so can't verify all these...they *should* work, tho
// refs:
// http://www.pjrc.com/tech/8051/ide/fat32.html
// http://msdn.microsoft.com/en-US/windows/hardware/gg463084
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto, Pack = 0, Size = 90)]
public struct BootSector_FAT32
{
[FieldOffset(0)]
public JumpBoot JumpBoot;
[FieldOffset(11)]
public short BPB_BytsPerSec;
[FieldOffset(13)]
public byte BPB_SecPerClus;
[FieldOffset(14)]
public short BPB_RsvdSecCnt;
[FieldOffset(16)]
public byte BPB_NumFATs;
[FieldOffset(17)]
public short BPB_RootEntCnt;
[FieldOffset(19)]
public short BPB_TotSec16;
[FieldOffset(21)]
public byte BPB_Media;
[FieldOffset(22)]
public short BPB_FATSz16;
[FieldOffset(24)]
public short BPB_SecPerTrk;
[FieldOffset(26)]
public short BPB_NumHeads;
[FieldOffset(28)]
public int BPB_HiddSec;
[FieldOffset(32)]
public int BPB_TotSec32;
[FieldOffset(36)]
public FAT32 FAT;
}
[StructLayout(LayoutKind.Sequential)]
public struct FAT32
{
public int BPB_FATSz32;
public short BPB_ExtFlags;
public short BPB_FSVer;
public int BPB_RootClus;
public short BPB_FSInfo;
public short BPB_BkBootSec;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] BPB_Reserved;
public byte BS_DrvNum;
public byte BS_Reserved1;
public byte BS_BootSig;
public int BS_VolID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string BS_VolLab;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string BS_FilSysType;
}
而测试工具:
class Program
{
static void Main(string[] args)
{
// To the metal, baby!
using (var fileHandle = NativeMethods.CreateFile(
// Magic "give me the device" syntax
@"\\.\c:",
// MUST explicitly provide both of these, not ReadWrite
FileAccess.Read | FileAccess.Write,
// MUST explicitly provide both of these, not ReadWrite
FileShare.Write | FileShare.Read,
IntPtr.Zero,
FileMode.Open,
FileAttributes.Normal,
IntPtr.Zero))
{
if (fileHandle.IsInvalid)
{
// Doh!
throw new Win32Exception();
}
else
{
// Boot sector ~ 512 bytes long
byte[] buffer = new byte[512];
NativeOverlapped overlapped = new NativeOverlapped();
NativeMethods.ReadFile(fileHandle, buffer, buffer.Length, IntPtr.Zero, ref overlapped);
// Pin it so we can transmogrify it into a FAT structure
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
// note, I've got an NTFS drive, change yours to suit
var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(BootSector_NTFS));
Console.WriteLine(
"I think that the Master File Table is at absolute position:{0}, sector:{1}",
bootSector.GetMftAbsoluteIndex(),
bootSector.GetMftAbsoluteIndex() / bootSector.BytesPerSector);
Console.WriteLine("MFT record size:{0}", bootSector.ClustersPerMftRecord * bootSector.SectorsPerCluster * bootSector.BytesPerSector);
// If you've got LinqPad, uncomment this to look at boot sector
bootSector.DumpToHtmlString();
Pause();
Console.WriteLine("Jumping to Master File Table...");
long lpNewFilePointer;
if (!NativeMethods.SetFilePointerEx(fileHandle, bootSector.GetMftAbsoluteIndex(), out lpNewFilePointer, SeekOrigin.Begin))
{
throw new Win32Exception();
}
Console.WriteLine("Position now: {0}", lpNewFilePointer);
// Read in one MFT entry
byte[] mft_buffer = new byte[bootSector.GetMftEntrySize()];
Console.WriteLine("Reading $MFT entry...calculated size: 0x{0}", bootSector.GetMftEntrySize().ToString("X"));
var seekIndex = bootSector.GetMftAbsoluteIndex();
overlapped.OffsetHigh = (int)(seekIndex >> 32);
overlapped.OffsetLow = (int)seekIndex;
NativeMethods.ReadFile(fileHandle, mft_buffer, mft_buffer.Length, IntPtr.Zero, ref overlapped);
// Pin it for transmogrification
var mft_handle = GCHandle.Alloc(mft_buffer, GCHandleType.Pinned);
try
{
var mftRecords = (MFTSystemRecords)Marshal.PtrToStructure(mft_handle.AddrOfPinnedObject(), typeof(MFTSystemRecords));
mftRecords.DumpToHtmlString();
}
finally
{
// make sure we clean up
mft_handle.Free();
}
}
finally
{
// make sure we clean up
handle.Free();
}
}
}
Pause();
}
private static void Pause()
{
Console.WriteLine("Press enter to continue...");
Console.ReadLine();
}
}
public static class Dumper
{
public static string DumpToHtmlString<T>(this T objectToSerialize)
{
string strHTML = "";
try
{
var writer = LINQPad.Util.CreateXhtmlWriter(true);
writer.Write(objectToSerialize);
strHTML = writer.ToString();
}
catch (Exception exc)
{
Debug.Assert(false, "Investigate why ?" + exc);
}
var shower = new Thread(
() =>
{
var dumpWin = new Window();
var browser = new WebBrowser();
dumpWin.Content = browser;
browser.NavigateToString(strHTML);
dumpWin.ShowDialog();
});
shower.SetApartmentState(ApartmentState.STA);
shower.Start();
return strHTML;
}
public static string Dump(this object value)
{
return JsonConvert.SerializeObject(value, Formatting.Indented);
}
}
罗伯特,我不认为你想达到什么是真的有可能不积极操作的文件系统数据结构,其从它的声音,安装文件系统做的。 我不认为我必须告诉你多么危险和不明智的这种锻炼吧。
但是,如果你需要做的,我想我可以给你,让你开始了“在一张餐巾纸的背面素描”:
你可以充分利用“稀疏文件”支持NTFS的简单地通过调整LCN / VCN映射添加“差距”。 一旦你这样做,只是打开文件,寻找到新的位置和写入数据。 NTFS将透明地分配空间和文件,您创建一个孔的中间写入数据。
欲了解更多,看看这个网页约碎片整理的支持在NTFS的提示你如何能操作的东西了一下,让你在文件的中间插入集群。 至少使用认可的API这样的事情,你是不太可能损坏无法修复文件系统,但你仍然可以可怕软管您的文件,我猜。
获取你想要的文件,检索指针割裂开来,你需要,添加尽可能多的额外空间,因为你需要,和移动文件。 有一个关于这种在Russinovich编写/约内斯库“Windows内部”书事有趣的一页( http://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-Developer/dp/0735625301 )
摘要问题,抽象的回答:
这当然是可能的脂肪,可能是在其他大多数FS,你就基本上可以分段的文件,而不是碎片整理的较常见的过程做到这一点。
FAT与哪个产生,其中数据被存储簇号的链周围簇指针组织,第一连杆索引存储在与文件记录,第二个是在索引[第一连杆的数目]等存储在分配表这是可以在任何地方插入另一个链接链中,只要你在一个集群的边界插入端的数据。
机会是你将有更容易时间找到一个用C做这个开源库 。 虽然它可能是可以做到的,在C#与PInvoke的,你不会找到任何好的示例代码左右浮动,为您上手。
我怀疑你没有过的文件格式(视频文件?)任何控制,如果你这样做会更容易设计你的数据存储,以避免在首位的问题。
你并不需要(也可能无法)修改文件访问表。 您可以使用过滤器驱动器或可堆叠FS达成一致。 让我们考虑4K的簇大小。 我只是写出的原因,我在最后说明了设计。
新文件的创建将在头文件的布局图。 头会提到的条目数和条目列表。 的报头的大小将是相同的簇的大小。 为简单起见让报头是固定大小的与4K条目。 例如,假设有发言权20KB的一个文件中的报头可以提及:[DWORD:5] [DWORD:1] [DWORD:2] [DWORD:3] [DWORD:4] [DWORD:5]。 该文件目前已没有插入。
假设某人插入扇区3后的簇可以将其添加到文件的末尾和布局映射改变为:[5] [1] [2] [3] [5] [6] [4]
假设某人需要寻求集群4.您将需要访问的布局图和计算偏移量,然后再寻找它。 这将是之后的第一个5个集群,将在16K开始。
假设某人读取或连续写入文件。 读取和写入将不得不以同样的方式映射。
假设报头具有仅一个条目左侧:我们将需要通过使用相同的格式与上述其它指针在文件的末端具有一个指向新集群来扩展它。 要知道,我们有一个以上的集群内的所有我们需要做的是看项目的数量,并计算出所需要存储它的簇的数目。
您可以实现以上所有使用Windows或过滤器驱动程序堆叠式文件系统在Linux(LKM)。 实现的功能的基本水平是在一个困难的毕业生,学校小项目的水平。 得到这个工作,因为商业的文件系统可以说是相当具有挑战性特别是因为你不想影响IO速度。
需要注意的是上面的过滤器将不会在磁盘布局的任何变化的影响/碎片整理等,还可以整理自己的文件,如果你认为它会有所帮助。
号在Windows中你所问的是不能直接。
这是因为在Windows中,文件是字节的逻辑连续集合,它是不可能插入字节到文件的中间,而不会覆盖。
要理解为什么,我们进行的是什么,如果它是可能的话,将意味着一个思想实验。
首先,内存映射文件就会突然变得更加复杂。 如果我们已经映射在特定地址的文件,然后把一些额外的字节在它的中间,你会意味着对于内存映射? 如果内存映射现在突然动? 如果是的话,会发生什么情况,不指望它的计划?
其次,让我们考虑一下,如果两个手柄都开到同一个文件有什么用GetFilePointer发生,并在该文件的中间插入一个额外的字节。 让我们假设进程A打开文件进行读取,而进程B已经打开了阅读和写作。
进程A要保存它的位置而做了几个读取,所以写一些代码有点像
DWORD DoAndThenRewind(HANDLE hFile, FARPROC fp){
DWORD result;
LARGEINTEGER zero = { 0 };
LARGEINTEGER li;
SetFilePointer(hFile, zero, &li, FILE_CURRENT);
result = fp();
SetFilePointer(hFile, &li, &li, FILE_BEGIN);
return result;
}
现在发生了什么这个功能,如果进程B要在文件中插入一些额外的字节? 那么,如果我们加入其中一个方法是目前后的字节数,一切都很好 - 文件指针(这是从文件开始的线性地址)保持不变之前和之后,一切都很好。
但是,如果我们在之前添加额外的字节,其中A法是,好了,突然我们捕获的文件指针都是对齐,而坏的事情开始发生。
或者换一种说法,将字节到文件的中间意味着我们突然需要创造描述的更聪明的方式,我们是倒带目的的文件,因为文件不再字节的逻辑相连的选择。
因此,迄今为止,我们已经讨论了为什么它可能是一个坏主意的Windows揭露这种功能; 但是这并没有真正回答这个问题:“它实际上是可能的”。 这里的答案是还没有。 这不可能。
为什么? 因为没有这样的功能被暴露给用户模式程序做到这一点。 作为一个用户模式程序你有得到一个句柄到一个文件中(NtCreateFile / NtOpenFile)一个机制,你可以阅读,并通过NtReadFile然后/ NtWriteFile写它,你可以寻求它并将其重命名,并通过NtSetFileInformation删除它,你可以释放经由NtClose手柄参考。
即使从内核模式,你没有更多的选择。 文件系统API是抽象远离你,和文件系统的逻辑处理文件的字节连续的,而不是看作字节范围或任何的链表这将使它容易暴露一个方法让你在中间插入非覆盖字节一份文件。
这并不是说,这是不可能的每本身 。 正如其他人所说,这可能为你打开磁盘本身,假装是NTFS和改变直接分配给特定的FCB的磁盘簇。 但是这样做是勇敢的。 NTFS几乎没有记载,是复杂的,如有变动,以及难以改变,即使它不是由安装操作系统,当它没关系。
因此,答案恐怕是否定的。 这是不可能通过正常的Windows安全机制,以额外的字节添加到文件的中间为插入,而不是作为一个重写操作。
相反,考虑看你的问题,看看它是否适合您将文件块成较小的文件,并有一个索引文件。 这样,你就可以修改索引文件中插入额外的块。 通过打破你对需要居住在一个文件中的数据的依赖,你会发现它更容易避免文件系统的要求,即文件在逻辑上是字节的连续集合。 然后,您就可以修改索引文件添加额外块到你的“pseduofile”,而不需要读取整个伪文件到内存中。
你知道,这是不结盟的地方近99.99%是不可能的插入不匹配的数据? (也许可以使用基于压缩一些黑客行为。)我认为你做的。
在“最简单”的解决方案是创建稀疏的运行记录,然后写在稀疏范围。
这一切真的取决于原来的问题是什么,那就是你想达到的目标。 一个FAT / NTFS表的修改是没有问题的,这是你的问题的解决方案 - 潜在的优雅和高效,但更可能是非常危险的和不恰当的。 你提到你有在用户的系统上没有控制,其中将使用它,所以大概是至少其中的一些管理员会反对反对黑客进入文件系统内部。
不管怎么说,让我们回到问题。 由于信息不完全,几个用例可以设想,并且该解决方案将是要么容易还是困难取决于使用情况。
如果您知道编辑之后将不再需要一段时间的文件,然后保存编辑在半秒很容易 - 只需关闭窗口,并让应用程序完成减排的背景下,即使这需要一个半小时。 我知道这听起来很愚蠢,但这是一个频繁使用的情况下 - 一旦你完成编辑文件,保存它,关闭程序,而你并不需要那个文件了很长一段时间。
除非你做的。 也许用户决定编辑更多一些,也许另一个用户走来。 在这两种情况下,你的应用程序可以很容易地检测该文件是在被保存到硬盘的过程中(例如,你可能有周围隐藏的后卫文件,而被保存在主文件)。 在这种情况下,你会打开原来的样子(部分保存)的文件,但呈现给用户,这使得它看起来好像该文件是在最终状态的文件的自定义视图。 毕竟,你把所有有关哪些文件块必须被移动,其中的信息。
除非用户需要在另一个编辑器立即打开该文件(这不是一个很常见的情况,特别是对于一个非常特殊的文件格式,但后来谁知道)。 如果是这样,你可以访问其他编辑器的源代码? 或者,你可以跟其他编辑器的开发者,并说服他们来处理不完全保存的文件,就好像是在最后的状态(这并不难 - 所有它需要的是读取保护文件的偏移信息)。 我可以想象其他编辑器的开发人员与长期保存的时间也同样感到沮丧,并会乐意接受你的解决方案,这将有助于他们的产品。
还有什么,我们有吗? 也许用户想立即复制或别的地方移动文件。 微软可能不会改变Windows资源管理器为您的利益。 在这种情况下,你要么需要实现UMDF驱动程序,或者简单地说禁止用户这样做(例如重命名原始文件,并隐藏它,留下它的位置空占位符;当用户试图将文件至少复制他就知道出事了)。
另一种可能性,它不适合在上面的层次1-4很好,来了,如果你事先知道哪些文件进行编辑。 在这种情况下,你可以“预疏”的文件中插入随机差距沿文件的体积均匀。 这是因为你提到你的文件格式的特殊性:有可能是没有数据的空白,提供的链接正确地指向下一个后跟的数据块。 如果你知道哪些文件进行编辑(不是不合理的假设 - 万兆文件有多少谎言在你的硬盘驱动器),用户开始编辑前(之前说了,晚上)你“膨胀”的文件,然后只需左右移动当你需要数据的这些小块插入新的数据。 这当然也依赖于你不必插入太多的假设。
在任何情况下,总是取决于你的用户真正想要的不止一个答案。 但我的建议来自于设计师的角度,而不是从程序员的。
编辑 - 另一种方法 - 怎么样转换到Mac完成这个任务? 他们有卓越的编辑能力,具有自动化功能!
编辑 - 原规范建议的文件已被修改了很多,相反,它被修改一次。 建议正如其他人指出,这样做在后台操作:复制到新的文件,删除旧文件,重命名新文件到旧文件。
我会放弃这种做法。 数据库是你在找什么for./YR