We can now get file records from the MFT
This commit is contained in:
parent
10caa22bbc
commit
66564de7e0
@ -40,6 +40,9 @@ struct ntfs_file_handle {
|
||||
|
||||
uint64_t mft_offset;
|
||||
|
||||
uint64_t file_record_size;
|
||||
uint64_t sectors_per_file_record;
|
||||
|
||||
uint32_t size_bytes;
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// This is the total size of a file record, including the attributes
|
||||
// TODO: calculate this
|
||||
#define MIN_FILE_RECORD_SIZE 1024
|
||||
|
||||
struct mft_file_record {
|
||||
char name[4];
|
||||
uint16_t update_seq_offset;
|
||||
@ -98,12 +102,124 @@ int ntfs_check_signature(struct volume *part) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a count and cluster from the runlist, if next is true then it updates the list intenrally
|
||||
* so the next call will return the next element
|
||||
*
|
||||
* if returned false we got to the end of the file.
|
||||
*/
|
||||
static bool ntfs_get_next_run_list_element(uint8_t **runlist, uint64_t *out_cluster_count, uint64_t *out_cluster, bool next) {
|
||||
uint8_t *runlist_ptr = *runlist;
|
||||
|
||||
// we have reached the end of the file
|
||||
if (runlist_ptr[0] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t low = runlist_ptr[0] & 0xF;
|
||||
uint8_t high = (runlist_ptr[0] >> 4) & 0xF;
|
||||
runlist_ptr++;
|
||||
|
||||
// get the run length
|
||||
uint64_t count = 0;
|
||||
for (int i = low; i > 0; i--) {
|
||||
count <<= 8;
|
||||
count |= runlist_ptr[i - 1];
|
||||
}
|
||||
runlist_ptr += low;
|
||||
|
||||
// get the high byte first
|
||||
int8_t high_byte = (int8_t)runlist_ptr[high - 1];
|
||||
|
||||
// get the run offset
|
||||
uint64_t cluster = 0;
|
||||
for (int i = high; i > 0; i--) {
|
||||
cluster <<= 8;
|
||||
cluster |= runlist_ptr[i - 1];
|
||||
}
|
||||
runlist_ptr += high;
|
||||
|
||||
// if the offset is negative, fill the empty bytes with 0xff
|
||||
if (high_byte < 0 && high < 8) {
|
||||
uint64_t fill = 0;
|
||||
for (int i = 8; i > high; i--) {
|
||||
fill >>= 8;
|
||||
fill |= 0xFF00000000000000;
|
||||
}
|
||||
cluster |= fill;
|
||||
}
|
||||
|
||||
// out it
|
||||
*out_cluster = cluster;
|
||||
*out_cluster_count = count;
|
||||
|
||||
// update it
|
||||
if (next) {
|
||||
*runlist = runlist_ptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ntfs_get_file_record(struct ntfs_file_handle *handle, uint64_t mft_record_no, uint8_t *file_record_buffer) {
|
||||
uint8_t *runlist = handle->mft_run_list;
|
||||
|
||||
// get the
|
||||
uint64_t count = 0;
|
||||
uint64_t cluster = 0;
|
||||
if (!ntfs_get_next_run_list_element(&runlist, &count, &cluster, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_per_cluster = handle->bpb.bytes_per_sector * handle->bpb.sectors_per_cluster;
|
||||
uint64_t byte_count = count * bytes_per_cluster;
|
||||
uint64_t sector = cluster * handle->bpb.sectors_per_cluster;
|
||||
uint64_t record_count = 0;
|
||||
do {
|
||||
// consume the items from the current runlist
|
||||
if (byte_count > 0) {
|
||||
sector += handle->sectors_per_file_record;
|
||||
byte_count -= handle->file_record_size;
|
||||
} else {
|
||||
// get the next run list...
|
||||
if (!ntfs_get_next_run_list_element(&runlist, &count, &cluster, true)) {
|
||||
// reached the end of the mft, did not find it...
|
||||
return false;
|
||||
}
|
||||
byte_count = count * bytes_per_cluster;
|
||||
sector = cluster * handle->bpb.sectors_per_cluster;
|
||||
continue;
|
||||
}
|
||||
record_count++;
|
||||
} while (record_count < mft_record_no);
|
||||
|
||||
// we found the sector of the file record!
|
||||
uint64_t offset = sector * handle->bpb.bytes_per_sector;
|
||||
|
||||
|
||||
if(!volume_read(handle->part, file_record_buffer, offset, handle->file_record_size))
|
||||
panic("NTFS: Failed to read file record from mft");
|
||||
|
||||
// make sure this is a valid file record
|
||||
struct mft_file_record* fr = (struct mft_file_record*)file_record_buffer;
|
||||
if (strncmp(fr->name, "FILE", SIZEOF_ARRAY(fr->name)))
|
||||
panic("NTFS: File record has invalid signature (got %c%c%c%c, should be FILE)!",
|
||||
fr->name[0], fr->name[1], fr->name[2], fr->name[3]);
|
||||
|
||||
// we good!
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the the directory's file record from the mft
|
||||
*/
|
||||
// static int ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_record, char *file_record) {
|
||||
static bool ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_record, uint8_t *file_record) {
|
||||
// get the record of the directory
|
||||
if (!ntfs_get_file_record(handle, mft_record, file_record))
|
||||
return false;
|
||||
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute from the given file record
|
||||
@ -115,8 +231,9 @@ static bool ntfs_get_file_record_attr(uint8_t* file_record, uint32_t attr_type,
|
||||
uint8_t* cur_attr_ptr = file_record + fr->attribute_offset;
|
||||
|
||||
while (true) {
|
||||
if (cur_attr_ptr + sizeof(struct file_record_attr_header) > file_record + 4096)
|
||||
panic("File record attribute is outside of file record");
|
||||
// TODO: don't check for the min size, but for the actual size...
|
||||
if (cur_attr_ptr + sizeof(struct file_record_attr_header) > file_record + MIN_FILE_RECORD_SIZE)
|
||||
panic("NTFS: File record attribute is outside of file record");
|
||||
|
||||
struct file_record_attr_header *cur_attr = (struct file_record_attr_header *)cur_attr_ptr;
|
||||
|
||||
@ -130,7 +247,7 @@ static bool ntfs_get_file_record_attr(uint8_t* file_record, uint32_t attr_type,
|
||||
return false;
|
||||
|
||||
if (cur_attr->length == 0)
|
||||
panic("File record attribute has zero length");
|
||||
panic("NTFS: File record attribute has zero length");
|
||||
|
||||
cur_attr_ptr += cur_attr->length;
|
||||
}
|
||||
@ -144,39 +261,50 @@ static void ntfs_read_root(struct ntfs_file_handle *handle) {
|
||||
handle->mft_offset = (uint64_t)handle->bpb.mft_cluster * (uint64_t)handle->bpb.sectors_per_cluster * (uint64_t)handle->bpb.bytes_per_sector;
|
||||
|
||||
// read the mft file record, this should be the size of a sector
|
||||
// but we will use 4096 since it should cover it
|
||||
uint8_t file_record_buffer[4096];
|
||||
uint8_t file_record_buffer[handle->file_record_size];
|
||||
if (!volume_read(handle->part, file_record_buffer, handle->mft_offset, sizeof(file_record_buffer)))
|
||||
panic("Failed to read MFT file record");
|
||||
panic("NTFS: Failed to read MFT file record");
|
||||
|
||||
// get the file attribute
|
||||
struct file_record_attr_header_non_res *attr;
|
||||
if (!ntfs_get_file_record_attr(file_record_buffer, FR_ATTRIBUTE_DATA, (uint8_t **)&attr))
|
||||
panic("MFT file record missing DATA attribute");
|
||||
panic("NTFS: MFT file record missing DATA attribute");
|
||||
|
||||
// verify the attr and run list are in the buffer
|
||||
if ((uint8_t *)attr + sizeof(*attr) > file_record_buffer + sizeof(file_record_buffer))
|
||||
panic("MFT file record attribute is outside of file record");
|
||||
panic("NTFS: MFT file record attribute is outside of file record");
|
||||
if ((uint8_t *)attr + attr->run_offset + 256 > file_record_buffer + sizeof(file_record_buffer))
|
||||
panic("MFT Run list is outside of file record");
|
||||
panic("NTFS: MFT Run list is outside of file record");
|
||||
|
||||
// save the run list
|
||||
memcpy(handle->mft_run_list, (uint8_t *)attr + attr->run_offset, sizeof(handle->mft_run_list));
|
||||
|
||||
// TODO: read the root directory
|
||||
// read the root directory record, which has the number 5
|
||||
if (!ntfs_read_directory(handle, 5, file_record_buffer))
|
||||
panic("NTFS: Missing root directory file record!");
|
||||
}
|
||||
|
||||
// static int ntfs_find_file_in_directory(char *dir, size_t dir_size, short *name, struct index_entry* entry) {
|
||||
// }
|
||||
|
||||
int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *path) {
|
||||
// save the part
|
||||
ret->part = part;
|
||||
|
||||
// start by reading the bpb so we can access it later on
|
||||
if (!volume_read(part, &ret->bpb, 0, sizeof(ret->bpb)))
|
||||
panic("Failed to read the BPB");
|
||||
panic("NTFS: Failed to read the BPB");
|
||||
|
||||
// in NTFS sector size can be 512 to 4096 bytes, file records are
|
||||
// at least 1024 bytes, in here calculate the sectors per file record
|
||||
// and the file record size
|
||||
if (ret->bpb.bytes_per_sector <= MIN_FILE_RECORD_SIZE) {
|
||||
// this has multiple sectors
|
||||
ret->sectors_per_file_record = MIN_FILE_RECORD_SIZE / ret->bpb.bytes_per_sector;
|
||||
ret->file_record_size = MIN_FILE_RECORD_SIZE;
|
||||
} else {
|
||||
// this has a single sector
|
||||
ret->sectors_per_file_record = 1;
|
||||
ret->file_record_size = ret->bpb.bytes_per_sector;
|
||||
}
|
||||
|
||||
// now prepare the root directory so we can search for
|
||||
// the rest of the stuff
|
||||
ntfs_read_root(ret);
|
||||
|
Loading…
Reference in New Issue
Block a user