1

In linux there is the fstat system call which gives the inode number of a filedescriptor.

Is there any system call or winapi function which would give MFT Record Number of a given file, from its HANDLE or file path?

If there isn't any function or system call so how should I reach to the MFT Record of a file in MFT Table?

Mehdi Ijadnazar
  • 4,532
  • 4
  • 35
  • 35
  • Why would you need that? – Daniel Sep 03 '16 at 16:45
  • I'm writing a secure delete program from scratch, and I need to remove a given file's metadata, so I need to remove its MFT Record – Mehdi Ijadnazar Sep 03 '16 at 16:47
  • Not sure that this is a well defined thing you are asking for. – David Heffernan Sep 03 '16 at 17:04
  • @DavidHeffernan could you please explain why, I didn't get what you said. and I really need to remove metadata of a file, could you give some hints on that? – Mehdi Ijadnazar Sep 03 '16 at 17:08
  • You didn't ask about removing meta data. You asked for the mft record number. Why not ask about the actual problem. And bear in mind all possible file systems. – David Heffernan Sep 03 '16 at 17:09
  • https://msdn.microsoft.com/en-us/library/bb470206(v=vs.85).aspx *A file may have one or more MFT records, and can contain one or more attributes.* – David Heffernan Sep 03 '16 at 17:11
  • I am writing this for NTFS, and the only thing remaining to do is to find the MFT record of the file and to modify it with unrelated data – Mehdi Ijadnazar Sep 03 '16 at 17:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122582/discussion-between-mahdi-and-david-heffernan). – Mehdi Ijadnazar Sep 03 '16 at 17:20
  • You can use `GetFileInformationByHandleEx` to retrieve ID of the target file. ALthough the ID somehow correspond to MFT file record number, its AFAIK ORed with something, so it cannot be used directly. Maybe, it would be better to parse the whole MFT and find the file base MFT record (in general, a file/directory may be represented by multiple MFT records). Within the FILE_NAME attribute, there is a pointer to file's parent directory, so you can reconstruct the directory tree solely from the MFT. – Martin Drab Sep 03 '16 at 18:34
  • 1
    @MartinDrab - ID(if you mean `FILE_INTERNAL_INFORMATION`) very direct correspond to MFT file record number - this exactly is same as MFT_SEGMENT_REFERENCE. and it can be used direct - and look for https://github.com/jpippy/os-design/blob/master/ntfs-4/fileinfo.c#L1656 – RbMm Sep 03 '16 at 21:50
  • IIRC I tried to use this "direct ID" in `O|penFileById` and it did not work well (without the upper 24 bits). But nevermind. Thanks for the GitHub link. – Martin Drab Sep 03 '16 at 23:31
  • @MartinDrab - "i tried and it did not work" - so what ? for me all work. and when we open file by file ID we must use `FILE_INTERNAL_INFORMATION(MftRecordIndex+SequenceNumber)` as is. but when we use it in NTFS_FILE_RECORD_INPUT_BUFFER we must zero `SequenceNumber` (high 16 bits (not 24)) – RbMm Sep 04 '16 at 07:35
  • Yeah, I figured this out already. – Martin Drab Sep 04 '16 at 10:15
  • @MartinDrab open file by ID and get MFT record use DIFFERENT input. when open by ID - 8 byte (MftRecordIndex+SequenceNumber), when get MFT record - 8 byte (MftRecordIndex only). – RbMm Sep 04 '16 at 10:37
  • 2
    Note also that writing to an existing disk sector may not actually overwrite that disk sector. SSDs, in particular, allocate new sectors upon write, in order to level wear. The old data is still somewhere on the SSD, waiting to be overwritten by some future write request. – Raymond Chen Sep 04 '16 at 21:08

1 Answers1

1

for got MFT Record Number for given file need use FileInternalInformation - here returned FILE_INTERNAL_INFORMATION. really this is 48 low bit MftRecordIndex and 16 high bit SequenceNumber

struct 
{
    LONGLONG    MftRecordIndex : 48;
    LONGLONG    SequenceNumber : 16;
};

look also MFT_SEGMENT_REFERENCE - this is same struct

then for got MFT Record use FSCTL_GET_NTFS_FILE_RECORD as input data - FileReferenceNumber - this is FILE_INTERNAL_INFORMATION.IndexNumber but(!) only low 48 bits(MftRecordIndex) so you need zero high 16 bits(SequenceNumber) and then use FILE_INTERNAL_INFORMATION in place NTFS_FILE_RECORD_INPUT_BUFFER for know NTFS_FILE_RECORD_OUTPUT_BUFFER size - you need first get NTFS_VOLUME_DATA_BUFFER with help FSCTL_GET_NTFS_VOLUME_DATA and use NTFS_VOLUME_DATA_BUFFER.BytesPerFileRecordSegment

NTSTATUS Test(POBJECT_ATTRIBUTES poa)
{
    HANDLE hFile, hVolume = 0;
    IO_STATUS_BLOCK iosb;

    NTSTATUS status = NtOpenFile(&hFile, SYNCHRONIZE, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);

    if (0 <= status)
    {
        union 
        {
            FILE_INTERNAL_INFORMATION fii;

            NTFS_FILE_RECORD_INPUT_BUFFER nfrib;

            struct  
            {
                LONGLONG MftRecordIndex : 48;
                LONGLONG SequenceNumber : 16;
            };
        };

        if (0 <= (status = NtQueryInformationFile(hFile, &iosb, &fii, sizeof(fii), FileInternalInformation)))
        {
            //need open '\Device\HarddiskVolume<N>' or '<X>:'
            status = OpenVolume(hFile, &hVolume);
        }

        NtClose(hFile);

        if (0 <= status)
        {
            NTFS_VOLUME_DATA_BUFFER nvdb;

            if (0 <= (status = NtFsControlFile(hVolume, 0, 0, 0, &iosb, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb))))
            {
                DWORD cb = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER,
                    FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);

                PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);

                SequenceNumber = 0;

                if (0 <= (status = NtFsControlFile(hVolume, 0, 0, 0, &iosb, 
                    FSCTL_GET_NTFS_FILE_RECORD, &nfrib, sizeof nfrib, pnfrob, cb)))
                {
                    NTFS_FILE_RECORD_HEADER* pnfrh = (NTFS_FILE_RECORD_HEADER*)pnfrob->FileRecordBuffer;;
                }
            }

            NtClose(hVolume);
        }
    }

    return status;
}

NTFS_FILE_RECORD_HEADER - this is FILE_RECORD_SEGMENT_HEADER (i take self structs name from here)

RbMm
  • 31,280
  • 3
  • 35
  • 56
  • Documentation does not concur with you. Says that this is actually the fileid – David Heffernan Sep 03 '16 at 19:32
  • @DavidHeffernan - what is wrong ? what you mean under fileid ? this is really worked. – RbMm Sep 03 '16 at 19:33
  • Your obsession with avoiding win32 is rather odd. Why get the fileid that way when it can be obtained using win32. And the user asked for mft record index not fileid. A file can have many mft records. – David Heffernan Sep 03 '16 at 19:38
  • @DavidHeffernan - all what i wrote accurately and without errors, i dont understand what you not like here – RbMm Sep 03 '16 at 19:38
  • @DavidHeffernan - i wrote about how got `MFT Record Number` for `stream` and got `MFT Record` from `MFT Record Number` - code only example of use data structs. i got ready self code written on native api. if somebody want - this is easy rewrite on win32. but this already not related to question – RbMm Sep 03 '16 at 19:44
  • You ignored my comment. – David Heffernan Sep 03 '16 at 19:53
  • @DavidHeffernan - i some edit post, add links from https://msdn.microsoft.com/en-us/library/bb470206(v=vs.85).aspx about `A file may have one or more MFT records` this is mean that `BaseFileRecordSegment` (A file reference to the base file record segment for this file. If this is the base file record, the value is 0. ) can be not zero – RbMm Sep 03 '16 at 20:03
  • @DavidHeffernan - "And the user asked for mft record index not fileid" - i and show hot got `mft record index` ! A file can have many mft records. - so what ? what is wrong in my code (you not replay). about 'many records' - look for `BaseFileRecordSegment` here - https://msdn.microsoft.com/en-us/library/bb470124(v=vs.85).aspx – RbMm Sep 03 '16 at 20:40
  • @DavidHeffernan The documentation is not infallible. This answer is correct and is consistent with what I was seeing in the debugger. If you look at the `FileReferenceNumber` in hex, it becomes obvious that this answer is correct. Assuming everybody else is wrong is very counterproductive. – doug65536 Oct 08 '16 at 22:01
  • @doug Consistent with what you were seeing in the debugger? If that is proof enough for you that it is true everywhere, fine. – David Heffernan Oct 09 '16 at 06:36
  • @DavidHeffernan Sorry, but I am having trouble following these comments. I was referring to the 48-bit+16-bit split of the file ID. When I saw that in my code when debugging, I thought my pointer was misaligned. This answer confirmed that the file ID is actually a bit field, where the high 16 bits are a separate value, as mentioned in this answer. I also noticed that the low 48 bits appear to be roughly increasing by one, with some gaps, across the records in `FSCTL_ENUM_USN_DATA` results. Are you asserting that this answer is not correct solely because "the documentation doesn't say that"? – doug65536 Oct 09 '16 at 16:30