30 votes

Insérer des octets au milieu d'un fichier (dans le système de fichiers Windows) sans lire tout le fichier (en utilisant la table d'allocation des fichiers)?

J'ai besoin d'un moyen pour insérer un fichier clusters dans le milieu d'un fichier à insérer des données.

Normalement, je viens de lire l'ensemble du dossier et de l'écrire à nouveau avec les changements, mais les fichiers de plusieurs gigaoctets, et il faut 30 minutes juste pour lire le fichier et de l'écrire à nouveau.

La taille de cluster ne me dérange pas, je peux essentiellement écrire des zéros à la fin de mon insérés les clusters, et il faudra encore travailler dans ce format de fichier.

Comment puis-je utiliser le Fichier Windows API (ou un autre mécanisme) pour modifier la Table d'Allocation de Fichier d'un fichier, l'insertion d'un ou plusieurs inutilisés grappes à un point donné dans le milieu du fichier?

26voto

JerKimball Points 8994

[EDIT:]

Bla - je vais dire "ce n'est pas faisable, au moins pas via MFT modification, sans BEAUCOUP de douleur"; tout d'abord, le NTFS MFT structures elles-mêmes ne sont pas à 100% "ouvert", donc je commence à plonger dans le reverse-engineering-territoire, ce qui a des conséquences juridiques, je suis pas d'humeur à traiter. Aussi, faire dans .NET est un hyper-processus fastidieux de la cartographie et de l'ordonnancement des structures basées sur beaucoup de conjecture (et ne me lancez pas sur le fait que la plupart de la MFT structures sont compressés dans des voies détournées). Histoire courte, alors que j'ai appris énormément de choses sur la façon NTFS "œuvres", je suis pas près de trouver une solution à ce problème.

[/EDIT]

Pouah...sooo beaucoup Ordonnancement des bêtises....

Ce qui m'a frappé comme une "intéressant", donc j'ai été obligé de fouiner sur le problème...c'est toujours une "réponse en cours", mais je voulais poster ce que j'avais à aider les autres à venir avec quelque chose. :)

Aussi, j'ai une idée que ce serait BEAUCOUP plus facile sur FAT32, mais étant donné que je n'ai que NTFS pour travailler avec...

Soi - beaucoup de pinvoking et de l'ordonnancement, donc nous allons commencer par là et de travailler à l'envers:

Comme on peut le deviner, la norme .Fichier NET/IO api ne va pas beaucoup vous aider ici - nous avons besoin au niveau de l'appareil d'accès:

[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);

Nous allons utiliser ces vilaines win32 bêtes ainsi:

// 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));

Whoa, whoa whoa - ce que le diable est un BootSector_NTFS? C'est un byte-mappé struct qui s'adapte aussi près que je peux compter à ce que la structure NTFS ressemble (FAT32 inclus en tant que bien):

[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;
}

Alors maintenant, nous pouvons tracer un gâchis o'bytes pour en revenir à cette structure:

// 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);

Qui, à ce point de sorties:

I think that the Master File Table is at 
absolute position:3221225472, sector:6291456

Nous allons confirmer que rapide à l'aide de l'OEM outil de soutien à la 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)

Cool, on dirait que nous sommes sur la bonne voie...en avant!

            // 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();
}

Argh, plus de structures natives de discuter, de sorte que la MFT sont disposées de telle façon que les 16 premiers ou si les entrées sont "fixes":

[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 est:

[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();
        }
    }
}

Et...voici où j'en pierre pour le moment, principalement parce que j'ai envie de manger le dîner, par exemple. Je reviendrai sur cela, cependant!

Références (partiellement pour ma propre mémoire, en partie pour aider les autres enquêteurs)

Code complet de vidage'following:

Tous les natifs mappages je vitrée sur le dessus (en raison de poster des limitations de taille, n'est pas complet resucée):

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;
}

Et le harnais de test:

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);
    }
}

7voto

Nik Bougalis Points 7521

Robert, je ne pense pas que ce que vous voulez atteindre est vraiment possible de le faire sans activement la manipulation du système de fichiers de structures de données pour un système de fichiers qui, à partir des sons de celui-ci, est monté. Je ne pense pas que j'ai à vous dire combien dangereux et imprudent dans ce type d'exercice il.

Mais si vous avez besoin de le faire, je suppose que je peux vous donner un "croquis sur le dos d'une serviette pour vous aider à démarrer:

Vous pourriez tirer parti de la "fichier fragmenté" support de NTFS simplement ajouter des "lacunes" en jouant sur la LCN/VCN mappages. Une fois que vous faites, il suffit d'ouvrir le fichier, chercher vers le nouvel emplacement et écrire vos données. NTFS de manière transparente allouer l'espace et écrire les données dans le milieu du fichier, où vous avez créé un trou.

Pour en savoir plus, regardez cette page sur la défragmentation de soutien en NTFS pour des conseils sur comment vous pouvez le manipuler un peu les choses et vous permettent d'insérer des clusters dans le milieu du fichier. Au moins à l'aide de la sanctionnée API pour ce genre de chose, il est rare de corrompre le système de fichiers au-delà de la réparation, bien que vous pouvez toujours horriblement tuyau de votre dossier, je suppose.

Obtenir la récupération des pointeurs pour le fichier que vous souhaitez, de les diviser où vous en avez besoin, ajouter autant d'espace supplémentaire que vous avez besoin, et de déplacer le fichier. Il y a un chapitre très intéressant sur ce genre de chose dans le Russinovich/Ionescu "Windows Internals" (livrehttp://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-Developer/dp/0735625301)

2voto

Sten Petrov Points 3916

Question abstraite, résumé de réponse:

Il est certainement possible de le faire dans de la GRAISSE, et probablement dans la plupart des autres FS, vous serait essentiellement le découpage du fichier, plutôt que le plus commun du processus de défragmentation.

La GRAISSE est organisée autour de cluster pointeurs qui produisent une chaîne de numéros de cluster où les données sont stockées, le premier lien de l'index est stocké avec l'enregistrement du fichier, le second est stocké dans la table d'allocation au niveau de l'index [le premier lien est le numéro], etc. Il est possible d'insérer un autre lien n'importe où dans la chaîne, aussi longtemps que les données que vous insérez se termine à la limite de un cluster.

Les Chances sont que vous aurez beaucoup plus de facilité à le faire en C par trouver une bibliothèque open source. Alors qu'il est probablement possible de le faire en C# avec PInvoke vous ne trouverez pas de bon exemple de code flottant autour de vous pour commencer.

Je soupçonne que vous n'avez pas de contrôle sur le format de fichier (fichiers vidéo?), si vous le faites, il serait beaucoup plus facile à concevoir le stockage de vos données afin d'éviter le problème en premier lieu.

2voto

SecurityMatt Points 3467

Pas de. Ce que vous demandez n'est pas possible directement dans Windows.

C'est parce que dans Windows, les fichiers sont logiquement contiguë collection d'octets, et il n'est pas possible d'insérer les octets dans le milieu du fichier sans l'écraser.

Pour comprendre pourquoi, nous allons mener une expérience de pensée de ce que cela signifierait si c'était possible.

Tout d'abord, les fichiers mappés en mémoire serait soudainement devenu beaucoup plus compliqué. Si nous avons élaboré un fichier à une adresse particulière, puis mettre quelques octets supplémentaires dans le milieu, ce que cela signifierait pour le mappage de la mémoire? Si le mappage de la mémoire maintenant, déplacez soudainement? Et si oui, qu'advient-il du programme qui n'est pas prévu pour?

Deuxièmement, considérons ce qui se passe avec GetFilePointer si les deux poignées sont ouvertes pour le même fichier, et on insère octets supplémentaires au moyen de ce fichier. Supposons qu'Un Processus a ouvert le fichier pour la lecture, et le Processus B a ouvert pour la lecture et l'écriture.

Processus Un veut sauver son emplacement tout en faisant quelques lectures, afin qu'il écrit du code un peu comme

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;
}

Maintenant ce qui se passe à cette fonction que si le Processus B veut insérer quelques octets supplémentaires dans le fichier? Eh bien, si l'on ajoute les octets après où Un Processus est actuellement, tout est très bien - le pointeur de fichier (qui est l'adresse linéaire à partir du début du fichier) reste la même avant et après, et tout est bien.

Mais si l'on ajoute des octets supplémentaires en avant où Un Processus est, bien, tout à coup notre fichier capturé les pointeurs sont toutes alignées, et de mauvaises choses commencent à se produire.

Ou pour le dire d'une autre façon, en ajoutant les octets dans le milieu du fichier signifie que tout à coup nous avons besoin d'inventer de plus intelligent des façons de décrire où nous en sommes dans le fichier de rembobinage, étant donné que les fichiers ne sont plus une logique de sélection contigu d'octets.

Si jusqu'ici nous avons discuté de pourquoi c'est probablement une mauvaise idée pour Windows pour exposer ce genre de fonctionnalité; mais ce n'est pas vraiment répondre à la question "est-il réellement possible". Ici, la réponse est encore non. Il n'est pas possible.

Pourquoi? Car aucune fonctionnalité est exposée à des programmes en mode utilisateur pour ce faire. Comme un programme en mode utilisateur, vous avez un mécanisme pour obtenir un handle vers un fichier (NtCreateFile/NtOpenFile), vous pouvez lire et écrire via NtReadFile/NtWriteFile, vous pouvez chercher et de le renommer et de le supprimer via NtSetFileInformation, et vous pouvez relâcher la poignée de référence via NtClose.

Même à partir du noyau de la mode, vous n'avez pas beaucoup plus d'options. Le système de fichiers de l'API est abstrait, loin de vous, et des systèmes de fichiers de traiter des fichiers en tant que logiquement contiguë collections d'octets, et non pas comme des listes liées de byte-range ou tout ce qui pourrait le rendre facile pour exposer une méthode pour vous permettre d'insérer des non-écrasement d'octets dans le milieu d'un fichier.

Cela ne veut pas dire qu'il n'est pas possible per se. Comme d'autres l'ont mentionné, il est possible pour vous d'ouvrir le disque lui-même, faire semblant d'être en NTFS et modifier les clusters de disque attribué à un particulier FCB directement. Mais le fait est courageux. NTFS est à peine documenté, est complexe, sujet à changement, et difficile à modifier, même quand il n'est pas monté par le système d'exploitation, jamais l'esprit quand il est.

Donc, la réponse, j'ai peur que est pas. Il n'est pas possible via normal et sûr mécanismes de Windows pour ajouter des octets supplémentaires au moyen d'un fichier en tant qu'insertion plutôt que comme un écrasement de l'opération.

Au lieu de cela, envisager de regarder votre problème pour voir si c'est approprié pour vous de segmenter vos fichiers en fichiers plus petits et ont un fichier d'index. De cette façon vous serez en mesure de modifier le fichier d'index pour insérer supplémentaire morceaux. Par briser votre dépendance à l'égard des données ayant besoin de résider dans un seul fichier, vous trouverez qu'il est plus facile d'éviter le système de fichiers de l'exigence qu'un fichier est logiquement contiguë collection d'octets. Vous aurez alors la possibilité de modifier le fichier d'index pour ajouter des morceaux à votre "pseduofile" sans avoir besoin de lire l'ensemble de la pseudofile dans la mémoire.

2voto

user1952500 Points 1763

Vous n'avez pas besoin de (et probablement ne peut pas faire) modifier le fichier de table access. Vous pouvez obtenir le même à l'aide d'un filtre de pilote ou un empilables FS. Considérons une taille de cluster de 4K. Je ne suis que l'écriture de la conception pour des raisons que j'explique à la fin.

  1. Création d'un nouveau fichier un schéma de carte de le fichier dans un en-tête. L'en-tête de mentionner le nombre d'entrées et d'une liste d'entrées. La taille de l'en-tête sera la même que la taille de la grappe. Pour des raisons de simplicité de laisser l'en-tête de taille fixe avec 4K entrées. Par exemple, supposons qu'il y a un fichier de dire 20KB l'en-tête peut mentionner: [DWORD:5][DWORD:1][DWORD:2][DWORD:3][DWORD:4][DWORD:5]. Ce fichier n'a pas eu d'insertions.

  2. Supposons que quelqu'un insère un cluster après le secteur de 3. Vous pouvez l'ajouter à la fin du fichier et modifier le schéma de la carte de: [5][1][2][3][5][6][4]

  3. Supposons que quelqu'un a besoin de chercher à cluster 4. Vous aurez besoin pour accéder à la mise en page-carte et calculer le décalage et qui cherchent à elle. Ce sera après les 5 premières grappes de telle sorte débutera à 16K.

  4. Supposons que quelqu'un lit ou écrit en série pour le fichier. Les lectures et les écritures devront carte de la même manière.

  5. Supposons que l'en-tête a qu'une seule entrée à gauche: nous avons besoin de l'étendre par le fait d'avoir un pointeur vers un nouveau cluster à la fin du fichier en utilisant le même format que les autres pointeurs ci-dessus. De savoir que nous avons plus d'un cluster de tout ce que nous devons faire est de regarder le nombre d'articles et de calculer le nombre de clusters qui sont nécessaires pour le stocker.

Vous pouvez mettre en œuvre tous les ci-dessus à l'aide d'un pilote de filtre sur Windows ou empilable, système de fichiers (LKM) sur Linux. Mettre en œuvre le niveau de fonctionnalité de base est au niveau d'un grad-école mini projet en difficulté. Se présente au travail en tant que commercial système de fichiers peut être très difficile, surtout étant donné que vous ne voulez pas affecter IO vitesses.

Notez que le filtre ne sera pas affecté par un changement de mise en page du disque / de la défragmentation etc. Vous pouvez également défragmenter votre propre fichier si vous le jugez utile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X