From f59f18458c0c990665008b6ac996ee54082b5366 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Fri, 3 Sep 2021 11:03:44 +0300 Subject: [PATCH 1/7] Starting to work on NTFS --- stage23/fs/file.s2.c | 15 +++++++++++ stage23/fs/ntfs.h | 20 +++++++++++++++ stage23/fs/ntfs.s2.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 stage23/fs/ntfs.h create mode 100644 stage23/fs/ntfs.s2.c diff --git a/stage23/fs/file.s2.c b/stage23/fs/file.s2.c index a90e9fcb..88d678d7 100644 --- a/stage23/fs/file.s2.c +++ b/stage23/fs/file.s2.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,20 @@ int fopen(struct file_handle *ret, struct volume *part, const char *filename) { return 0; } + if (ntfs_check_signature(part)) { + struct ntfs_file_handle *fd = ext_mem_alloc(sizeof(struct ntfs_file_handle)); + + int r = ntfs_open(fd, part, filename); + if (r) + return r; + + ret->fd = (void *)fd; + ret->read = (void *)ntfs_read; + ret->size = fd->size_bytes; + + return 0; + } + return -1; } diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h new file mode 100644 index 00000000..81fe5270 --- /dev/null +++ b/stage23/fs/ntfs.h @@ -0,0 +1,20 @@ +#ifndef __FS__NTFS_H__ +#define __FS__NTFS_H__ + +#include +#include +#include +#include +#include + +struct ntfs_file_handle { + struct volume *part; + uint32_t size_bytes; +}; + +int ntfs_check_signature(struct volume *part); + +int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *path); +int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t count); + +#endif \ No newline at end of file diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c new file mode 100644 index 00000000..4ce71f28 --- /dev/null +++ b/stage23/fs/ntfs.s2.c @@ -0,0 +1,61 @@ +#include + +struct ntfs_bpb { + uint8_t jump[3]; + char oem[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t fats_count; + uint16_t directory_entries_count; + uint16_t sector_totals; + uint8_t media_descriptor_type; + uint16_t sectors_per_fat_16; + uint16_t sectors_per_track; + uint16_t heads_count; + uint32_t hidden_sectors_count; + uint32_t large_sectors_count; + + // ntfs + uint32_t sectors_per_fat_32; + uint64_t sectors_count_64; + uint64_t mft_cluster; +} __attribute__((packed)); + +int ntfs_check_signature(struct volume *part) { + struct ntfs_bpb bpb = { 0 }; + if (volume_read(part, &bpb, 0, sizeof(bpb))) { + return 1; + } + + // + // validate the bpb + // + + if (strncmp(bpb.oem, "NTFS ", SIZEOF_ARRAY(bpb.oem))) { + return 1; + } + + if (bpb.sector_totals != 0) { + return 1; + } + + if (bpb.large_sectors_count != 0) { + return 1; + } + + if (bpb.sectors_count_64 == 0) { + return 1; + } + + // this is a valid ntfs sector + return 0; +} + +int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *path) { + +} + +int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t count) { + +} From eced64231df9be68117ecabdad4bb1713454749c Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Fri, 3 Sep 2021 11:12:56 +0300 Subject: [PATCH 2/7] Added test to makefile --- Makefile | 22 ++++++++++++++++++++++ stage23/fs/ntfs.s2.c | 11 +++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d12b51c6..253ad82c 100644 --- a/Makefile +++ b/Makefile @@ -304,6 +304,28 @@ iso9660-test: xorriso -as mkisofs -b boot/limine-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table test_image/ -o test.iso qemu-system-x86_64 -net none -smp 4 -cdrom test.iso -debugcon stdio +.PHONY: ntfs-test +ntfs-test: + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-bios + $(MAKE) bin/limine-install + $(MAKE) -C test + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.ntfs `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv bin/* test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + bin/limine-install test.hdd + qemu-system-x86_64 -net none -smp 4 -hda test.hdd -debugcon stdio + .PHONY: full-hybrid-test full-hybrid-test: $(MAKE) ovmf-x64 diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index 4ce71f28..15e8ee10 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -53,9 +53,16 @@ int ntfs_check_signature(struct volume *part) { } int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *path) { - + (void)ret; + (void)part; + (void)path; + return 1; } int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t count) { - + (void)file; + (void)buf; + (void)loc; + (void)count; + return 1; } From 10caa22bbc2ae347db604b1a59d170489c6e8a18 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Fri, 3 Sep 2021 18:24:22 +0300 Subject: [PATCH 3/7] got basic mft parsing --- stage23/fs/ntfs.h | 31 +++++++ stage23/fs/ntfs.s2.c | 188 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 189 insertions(+), 30 deletions(-) diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h index 81fe5270..e1b8a8f4 100644 --- a/stage23/fs/ntfs.h +++ b/stage23/fs/ntfs.h @@ -7,8 +7,39 @@ #include #include +struct ntfs_bpb { + uint8_t jump[3]; + char oem[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t fats_count; + uint16_t directory_entries_count; + uint16_t sector_totals; + uint8_t media_descriptor_type; + uint16_t sectors_per_fat_16; + uint16_t sectors_per_track; + uint16_t heads_count; + uint32_t hidden_sectors_count; + uint32_t large_sectors_count; + + // ntfs + uint32_t sectors_per_fat_32; + uint64_t sectors_count_64; + uint64_t mft_cluster; +} __attribute__((packed)); + struct ntfs_file_handle { struct volume *part; + + struct ntfs_bpb bpb; + uint8_t mft_run_list[256]; + uint8_t root_run_list[128]; + uint8_t resident_index[256]; + uint8_t attribute_list[256]; + + uint64_t mft_offset; + uint32_t size_bytes; }; diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index 15e8ee10..a4d0ad0b 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -1,31 +1,77 @@ #include -struct ntfs_bpb { - uint8_t jump[3]; - char oem[8]; - uint16_t bytes_per_sector; - uint8_t sectors_per_cluster; - uint16_t reserved_sectors; - uint8_t fats_count; - uint16_t directory_entries_count; - uint16_t sector_totals; - uint8_t media_descriptor_type; - uint16_t sectors_per_fat_16; - uint16_t sectors_per_track; - uint16_t heads_count; - uint32_t hidden_sectors_count; - uint32_t large_sectors_count; - - // ntfs - uint32_t sectors_per_fat_32; - uint64_t sectors_count_64; - uint64_t mft_cluster; +#include + +#include + +struct mft_file_record { + char name[4]; + uint16_t update_seq_offset; + uint16_t update_seq_size; + uint64_t log_seq_number; + uint16_t sequence_number; + uint16_t hard_link_count; + uint16_t attribute_offset; + uint16_t flags; + uint32_t real_size; + uint32_t allocated_size; + uint64_t base_record_number; +} __attribute__((packed)); + +struct file_record_attr_header { + uint32_t type; + uint32_t length; + uint8_t non_res_flag; + uint8_t name_length; + uint16_t name_offset; + uint16_t flags; + uint16_t attribute_id; +} __attribute__((packed)); + +#define FR_ATTRIBUTE_LIST 0x00000020 +#define FR_ATTRIBUTE_NAME 0x00000030 +#define FR_ATTRIBUTE_VOLUME_NAME 0x00000060 +#define FR_ATTRIBUTE_DATA 0x00000080 +#define FR_ATTRIBUTE_INDEX_ROOT 0x00000090 +#define FR_ATTRIBUTE_INDEX_ALLOC 0x000000A0 + +struct file_record_attr_header_res { + struct file_record_attr_header header; + uint32_t info_length; + uint16_t info_offset; + uint16_t index_flag; +} __attribute__((packed)); + +struct file_record_attr_header_non_res { + struct file_record_attr_header header; + uint64_t first_vcn; + uint64_t last_vcn; + uint16_t run_offset; +} __attribute__((packed)); + +struct index_entry { + uint64_t mft_record; + uint16_t entry_size; + uint16_t name_offset; + uint16_t index_Flag; + uint16_t padding; + uint64_t mft_parent_record; + uint64_t creation_time; + uint64_t altered_time; + uint64_t mft_changed_time; + uint64_t read_time; + uint64_t alloc_size; + uint64_t real_size; + uint64_t file_flags; + uint8_t name_length; + uint8_t name_type; + wchar_t name[]; } __attribute__((packed)); int ntfs_check_signature(struct volume *part) { - struct ntfs_bpb bpb = { 0 }; - if (volume_read(part, &bpb, 0, sizeof(bpb))) { - return 1; + struct ntfs_bpb bpb; + if (!volume_read(part, &bpb, 0, sizeof(bpb))) { + return 0; } // @@ -33,29 +79,111 @@ int ntfs_check_signature(struct volume *part) { // if (strncmp(bpb.oem, "NTFS ", SIZEOF_ARRAY(bpb.oem))) { - return 1; + return 0; } if (bpb.sector_totals != 0) { - return 1; + return 0; } if (bpb.large_sectors_count != 0) { - return 1; + return 0; } if (bpb.sectors_count_64 == 0) { - return 1; + return 0; } // this is a valid ntfs sector - return 0; + return 1; } +/** + * 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) { + +// } + +/** + * Get an attribute from the given file record + */ +static bool ntfs_get_file_record_attr(uint8_t* file_record, uint32_t attr_type, uint8_t **out_attr) { + struct mft_file_record *fr = (struct mft_file_record *)file_record; + + // get the offset to the first attribute + 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"); + + struct file_record_attr_header *cur_attr = (struct file_record_attr_header *)cur_attr_ptr; + + if (cur_attr->type == attr_type) { + *out_attr = cur_attr_ptr; + return true; + } + + // we either found an attr with higher type or the end type + if (cur_attr->type > attr_type || cur_attr->type == 0xFF) + return false; + + if (cur_attr->length == 0) + panic("File record attribute has zero length"); + + cur_attr_ptr += cur_attr->length; + } +} + +/** + * Prepare for reading a file by reading the root directory into the file handle + */ +static void ntfs_read_root(struct ntfs_file_handle *handle) { + // calculate the offset for the mft + 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]; + if (!volume_read(handle->part, file_record_buffer, handle->mft_offset, sizeof(file_record_buffer))) + panic("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"); + + // 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"); + if ((uint8_t *)attr + attr->run_offset + 256 > file_record_buffer + sizeof(file_record_buffer)) + panic("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 +} + +// 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) { - (void)ret; - (void)part; + // 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"); + + // now prepare the root directory so we can search for + // the rest of the stuff + ntfs_read_root(ret); + + // TODO: search for the file we want (void)path; + return 1; } From 66564de7e0769f06a8dd0bb5ff12a50cc49d8284 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Tue, 28 Sep 2021 21:05:50 +0300 Subject: [PATCH 4/7] We can now get file records from the MFT --- stage23/fs/ntfs.h | 3 + stage23/fs/ntfs.s2.c | 160 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 147 insertions(+), 16 deletions(-) diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h index e1b8a8f4..dbb22d34 100644 --- a/stage23/fs/ntfs.h +++ b/stage23/fs/ntfs.h @@ -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; }; diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index a4d0ad0b..39df29a8 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -4,6 +4,10 @@ #include +// 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); From 3f37d82cdd3ec231e822588ff6376bd563070476 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Tue, 28 Sep 2021 22:07:43 +0300 Subject: [PATCH 5/7] got directory reading and more specifically root directory reading --- stage23/fs/ntfs.h | 18 +++-- stage23/fs/ntfs.s2.c | 181 +++++++++++++++++++++++++++++++++---------- 2 files changed, 152 insertions(+), 47 deletions(-) diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h index dbb22d34..6b80092e 100644 --- a/stage23/fs/ntfs.h +++ b/stage23/fs/ntfs.h @@ -33,16 +33,22 @@ struct ntfs_file_handle { struct volume *part; struct ntfs_bpb bpb; - uint8_t mft_run_list[256]; - uint8_t root_run_list[128]; - uint8_t resident_index[256]; - uint8_t attribute_list[256]; - - uint64_t mft_offset; + // file record sizes uint64_t file_record_size; uint64_t sectors_per_file_record; + // MFT info, the offset and its runlist + uint64_t mft_offset; + uint8_t mft_run_list[256]; + + // the runlist, resident index and attribute list of the + // current open file/directory + uint8_t run_list[128]; + uint8_t resident_index[256]; + uint8_t attribute_list[256]; + + // info about the current file uint32_t size_bytes; }; diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index 39df29a8..a58faa9e 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -1,5 +1,5 @@ #include - +#include #include #include @@ -102,6 +102,45 @@ int ntfs_check_signature(struct volume *part) { return 1; } +// the temp buffer is used for storing dirs and alike +// in memory, because limine only has allocate without +// free we are going to allocate it once globally and just +// make sure to only use it in the ntfs_open function... +static uint8_t *temp_buffer = NULL; +static size_t temp_buffer_size = 0; + +/** + * Get an attribute from the given file record + */ +static bool ntfs_get_file_record_attr(uint8_t* file_record, uint32_t attr_type, uint8_t **out_attr) { + struct mft_file_record *fr = (struct mft_file_record *)file_record; + + // get the offset to the first attribute + uint8_t *cur_attr_ptr = file_record + fr->attribute_offset; + + while (true) { + // 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; + + if (cur_attr->type == attr_type) { + *out_attr = cur_attr_ptr; + return true; + } + + // we either found an attr with higher type or the end type + if (cur_attr->type > attr_type || cur_attr->type == 0xFF) + return false; + + if (cur_attr->length == 0) + panic("NTFS: File record attribute has zero length"); + + cur_attr_ptr += cur_attr->length; + } +} + /** * 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 @@ -201,7 +240,7 @@ static bool ntfs_get_file_record(struct ntfs_file_handle *handle, uint64_t mft_r 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; + 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]); @@ -213,44 +252,57 @@ static bool ntfs_get_file_record(struct ntfs_file_handle *handle, uint64_t mft_r /** * Read the the directory's file record from the mft */ -static bool ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_record, uint8_t *file_record) { +static int 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 0; - return true; -} + // + // get the runlist of the directory + // -/** - * Get an attribute from the given file record - */ -static bool ntfs_get_file_record_attr(uint8_t* file_record, uint32_t attr_type, uint8_t **out_attr) { - struct mft_file_record *fr = (struct mft_file_record *)file_record; + // get the index alloc attribute, it should have the runlist offset + // copy the runlist from it to our handle for easier access + uint8_t *index_alloc_ptr; + if (!ntfs_get_file_record_attr(file_record, FR_ATTRIBUTE_INDEX_ALLOC, &index_alloc_ptr)) + panic("NTFS: Directory has no runlist?!"); + struct file_record_attr_header_non_res *index_alloc = (struct file_record_attr_header_non_res *)index_alloc_ptr; + uint8_t *runlist_ptr = index_alloc_ptr + index_alloc->run_offset; + if (runlist_ptr - file_record + 128u > handle->file_record_size) + panic("NTFS: runlist is outside of file record!"); + memcpy(handle->run_list, runlist_ptr, sizeof(handle->run_list)); - // get the offset to the first attribute - uint8_t* cur_attr_ptr = file_record + fr->attribute_offset; + // calculate the directory size by just going through the runlist + uint8_t *runlist = handle->run_list; + uint64_t dir_size = 0; + uint64_t cluster = 0; + uint64_t cluster_count = 0; + bool status = false; + do { + status = ntfs_get_next_run_list_element(&runlist, &cluster_count, &cluster, true); + if (status) + dir_size += cluster_count; + } while(status); + dir_size *= handle->bpb.sectors_per_cluster * handle->bpb.bytes_per_sector; - while (true) { - // 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; - - if (cur_attr->type == attr_type) { - *out_attr = cur_attr_ptr; - return true; + // allocate a buffer for the directory data + if (temp_buffer == NULL) { + // allocate enough just in case, idk how much is good + temp_buffer_size = dir_size > 64 * 1024 ? dir_size : 64 * 1024; + temp_buffer = ext_mem_alloc(temp_buffer_size); + } else { + // we must truncate it... + if (dir_size > temp_buffer_size) { + dir_size = temp_buffer_size; } - - // we either found an attr with higher type or the end type - if (cur_attr->type > attr_type || cur_attr->type == 0xFF) - return false; - - if (cur_attr->length == 0) - panic("NTFS: File record attribute has zero length"); - - cur_attr_ptr += cur_attr->length; } + + // read the directory + if (ntfs_read(handle, temp_buffer, 0, dir_size) != (int)dir_size) + panic("NTFS: EOF before reading directory fully..."); + + // return the size of the directory + return dir_size; } /** @@ -261,14 +313,15 @@ 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 - uint8_t file_record_buffer[handle->file_record_size]; + uint8_t file_record_buffer[MIN_FILE_RECORD_SIZE]; if (!volume_read(handle->part, file_record_buffer, handle->mft_offset, sizeof(file_record_buffer))) 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)) + uint8_t *attr_ptr = NULL; + if (!ntfs_get_file_record_attr(file_record_buffer, FR_ATTRIBUTE_DATA, &attr_ptr)) panic("NTFS: MFT file record missing DATA attribute"); + struct file_record_attr_header_non_res *attr = (struct file_record_attr_header_non_res *)attr_ptr; // verify the attr and run list are in the buffer if ((uint8_t *)attr + sizeof(*attr) > file_record_buffer + sizeof(file_record_buffer)) @@ -304,7 +357,7 @@ int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *pat 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); @@ -316,9 +369,55 @@ int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *pat } int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t count) { - (void)file; - (void)buf; - (void)loc; - (void)count; - return 1; + // get the runlist + uint8_t *runlist = file->run_list; + + // we are going to go over the runlist until we get to the offset + // once we get to the offset we are going to continue going over + // the runlist while copying bytes + uint64_t wanted_to_read = count; + uint64_t bytes_per_cluster = file->bpb.sectors_per_cluster * file->bpb.bytes_per_sector; + do { + // get the next element from the runlist + uint64_t cluster_count; + uint64_t cluster; + if (!ntfs_get_next_run_list_element(&runlist, &cluster_count, &cluster, true)) + break; + + // calculate the cont size and offset on disk + uint64_t total_cont_bytes = cluster_count * bytes_per_cluster; + uint64_t abs_byte = cluster * bytes_per_cluster; + + // check if we arrived at the wanted offset + if (loc != 0) { + if (loc >= total_cont_bytes) { + // we need to go more... + loc -= total_cont_bytes; + } else { + // we got to the offset, adjust base and size + // and set the loc to 0 + total_cont_bytes -= loc; + abs_byte += loc; + loc = 0; + } + } + + if (loc == 0) { + // get how much we wanna read now and + // subtract that from the total we need + // to read + size_t read_now = total_cont_bytes; + if (read_now > count) { + read_now = count; + } + count -= read_now; + + // read it! + if (!volume_read(file->part, buf, abs_byte, read_now)) + panic("NTFS: Runlist points to outside the volume (%x)", abs_byte); + } + } while(count); + + // return how much we read... + return wanted_to_read - count; } From 0cb083b3e4c8935f564100de9a4434b31ca97f87 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Tue, 28 Sep 2021 23:27:15 +0300 Subject: [PATCH 6/7] ntfs is now fully working, or at least we can boot limine with it --- stage23/fs/ntfs.h | 2 + stage23/fs/ntfs.s2.c | 194 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 179 insertions(+), 17 deletions(-) diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h index 6b80092e..373f92b8 100644 --- a/stage23/fs/ntfs.h +++ b/stage23/fs/ntfs.h @@ -45,6 +45,8 @@ struct ntfs_file_handle { // the runlist, resident index and attribute list of the // current open file/directory uint8_t run_list[128]; + + bool has_resident; uint8_t resident_index[256]; uint8_t attribute_list[256]; diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index a58faa9e..73d25865 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -4,10 +4,17 @@ #include +// created using documentation from: +// https://dubeyko.com/development/FileSystems/NTFS/ntfsdoc.pdf + // This is the total size of a file record, including the attributes // TODO: calculate this #define MIN_FILE_RECORD_SIZE 1024 +// The mft number is only 48bit, the other bits are used +// for something else +#define MFT_RECORD_NO_MASK (0xFFFFFFFFFFFF) + struct mft_file_record { char name[4]; uint16_t update_seq_offset; @@ -53,6 +60,21 @@ struct file_record_attr_header_non_res { uint16_t run_offset; } __attribute__((packed)); +struct index_record { + char name[4]; + uint16_t update_seq_offset; + uint16_t update_seq_size; + uint64_t log_seq_number; + uint64_t vcn; + uint32_t index_entry_offset; + uint32_t index_entry_size; + uint32_t index_entry_alloc; + uint8_t leaf_node; + uint8_t _reserved[3]; + uint16_t update_seq; + uint16_t sequence_array[]; +} __attribute__((packed)); + struct index_entry { uint64_t mft_record; uint16_t entry_size; @@ -69,7 +91,7 @@ struct index_entry { uint64_t file_flags; uint8_t name_length; uint8_t name_type; - wchar_t name[]; + uint16_t name[]; } __attribute__((packed)); int ntfs_check_signature(struct volume *part) { @@ -106,8 +128,9 @@ int ntfs_check_signature(struct volume *part) { // in memory, because limine only has allocate without // free we are going to allocate it once globally and just // make sure to only use it in the ntfs_open function... -static uint8_t *temp_buffer = NULL; -static size_t temp_buffer_size = 0; +static uint8_t *dir_buffer = NULL; +static size_t dir_buffer_size = 0; +static size_t dir_buffer_cap = 0; /** * Get an attribute from the given file record @@ -203,6 +226,9 @@ static bool ntfs_get_next_run_list_element(uint8_t **runlist, uint64_t *out_clus 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; + // make sure we only take the number itself + mft_record_no &= MFT_RECORD_NO_MASK; + // get the uint64_t count = 0; uint64_t cluster = 0; @@ -230,6 +256,7 @@ static bool ntfs_get_file_record(struct ntfs_file_handle *handle, uint64_t mft_r continue; } record_count++; + } while (record_count < mft_record_no); // we found the sector of the file record! @@ -252,10 +279,10 @@ static bool ntfs_get_file_record(struct ntfs_file_handle *handle, uint64_t mft_r /** * Read the the directory's file record from the mft */ -static int ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_record, uint8_t *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 0; + if (!ntfs_get_file_record(handle, mft_record, file_record)) + return false; // // get the runlist of the directory @@ -266,6 +293,7 @@ static int ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_rec uint8_t *index_alloc_ptr; if (!ntfs_get_file_record_attr(file_record, FR_ATTRIBUTE_INDEX_ALLOC, &index_alloc_ptr)) panic("NTFS: Directory has no runlist?!"); + struct file_record_attr_header_non_res *index_alloc = (struct file_record_attr_header_non_res *)index_alloc_ptr; uint8_t *runlist_ptr = index_alloc_ptr + index_alloc->run_offset; if (runlist_ptr - file_record + 128u > handle->file_record_size) @@ -286,23 +314,25 @@ static int ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_rec dir_size *= handle->bpb.sectors_per_cluster * handle->bpb.bytes_per_sector; // allocate a buffer for the directory data - if (temp_buffer == NULL) { + if (dir_buffer == NULL) { // allocate enough just in case, idk how much is good - temp_buffer_size = dir_size > 64 * 1024 ? dir_size : 64 * 1024; - temp_buffer = ext_mem_alloc(temp_buffer_size); + dir_buffer_cap = dir_size > 64 * 1024 ? dir_size : 64 * 1024; + dir_buffer = ext_mem_alloc(dir_buffer_cap); } else { // we must truncate it... - if (dir_size > temp_buffer_size) { - dir_size = temp_buffer_size; + if (dir_size > dir_buffer_cap) { + dir_size = dir_buffer_cap; } } + // set the size of the dir size + dir_buffer_size = dir_size; + // read the directory - if (ntfs_read(handle, temp_buffer, 0, dir_size) != (int)dir_size) + if (ntfs_read(handle, dir_buffer, 0, dir_size) != (int)dir_size) panic("NTFS: EOF before reading directory fully..."); - // return the size of the directory - return dir_size; + return true; } /** @@ -337,6 +367,85 @@ static void ntfs_read_root(struct ntfs_file_handle *handle) { panic("NTFS: Missing root directory file record!"); } +/** + * Search for a file in the ntfs directory, assumes the directory has been read and is stored in + * the temp buffer + */ +static bool ntfs_find_file_in_directory(const char* filename, struct index_entry** out_entry) { + size_t dir_size = dir_buffer_size; + uint8_t *dir_ptr = dir_buffer; + + (void)filename; + (void)out_entry; + + // TODO: iterate resident record... + + // get the size of the name we need to compare + const char* temp_filename = filename; + size_t filename_size = 0; + while (*temp_filename != '\0' && *temp_filename != '\\' && *temp_filename != '/') { + filename_size++; + temp_filename++; + } + + // iterate the non-resident files in the directory + size_t offset = 0; + while (dir_size) { + // check if the dir pointer is still in the buffer, if not then we could + // not find the file... + if (dir_ptr + sizeof(struct index_record) > dir_buffer + dir_buffer_size) + panic("NTFS: Tried to read index record outside of directory"); + + // get the index and check it, if it is not valid just return + // we did not find the file + struct index_record *index_record = (struct index_record *)dir_ptr; + if (strncmp(index_record->name, "INDX", SIZEOF_ARRAY(index_record->name))) + return false; + + // calculate the offset to the entry + size_t index_size = index_record->index_entry_size; + offset += index_record->index_entry_offset + offsetof(struct index_record, index_entry_offset); + uint8_t *entry_ptr = dir_ptr + offset; + + // loop the record for all of its indexes + while (index_size) { + // make sure we still have an entry + if (entry_ptr + sizeof(struct index_entry) > dir_buffer + dir_buffer_size) + panic("NTFS: Tried to read index entry outside of directory"); + + // get the entry, if size is zero we done + struct index_entry *entry = (struct index_entry *)entry_ptr; + if (entry->entry_size == 0) + break; + + if (filename_size == entry->name_length) { + // compare filename + for (int i = 0; i < entry->name_length; i++) { + if (filename[i] != entry->name[i]) { + goto next_entry; + } + } + + // name is good, return the entry and return true + // that we found the entry + *out_entry = entry; + return true; + } + + // next entry + next_entry: + entry_ptr += entry->entry_size; + index_size -= entry->entry_size; + } + + // next record + dir_ptr += index_record->index_entry_size; + dir_size -= index_record->index_entry_size; + } + + return false; +} + int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *path) { // save the part ret->part = part; @@ -357,15 +466,66 @@ int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *pat ret->sectors_per_file_record = 1; ret->file_record_size = ret->bpb.bytes_per_sector; } + if (ret->file_record_size != MIN_FILE_RECORD_SIZE) + panic("NTFS: TODO: support file record size which is not 1024 bytes"); // now prepare the root directory so we can search for // the rest of the stuff ntfs_read_root(ret); - // TODO: search for the file we want - (void)path; + // iterate the directories to find the entry + const char* current_path = path; + struct index_entry* entry = NULL; + for (;;) { + // skip slash + current_path++; + + // find the file in the directory + entry = NULL; + if (!ntfs_find_file_in_directory(current_path, &entry)) + return 1; - return 1; + size_t filename_len = entry->name_length; + + // check if this is the last entry + uint8_t file_record_buffer[MIN_FILE_RECORD_SIZE]; + if (*(current_path + filename_len) == '\0') { + // we found the file! + ret->size_bytes = entry->real_size; + + // get its runlist... + if (!ntfs_get_file_record(ret, entry->mft_record, file_record_buffer)) + panic("NTFS: Failed to get file record of file"); + + // get the file attribute + uint8_t *attr_ptr = NULL; + if (!ntfs_get_file_record_attr(file_record_buffer, FR_ATTRIBUTE_DATA, &attr_ptr)) + panic("NTFS: File record missing DATA attribute"); + struct file_record_attr_header_non_res *attr = (struct file_record_attr_header_non_res *)attr_ptr; + + // verify the attr and run list are in the buffer + if ((uint8_t *)attr + sizeof(*attr) > file_record_buffer + sizeof(file_record_buffer)) + panic("NTFS: File record attribute is outside of file record"); + if ((uint8_t *)attr + attr->run_offset + 256 > file_record_buffer + sizeof(file_record_buffer)) + panic("NTFS: Run list is outside of file record"); + + // save the run list + memcpy(ret->run_list, (uint8_t *)attr + attr->run_offset, sizeof(ret->run_list)); + + return 0; + + } else { + // read the directory + if (!ntfs_read_directory(ret, entry->mft_record, file_record_buffer)) + panic("NTFS: Failed to read directory"); + + // next path element + current_path += filename_len; + } + } + + // should not be able to reach here... + __builtin_unreachable(); } int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t count) { From 893020f30322e5ffb36d59371fa2bb1a3a8497c9 Mon Sep 17 00:00:00 2001 From: Itay Almog Date: Wed, 29 Sep 2021 00:27:18 +0300 Subject: [PATCH 7/7] cleanup and bufixes ntfs is weird so we can't trust the names in the index since sometimes they are wrong (?) also the read function should return 0 for success and not read size... --- stage23/fs/ntfs.h | 4 --- stage23/fs/ntfs.s2.c | 70 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/stage23/fs/ntfs.h b/stage23/fs/ntfs.h index 373f92b8..f2e713e5 100644 --- a/stage23/fs/ntfs.h +++ b/stage23/fs/ntfs.h @@ -46,10 +46,6 @@ struct ntfs_file_handle { // current open file/directory uint8_t run_list[128]; - bool has_resident; - uint8_t resident_index[256]; - uint8_t attribute_list[256]; - // info about the current file uint32_t size_bytes; }; diff --git a/stage23/fs/ntfs.s2.c b/stage23/fs/ntfs.s2.c index 73d25865..76ae7f04 100644 --- a/stage23/fs/ntfs.s2.c +++ b/stage23/fs/ntfs.s2.c @@ -60,6 +60,21 @@ struct file_record_attr_header_non_res { uint16_t run_offset; } __attribute__((packed)); +struct file_record_attr_name { + uint64_t mft_parent_record; + uint64_t creation_time; + uint64_t altered_time; + uint64_t mft_changed_time; + uint64_t read_time; + uint64_t allocated_size; + uint64_t real_size; + uint32_t flags; + uint32_t reparse; + uint8_t name_length; + uint8_t name_type; + uint16_t name[]; +} __attribute__((packed)); + struct index_record { char name[4]; uint16_t update_seq_offset; @@ -329,7 +344,7 @@ static bool ntfs_read_directory(struct ntfs_file_handle *handle, uint64_t mft_re dir_buffer_size = dir_size; // read the directory - if (ntfs_read(handle, dir_buffer, 0, dir_size) != (int)dir_size) + if (ntfs_read(handle, dir_buffer, 0, dir_size)) panic("NTFS: EOF before reading directory fully..."); return true; @@ -371,13 +386,10 @@ static void ntfs_read_root(struct ntfs_file_handle *handle) { * Search for a file in the ntfs directory, assumes the directory has been read and is stored in * the temp buffer */ -static bool ntfs_find_file_in_directory(const char* filename, struct index_entry** out_entry) { +static bool ntfs_find_file_in_directory(struct ntfs_file_handle *handle, const char* filename, struct index_entry** out_entry) { size_t dir_size = dir_buffer_size; uint8_t *dir_ptr = dir_buffer; - (void)filename; - (void)out_entry; - // TODO: iterate resident record... // get the size of the name we need to compare @@ -419,9 +431,25 @@ static bool ntfs_find_file_in_directory(const char* filename, struct index_entry break; if (filename_size == entry->name_length) { - // compare filename - for (int i = 0; i < entry->name_length; i++) { - if (filename[i] != entry->name[i]) { + // this name seem legit, need to get the real name from the mft + // sometimes it works to use the index name but sometimes it has + // invalid names for whatever reason that I can not understand, so + // just always take it from the mft file record + uint8_t file_record_buffer[MIN_FILE_RECORD_SIZE]; + if (!ntfs_get_file_record(handle, entry->mft_record, file_record_buffer)) + panic("NTFS: Failed to get file record"); + + uint8_t *name_attr = NULL; + if (!ntfs_get_file_record_attr(file_record_buffer, FR_ATTRIBUTE_NAME, &name_attr)) + panic("NTFS: File record missing name attribute"); + + // get the offset to the actual info + struct file_record_attr_header_res *header = (struct file_record_attr_header_res *)name_attr; + struct file_record_attr_name *name = (struct file_record_attr_name *)(name_attr + header->info_offset); + + // compare the name + for (int i = 0; i < name->name_length; i++) { + if (name->name[i] != filename[i]) { goto next_entry; } } @@ -438,9 +466,16 @@ static bool ntfs_find_file_in_directory(const char* filename, struct index_entry index_size -= entry->entry_size; } - // next record - dir_ptr += index_record->index_entry_size; - dir_size -= index_record->index_entry_size; + // next record, need to do some rounding + index_size = index_record->index_entry_size; + if (index_size < 0x1000) { + index_size = 0x1000; + } else { + index_size = (index_size + 0x100) & 0xffffff00; + } + + dir_ptr += index_size; + dir_size -= index_size; } return false; @@ -478,11 +513,13 @@ int ntfs_open(struct ntfs_file_handle *ret, struct volume *part, const char *pat struct index_entry* entry = NULL; for (;;) { // skip slash - current_path++; + while (*current_path == '\\' || *current_path == '/') { + current_path++; + } // find the file in the directory entry = NULL; - if (!ntfs_find_file_in_directory(current_path, &entry)) + if (!ntfs_find_file_in_directory(ret, current_path, &entry)) return 1; size_t filename_len = entry->name_length; @@ -532,10 +569,11 @@ int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t c // get the runlist uint8_t *runlist = file->run_list; + // TODO: remember the last read location so we can have faster sequential reads... + // we are going to go over the runlist until we get to the offset // once we get to the offset we are going to continue going over // the runlist while copying bytes - uint64_t wanted_to_read = count; uint64_t bytes_per_cluster = file->bpb.sectors_per_cluster * file->bpb.bytes_per_sector; do { // get the next element from the runlist @@ -578,6 +616,6 @@ int ntfs_read(struct ntfs_file_handle *file, void *buf, uint64_t loc, uint64_t c } } while(count); - // return how much we read... - return wanted_to_read - count; + // if we didn't read it all then we got a problem + return count != 0; }