On NTFS the $MFT file itself has an entry for every file. The FileId has 16 bits of sequence number and 48-bits of $MFT file index. FileId => [seq << 48 | mft_index]. You can read $MFT records using
DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, ...)
to obtain
NTFS_FILE_RECORD_OUTPUT_BUFFER
and access its
FileRecordBuffer
member which the NTFS info for the fileId in the
FileReferenceNumber
parameter of the
NTFS_FILE_RECORD_OUTPUT_BUFFER
matching the
NTFS_FILE_RECORD_INPUT_BUFFER
unless the fileId is invalid.
The MFT is fairly well documented in many places on the web. That said, it is important to note that an MFT FILE-RECORD contains key-value pairs of attributes. Among the things in those attributes are the hardlink names, any reparse-point, and any NTFS streams (like a dir) that are part of that file.
Put another way, a given fileId can refer to multiple "file names" if it has multiple hardlinks pointing to it. So the canonical name is the first hardlink found in the key-value attributes of the $MFT record, which is then walked backward/up through the parent directory tree to build the canonical path to the file.