We can now get file records from the MFT
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 <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);
