From 2d2460617026e161394c72877f08417d2b35c0dc Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Tue, 31 Aug 2021 18:43:11 +0200 Subject: [PATCH 1/9] Added the new "timeline" option --- include/drat/print-fs-records.c | 26 +- include/drat/print-fs-records.h | 1 + include/drat/string/j.c | 43 ++ include/drat/string/j.h | 3 + src/commands.h | 2 + src/commands/timeline.c | 703 ++++++++++++++++++++++++++++++++ 6 files changed, 777 insertions(+), 1 deletion(-) create mode 100644 src/commands/timeline.c diff --git a/include/drat/print-fs-records.c b/include/drat/print-fs-records.c index 13db146..0a1f6eb 100644 --- a/include/drat/print-fs-records.c +++ b/include/drat/print-fs-records.c @@ -14,6 +14,27 @@ #include // nx_block_size #include // drec_val_to_short_type_string() +char *get_file_name(j_rec_t** file_record) { + j_rec_t* fs_rec = *file_record; // Only one record gets passed + j_key_t* hdr = fs_rec->data; + j_inode_val_t* val = fs_rec->data + fs_rec->key_len; + switch ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) { + case APFS_TYPE_INODE: { + char* itemname = get_item_name(val->xfields, fs_rec->val_len); + if (itemname == NULL) { + printf("No Xfields found\n"); + return NULL; + } + return itemname; + } break; + default: { + fprintf(stderr, "Not an inode...\n"); + return NULL; + } + } + return NULL; +} + void print_fs_records(j_rec_t** fs_records) { size_t num_records = 0; @@ -49,6 +70,7 @@ void print_fs_records(j_rec_t** fs_records) { j_inode_key_t* key = fs_rec->data; j_inode_val_t* val = fs_rec->data + fs_rec->key_len; fprintf(stderr, "INODE"); + print_j_inode_info(val, fs_rec->val_len); } break; case APFS_TYPE_XATTR: { j_xattr_key_t* key = fs_rec->data; @@ -101,7 +123,9 @@ void print_fs_records(j_rec_t** fs_records) { } break; case APFS_TYPE_DIR_REC: { // Apple's spec inorrectly says to use `j_drec_key_t`; see NOTE in - j_drec_hashed_key_t* key = fs_rec->data; + // On iOS 12 dmgs keys are of j_drec_key_t type + //j_drec_hashed_key_t* key = fs_rec->data; + j_drec_key_t* key = fs_rec->data; j_drec_val_t* val = fs_rec->data + fs_rec->key_len; fprintf(stderr, "DIR REC" diff --git a/include/drat/print-fs-records.h b/include/drat/print-fs-records.h index e9a29e0..d9963ec 100644 --- a/include/drat/print-fs-records.h +++ b/include/drat/print-fs-records.h @@ -3,6 +3,7 @@ #include +char* get_file_name(j_rec_t** file_record); void print_fs_records(j_rec_t** fs_records); #endif // DRAT_PRINT_FS_RECORDS_H diff --git a/include/drat/string/j.c b/include/drat/string/j.c index 3289fc4..4128b5e 100644 --- a/include/drat/string/j.c +++ b/include/drat/string/j.c @@ -62,6 +62,28 @@ static void print_xf_details(bool has_xfields, xf_blob_t* xfields) { } } +char* get_fn_record(xf_blob_t* xfields) { + xf_pair_t** xf_pairs_array = get_xf_pairs_array(xfields); + if (!xf_pairs_array) { + fprintf(stderr, "\nERROR: %s: Call to `get_xf_pairs_array()` failed.\n", __func__); + } else { + for (xf_pair_t** cursor = xf_pairs_array; *cursor; cursor++) { + xf_pair_t* xf_pair = *cursor; + void* value = &(xf_pair->value); + switch(xf_pair->key.x_type) { + case INO_EXT_TYPE_NAME: { + char* name = value; + return name; + } break; + default: { + printf("Got xfield of type: %d\n", xf_pair->key.x_type); + } break; + } + } + } + return NULL; +} + char* j_key_type_to_string(uint8_t j_key_type) { switch (j_key_type) { case APFS_TYPE_SNAP_METADATA: return "Snapshot metadata"; @@ -152,6 +174,27 @@ char* get_j_inode_bsd_flags_string(uint32_t bsd_flags) { return get_flags_enum_string(flags, ARRAY_SIZE(flags), bsd_flags, false); } +void print_j_inode_info(j_inode_val_t* val, uint16_t val_len) { + printf("\n"); + printf("Creation time: %s", apfs_timestamp_to_string(val->create_time)); + printf("Last modification time: %s", apfs_timestamp_to_string(val->mod_time)); + printf("Last access time: %s", apfs_timestamp_to_string(val->change_time)); + printf("\n"); + print_xf_details(val_len != sizeof(j_inode_val_t), val->xfields); +} + +char* get_item_name(uint8_t *xfields, uint16_t val_len) { + if (val_len == sizeof(j_inode_val_t)) { + printf("Len is the same, no Xfields\n"); + return NULL; + } + return get_fn_record(xfields); +} + +char *get_j_inode_type(j_inode_val_t* val) { + return j_inode_mode_to_string(val->mode); +} + void print_j_inode_val(j_inode_val_t* val, uint16_t val_len) { printf("Parent ID: 0x%" PRIx64 "\n", val->parent_id); printf("Private ID: 0x%" PRIx64 "\n", val->private_id); diff --git a/include/drat/string/j.h b/include/drat/string/j.h index 93f5550..46024f0 100644 --- a/include/drat/string/j.h +++ b/include/drat/string/j.h @@ -11,10 +11,13 @@ char* j_key_type_to_string(uint8_t j_key_type); void print_j_key(j_key_t* key); void print_j_inode_key(j_inode_key_t* key); +char* get_j_inode_type(j_inode_val_t* val); char* j_inode_mode_to_string(apfs_mode_t mode); char* get_j_inode_internal_flags_string(uint64_t internal_flags); char* get_j_inode_bsd_flags_string(uint32_t bsd_flags); +char* get_item_name(uint8_t *xfields, uint16_t val_len); +void print_j_inode_info(j_inode_val_t* val, uint16_t val_len); void print_j_inode_val(j_inode_val_t* val, uint16_t val_len); void print_j_file_extent_key(j_file_extent_key_t* key); void print_j_file_extent_val(j_file_extent_val_t* val); diff --git a/src/commands.h b/src/commands.h index 58c3767..71843e9 100644 --- a/src/commands.h +++ b/src/commands.h @@ -23,6 +23,7 @@ command_function cmd_explore_omap_tree; command_function cmd_inspect; command_function cmd_list_raw; command_function cmd_list; +command_function cmd_timeline; command_function cmd_modify; command_function cmd_read; command_function cmd_recover_raw; @@ -38,6 +39,7 @@ static drat_command_t drat_commands[] = { { "inspect" , cmd_inspect , "Inspect APFS partition" }, { "list-raw" , cmd_list_raw , "List directory contents or file info based on its filesystem OID" }, { "list" , cmd_list , "List directory contents or file info based on its filepath" }, + { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, { "read" , cmd_read , "Read a block and display information about it" }, { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, diff --git a/src/commands/timeline.c b/src/commands/timeline.c new file mode 100644 index 0000000..b1072cf --- /dev/null +++ b/src/commands/timeline.c @@ -0,0 +1,703 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct file_metadata { + char *filename; + uint64_t create_time; + uint64_t mod_time; + uint64_t change_time; + uint64_t access_time; + cp_key_class_t default_protection_class; + // cprotect xattr ? +} file_metadata; + +static int fs_file_index = 0; + +int compare_create(file_metadata *first, file_metadata *second) { + struct file_metadata *ptr_to_first = *(file_metadata **)first; + struct file_metadata *ptr_to_second = *(file_metadata **)second; + return ptr_to_first->create_time - ptr_to_second->create_time; +} + +int compare_mod(file_metadata *first, file_metadata *second) { + struct file_metadata *ptr_to_first = *(file_metadata **)first; + struct file_metadata *ptr_to_second = *(file_metadata **)second; + return ptr_to_first->mod_time - ptr_to_second->mod_time; +} + +int compare_change(file_metadata *first, file_metadata *second) { + struct file_metadata *ptr_to_first = *(file_metadata **)first; + struct file_metadata *ptr_to_second = *(file_metadata **)second; + return ptr_to_first->change_time - ptr_to_second->change_time; +} + +int compare_access(file_metadata *first, file_metadata *second) { + struct file_metadata *ptr_to_first = *(file_metadata **)first; + struct file_metadata *ptr_to_second = *(file_metadata **)second; + return ptr_to_first->access_time - ptr_to_second->access_time; +} + +const char* nth_strchr(const char* s, char c, int n) { + int c_count; + char* nth_ptr; + for (c_count=1,nth_ptr=strchr(s,c); nth_ptr != NULL && c_count < n && c!=0; c_count++) { + nth_ptr = strchr(nth_ptr+1, c); + } + return nth_ptr; +} + +// /private/var/etc -> /var/etc -> /etc -> null +char *getRemainingUrl(char *url, char token) { + const char *position_ptr = nth_strchr(url, token, 2); + int position = (position_ptr == NULL ? -1 : position_ptr - url); + + if (position > 0) { + char *newurl = malloc(strlen(url) - position); + strncpy(newurl, url+position, strlen(url) - position + 1); + return newurl; + } + return NULL; +} + +// /private/var/etc -> private +char *getFirstEntry(char *url, char token) { + char *left_position_ptr = strchr(url, token); + if (left_position_ptr == NULL) { + return NULL; + } + + int left_position = left_position_ptr - url; + const char *right_position_ptr = nth_strchr(url, token, 2); + if (right_position_ptr == NULL) { + char *entry = malloc(strlen(url)); + strncpy(entry, url + left_position + 1, strlen(url)); + return entry; + } + int right_position = right_position_ptr - url; + char *entry = malloc(right_position - left_position - 1); + strncpy(entry, url+left_position+1, right_position - left_position - 1); + return entry; +} + + +void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, struct file_metadata **fs_files, char *path, char *full_path) +{ + if (fs_records == NULL || fs_records[0] == NULL) { + return; + } + int res = -1; + char *entry = NULL; + char *rest = NULL; + if (path != NULL) { + entry = getFirstEntry(path, '/'); + rest = getRemainingUrl(path, '/'); + } + for (j_rec_t **fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) // for each directory entry... + { + j_rec_t *fs_rec = *fs_rec_cursor; + j_key_t *hdr = fs_rec->data; + if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) + { + j_drec_key_t *key = fs_rec->data; + if (path != NULL) { + res = strcmp((char *)key->name, entry); + } + if (res == 0 || path == NULL || strcmp(path, "/") == 0) { + printf("Analysing record: %s\n", (char *)key->name); + // diff between found index and start index + signed int matching_record_index = fs_rec_cursor - fs_records; + // Get the file ID of the matching record's target + j_rec_t *fs_rec = fs_records[matching_record_index]; + j_drec_val_t *val = fs_rec->data + fs_rec->key_len; + + // Get the records for the target + oid_t dir_oid = val->file_id; + char *new_full_path = malloc(strlen(full_path) + strlen((char *)key->name) + 2); + strncpy(new_full_path, full_path, strlen(full_path)); + strncpy(new_full_path + strlen(full_path), "/", 1); + strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); + //printf("Entering absolute directory: %s\n", new_full_path); + parse_fs(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, fs_files, rest, new_full_path); + } + } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE){ + oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; + j_rec_t **file_record = get_fs_records(fs_omap_btree, fs_root_btree, file_oid, (xid_t)(~0)); + print_fs_records(file_record); + // get the inode value records + j_rec_t* fs_rec = *file_record; + j_key_t* hdr = fs_rec->data; + j_inode_val_t* val = fs_rec->data + fs_rec->key_len; + printf("Inode record type: %s\n", get_j_inode_type(val)); + char *fn = get_file_name(file_record); + bool is_reg_file = strcmp(get_j_inode_type(val), "Regular file") == 0; + if (fn && is_reg_file) { + struct file_metadata *fs_file = malloc(sizeof(struct file_metadata)); + fs_file->filename = full_path; + fs_file->create_time = val->create_time; + fs_file->mod_time = val->mod_time; + fs_file->change_time = val->change_time; + fs_file->access_time = val->access_time; + fs_file->default_protection_class = val->default_protection_class; + fs_files[fs_file_index++] = fs_file; + } + } else { + continue; + } + } + free_j_rec_array(fs_records); +} + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char **argv) +{ + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2 0 /Users/john/Documents access\n" + "Available timestamps: modify, access, change, birth\n", + argv[0], + argv[0]); +} + +int cmd_timeline(int argc, char **argv) +{ + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 5) + { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + + nx_path = argv[1]; + char* timestamp = argv[4]; + + uint32_t volume_id; + bool parse_success = sscanf(argv[2], "%u", &volume_id); + if (!parse_success) + { + fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); + print_usage(argc, argv); + return 1; + } + + char *path_stack = argv[3]; + + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) + { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); + + // Using `nx_superblock_t*`, but allocating a whole block of memory. + // This way, we can read the entire block and validate its checksum, + // but still have direct access to the fields in `nx_superblock_t` + // without needing to epxlicitly cast to that datatype. + nx_superblock_t *nxsb = malloc(nx_block_size); + if (!nxsb) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) + { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ... "); + if (!is_cksum_valid(nxsb)) + { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + fprintf(stderr, "OK.\n"); + + if (!is_nx_superblock(nxsb)) + { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) + { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + + fprintf(stderr, "Locating the checkpoint descriptor area:\n"); + + uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); + + char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); + if (!xp_desc) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); + return -1; + } + + if (nxsb->nx_xp_desc_blocks >> 31) + { + fprintf(stderr, "- It is not contiguous.\n"); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous + return 0; + } + else + { + fprintf(stderr, "- It is contiguous.\n"); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + + fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); + if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) + { + fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); + + uint32_t i_latest_nx = 0; + xid_t xid_latest_nx = 0; + + xid_t max_xid = ~0; // `~0` is the highest possible XID + + for (uint32_t i = 0; i < xp_desc_blocks; i++) + { + if (!is_cksum_valid(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + + if (is_nx_superblock(xp_desc[i])) + { + if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) + { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + + if ( + (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid > xid_latest_nx) && (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid <= max_xid)) + { + i_latest_nx = i; + xid_latest_nx = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; + } + } + else if (!is_checkpoint_map_phys(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + + if (xid_latest_nx == 0) + { + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); + return 0; + } + + // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` + // anymore; replace that data with the latest NXSB. + // This also lets us avoid repeatedly casting to `nx_superblock_t*`. + memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); + + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); + + // Copy the contents of the checkpoint we are currently considering to its + // own array for easy access. The checkpoint descriptor area is a ring + // buffer stored as an array, so doing this also allows us to handle the + // case where the checkpoint we're considering wraps around the ring buffer. + fprintf(stderr, "Loading the corresponding checkpoint ... "); + + // The array `xp` will comprise the blocks in the checkpoint, in order. + char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); + if (!xp) + { + fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); + return -1; + } + + if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) + { + // The simple case: the checkpoint is already contiguous in `xp_desc`. + memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); + } + else + { + // The case where the checkpoint wraps around from the end of the + // checkpoint descriptor area to the start. + uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; + uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; + memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); + memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); + } + fprintf(stderr, "OK.\n"); + + // We could `free(xp_desc)` at this point, but instead, we retain our copy + // of the checkpoint descriptor area in case any of the Ephemeral objects + // referenced by the current checkpoint are malformed; then, we can + // retrieve an older checkpoint without having to read the checkpoint + // descriptor area again. + + uint32_t xp_obj_len = 0; // This variable will equal the number of + // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) + { + if (is_checkpoint_map_phys(xp[i])) + { + xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; + } + } + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + + fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); + char(*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); + if (!xp_obj) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); + return -1; + } + uint32_t num_read = 0; + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) + { + if (is_checkpoint_map_phys(xp[i])) + { + checkpoint_map_phys_t *xp_map = xp[i]; // Avoid lots of casting + for (uint32_t j = 0; j < xp_map->cpm_count; j++) + { + if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); + return -1; + } + num_read++; + } + } + } + fprintf(stderr, "OK.\n"); + assert(num_read = xp_obj_len); + + fprintf(stderr, "Validating the Ephemeral objects ... "); + for (uint32_t i = 0; i < xp_obj_len; i++) + { + if (!is_cksum_valid(xp_obj[i])) + { + fprintf(stderr, "FAILED.\n"); + fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + } + fprintf(stderr, "OK.\n"); + + free(xp); + free(xp_desc); + + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); + + fprintf(stderr, "Loading the container object map ... "); + omap_phys_t *nx_omap = malloc(nx_block_size); + if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the container object map ... "); + if (!is_cksum_valid(nx_omap)) + { + fprintf(stderr, "FAILED.\n"); + return 0; + } + fprintf(stderr, "OK.\n"); + + if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return 0; + } + + fprintf(stderr, "Reading the root node of the container object map B-tree ... "); + btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); + if (!nx_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); + return -1; + } + if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the container object map B-tree ... "); + if (!is_cksum_valid(nx_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + uint32_t num_file_systems = 0; + for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) + { + if (nxsb->nx_fs_oid[i] == 0) + { + break; + } + num_file_systems++; + } + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Reading the APFS volume superblocks ... "); + char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); + if (!apsbs) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return -1; + } + for (uint32_t i = 0; i < num_file_systems; i++) + { + omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + if (!fs_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + return -1; + } + if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the APFS volume superblocks ... "); + for (uint32_t i = 0; i < num_file_systems; i++) + { + if (!is_cksum_valid(apsbs + i)) + { + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + + if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) + { + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "\n Volume list\n================\n"); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); + } + + if (volume_id >= num_file_systems) + { + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); + return 0; + } + apfs_superblock_t *apsb = apsbs + volume_id; + + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + + fprintf(stderr, "Reading the volume object map ... "); + omap_phys_t *fs_omap = malloc(nx_block_size); + if (!fs_omap) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return -1; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) + { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return 0; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return 0; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return -1; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return -1; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t *fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return -1; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return -1; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) + { + fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); + return 0; + } + fprintf(stderr, "OK.\n"); + + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) + { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); + + struct file_metadata **fs_files = malloc(sizeof(struct file_metadata *) * apsb->apfs_num_files); + printf("Entering directory /\n"); + char *path_builder = ""; + parse_fs(fs_records, fs_omap_btree, fs_root_btree, fs_files, path, path_builder); + printf("Sorting %d elements by timestamp...\n", fs_file_index); + int (*comparePtr)(file_metadata *, file_metadata *); + if (strcmp(timestamp, "modify")) { + comparePtr = &compare_mod; + } else if (strcmp(timestamp, "access")) { + comparePtr = &compare_access; + } else if (strcmp(timestamp, "change")) { + comparePtr = &compare_change; + } else if (strcmp(timestamp, "birth")) { + comparePtr = &compare_create; + } else { + fprintf(stderr, "Wrong timestamp requested\n"); + } + qsort(fs_files, fs_file_index, sizeof(file_metadata *), comparePtr); + printf("======================================================================\n"); + printf("Timeline:\n"); + printf("======================================================================\n"); + for (int i = 0; i < fs_file_index; i++) { + printf("Filename: %s\n", fs_files[i]->filename); + printf("%s timestamp: %s\n", timestamp, apfs_timestamp_to_string(fs_files[i]->create_time)); + printf("\n"); + } + + free(fs_omap_btree); + free(fs_omap); + free(apsbs); + free(nx_omap_btree); + free(nx_omap); + free(xp_obj); + free(nxsb); + fclose(nx); + fprintf(stderr, "END: All done.\n"); + return 0; +} From 230ceaa9592647fa843a8a65b9aa6db11b811c0f Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Wed, 1 Sep 2021 00:26:18 +0200 Subject: [PATCH 2/9] Added rollback option --- include/drat/utilities.h | 3 + src/commands.h | 2 + src/commands/rollback.c | 683 +++++++++++++++++++++++++++++++++++++++ src/commands/timeline.c | 6 +- 4 files changed, 692 insertions(+), 2 deletions(-) create mode 100644 include/drat/utilities.h create mode 100644 src/commands/rollback.c diff --git a/include/drat/utilities.h b/include/drat/utilities.h new file mode 100644 index 0000000..d2f6f89 --- /dev/null +++ b/include/drat/utilities.h @@ -0,0 +1,3 @@ +const char* nth_strchr(const char* s, char c, int n); +char *getRemainingUrl(char *url, char token); +char *getFirstEntry(char *url, char token); \ No newline at end of file diff --git a/src/commands.h b/src/commands.h index 71843e9..7cac690 100644 --- a/src/commands.h +++ b/src/commands.h @@ -24,6 +24,7 @@ command_function cmd_inspect; command_function cmd_list_raw; command_function cmd_list; command_function cmd_timeline; +command_function cmd_rollback; command_function cmd_modify; command_function cmd_read; command_function cmd_recover_raw; @@ -40,6 +41,7 @@ static drat_command_t drat_commands[] = { { "list-raw" , cmd_list_raw , "List directory contents or file info based on its filesystem OID" }, { "list" , cmd_list , "List directory contents or file info based on its filepath" }, { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, + { "rollback" , cmd_rollback , "Print file contents in an older filesystem state" }, // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, { "read" , cmd_read , "Read a block and display information about it" }, { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, diff --git a/src/commands/rollback.c b/src/commands/rollback.c new file mode 100644 index 0000000..78383d6 --- /dev/null +++ b/src/commands/rollback.c @@ -0,0 +1,683 @@ +// Get all the filenames from a previous xid with their data stream +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char **argv) +{ + fprintf( + argc == 1 ? stdout : stderr, + "Usage: %s \n" + "Example: %s /dev/disk0s2 0 /Users/john/Documents/file.txt 1\n", + argv[0], + argv[0]); +} + +int cmd_rollback(int argc, char **argv) +{ + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 5) + { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + int rollbacks = atoi(argv[4]); + if (rollbacks < 0) { + fprintf(stderr, "Rollbacks must be positive integers.\n"); + return 1; + } + nx_path = argv[1]; + + uint32_t volume_id; + bool parse_success = sscanf(argv[2], "%u", &volume_id); + if (!parse_success) + { + fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); + print_usage(argc, argv); + return 1; + } + + char *path_stack = argv[3]; + + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) + { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); + + // Using `nx_superblock_t*`, but allocating a whole block of memory. + // This way, we can read the entire block and validate its checksum, + // but still have direct access to the fields in `nx_superblock_t` + // without needing to epxlicitly cast to that datatype. + nx_superblock_t *nxsb = malloc(nx_block_size); + if (!nxsb) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) + { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ... "); + if (!is_cksum_valid(nxsb)) + { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + fprintf(stderr, "OK.\n"); + + if (!is_nx_superblock(nxsb)) + { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) + { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + + fprintf(stderr, "Locating the checkpoint descriptor area:\n"); + + uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); + + char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); + if (!xp_desc) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); + return -1; + } + + if (nxsb->nx_xp_desc_blocks >> 31) + { + fprintf(stderr, "- It is not contiguous.\n"); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous + return 0; + } + else + { + fprintf(stderr, "- It is contiguous.\n"); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + + fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); + if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) + { + fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); + + uint32_t i_target_nx = 0; + xid_t xid_latest_nx = 0; + xid_t nx_xids[xp_desc_blocks]; + + xid_t max_xid = ~0; // `~0` is the highest possible XID + + for (uint32_t i = 0; i < xp_desc_blocks; i++) + { + if (!is_cksum_valid(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + if (is_nx_superblock(xp_desc[i])) + { + if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) + { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + //printf("Found nxsb block at index %d with XID: %" PRIu64 "\n", i, ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid); + if ( + (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid > xid_latest_nx) && (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid <= max_xid)) + { + xid_latest_nx = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; + } + } + else if (!is_checkpoint_map_phys(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + + if (xid_latest_nx == 0) + { + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); + return 0; + } + xid_t target_xid = xid_latest_nx - rollbacks; + // Do it again... + for (uint32_t i = 0; i < xp_desc_blocks; i++) + { + if (!is_cksum_valid(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + if (is_nx_superblock(xp_desc[i])) + { + if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) + { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + + if (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid == target_xid) + { + i_target_nx = i; + } + } + else if (!is_checkpoint_map_phys(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + if (i_target_nx == 0) { + fprintf(stderr, "Could not find the requested rollback, it has probably been overwritten, try with another one\n"); + return 1; + } + printf("Found target XID at index %d\n", i_target_nx); + + // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` + // anymore; replace that data with the latest NXSB. + // This also lets us avoid repeatedly casting to `nx_superblock_t*`. + memcpy(nxsb, xp_desc[i_target_nx], sizeof(nx_superblock_t)); + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_target_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); + + // Copy the contents of the checkpoint we are currently considering to its + // own array for easy access. The checkpoint descriptor area is a ring + // buffer stored as an array, so doing this also allows us to handle the + // case where the checkpoint we're considering wraps around the ring buffer. + fprintf(stderr, "Loading the corresponding checkpoint ... "); + + // The array `xp` will comprise the blocks in the checkpoint, in order. + char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); + if (!xp) + { + fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); + return -1; + } + + if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) + { + // The simple case: the checkpoint is already contiguous in `xp_desc`. + memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); + } + else + { + // The case where the checkpoint wraps around from the end of the + // checkpoint descriptor area to the start. + uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; + uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; + memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); + memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); + } + fprintf(stderr, "OK.\n"); + + // We could `free(xp_desc)` at this point, but instead, we retain our copy + // of the checkpoint descriptor area in case any of the Ephemeral objects + // referenced by the current checkpoint are malformed; then, we can + // retrieve an older checkpoint without having to read the checkpoint + // descriptor area again. + + uint32_t xp_obj_len = 0; // This variable will equal the number of + // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) + { + if (is_checkpoint_map_phys(xp[i])) + { + xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; + } + } + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + + fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); + char(*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); + if (!xp_obj) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); + return -1; + } + uint32_t num_read = 0; + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) + { + if (is_checkpoint_map_phys(xp[i])) + { + checkpoint_map_phys_t *xp_map = xp[i]; // Avoid lots of casting + for (uint32_t j = 0; j < xp_map->cpm_count; j++) + { + if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); + return -1; + } + num_read++; + } + } + } + fprintf(stderr, "OK.\n"); + assert(num_read = xp_obj_len); + + fprintf(stderr, "Validating the Ephemeral objects ... "); + for (uint32_t i = 0; i < xp_obj_len; i++) + { + if (!is_cksum_valid(xp_obj[i])) + { + fprintf(stderr, "FAILED.\n"); + fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + } + fprintf(stderr, "OK.\n"); + + free(xp); + free(xp_desc); + + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); + + fprintf(stderr, "Loading the container object map ... "); + omap_phys_t *nx_omap = malloc(nx_block_size); + if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the container object map ... "); + if (!is_cksum_valid(nx_omap)) + { + fprintf(stderr, "FAILED.\n"); + return 0; + } + fprintf(stderr, "OK.\n"); + + if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return 0; + } + + fprintf(stderr, "Reading the root node of the container object map B-tree ... "); + btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); + if (!nx_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); + return -1; + } + if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the container object map B-tree ... "); + if (!is_cksum_valid(nx_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + uint32_t num_file_systems = 0; + for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) + { + if (nxsb->nx_fs_oid[i] == 0) + { + break; + } + num_file_systems++; + } + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Reading the APFS volume superblocks ... "); + char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); + if (!apsbs) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return -1; + } + for (uint32_t i = 0; i < num_file_systems; i++) + { + omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + if (!fs_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + return -1; + } + if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the APFS volume superblocks ... "); + for (uint32_t i = 0; i < num_file_systems; i++) + { + if (!is_cksum_valid(apsbs + i)) + { + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + + if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) + { + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return 0; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "\n Volume list\n================\n"); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); + } + + if (volume_id >= num_file_systems) + { + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); + return 0; + } + apfs_superblock_t *apsb = apsbs + volume_id; + + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + + fprintf(stderr, "Reading the volume object map ... "); + omap_phys_t *fs_omap = malloc(nx_block_size); + if (!fs_omap) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return -1; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) + { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return 0; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return 0; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return -1; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return -1; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t *fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return -1; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return -1; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) + { + fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); + return 0; + } + fprintf(stderr, "OK.\n"); + + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) + { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); + + char* path_element; + while ((path_element = strsep(&path, "/")) != NULL ) { + // If path element is empty string, skip it + if (*path_element == '\0') { + continue; + } + + signed int matching_record_index = -1; + for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC ) { + j_drec_key_t* key = fs_rec->data; // hashed_key when?? + if (strcmp((char*)key->name, path_element) == 0) { + matching_record_index = fs_rec_cursor - fs_records; + break; + } + } + } + + if (matching_record_index == -1) { + // No match + fprintf(stderr, "Could not find a dentry for that path. Exiting.\n"); + return -1; + } + + // Get the file ID of the matching record's target + j_rec_t* fs_rec = fs_records[matching_record_index]; + j_drec_val_t* val = fs_rec->data + fs_rec->key_len; + + // Get the records for the target + fs_oid = val->file_id; + free_j_rec_array(fs_records); + fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); + } + + fprintf(stderr, "\nRecords for file-system object %#" PRIx64 " -- `%s` --\n", fs_oid, path_stack); + // `fs_records` now contains the records for the item at the specified path + print_fs_records(fs_records); + + // Output content from all matching file extents + char* buffer = malloc(nx_block_size); + if (!buffer) { + fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); + return -1; + } + + // Get file size + uint64_t file_size = 0; + for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + printf("Looping\n"); + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE ) { + j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; + file_size = get_file_size(inode, fs_rec->val_len); + printf("File size: %" PRIu64 "\n", file_size); + } + } + if (file_size == 0) { + // Not a file, or file size couldn't be found; abort. + exit(-1); + } + + // Keep track of how many more bytes we need to output + uint64_t bytes_remaining = file_size; + + bool found_file_extent = false; + // Go through all the fs records + for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + + // Go through all the extent records + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { + found_file_extent = true; + j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; + + // Output the content from this particular file extent + uint64_t block_addr = val->phys_block_num; + + uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; + for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { + if (read_blocks(buffer, block_addr, 1) != 1) { + fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); + return -1; + } + + uint64_t bytes_to_write = nx_block_size; + if (bytes_remaining < nx_block_size) { + bytes_to_write = bytes_remaining; + } + if (fwrite(buffer, bytes_to_write, 1, stdout) != 1) { + fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); + return -1; + } + bytes_remaining -= bytes_to_write; + + if (bytes_remaining == 0) { + break; // Exit extent loop + } + } + } + + if (bytes_remaining == 0) { + break; // Exit fs record loop + } + } + if (!found_file_extent) { + fprintf(stderr, "Could not find any file extents for the specified path.\n"); + } + + free_j_rec_array(fs_records); + + // TODO: RESUME HERE + + free(fs_omap_btree); + free(fs_omap); + + // Closing statements; de-allocate all memory, close all file descriptors. + free(apsbs); + free(nx_omap_btree); + free(nx_omap); + free(xp_obj); + free(nxsb); + fclose(nx); + fprintf(stderr, "END: All done.\n"); + return 0; +} diff --git a/src/commands/timeline.c b/src/commands/timeline.c index b1072cf..b7881c3 100644 --- a/src/commands/timeline.c +++ b/src/commands/timeline.c @@ -28,6 +28,7 @@ #include #include #include +#include typedef struct file_metadata { char *filename; @@ -142,7 +143,8 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node oid_t dir_oid = val->file_id; char *new_full_path = malloc(strlen(full_path) + strlen((char *)key->name) + 2); strncpy(new_full_path, full_path, strlen(full_path)); - strncpy(new_full_path + strlen(full_path), "/", 1); + const char* separator = "/"; + strncpy(new_full_path + strlen(full_path), separator, 1); strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); //printf("Entering absolute directory: %s\n", new_full_path); parse_fs(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, fs_files, rest, new_full_path); @@ -183,7 +185,7 @@ static void print_usage(int argc, char **argv) fprintf( argc == 1 ? stdout : stderr, - "Usage: %s \n" + "Usage: %s \n" "Example: %s /dev/disk0s2 0 /Users/john/Documents access\n" "Available timestamps: modify, access, change, birth\n", argv[0], From e0779ada7714335c8268a40c330a2bf7fa5eaf06 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Wed, 1 Sep 2021 11:31:25 +0200 Subject: [PATCH 3/9] Function renaming --- include/drat/print-fs-records.c | 2 +- include/drat/utilities.h | 4 +- out.txt | 104 ++++++++++++++++++++++++++++++++ src/commands/rollback.c | 3 +- src/commands/timeline.c | 18 +++--- 5 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 out.txt diff --git a/include/drat/print-fs-records.c b/include/drat/print-fs-records.c index 0a1f6eb..c1c017d 100644 --- a/include/drat/print-fs-records.c +++ b/include/drat/print-fs-records.c @@ -127,7 +127,7 @@ void print_fs_records(j_rec_t** fs_records) { //j_drec_hashed_key_t* key = fs_rec->data; j_drec_key_t* key = fs_rec->data; j_drec_val_t* val = fs_rec->data + fs_rec->key_len; - + fprintf(stderr, "DIR REC" " || %s" " || target ID = %#8" PRIx64 diff --git a/include/drat/utilities.h b/include/drat/utilities.h index d2f6f89..d672c39 100644 --- a/include/drat/utilities.h +++ b/include/drat/utilities.h @@ -1,3 +1,3 @@ const char* nth_strchr(const char* s, char c, int n); -char *getRemainingUrl(char *url, char token); -char *getFirstEntry(char *url, char token); \ No newline at end of file +char *get_remaining_url(char *url, char token); +char *get_first_entry(char *url, char token); \ No newline at end of file diff --git a/out.txt b/out.txt new file mode 100644 index 0000000..7cfb092 --- /dev/null +++ b/out.txt @@ -0,0 +1,104 @@ +Opening file at `../apfs.dmg` in read-only mode ... OK. +Simulating a mount of the APFS container. +Validating checksum of block 0x0 ... OK. +Locating the checkpoint descriptor area: +- Its length is 48 blocks. +- It is contiguous. +- The address of its first block is 0xb43d. +Loading the checkpoint descriptor area into memory ... OK. +Locating the most recent well-formed container superblock in the checkpoint descriptor area: +- It lies at index 3 within the checkpoint descriptor area. +- The corresponding checkpoint starts at index 2 within the checkpoint descriptor area, and spans 2 blocks. + +Loading the corresponding checkpoint ... OK. +- There are 5 checkpoint-mappings in this checkpoint. + +Reading the Ephemeral objects used by this checkpoint ... OK. +Validating the Ephemeral objects ... OK. +The container superblock states that the container object map has Physical OID 0x1ae. +Loading the container object map ... OK. +Validating the container object map ... OK. +Reading the root node of the container object map B-tree ... OK. +Validating the root node of the container object map B-tree ... OK. +The container superblock lists 1 APFS volumes, whose superblocks have the following Virtual OIDs: +- 0x402 + +Reading the APFS volume superblocks ... OK. +Validating the APFS volume superblocks ... OK. + + Volume list +================ + 0: Peace16A366.D10D101D20D201OS +The volume object map has Physical OID 0x1a6. +Reading the volume object map ... OK. +Validating the volume object map ... OK. +Reading the root node of the volume object map B-tree ... OK. +Validating the root node of the volume object map B-tree ... OK. +The file-system tree root for this volume has Virtual OID 0xa7c5. +Looking up this Virtual OID in the volume object map ... corresponding block address is 0x19e. +Reading ... validating ... OK. +Entering directory / +- INODE +Creation time: Wed Aug 29 09:12:17 2018 +Last modification time: Sat Aug 28 15:54:39 2021 +Last access time: Sat Aug 28 15:54:39 2021 + +Number of extended fields: 1 +Details of extended fields: + +== KEY == +Type: Item name +Size: 5 bytes +Flags: Do not copy +== VALUE == +Item name: root + + +- DIR REC || Dirctry || target ID = 0x10002f6ae || name = .DocumentRevisions-V100 +- DIR REC || Dirctry || target ID = 0x13 || name = .HFS+ Private Directory Data +- DIR REC || Dirctry || target ID = 0x10002f6b9 || name = .TemporaryItems +- DIR REC || RegFile || target ID = 0x2f731 || name = .Trashes +- DIR REC || Dirctry || target ID = 0x49b || name = .ba +- DIR REC || RegFile || target ID = 0x499 || name = .file +- DIR REC || Dirctry || target ID = 0x10002f6a8 || name = .fseventsd +- DIR REC || Dirctry || target ID = 0x2a7 || name = .mb +- DIR REC || Dirctry || target ID = 0x14 || name = Applications +- DIR REC || Dirctry || target ID = 0x3002 || name = Developer +- DIR REC || Dirctry || target ID = 0x28c5 || name = Library +- DIR REC || Dirctry || target ID = 0x2cab || name = System +- DIR REC || Dirctry || target ID = 0x2b5c2 || name = bin +- DIR REC || Dirctry || target ID = 0x2b5dc || name = cores +- DIR REC || Dirctry || target ID = 0x2b5e6 || name = dev +- DIR REC || Symlink || target ID = 0x2b5f0 || name = etc +- DIR REC || Dirctry || target ID = 0x2b5f9 || name = private +- DIR REC || Dirctry || target ID = 0x2f0c0 || name = sbin +- DIR REC || Symlink || target ID = 0x2f0e7 || name = tmp +- DIR REC || Dirctry || target ID = 0x2f0d0 || name = usr +- DIR REC || Symlink || target ID = 0x2f57c || name = var + +Inode record type: Directory +Analysing record: etc +- INODE +Creation time: Wed Aug 29 09:20:42 2018 +Last modification time: Wed Aug 29 09:20:42 2018 +Last access time: Wed Aug 29 09:20:42 2018 + +Number of extended fields: 1 +Details of extended fields: + +== KEY == +Type: Item name +Size: 4 bytes +Flags: Do not copy +== VALUE == +Item name: etc + + +- XATTR || name = com.apple.fs.symlink + +Inode record type: Symbolic link +Sorting 0 elements by timestamp... +====================================================================== +Timeline: +====================================================================== +END: All done. diff --git a/src/commands/rollback.c b/src/commands/rollback.c index 78383d6..1d37001 100644 --- a/src/commands/rollback.c +++ b/src/commands/rollback.c @@ -1,4 +1,3 @@ -// Get all the filenames from a previous xid with their data stream #include #include #include @@ -599,7 +598,7 @@ int cmd_rollback(int argc, char **argv) return -1; } - // Get file size + // Get file size from the inode object records uint64_t file_size = 0; for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { j_rec_t* fs_rec = *fs_rec_cursor; diff --git a/src/commands/timeline.c b/src/commands/timeline.c index b7881c3..c7be08b 100644 --- a/src/commands/timeline.c +++ b/src/commands/timeline.c @@ -76,7 +76,7 @@ const char* nth_strchr(const char* s, char c, int n) { } // /private/var/etc -> /var/etc -> /etc -> null -char *getRemainingUrl(char *url, char token) { +char *get_remaining_url(char *url, char token) { const char *position_ptr = nth_strchr(url, token, 2); int position = (position_ptr == NULL ? -1 : position_ptr - url); @@ -89,7 +89,7 @@ char *getRemainingUrl(char *url, char token) { } // /private/var/etc -> private -char *getFirstEntry(char *url, char token) { +char *get_first_entry(char *url, char token) { char *left_position_ptr = strchr(url, token); if (left_position_ptr == NULL) { return NULL; @@ -109,7 +109,7 @@ char *getFirstEntry(char *url, char token) { } -void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, struct file_metadata **fs_files, char *path, char *full_path) +void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, struct file_metadata **fs_files, char *path_to_process, char *full_path) { if (fs_records == NULL || fs_records[0] == NULL) { return; @@ -117,9 +117,9 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node int res = -1; char *entry = NULL; char *rest = NULL; - if (path != NULL) { - entry = getFirstEntry(path, '/'); - rest = getRemainingUrl(path, '/'); + if (path_to_process != NULL) { + entry = get_first_entry(path_to_process, '/'); + rest = get_remaining_url(path_to_process, '/'); } for (j_rec_t **fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) // for each directory entry... { @@ -128,10 +128,10 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) { j_drec_key_t *key = fs_rec->data; - if (path != NULL) { + if (path_to_process != NULL) { res = strcmp((char *)key->name, entry); } - if (res == 0 || path == NULL || strcmp(path, "/") == 0) { + if (res == 0 || path_to_process == NULL || strcmp(path_to_process, "/") == 0) { printf("Analysing record: %s\n", (char *)key->name); // diff between found index and start index signed int matching_record_index = fs_rec_cursor - fs_records; @@ -149,7 +149,7 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node //printf("Entering absolute directory: %s\n", new_full_path); parse_fs(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, fs_files, rest, new_full_path); } - } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE){ + } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE) { oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; j_rec_t **file_record = get_fs_records(fs_omap_btree, fs_root_btree, file_oid, (xid_t)(~0)); print_fs_records(file_record); From bb64d8779ba450462cf45940da90cb9cc39e11a0 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Thu, 2 Sep 2021 22:57:03 +0200 Subject: [PATCH 4/9] Added "dumpfiles" command to retrieve files from all xids --- include/drat/print-fs-records.c | 2 +- src/commands.h | 2 + src/commands/dumpfiles.c | 625 ++++++++++++++++++++++++++++++++ src/commands/rollback.c | 3 - 4 files changed, 628 insertions(+), 4 deletions(-) create mode 100644 src/commands/dumpfiles.c diff --git a/include/drat/print-fs-records.c b/include/drat/print-fs-records.c index c1c017d..f580ef9 100644 --- a/include/drat/print-fs-records.c +++ b/include/drat/print-fs-records.c @@ -123,7 +123,7 @@ void print_fs_records(j_rec_t** fs_records) { } break; case APFS_TYPE_DIR_REC: { // Apple's spec inorrectly says to use `j_drec_key_t`; see NOTE in - // On iOS 12 dmgs keys are of j_drec_key_t type + // iOS 12 dmgs keys are of j_drec_key_t type (pre 2019) //j_drec_hashed_key_t* key = fs_rec->data; j_drec_key_t* key = fs_rec->data; j_drec_val_t* val = fs_rec->data + fs_rec->key_len; diff --git a/src/commands.h b/src/commands.h index 7cac690..3a25c65 100644 --- a/src/commands.h +++ b/src/commands.h @@ -25,6 +25,7 @@ command_function cmd_list_raw; command_function cmd_list; command_function cmd_timeline; command_function cmd_rollback; +command_function cmd_dumpfiles; command_function cmd_modify; command_function cmd_read; command_function cmd_recover_raw; @@ -42,6 +43,7 @@ static drat_command_t drat_commands[] = { { "list" , cmd_list , "List directory contents or file info based on its filepath" }, { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, { "rollback" , cmd_rollback , "Print file contents in an older filesystem state" }, + { "dumpfiles" , cmd_dumpfiles , "Try to recover files from a directory" }, // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, { "read" , cmd_read , "Read a block and display information about it" }, { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, diff --git a/src/commands/dumpfiles.c b/src/commands/dumpfiles.c new file mode 100644 index 0000000..6f023ec --- /dev/null +++ b/src/commands/dumpfiles.c @@ -0,0 +1,625 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static int fs_file_index = 0; + +struct fs_browse_info { + btree_node_phys_t *fs_omap_btree; + btree_node_phys_t *fs_root_btree; +}; + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char **argv) +{ + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2 0 /private/var/mobile/ /Users/john/Desktop/\n", + argv[0], + argv[0]); +} + +void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, char *path_to_process, char *full_path, char *outdir) +{ + if (fs_records == NULL || fs_records[0] == NULL) { + return; + } + int res = -1; + char *entry = NULL; + char *rest = NULL; + if (path_to_process != NULL) { + entry = get_first_entry(path_to_process, '/'); + rest = get_remaining_url(path_to_process, '/'); + } + for (j_rec_t **fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) // for each directory entry... + { + j_rec_t *fs_rec = *fs_rec_cursor; + j_key_t *hdr = fs_rec->data; + if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) + { + j_drec_key_t *key = fs_rec->data; + //j_drec_hashed_key_t *key = fs_rec->data; + if (path_to_process != NULL) { + res = strcmp((char *)key->name, entry); + } + if (res == 0 || path_to_process == NULL || strcmp(path_to_process, "/") == 0) { + printf("Analysing record: %s\n", (char *)key->name); + // diff between found index and start index + signed int matching_record_index = fs_rec_cursor - fs_records; + // Get the file ID of the matching record's target + j_rec_t *fs_rec = fs_records[matching_record_index]; + j_drec_val_t *val = fs_rec->data + fs_rec->key_len; + + // Get the records for the target + oid_t dir_oid = val->file_id; + char *new_full_path = malloc(strlen(full_path) + strlen((char *)key->name) + 2); + strncpy(new_full_path, full_path, strlen(full_path)); + const char* separator = "/"; + strncpy(new_full_path + strlen(full_path), separator, 1); + strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); + //printf("Entering absolute path: %s\n", new_full_path); + parse_fs_and_write(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, rest, new_full_path, outdir); + } + } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE) { + oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; + j_rec_t **file_record = get_fs_records(fs_omap_btree, fs_root_btree, file_oid, (xid_t)(~0)); + print_fs_records(file_record); + // get the inode value records + j_rec_t* fs_rec = *file_record; + j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; + printf("Inode record type: %s\n", get_j_inode_type(inode)); + char *fn = get_file_name(file_record); + bool is_reg_file = strcmp(get_j_inode_type(inode), "Regular file") == 0; + if (fn && is_reg_file) { + // Output content from all matching file extents + char* buffer = malloc(nx_block_size); + if (!buffer) { + fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); + return; + } + uint64_t file_size = get_file_size(inode, fs_rec->val_len); + printf("File size: %" PRIu64 "\n", file_size); + if (file_size == 0) { + // Not a file, or file size couldn't be found; abort. + exit(-1); + } + + // Keep track of how many more bytes we need to output + uint64_t bytes_remaining = file_size; + + bool found_file_extent = false; + // Go through all the fs records + for (j_rec_t** fs_rec_cursor = file_record; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + + // Go through all the extent records + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { + found_file_extent = true; + j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; + + // Output the content from this particular file extent + uint64_t block_addr = val->phys_block_num; + + uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; + for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { + if (read_blocks(buffer, block_addr, 1) != 1) { + fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); + exit(-1); + } + + uint64_t bytes_to_write = nx_block_size; + if (bytes_remaining < nx_block_size) { + bytes_to_write = bytes_remaining; + } + char *result = malloc(strlen(outdir) + strlen(fn) + 1); + strcpy(result, outdir); + strcat(result, fn); + FILE *fp = fopen(result, "ab"); + if (fwrite(buffer, bytes_to_write, 1, fp) != 1) { + fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); + exit(-1); + } + fclose(fp); + + bytes_remaining -= bytes_to_write; + + if (bytes_remaining == 0) { + break; // Exit extent loop + } + } + } + + if (bytes_remaining == 0) { + break; // Exit fs record loop + } + } + if (!found_file_extent) { + fprintf(stderr, "Could not find any file extents for the specified path.\n"); + } + free_j_rec_array(file_record); + } else { + continue; + } + } + } + free_j_rec_array(fs_records); +} + +struct fs_browse_info* get_root_fs_tree(nx_superblock_t* nxsb, uint32_t volume_id, char(*xp_desc)[nx_block_size], uint32_t xp_desc_blocks) { + // Copy the contents of the checkpoint we are currently considering to its + // own array for easy access. The checkpoint descriptor area is a ring + // buffer stored as an array, so doing this also allows us to handle the + // case where the checkpoint we're considering wraps around the ring buffer. + fprintf(stderr, "Loading the corresponding checkpoint ... "); + + // The array `xp` will comprise the blocks in the checkpoint, in order. + char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); + if (!xp) + { + fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); + return NULL; + } + + if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) + { + // The simple case: the checkpoint is already contiguous in `xp_desc`. + memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); + } + else + { + // The case where the checkpoint wraps around from the end of the + // checkpoint descriptor area to the start. + uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; + uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; + memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); + memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); + } + fprintf(stderr, "OK.\n"); + + // We could `free(xp_desc)` at this point, but instead, we retain our copy + // of the checkpoint descriptor area in case any of the Ephemeral objects + // referenced by the current checkpoint are malformed; then, we can + // retrieve an older checkpoint without having to read the checkpoint + // descriptor area again. + + uint32_t xp_obj_len = 0; // This variable will equal the number of + // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) + { + if (is_checkpoint_map_phys(xp[i])) + { + xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; + } + } + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + + free(xp); + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); + + fprintf(stderr, "Loading the container object map ... "); + omap_phys_t *nx_omap = malloc(nx_block_size); + if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the container object map ... "); + if (!is_cksum_valid(nx_omap)) + { + fprintf(stderr, "FAILED.\n"); + return NULL; + } + fprintf(stderr, "OK.\n"); + + if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return NULL; + } + + fprintf(stderr, "Reading the root node of the container object map B-tree ... "); + btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); + if (!nx_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); + return NULL; + } + if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the container object map B-tree ... "); + if (!is_cksum_valid(nx_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + uint32_t num_file_systems = 0; + for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) + { + if (nxsb->nx_fs_oid[i] == 0) + { + break; + } + num_file_systems++; + } + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Reading the APFS volume superblocks ... "); + char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); + if (!apsbs) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return NULL; + } + for (uint32_t i = 0; i < num_file_systems; i++) + { + omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + if (!fs_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + return NULL; + } + if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); + return NULL; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the APFS volume superblocks ... "); + for (uint32_t i = 0; i < num_file_systems; i++) + { + if (!is_cksum_valid(apsbs + i)) + { + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return NULL; + } + + if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) + { + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return NULL; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "\n Volume list\n================\n"); + for (uint32_t i = 0; i < num_file_systems; i++) + { + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); + } + + if (volume_id >= num_file_systems) + { + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); + return NULL; + } + apfs_superblock_t *apsb = apsbs + volume_id; + + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + + fprintf(stderr, "Reading the volume object map ... "); + omap_phys_t *fs_omap = malloc(nx_block_size); + if (!fs_omap) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return NULL; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) + { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return NULL; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return NULL; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return NULL; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return NULL; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t *fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return NULL; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return NULL; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) + { + fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); + return NULL; + } + fprintf(stderr, "OK.\n"); + struct fs_browse_info* return_info = malloc(sizeof(struct fs_browse_info)); + return_info->fs_omap_btree = fs_omap_btree; + return_info->fs_root_btree = fs_root_btree; + free(fs_omap); + free(apsbs); + free(nx_omap); + return return_info; +} + + +int cmd_dumpfiles(int argc, char **argv) +{ + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 5) + { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + + nx_path = argv[1]; + + uint32_t volume_id; + bool parse_success = sscanf(argv[2], "%u", &volume_id); + if (!parse_success) + { + fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); + print_usage(argc, argv); + return 1; + } + + char *path_stack = argv[3]; + char *outdir = argv[4]; + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) + { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); + + // Using `nx_superblock_t*`, but allocating a whole block of memory. + // This way, we can read the entire block and validate its checksum, + // but still have direct access to the fields in `nx_superblock_t` + // without needing to epxlicitly cast to that datatype. + nx_superblock_t *nxsb = malloc(nx_block_size); + if (!nxsb) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) + { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ... "); + if (!is_cksum_valid(nxsb)) + { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + fprintf(stderr, "OK.\n"); + if (!is_nx_superblock(nxsb)) + { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) + { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + + fprintf(stderr, "Locating the checkpoint descriptor area:\n"); + + uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); + + char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); + if (!xp_desc) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); + return 1; + } + + if (nxsb->nx_xp_desc_blocks >> 31) + { + fprintf(stderr, "- It is not contiguous.\n"); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous + return 1; + } + else + { + fprintf(stderr, "- It is contiguous.\n"); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + + fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); + if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) + { + fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + return 1; + } + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "Locating the requested container superblock in the checkpoint descriptor area:\n"); + uint32_t i_target_nx = 0; + xid_t xid_latest_nx = 0; + uint32_t nx_xids[xp_desc_blocks]; // collect all the valid indexes + + xid_t max_xid = ~0; // `~0` is the highest possible XID + uint32_t j = 0; + for (uint32_t i = 0; i < xp_desc_blocks; i++) + { + if (!is_cksum_valid(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + if (is_nx_superblock(xp_desc[i])) + { + if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) + { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + printf("Found nxsb block at index %d with XID: %" PRIu64 "\n", i, ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid); + nx_xids[j] = i; + j++; + } + else if (!is_checkpoint_map_phys(xp_desc[i])) + { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + + if (j == 0) + { + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); + return 1; + } + + for (uint32_t i = 0; i < j; i++) { + memcpy(nxsb, xp_desc[nx_xids[i]], sizeof(nx_superblock_t)); + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", nx_xids[i]); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); + struct fs_browse_info *fs_trees = get_root_fs_tree(nxsb, volume_id, xp_desc, xp_desc_blocks); + if (fs_trees == NULL) { + fprintf(stderr, "Error getting the fs tree\n"); + continue; + } + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) + { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); + + printf("Entering directory /\n"); + char *path_builder = ""; + parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir); + free(fs_trees->fs_omap_btree); + free(fs_trees->fs_root_btree); + free(fs_trees); + } + free(xp_desc); + fprintf(stderr, "END: All done.\n"); + return 0; +} diff --git a/src/commands/rollback.c b/src/commands/rollback.c index 1d37001..170b7a6 100644 --- a/src/commands/rollback.c +++ b/src/commands/rollback.c @@ -154,8 +154,6 @@ int cmd_rollback(int argc, char **argv) uint32_t i_target_nx = 0; xid_t xid_latest_nx = 0; - xid_t nx_xids[xp_desc_blocks]; - xid_t max_xid = ~0; // `~0` is the highest possible XID for (uint32_t i = 0; i < xp_desc_blocks; i++) @@ -603,7 +601,6 @@ int cmd_rollback(int argc, char **argv) for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { j_rec_t* fs_rec = *fs_rec_cursor; j_key_t* hdr = fs_rec->data; - printf("Looping\n"); if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE ) { j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; file_size = get_file_size(inode, fs_rec->val_len); From 4207cd3718bd5e63dcf17c6d17446d380d41bc38 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Fri, 3 Sep 2021 02:26:58 +0200 Subject: [PATCH 5/9] Doesn't work yet, need to calculate the next correct nxsb --- include/drat/print-fs-records.c | 4 +-- src/commands/dumpfiles.c | 45 ++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/include/drat/print-fs-records.c b/include/drat/print-fs-records.c index f580ef9..b4ad403 100644 --- a/include/drat/print-fs-records.c +++ b/include/drat/print-fs-records.c @@ -124,8 +124,8 @@ void print_fs_records(j_rec_t** fs_records) { case APFS_TYPE_DIR_REC: { // Apple's spec inorrectly says to use `j_drec_key_t`; see NOTE in // iOS 12 dmgs keys are of j_drec_key_t type (pre 2019) - //j_drec_hashed_key_t* key = fs_rec->data; - j_drec_key_t* key = fs_rec->data; + j_drec_hashed_key_t* key = fs_rec->data; + //j_drec_key_t* key = fs_rec->data; j_drec_val_t* val = fs_rec->data + fs_rec->key_len; fprintf(stderr, "DIR REC" diff --git a/src/commands/dumpfiles.c b/src/commands/dumpfiles.c index 6f023ec..18b78eb 100644 --- a/src/commands/dumpfiles.c +++ b/src/commands/dumpfiles.c @@ -47,12 +47,21 @@ static void print_usage(int argc, char **argv) argc == 1 ? stdout : stderr, "Usage: %s \n" - "Example: %s /dev/disk0s2 0 /private/var/mobile/ /Users/john/Desktop/\n", + "Example: %s /dev/disk0s2 0 /private/var/mobile /Users/john/Desktop/\n", argv[0], argv[0]); } -void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, char *path_to_process, char *full_path, char *outdir) +char *gen_directory(char *base_dir, char *fn, int xid) { + char xid_str[10]; + sprintf(xid_str, "%d", xid); + char *out = malloc(strlen(base_dir) + strlen(xid_str) + strlen(fn) + 3); + // cannot import sys/stat.h to mkdir because of constant redefinition :( + sprintf(out, "%s_%s_%s", base_dir, xid_str, fn); + return out; +} + +void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, char *path_to_process, char *full_path, char *outdir, xid_t xid) { if (fs_records == NULL || fs_records[0] == NULL) { return; @@ -60,6 +69,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, int res = -1; char *entry = NULL; char *rest = NULL; + char *result; if (path_to_process != NULL) { entry = get_first_entry(path_to_process, '/'); rest = get_remaining_url(path_to_process, '/'); @@ -70,8 +80,8 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, j_key_t *hdr = fs_rec->data; if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) { - j_drec_key_t *key = fs_rec->data; - //j_drec_hashed_key_t *key = fs_rec->data; + //j_drec_key_t *key = fs_rec->data; + j_drec_hashed_key_t *key = fs_rec->data; if (path_to_process != NULL) { res = strcmp((char *)key->name, entry); } @@ -91,7 +101,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, strncpy(new_full_path + strlen(full_path), separator, 1); strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); //printf("Entering absolute path: %s\n", new_full_path); - parse_fs_and_write(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, rest, new_full_path, outdir); + parse_fs_and_write(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, rest, new_full_path, outdir, xid); } } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE) { oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; @@ -103,6 +113,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, printf("Inode record type: %s\n", get_j_inode_type(inode)); char *fn = get_file_name(file_record); bool is_reg_file = strcmp(get_j_inode_type(inode), "Regular file") == 0; + result = gen_directory(outdir, fn, xid); if (fn && is_reg_file) { // Output content from all matching file extents char* buffer = malloc(nx_block_size); @@ -113,8 +124,9 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, uint64_t file_size = get_file_size(inode, fs_rec->val_len); printf("File size: %" PRIu64 "\n", file_size); if (file_size == 0) { - // Not a file, or file size couldn't be found; abort. - exit(-1); + // Not a file, or file size couldn't be found; skip it in this case. + //exit(-1); + continue; } // Keep track of how many more bytes we need to output @@ -145,9 +157,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, if (bytes_remaining < nx_block_size) { bytes_to_write = bytes_remaining; } - char *result = malloc(strlen(outdir) + strlen(fn) + 1); - strcpy(result, outdir); - strcat(result, fn); + FILE *fp = fopen(result, "ab"); if (fwrite(buffer, bytes_to_write, 1, fp) != 1) { fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); @@ -177,6 +187,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, } } free_j_rec_array(fs_records); + free(result); } struct fs_browse_info* get_root_fs_tree(nx_superblock_t* nxsb, uint32_t volume_id, char(*xp_desc)[nx_block_size], uint32_t xp_desc_blocks) { @@ -551,7 +562,8 @@ int cmd_dumpfiles(int argc, char **argv) fprintf(stderr, "Locating the requested container superblock in the checkpoint descriptor area:\n"); uint32_t i_target_nx = 0; xid_t xid_latest_nx = 0; - uint32_t nx_xids[xp_desc_blocks]; // collect all the valid indexes + uint32_t nx_indexes[xp_desc_blocks]; // collect all the valid indexes + xid_t nx_xids[xp_desc_blocks]; xid_t max_xid = ~0; // `~0` is the highest possible XID uint32_t j = 0; @@ -570,8 +582,8 @@ int cmd_dumpfiles(int argc, char **argv) continue; } printf("Found nxsb block at index %d with XID: %" PRIu64 "\n", i, ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid); - nx_xids[j] = i; - j++; + nx_indexes[j] = i; + nx_xids[j++] = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; } else if (!is_checkpoint_map_phys(xp_desc[i])) { @@ -585,10 +597,9 @@ int cmd_dumpfiles(int argc, char **argv) fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); return 1; } - for (uint32_t i = 0; i < j; i++) { - memcpy(nxsb, xp_desc[nx_xids[i]], sizeof(nx_superblock_t)); - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", nx_xids[i]); + memcpy(nxsb, xp_desc[nx_indexes[i]], sizeof(nx_superblock_t)); + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", nx_indexes[i]); fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); struct fs_browse_info *fs_trees = get_root_fs_tree(nxsb, volume_id, xp_desc, xp_desc_blocks); if (fs_trees == NULL) { @@ -614,7 +625,7 @@ int cmd_dumpfiles(int argc, char **argv) printf("Entering directory /\n"); char *path_builder = ""; - parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir); + parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir, nx_xids[i]); free(fs_trees->fs_omap_btree); free(fs_trees->fs_root_btree); free(fs_trees); From bcd566e4b89eb079c22fbcb79265593255abc652 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Fri, 3 Sep 2021 20:13:55 +0200 Subject: [PATCH 6/9] Working commit: timeline + dumpfiles --- include/drat/utilities.c | 133 ++++++++ include/drat/utilities.h | 40 ++- src/commands.h | 4 +- src/commands/dumpfiles.c | 471 ++++----------------------- src/commands/rollback.c | 679 --------------------------------------- src/commands/timeline.c | 610 ++++++++--------------------------- 6 files changed, 369 insertions(+), 1568 deletions(-) create mode 100644 include/drat/utilities.c delete mode 100644 src/commands/rollback.c diff --git a/include/drat/utilities.c b/include/drat/utilities.c new file mode 100644 index 0000000..bfc360f --- /dev/null +++ b/include/drat/utilities.c @@ -0,0 +1,133 @@ +#include + +const char* nth_strchr(const char* s, char c, int n) { + int c_count; + char* nth_ptr; + for (c_count=1,nth_ptr=strchr(s,c); nth_ptr != NULL && c_count < n && c!=0; c_count++) { + nth_ptr = strchr(nth_ptr+1, c); + } + return nth_ptr; +} + +// /private/var/etc -> /var/etc -> /etc -> null +char* get_remaining_url(char *url, char token) { + const char *position_ptr = nth_strchr(url, token, 2); + int position = (position_ptr == NULL ? -1 : position_ptr - url); + + if (position > 0) { + char *newurl = malloc(strlen(url) - position); + strncpy(newurl, url+position, strlen(url) - position + 1); + return newurl; + } + return NULL; +} + +// /private/var/etc -> private +char* get_first_entry(char *url, char token) { + char *left_position_ptr = strchr(url, token); + if (left_position_ptr == NULL) { + return NULL; + } + + int left_position = left_position_ptr - url; + const char *right_position_ptr = nth_strchr(url, token, 2); + if (right_position_ptr == NULL) { + char *entry = malloc(strlen(url)); + strncpy(entry, url + left_position + 1, strlen(url)); + return entry; + } + int right_position = right_position_ptr - url; + char *entry = malloc(right_position - left_position - 1); + strncpy(entry, url+left_position+1, right_position - left_position - 1); + return entry; +} + +struct fs_browse_info* get_root_fs_tree(apfs_superblock_t* apsb, uint64_t nx_block_size) { + omap_phys_t *fs_omap = malloc(nx_block_size); + if (!fs_omap) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return NULL; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) + { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return NULL; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return NULL; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return NULL; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return NULL; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t *fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return NULL; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return NULL; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) + { + fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); + return NULL; + } + fprintf(stderr, "OK.\n"); + struct fs_browse_info* return_info = malloc(sizeof(struct fs_browse_info)); + return_info->fs_omap_btree = fs_omap_btree; + return_info->fs_root_btree = fs_root_btree; + free(fs_omap); + return return_info; +} diff --git a/include/drat/utilities.h b/include/drat/utilities.h index d672c39..ec3c694 100644 --- a/include/drat/utilities.h +++ b/include/drat/utilities.h @@ -1,3 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct fs_browse_info { + btree_node_phys_t *fs_omap_btree; + btree_node_phys_t *fs_root_btree; +}; + const char* nth_strchr(const char* s, char c, int n); -char *get_remaining_url(char *url, char token); -char *get_first_entry(char *url, char token); \ No newline at end of file +char* get_remaining_url(char *url, char token); +char* get_first_entry(char *url, char token); +struct fs_browse_info* get_root_fs_tree(apfs_superblock_t* apsb, uint64_t nx_block_size); \ No newline at end of file diff --git a/src/commands.h b/src/commands.h index 3a25c65..5486170 100644 --- a/src/commands.h +++ b/src/commands.h @@ -24,7 +24,6 @@ command_function cmd_inspect; command_function cmd_list_raw; command_function cmd_list; command_function cmd_timeline; -command_function cmd_rollback; command_function cmd_dumpfiles; command_function cmd_modify; command_function cmd_read; @@ -42,8 +41,7 @@ static drat_command_t drat_commands[] = { { "list-raw" , cmd_list_raw , "List directory contents or file info based on its filesystem OID" }, { "list" , cmd_list , "List directory contents or file info based on its filepath" }, { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, - { "rollback" , cmd_rollback , "Print file contents in an older filesystem state" }, - { "dumpfiles" , cmd_dumpfiles , "Try to recover files from a directory" }, + { "dumpfiles" , cmd_dumpfiles , "Try to recover lost files from a directory" }, // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, { "read" , cmd_read , "Read a block and display information about it" }, { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, diff --git a/src/commands/dumpfiles.c b/src/commands/dumpfiles.c index 18b78eb..f0d7228 100644 --- a/src/commands/dumpfiles.c +++ b/src/commands/dumpfiles.c @@ -30,13 +30,7 @@ #include #include #include - -static int fs_file_index = 0; - -struct fs_browse_info { - btree_node_phys_t *fs_omap_btree; - btree_node_phys_t *fs_root_btree; -}; +#include /** * Print usage info for this program. @@ -45,9 +39,8 @@ static void print_usage(int argc, char **argv) { fprintf( argc == 1 ? stdout : stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2 0 /private/var/mobile /Users/john/Desktop/\n", + "Usage: %s \n" + "Example: %s /dev/disk0s2 /private/var/mobile /Users/john/Desktop\n", argv[0], argv[0]); } @@ -55,8 +48,16 @@ static void print_usage(int argc, char **argv) char *gen_directory(char *base_dir, char *fn, int xid) { char xid_str[10]; sprintf(xid_str, "%d", xid); - char *out = malloc(strlen(base_dir) + strlen(xid_str) + strlen(fn) + 3); - // cannot import sys/stat.h to mkdir because of constant redefinition :( + if (base_dir[strlen(base_dir) - 1] != '/') { + const char slash = '/'; + strncat(base_dir, &slash, 1); + } + if (access(base_dir, F_OK) != 0) { + fprintf(stderr, "The chosen output directory does not exist\n"); + exit(1); + } + char* out = malloc(strlen(base_dir) + strlen(xid_str) + strlen(fn) + 3); + // cannot import sys/stat.h to mkdir because of constant redefinition sprintf(out, "%s_%s_%s", base_dir, xid_str, fn); return out; } @@ -125,7 +126,6 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, printf("File size: %" PRIu64 "\n", file_size); if (file_size == 0) { // Not a file, or file size couldn't be found; skip it in this case. - //exit(-1); continue; } @@ -139,20 +139,20 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, j_key_t* hdr = fs_rec->data; // Go through all the extent records - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { found_file_extent = true; j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; - + // Output the content from this particular file extent uint64_t block_addr = val->phys_block_num; uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; - for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { + for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { if (read_blocks(buffer, block_addr, 1) != 1) { fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); exit(-1); } - + uint64_t bytes_to_write = nx_block_size; if (bytes_remaining < nx_block_size) { bytes_to_write = bytes_remaining; @@ -164,7 +164,7 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, exit(-1); } fclose(fp); - + bytes_remaining -= bytes_to_write; if (bytes_remaining == 0) { @@ -190,280 +190,12 @@ void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, free(result); } -struct fs_browse_info* get_root_fs_tree(nx_superblock_t* nxsb, uint32_t volume_id, char(*xp_desc)[nx_block_size], uint32_t xp_desc_blocks) { - // Copy the contents of the checkpoint we are currently considering to its - // own array for easy access. The checkpoint descriptor area is a ring - // buffer stored as an array, so doing this also allows us to handle the - // case where the checkpoint we're considering wraps around the ring buffer. - fprintf(stderr, "Loading the corresponding checkpoint ... "); - - // The array `xp` will comprise the blocks in the checkpoint, in order. - char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); - if (!xp) - { - fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); - return NULL; - } - - if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) - { - // The simple case: the checkpoint is already contiguous in `xp_desc`. - memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); - } - else - { - // The case where the checkpoint wraps around from the end of the - // checkpoint descriptor area to the start. - uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; - uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; - memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); - memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); - } - fprintf(stderr, "OK.\n"); - - // We could `free(xp_desc)` at this point, but instead, we retain our copy - // of the checkpoint descriptor area in case any of the Ephemeral objects - // referenced by the current checkpoint are malformed; then, we can - // retrieve an older checkpoint without having to read the checkpoint - // descriptor area again. - - uint32_t xp_obj_len = 0; // This variable will equal the number of - // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) - { - if (is_checkpoint_map_phys(xp[i])) - { - xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; - } - } - fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); - - free(xp); - fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); - - fprintf(stderr, "Loading the container object map ... "); - omap_phys_t *nx_omap = malloc(nx_block_size); - if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the container object map ... "); - if (!is_cksum_valid(nx_omap)) - { - fprintf(stderr, "FAILED.\n"); - return NULL; - } - fprintf(stderr, "OK.\n"); - - if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return NULL; - } - - fprintf(stderr, "Reading the root node of the container object map B-tree ... "); - btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); - if (!nx_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); - return NULL; - } - if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the container object map B-tree ... "); - if (!is_cksum_valid(nx_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - uint32_t num_file_systems = 0; - for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) - { - if (nxsb->nx_fs_oid[i] == 0) - { - break; - } - num_file_systems++; - } - fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "Reading the APFS volume superblocks ... "); - char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); - if (!apsbs) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return NULL; - } - for (uint32_t i = 0; i < num_file_systems; i++) - { - omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - if (!fs_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - return NULL; - } - if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); - return NULL; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the APFS volume superblocks ... "); - for (uint32_t i = 0; i < num_file_systems; i++) - { - if (!is_cksum_valid(apsbs + i)) - { - fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return NULL; - } - - if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) - { - fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return NULL; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "\n Volume list\n================\n"); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); - } - - if (volume_id >= num_file_systems) - { - fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); - return NULL; - } - apfs_superblock_t *apsb = apsbs + volume_id; - - fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - - fprintf(stderr, "Reading the volume object map ... "); - omap_phys_t *fs_omap = malloc(nx_block_size); - if (!fs_omap) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return NULL; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) - { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return NULL; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return NULL; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return NULL; - } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return NULL; - } - fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t *fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return NULL; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return NULL; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) - { - fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); - return NULL; - } - fprintf(stderr, "OK.\n"); - struct fs_browse_info* return_info = malloc(sizeof(struct fs_browse_info)); - return_info->fs_omap_btree = fs_omap_btree; - return_info->fs_root_btree = fs_root_btree; - free(fs_omap); - free(apsbs); - free(nx_omap); - return return_info; -} - - int cmd_dumpfiles(int argc, char **argv) { setbuf(stdout, NULL); // Extrapolate CLI arguments, exit if invalid - if (argc != 5) + if (argc != 4) { fprintf(stderr, "Incorrect number of arguments.\n"); print_usage(argc, argv); @@ -472,17 +204,8 @@ int cmd_dumpfiles(int argc, char **argv) nx_path = argv[1]; - uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%u", &volume_id); - if (!parse_success) - { - fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); - print_usage(argc, argv); - return 1; - } - - char *path_stack = argv[3]; - char *outdir = argv[4]; + char *path_stack = argv[2]; + char *outdir = argv[3]; // Open (device special) file corresponding to an APFS container, read-only fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); nx = fopen(nx_path, "rb"); @@ -492,12 +215,10 @@ int cmd_dumpfiles(int argc, char **argv) report_fopen_error(); return -errno; } - fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); - - // Using `nx_superblock_t*`, but allocating a whole block of memory. - // This way, we can read the entire block and validate its checksum, - // but still have direct access to the fields in `nx_superblock_t` - // without needing to epxlicitly cast to that datatype. + fseek(nx, 0, SEEK_END); + long size = ftell(nx); + fseek(nx, 0, SEEK_SET); + printf("Image File size: %ld\n", size); nx_superblock_t *nxsb = malloc(nx_block_size); if (!nxsb) { @@ -511,12 +232,11 @@ int cmd_dumpfiles(int argc, char **argv) return -1; } - fprintf(stderr, "Validating checksum of block 0x0 ... "); + fprintf(stderr, "Validating checksum of block 0x0 ..."); if (!is_cksum_valid(nxsb)) { fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); } - fprintf(stderr, "OK.\n"); if (!is_nx_superblock(nxsb)) { fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); @@ -525,112 +245,59 @@ int cmd_dumpfiles(int argc, char **argv) { fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); } - - fprintf(stderr, "Locating the checkpoint descriptor area:\n"); - - uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); - - char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); - if (!xp_desc) - { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); - return 1; - } - - if (nxsb->nx_xp_desc_blocks >> 31) - { - fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous - return 1; - } - else - { - fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - - fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); - if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) - { - fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + printf("OK\n"); + uint64_t nx_block_size = nxsb->nx_block_size; + + for (uint32_t block_index = 0; block_index < size/nx_block_size; block_index++) { + char *apsb = malloc(nx_block_size); + if (!apsb) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); return 1; } - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "Locating the requested container superblock in the checkpoint descriptor area:\n"); - uint32_t i_target_nx = 0; - xid_t xid_latest_nx = 0; - uint32_t nx_indexes[xp_desc_blocks]; // collect all the valid indexes - xid_t nx_xids[xp_desc_blocks]; - - xid_t max_xid = ~0; // `~0` is the highest possible XID - uint32_t j = 0; - for (uint32_t i = 0; i < xp_desc_blocks; i++) - { - if (!is_cksum_valid(xp_desc[i])) + if (read_blocks(apsb, block_index, 1) != 1) { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIu32 ".\n", block_index); continue; } - if (is_nx_superblock(xp_desc[i])) + if (is_cksum_valid(apsb)) { - if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) + if (((apfs_superblock_t *)apsb)->apfs_magic == APFS_MAGIC) { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; + printf("Found valid APSB block at addr 0x%" PRIu32 "\n", block_index); + struct fs_browse_info *fs_trees = get_root_fs_tree(apsb, nx_block_size); + if (fs_trees == NULL) { + fprintf(stderr, "Error getting the fs tree\n"); + continue; + } + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) + { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + if (path_stack[strlen(path_stack) - 1] == '/' && strcmp(path_stack, "/") != 0) { + path_stack[strlen(path_stack) - 1] = '\0'; + } + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); + + printf("Entering directory /\n"); + char *path_builder = ""; + parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir, ((apfs_superblock_t *)apsb)->apfs_o.o_xid); + free(fs_trees->fs_omap_btree); + free(fs_trees->fs_root_btree); + free(fs_trees); + free(apsb); } - printf("Found nxsb block at index %d with XID: %" PRIu64 "\n", i, ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid); - nx_indexes[j] = i; - nx_xids[j++] = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; - } - else if (!is_checkpoint_map_phys(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; } } - - if (j == 0) - { - fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); - return 1; - } - for (uint32_t i = 0; i < j; i++) { - memcpy(nxsb, xp_desc[nx_indexes[i]], sizeof(nx_superblock_t)); - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", nx_indexes[i]); - fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); - struct fs_browse_info *fs_trees = get_root_fs_tree(nxsb, volume_id, xp_desc, xp_desc_blocks); - if (fs_trees == NULL) { - fprintf(stderr, "Error getting the fs tree\n"); - continue; - } - oid_t fs_oid = 0x2; - // Start with root directory oid - j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); - if (!fs_records) - { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - - char *path = malloc(strlen(path_stack) + 1); - if (!path) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); - return -1; - } - memcpy(path, path_stack, strlen(path_stack) + 1); - - printf("Entering directory /\n"); - char *path_builder = ""; - parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir, nx_xids[i]); - free(fs_trees->fs_omap_btree); - free(fs_trees->fs_root_btree); - free(fs_trees); - } - free(xp_desc); fprintf(stderr, "END: All done.\n"); return 0; } diff --git a/src/commands/rollback.c b/src/commands/rollback.c deleted file mode 100644 index 170b7a6..0000000 --- a/src/commands/rollback.c +++ /dev/null @@ -1,679 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char **argv) -{ - fprintf( - argc == 1 ? stdout : stderr, - "Usage: %s \n" - "Example: %s /dev/disk0s2 0 /Users/john/Documents/file.txt 1\n", - argv[0], - argv[0]); -} - -int cmd_rollback(int argc, char **argv) -{ - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 5) - { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - int rollbacks = atoi(argv[4]); - if (rollbacks < 0) { - fprintf(stderr, "Rollbacks must be positive integers.\n"); - return 1; - } - nx_path = argv[1]; - - uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%u", &volume_id); - if (!parse_success) - { - fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); - print_usage(argc, argv); - return 1; - } - - char *path_stack = argv[3]; - - // Open (device special) file corresponding to an APFS container, read-only - fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) - { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - return -errno; - } - fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); - - // Using `nx_superblock_t*`, but allocating a whole block of memory. - // This way, we can read the entire block and validate its checksum, - // but still have direct access to the fields in `nx_superblock_t` - // without needing to epxlicitly cast to that datatype. - nx_superblock_t *nxsb = malloc(nx_block_size); - if (!nxsb) - { - fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); - return -1; - } - - if (read_blocks(nxsb, 0x0, 1) != 1) - { - fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); - return -1; - } - - fprintf(stderr, "Validating checksum of block 0x0 ... "); - if (!is_cksum_valid(nxsb)) - { - fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); - } - fprintf(stderr, "OK.\n"); - - if (!is_nx_superblock(nxsb)) - { - fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); - } - if (nxsb->nx_magic != NX_MAGIC) - { - fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); - } - - fprintf(stderr, "Locating the checkpoint descriptor area:\n"); - - uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); - - char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); - if (!xp_desc) - { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); - return -1; - } - - if (nxsb->nx_xp_desc_blocks >> 31) - { - fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous - return 0; - } - else - { - fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - - fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); - if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) - { - fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); - - uint32_t i_target_nx = 0; - xid_t xid_latest_nx = 0; - xid_t max_xid = ~0; // `~0` is the highest possible XID - - for (uint32_t i = 0; i < xp_desc_blocks; i++) - { - if (!is_cksum_valid(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); - continue; - } - if (is_nx_superblock(xp_desc[i])) - { - if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) - { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; - } - //printf("Found nxsb block at index %d with XID: %" PRIu64 "\n", i, ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid); - if ( - (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid > xid_latest_nx) && (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid <= max_xid)) - { - xid_latest_nx = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; - } - } - else if (!is_checkpoint_map_phys(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; - } - } - - if (xid_latest_nx == 0) - { - fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); - return 0; - } - xid_t target_xid = xid_latest_nx - rollbacks; - // Do it again... - for (uint32_t i = 0; i < xp_desc_blocks; i++) - { - if (!is_cksum_valid(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); - continue; - } - if (is_nx_superblock(xp_desc[i])) - { - if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) - { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; - } - - if (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid == target_xid) - { - i_target_nx = i; - } - } - else if (!is_checkpoint_map_phys(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; - } - } - if (i_target_nx == 0) { - fprintf(stderr, "Could not find the requested rollback, it has probably been overwritten, try with another one\n"); - return 1; - } - printf("Found target XID at index %d\n", i_target_nx); - - // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` - // anymore; replace that data with the latest NXSB. - // This also lets us avoid repeatedly casting to `nx_superblock_t*`. - memcpy(nxsb, xp_desc[i_target_nx], sizeof(nx_superblock_t)); - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_target_nx); - fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); - - // Copy the contents of the checkpoint we are currently considering to its - // own array for easy access. The checkpoint descriptor area is a ring - // buffer stored as an array, so doing this also allows us to handle the - // case where the checkpoint we're considering wraps around the ring buffer. - fprintf(stderr, "Loading the corresponding checkpoint ... "); - - // The array `xp` will comprise the blocks in the checkpoint, in order. - char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); - if (!xp) - { - fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); - return -1; - } - - if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) - { - // The simple case: the checkpoint is already contiguous in `xp_desc`. - memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); - } - else - { - // The case where the checkpoint wraps around from the end of the - // checkpoint descriptor area to the start. - uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; - uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; - memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); - memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); - } - fprintf(stderr, "OK.\n"); - - // We could `free(xp_desc)` at this point, but instead, we retain our copy - // of the checkpoint descriptor area in case any of the Ephemeral objects - // referenced by the current checkpoint are malformed; then, we can - // retrieve an older checkpoint without having to read the checkpoint - // descriptor area again. - - uint32_t xp_obj_len = 0; // This variable will equal the number of - // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) - { - if (is_checkpoint_map_phys(xp[i])) - { - xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; - } - } - fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); - - fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); - char(*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); - if (!xp_obj) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); - return -1; - } - uint32_t num_read = 0; - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) - { - if (is_checkpoint_map_phys(xp[i])) - { - checkpoint_map_phys_t *xp_map = xp[i]; // Avoid lots of casting - for (uint32_t j = 0; j < xp_map->cpm_count; j++) - { - if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); - return -1; - } - num_read++; - } - } - } - fprintf(stderr, "OK.\n"); - assert(num_read = xp_obj_len); - - fprintf(stderr, "Validating the Ephemeral objects ... "); - for (uint32_t i = 0; i < xp_obj_len; i++) - { - if (!is_cksum_valid(xp_obj[i])) - { - fprintf(stderr, "FAILED.\n"); - fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; - } - } - fprintf(stderr, "OK.\n"); - - free(xp); - free(xp_desc); - - fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); - - fprintf(stderr, "Loading the container object map ... "); - omap_phys_t *nx_omap = malloc(nx_block_size); - if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the container object map ... "); - if (!is_cksum_valid(nx_omap)) - { - fprintf(stderr, "FAILED.\n"); - return 0; - } - fprintf(stderr, "OK.\n"); - - if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return 0; - } - - fprintf(stderr, "Reading the root node of the container object map B-tree ... "); - btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); - if (!nx_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); - return -1; - } - if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the container object map B-tree ... "); - if (!is_cksum_valid(nx_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - uint32_t num_file_systems = 0; - for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) - { - if (nxsb->nx_fs_oid[i] == 0) - { - break; - } - num_file_systems++; - } - fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "Reading the APFS volume superblocks ... "); - char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); - if (!apsbs) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return -1; - } - for (uint32_t i = 0; i < num_file_systems; i++) - { - omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - if (!fs_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - return -1; - } - if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the APFS volume superblocks ... "); - for (uint32_t i = 0; i < num_file_systems; i++) - { - if (!is_cksum_valid(apsbs + i)) - { - fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; - } - - if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) - { - fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "\n Volume list\n================\n"); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); - } - - if (volume_id >= num_file_systems) - { - fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); - return 0; - } - apfs_superblock_t *apsb = apsbs + volume_id; - - fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - - fprintf(stderr, "Reading the volume object map ... "); - omap_phys_t *fs_omap = malloc(nx_block_size); - if (!fs_omap) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return -1; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) - { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return 0; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return 0; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return -1; - } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return -1; - } - fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t *fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return -1; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return -1; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) - { - fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); - return 0; - } - fprintf(stderr, "OK.\n"); - - oid_t fs_oid = 0x2; - // Start with root directory oid - j_rec_t **fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0)); - if (!fs_records) - { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - - char *path = malloc(strlen(path_stack) + 1); - if (!path) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); - return -1; - } - memcpy(path, path_stack, strlen(path_stack) + 1); - - char* path_element; - while ((path_element = strsep(&path, "/")) != NULL ) { - // If path element is empty string, skip it - if (*path_element == '\0') { - continue; - } - - signed int matching_record_index = -1; - for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { - j_rec_t* fs_rec = *fs_rec_cursor; - j_key_t* hdr = fs_rec->data; - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC ) { - j_drec_key_t* key = fs_rec->data; // hashed_key when?? - if (strcmp((char*)key->name, path_element) == 0) { - matching_record_index = fs_rec_cursor - fs_records; - break; - } - } - } - - if (matching_record_index == -1) { - // No match - fprintf(stderr, "Could not find a dentry for that path. Exiting.\n"); - return -1; - } - - // Get the file ID of the matching record's target - j_rec_t* fs_rec = fs_records[matching_record_index]; - j_drec_val_t* val = fs_rec->data + fs_rec->key_len; - - // Get the records for the target - fs_oid = val->file_id; - free_j_rec_array(fs_records); - fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); - } - - fprintf(stderr, "\nRecords for file-system object %#" PRIx64 " -- `%s` --\n", fs_oid, path_stack); - // `fs_records` now contains the records for the item at the specified path - print_fs_records(fs_records); - - // Output content from all matching file extents - char* buffer = malloc(nx_block_size); - if (!buffer) { - fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); - return -1; - } - - // Get file size from the inode object records - uint64_t file_size = 0; - for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { - j_rec_t* fs_rec = *fs_rec_cursor; - j_key_t* hdr = fs_rec->data; - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE ) { - j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; - file_size = get_file_size(inode, fs_rec->val_len); - printf("File size: %" PRIu64 "\n", file_size); - } - } - if (file_size == 0) { - // Not a file, or file size couldn't be found; abort. - exit(-1); - } - - // Keep track of how many more bytes we need to output - uint64_t bytes_remaining = file_size; - - bool found_file_extent = false; - // Go through all the fs records - for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { - j_rec_t* fs_rec = *fs_rec_cursor; - j_key_t* hdr = fs_rec->data; - - // Go through all the extent records - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { - found_file_extent = true; - j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; - - // Output the content from this particular file extent - uint64_t block_addr = val->phys_block_num; - - uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; - for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { - if (read_blocks(buffer, block_addr, 1) != 1) { - fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); - return -1; - } - - uint64_t bytes_to_write = nx_block_size; - if (bytes_remaining < nx_block_size) { - bytes_to_write = bytes_remaining; - } - if (fwrite(buffer, bytes_to_write, 1, stdout) != 1) { - fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); - return -1; - } - bytes_remaining -= bytes_to_write; - - if (bytes_remaining == 0) { - break; // Exit extent loop - } - } - } - - if (bytes_remaining == 0) { - break; // Exit fs record loop - } - } - if (!found_file_extent) { - fprintf(stderr, "Could not find any file extents for the specified path.\n"); - } - - free_j_rec_array(fs_records); - - // TODO: RESUME HERE - - free(fs_omap_btree); - free(fs_omap); - - // Closing statements; de-allocate all memory, close all file descriptors. - free(apsbs); - free(nx_omap_btree); - free(nx_omap); - free(xp_obj); - free(nxsb); - fclose(nx); - fprintf(stderr, "END: All done.\n"); - return 0; -} diff --git a/src/commands/timeline.c b/src/commands/timeline.c index c7be08b..955eefa 100644 --- a/src/commands/timeline.c +++ b/src/commands/timeline.c @@ -31,85 +31,80 @@ #include typedef struct file_metadata { - char *filename; - uint64_t create_time; uint64_t mod_time; - uint64_t change_time; uint64_t access_time; + uint64_t change_time; + uint64_t create_time; cp_key_class_t default_protection_class; + char *filename; // cprotect xattr ? } file_metadata; +file_metadata** timeline; + static int fs_file_index = 0; +static int total_files = 0; int compare_create(file_metadata *first, file_metadata *second) { struct file_metadata *ptr_to_first = *(file_metadata **)first; struct file_metadata *ptr_to_second = *(file_metadata **)second; - return ptr_to_first->create_time - ptr_to_second->create_time; + if (ptr_to_first->create_time > ptr_to_second->create_time) return 1; + else if (ptr_to_first->create_time < ptr_to_second->create_time) return -1; + else return 0; } int compare_mod(file_metadata *first, file_metadata *second) { struct file_metadata *ptr_to_first = *(file_metadata **)first; struct file_metadata *ptr_to_second = *(file_metadata **)second; - return ptr_to_first->mod_time - ptr_to_second->mod_time; + if (ptr_to_first->mod_time > ptr_to_second->mod_time) return 1; + else if (ptr_to_first->mod_time < ptr_to_second->mod_time) return -1; + else return 0; } int compare_change(file_metadata *first, file_metadata *second) { struct file_metadata *ptr_to_first = *(file_metadata **)first; struct file_metadata *ptr_to_second = *(file_metadata **)second; - return ptr_to_first->change_time - ptr_to_second->change_time; + if (ptr_to_first->change_time > ptr_to_second->change_time) return 1; + else if (ptr_to_first->change_time < ptr_to_second->change_time) return -1; + else return 0; } int compare_access(file_metadata *first, file_metadata *second) { struct file_metadata *ptr_to_first = *(file_metadata **)first; struct file_metadata *ptr_to_second = *(file_metadata **)second; - return ptr_to_first->access_time - ptr_to_second->access_time; -} - -const char* nth_strchr(const char* s, char c, int n) { - int c_count; - char* nth_ptr; - for (c_count=1,nth_ptr=strchr(s,c); nth_ptr != NULL && c_count < n && c!=0; c_count++) { - nth_ptr = strchr(nth_ptr+1, c); - } - return nth_ptr; -} - -// /private/var/etc -> /var/etc -> /etc -> null -char *get_remaining_url(char *url, char token) { - const char *position_ptr = nth_strchr(url, token, 2); - int position = (position_ptr == NULL ? -1 : position_ptr - url); - - if (position > 0) { - char *newurl = malloc(strlen(url) - position); - strncpy(newurl, url+position, strlen(url) - position + 1); - return newurl; - } - return NULL; + if (ptr_to_first->access_time > ptr_to_second->access_time) return 1; + else if (ptr_to_first->access_time < ptr_to_second->access_time) return -1; + else return 0; } -// /private/var/etc -> private -char *get_first_entry(char *url, char token) { - char *left_position_ptr = strchr(url, token); - if (left_position_ptr == NULL) { - return NULL; - } - - int left_position = left_position_ptr - url; - const char *right_position_ptr = nth_strchr(url, token, 2); - if (right_position_ptr == NULL) { - char *entry = malloc(strlen(url)); - strncpy(entry, url + left_position + 1, strlen(url)); - return entry; +void merge_files(file_metadata **fs_files, int size) { + if (total_files == 0) { + timeline = malloc(sizeof(file_metadata *) * size); + for (int i = 0; i < size; i++) { + timeline[i] = malloc(sizeof(file_metadata)); + timeline[i]->filename = fs_files[i]->filename; + timeline[i]->create_time = fs_files[i]->create_time; + timeline[i]->mod_time = fs_files[i]->mod_time; + timeline[i]->change_time = fs_files[i]->change_time; + timeline[i]->access_time = fs_files[i]->access_time; + timeline[i]->default_protection_class = fs_files[i]->default_protection_class; + } + } else { + timeline = realloc(timeline, sizeof(file_metadata *) * (total_files + size)); + for (int i = total_files, j = 0; j < size; i++, j++) { + timeline[i] = malloc(sizeof(file_metadata)); + timeline[i]->filename = fs_files[j]->filename; + timeline[i]->create_time = fs_files[j]->create_time; + timeline[i]->mod_time = fs_files[j]->mod_time; + timeline[i]->change_time = fs_files[j]->change_time; + timeline[i]->access_time = fs_files[j]->access_time; + timeline[i]->default_protection_class = fs_files[j]->default_protection_class; + } } - int right_position = right_position_ptr - url; - char *entry = malloc(right_position - left_position - 1); - strncpy(entry, url+left_position+1, right_position - left_position - 1); - return entry; + total_files += size; } - -void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, struct file_metadata **fs_files, char *path_to_process, char *full_path) +void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, file_metadata **fs_files, char *path_to_process, char *full_path) { if (fs_records == NULL || fs_records[0] == NULL) { return; @@ -127,7 +122,8 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node j_key_t *hdr = fs_rec->data; if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) { - j_drec_key_t *key = fs_rec->data; + //j_drec_key_t *key = fs_rec->data; + j_drec_hashed_key_t *key = fs_rec->data; if (path_to_process != NULL) { res = strcmp((char *)key->name, entry); } @@ -174,7 +170,6 @@ void parse_fs(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node continue; } } - free_j_rec_array(fs_records); } /** @@ -185,8 +180,8 @@ static void print_usage(int argc, char **argv) fprintf( argc == 1 ? stdout : stderr, - "Usage: %s \n" - "Example: %s /dev/disk0s2 0 /Users/john/Documents access\n" + "Usage: %s \n" + "Example: %s /dev/disk0s2 /Users/john/Documents access\n" "Available timestamps: modify, access, change, birth\n", argv[0], argv[0]); @@ -197,7 +192,7 @@ int cmd_timeline(int argc, char **argv) setbuf(stdout, NULL); // Extrapolate CLI arguments, exit if invalid - if (argc != 5) + if (argc != 4) { fprintf(stderr, "Incorrect number of arguments.\n"); print_usage(argc, argv); @@ -205,19 +200,9 @@ int cmd_timeline(int argc, char **argv) } nx_path = argv[1]; - char* timestamp = argv[4]; - - uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%u", &volume_id); - if (!parse_success) - { - fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); - print_usage(argc, argv); - return 1; - } - - char *path_stack = argv[3]; - + + char* path_stack = argv[2]; + char* timestamp = argv[3]; // Open (device special) file corresponding to an APFS container, read-only fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); nx = fopen(nx_path, "rb"); @@ -227,12 +212,10 @@ int cmd_timeline(int argc, char **argv) report_fopen_error(); return -errno; } - fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); - - // Using `nx_superblock_t*`, but allocating a whole block of memory. - // This way, we can read the entire block and validate its checksum, - // but still have direct access to the fields in `nx_superblock_t` - // without needing to epxlicitly cast to that datatype. + fseek(nx, 0, SEEK_END); + long size = ftell(nx); + fseek(nx, 0, SEEK_SET); + printf("Image File size: %ld\n", size); nx_superblock_t *nxsb = malloc(nx_block_size); if (!nxsb) { @@ -246,13 +229,11 @@ int cmd_timeline(int argc, char **argv) return -1; } - fprintf(stderr, "Validating checksum of block 0x0 ... "); + fprintf(stderr, "Validating checksum of block 0x0 ..."); if (!is_cksum_valid(nxsb)) { fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); } - fprintf(stderr, "OK.\n"); - if (!is_nx_superblock(nxsb)) { fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); @@ -261,445 +242,110 @@ int cmd_timeline(int argc, char **argv) { fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); } + printf("OK\n"); + uint64_t nx_block_size = nxsb->nx_block_size; + struct file_metadata **fs_files; - fprintf(stderr, "Locating the checkpoint descriptor area:\n"); - - uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); - - char(*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); - if (!xp_desc) - { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); - return -1; - } - - if (nxsb->nx_xp_desc_blocks >> 31) - { - fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous - return 0; - } - else - { - fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - - fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); - if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) - { - fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); - return -1; + for (uint32_t block_index = 0; block_index < size/nx_block_size; block_index++) { + char *apsb = malloc(nx_block_size); + if (!apsb) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return 1; } - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); - - uint32_t i_latest_nx = 0; - xid_t xid_latest_nx = 0; - - xid_t max_xid = ~0; // `~0` is the highest possible XID - - for (uint32_t i = 0; i < xp_desc_blocks; i++) - { - if (!is_cksum_valid(xp_desc[i])) + if (read_blocks(apsb, block_index, 1) != 1) { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIu32 ".\n", block_index); continue; } - - if (is_nx_superblock(xp_desc[i])) + if (is_cksum_valid(apsb)) { - if (((nx_superblock_t *)xp_desc[i])->nx_magic != NX_MAGIC) - { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; - } - - if ( - (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid > xid_latest_nx) && (((nx_superblock_t *)xp_desc[i])->nx_o.o_xid <= max_xid)) + if (((apfs_superblock_t *)apsb)->apfs_magic == APFS_MAGIC) { - i_latest_nx = i; - xid_latest_nx = ((nx_superblock_t *)xp_desc[i])->nx_o.o_xid; - } - } - else if (!is_checkpoint_map_phys(xp_desc[i])) - { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; - } - } - - if (xid_latest_nx == 0) - { - fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); - return 0; - } - - // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` - // anymore; replace that data with the latest NXSB. - // This also lets us avoid repeatedly casting to `nx_superblock_t*`. - memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); - fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); - - // Copy the contents of the checkpoint we are currently considering to its - // own array for easy access. The checkpoint descriptor area is a ring - // buffer stored as an array, so doing this also allows us to handle the - // case where the checkpoint we're considering wraps around the ring buffer. - fprintf(stderr, "Loading the corresponding checkpoint ... "); - - // The array `xp` will comprise the blocks in the checkpoint, in order. - char(*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); - if (!xp) - { - fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); - return -1; - } - - if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) - { - // The simple case: the checkpoint is already contiguous in `xp_desc`. - memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); - } - else - { - // The case where the checkpoint wraps around from the end of the - // checkpoint descriptor area to the start. - uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; - uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; - memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); - memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); - } - fprintf(stderr, "OK.\n"); - - // We could `free(xp_desc)` at this point, but instead, we retain our copy - // of the checkpoint descriptor area in case any of the Ephemeral objects - // referenced by the current checkpoint are malformed; then, we can - // retrieve an older checkpoint without having to read the checkpoint - // descriptor area again. - - uint32_t xp_obj_len = 0; // This variable will equal the number of - // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) - { - if (is_checkpoint_map_phys(xp[i])) - { - xp_obj_len += ((checkpoint_map_phys_t *)xp[i])->cpm_count; - } - } - fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); - - fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); - char(*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); - if (!xp_obj) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); - return -1; - } - uint32_t num_read = 0; - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) - { - if (is_checkpoint_map_phys(xp[i])) - { - checkpoint_map_phys_t *xp_map = xp[i]; // Avoid lots of casting - for (uint32_t j = 0; j < xp_map->cpm_count; j++) - { - if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) + printf("Found valid APSB block at addr 0x%" PRIu32 "\n", block_index); + struct fs_browse_info *fs_trees = get_root_fs_tree(apsb, nx_block_size); + if (fs_trees == NULL) { + fprintf(stderr, "Error getting the fs tree\n"); + continue; + } + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); return -1; } - num_read++; - } - } - } - fprintf(stderr, "OK.\n"); - assert(num_read = xp_obj_len); - fprintf(stderr, "Validating the Ephemeral objects ... "); - for (uint32_t i = 0; i < xp_obj_len; i++) - { - if (!is_cksum_valid(xp_obj[i])) - { - fprintf(stderr, "FAILED.\n"); - fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; - } - } - fprintf(stderr, "OK.\n"); - - free(xp); - free(xp_desc); - - fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); - - fprintf(stderr, "Loading the container object map ... "); - omap_phys_t *nx_omap = malloc(nx_block_size); - if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the container object map ... "); - if (!is_cksum_valid(nx_omap)) - { - fprintf(stderr, "FAILED.\n"); - return 0; - } - fprintf(stderr, "OK.\n"); - - if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return 0; - } - - fprintf(stderr, "Reading the root node of the container object map B-tree ... "); - btree_node_phys_t *nx_omap_btree = malloc(nx_block_size); - if (!nx_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); - return -1; - } - if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the container object map B-tree ... "); - if (!is_cksum_valid(nx_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - uint32_t num_file_systems = 0; - for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) - { - if (nxsb->nx_fs_oid[i] == 0) - { - break; - } - num_file_systems++; - } - fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "Reading the APFS volume superblocks ... "); - char(*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); - if (!apsbs) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return -1; - } - for (uint32_t i = 0; i < num_file_systems; i++) - { - omap_entry_t *fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - if (!fs_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - return -1; - } - if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); - return -1; - } - } - fprintf(stderr, "OK.\n"); + if (path_stack[strlen(path_stack) - 1] == '/' && strcmp(path_stack, "/") != 0) { + path_stack[strlen(path_stack) - 1] = '\0'; + } + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); - fprintf(stderr, "Validating the APFS volume superblocks ... "); - for (uint32_t i = 0; i < num_file_systems; i++) - { - if (!is_cksum_valid(apsbs + i)) - { - fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + printf("Entering directory /\n"); + char *path_builder = ""; + fs_files = malloc(sizeof(struct file_metadata *) * ((apfs_superblock_t *)apsb)->apfs_num_files); - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; - } + parse_fs(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_files, path, path_builder); - if (((apfs_superblock_t *)(apsbs + i))->apfs_magic != APFS_MAGIC) - { - fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return 0; + if (fs_files == NULL) { + fprintf(stderr, "Error reading volume files\n"); + exit(-1); + } + merge_files(fs_files, fs_file_index); + fs_file_index = 0; + free(fs_trees->fs_omap_btree); + free(fs_trees->fs_root_btree); + free(fs_trees); + free_j_rec_array(fs_records); + free(fs_files); + free(apsb); + } } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "\n Volume list\n================\n"); - for (uint32_t i = 0; i < num_file_systems; i++) - { - fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t *)(apsbs + i))->apfs_volname); - } - - if (volume_id >= num_file_systems) - { - fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); - return 0; - } - apfs_superblock_t *apsb = apsbs + volume_id; - - fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - fprintf(stderr, "Reading the volume object map ... "); - omap_phys_t *fs_omap = malloc(nx_block_size); - if (!fs_omap) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return -1; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) - { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return 0; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return 0; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return -1; } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return -1; - } - fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t *fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return -1; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return -1; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) - { - fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); - return 0; - } - fprintf(stderr, "OK.\n"); - - oid_t fs_oid = 0x2; - // Start with root directory oid - j_rec_t **fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0)); - if (!fs_records) - { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - - char *path = malloc(strlen(path_stack) + 1); - if (!path) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); - return -1; - } - memcpy(path, path_stack, strlen(path_stack) + 1); - - struct file_metadata **fs_files = malloc(sizeof(struct file_metadata *) * apsb->apfs_num_files); - printf("Entering directory /\n"); - char *path_builder = ""; - parse_fs(fs_records, fs_omap_btree, fs_root_btree, fs_files, path, path_builder); - printf("Sorting %d elements by timestamp...\n", fs_file_index); + + printf("Sorting %d elements by timestamp...\n", total_files); int (*comparePtr)(file_metadata *, file_metadata *); - if (strcmp(timestamp, "modify")) { + int offset; + if (strcmp(timestamp, "modify") == 0) { comparePtr = &compare_mod; - } else if (strcmp(timestamp, "access")) { + offset = 0; + } else if (strcmp(timestamp, "access") == 0) { comparePtr = &compare_access; - } else if (strcmp(timestamp, "change")) { + offset = 1; + } else if (strcmp(timestamp, "change") == 0) { comparePtr = &compare_change; - } else if (strcmp(timestamp, "birth")) { + offset = 2; + } else if (strcmp(timestamp, "birth") == 0) { comparePtr = &compare_create; + offset = 3; } else { fprintf(stderr, "Wrong timestamp requested\n"); + return -1; } - qsort(fs_files, fs_file_index, sizeof(file_metadata *), comparePtr); + for (int i = 0; i < total_files; i++) { + printf("Filename: %s\n", timeline[i]->filename); + printf("%s timestamp: %s\n", timestamp, apfs_timestamp_to_string(*(((uint64_t *)timeline[i])+offset))); + printf("\n"); + } + qsort(timeline, total_files, sizeof(file_metadata *), comparePtr); printf("======================================================================\n"); printf("Timeline:\n"); printf("======================================================================\n"); - for (int i = 0; i < fs_file_index; i++) { - printf("Filename: %s\n", fs_files[i]->filename); - printf("%s timestamp: %s\n", timestamp, apfs_timestamp_to_string(fs_files[i]->create_time)); + for (int i = 0; i < total_files; i++) { + printf("Filename: %s\n", timeline[i]->filename); + printf("%s timestamp: %s\n", timestamp, apfs_timestamp_to_string(*(((uint64_t *)timeline[i])+offset))); printf("\n"); } - free(fs_omap_btree); - free(fs_omap); - free(apsbs); - free(nx_omap_btree); - free(nx_omap); - free(xp_obj); free(nxsb); fclose(nx); fprintf(stderr, "END: All done.\n"); return 0; -} +} \ No newline at end of file From 0dc50801d3dc54ce4b8335060454adf7e7f5034e Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Tue, 21 Sep 2021 02:10:56 +0200 Subject: [PATCH 7/9] Removed output file --- out.txt | 104 -------------------------------------------------------- 1 file changed, 104 deletions(-) delete mode 100644 out.txt diff --git a/out.txt b/out.txt deleted file mode 100644 index 7cfb092..0000000 --- a/out.txt +++ /dev/null @@ -1,104 +0,0 @@ -Opening file at `../apfs.dmg` in read-only mode ... OK. -Simulating a mount of the APFS container. -Validating checksum of block 0x0 ... OK. -Locating the checkpoint descriptor area: -- Its length is 48 blocks. -- It is contiguous. -- The address of its first block is 0xb43d. -Loading the checkpoint descriptor area into memory ... OK. -Locating the most recent well-formed container superblock in the checkpoint descriptor area: -- It lies at index 3 within the checkpoint descriptor area. -- The corresponding checkpoint starts at index 2 within the checkpoint descriptor area, and spans 2 blocks. - -Loading the corresponding checkpoint ... OK. -- There are 5 checkpoint-mappings in this checkpoint. - -Reading the Ephemeral objects used by this checkpoint ... OK. -Validating the Ephemeral objects ... OK. -The container superblock states that the container object map has Physical OID 0x1ae. -Loading the container object map ... OK. -Validating the container object map ... OK. -Reading the root node of the container object map B-tree ... OK. -Validating the root node of the container object map B-tree ... OK. -The container superblock lists 1 APFS volumes, whose superblocks have the following Virtual OIDs: -- 0x402 - -Reading the APFS volume superblocks ... OK. -Validating the APFS volume superblocks ... OK. - - Volume list -================ - 0: Peace16A366.D10D101D20D201OS -The volume object map has Physical OID 0x1a6. -Reading the volume object map ... OK. -Validating the volume object map ... OK. -Reading the root node of the volume object map B-tree ... OK. -Validating the root node of the volume object map B-tree ... OK. -The file-system tree root for this volume has Virtual OID 0xa7c5. -Looking up this Virtual OID in the volume object map ... corresponding block address is 0x19e. -Reading ... validating ... OK. -Entering directory / -- INODE -Creation time: Wed Aug 29 09:12:17 2018 -Last modification time: Sat Aug 28 15:54:39 2021 -Last access time: Sat Aug 28 15:54:39 2021 - -Number of extended fields: 1 -Details of extended fields: - -== KEY == -Type: Item name -Size: 5 bytes -Flags: Do not copy -== VALUE == -Item name: root - - -- DIR REC || Dirctry || target ID = 0x10002f6ae || name = .DocumentRevisions-V100 -- DIR REC || Dirctry || target ID = 0x13 || name = .HFS+ Private Directory Data -- DIR REC || Dirctry || target ID = 0x10002f6b9 || name = .TemporaryItems -- DIR REC || RegFile || target ID = 0x2f731 || name = .Trashes -- DIR REC || Dirctry || target ID = 0x49b || name = .ba -- DIR REC || RegFile || target ID = 0x499 || name = .file -- DIR REC || Dirctry || target ID = 0x10002f6a8 || name = .fseventsd -- DIR REC || Dirctry || target ID = 0x2a7 || name = .mb -- DIR REC || Dirctry || target ID = 0x14 || name = Applications -- DIR REC || Dirctry || target ID = 0x3002 || name = Developer -- DIR REC || Dirctry || target ID = 0x28c5 || name = Library -- DIR REC || Dirctry || target ID = 0x2cab || name = System -- DIR REC || Dirctry || target ID = 0x2b5c2 || name = bin -- DIR REC || Dirctry || target ID = 0x2b5dc || name = cores -- DIR REC || Dirctry || target ID = 0x2b5e6 || name = dev -- DIR REC || Symlink || target ID = 0x2b5f0 || name = etc -- DIR REC || Dirctry || target ID = 0x2b5f9 || name = private -- DIR REC || Dirctry || target ID = 0x2f0c0 || name = sbin -- DIR REC || Symlink || target ID = 0x2f0e7 || name = tmp -- DIR REC || Dirctry || target ID = 0x2f0d0 || name = usr -- DIR REC || Symlink || target ID = 0x2f57c || name = var - -Inode record type: Directory -Analysing record: etc -- INODE -Creation time: Wed Aug 29 09:20:42 2018 -Last modification time: Wed Aug 29 09:20:42 2018 -Last access time: Wed Aug 29 09:20:42 2018 - -Number of extended fields: 1 -Details of extended fields: - -== KEY == -Type: Item name -Size: 4 bytes -Flags: Do not copy -== VALUE == -Item name: etc - - -- XATTR || name = com.apple.fs.symlink - -Inode record type: Symbolic link -Sorting 0 elements by timestamp... -====================================================================== -Timeline: -====================================================================== -END: All done. From 45213a4cee3f52a7e24c6b9f14a1192bfac8f708 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Fri, 8 Oct 2021 23:10:45 +0200 Subject: [PATCH 8/9] merge --- .gitignore | 16 +- .readthedocs.yml | 18 +- CHANGELOG.md | 646 ++++++------ LICENSE.txt | 1348 ++++++++++++------------ README.md | 220 ++-- docs/Makefile | 48 +- docs/_ext/drat.py | 64 +- docs/commands/create-index.md | 40 +- docs/commands/explore-fs-tree.md | 694 ++++++------- docs/commands/explore-fs.md | 490 ++++----- docs/commands/explore-omap-tree.md | 602 +++++------ docs/commands/index.md | 66 +- docs/commands/inspect.md | 1152 ++++++++++----------- docs/commands/read.md | 50 +- docs/commands/recover.md | 144 +-- docs/commands/resolve-virtual-oids.md | 92 +- docs/commands/search.md | 386 +++---- docs/commands/version.md | 48 +- docs/conf.py | 118 +-- docs/global-arguments/block-size.md | 16 +- docs/global-arguments/container.md | 52 +- docs/global-arguments/index.md | 44 +- docs/global-arguments/max-xid.md | 28 +- docs/global-arguments/volume.md | 52 +- docs/index.md | 34 +- docs/requirements.txt | 6 +- include/apfs/README.md | 8 +- include/apfs/btree.h | 216 ++-- include/apfs/crypto.h | 242 ++--- include/apfs/cryptorolling.h | 250 ++--- include/apfs/cryptotypes.h | 44 +- include/apfs/dstream.h | 156 +-- include/apfs/fs.h | 410 ++++---- include/apfs/fusion.h | 168 +-- include/apfs/general.h | 40 +- include/apfs/j.h | 282 ++--- include/apfs/jconst.h | 398 +++---- include/apfs/jumpstart.h | 64 +- include/apfs/nx.h | 316 +++--- include/apfs/object.h | 218 ++-- include/apfs/omap.h | 166 +-- include/apfs/reaper.h | 226 ++-- include/apfs/sealed.h | 222 ++-- include/apfs/sibling.h | 78 +- include/apfs/snap.h | 146 +-- include/apfs/spaceman.h | 358 +++---- include/drat/asize.h | 12 +- include/drat/func/README.md | 14 +- include/drat/func/boolean.c | 160 +-- include/drat/func/boolean.h | 36 +- include/drat/func/btree.h | 98 +- include/drat/func/cksum.h | 22 +- include/drat/func/j.c | 78 +- include/drat/func/j.h | 20 +- include/drat/func/xf.c | 188 ++-- include/drat/func/xf.h | 46 +- include/drat/io.h | 38 +- include/drat/print-fs-records.h | 18 +- include/drat/string/README.md | 8 +- include/drat/string/btree.h | 24 +- include/drat/string/common.c | 282 ++--- include/drat/string/common.h | 74 +- include/drat/string/dstream.c | 40 +- include/drat/string/dstream.h | 16 +- include/drat/string/fs.h | 26 +- include/drat/string/general.c | 36 +- include/drat/string/general.h | 16 +- include/drat/string/j.h | 64 +- include/drat/string/nx.h | 32 +- include/drat/string/object.h | 24 +- include/drat/string/omap.h | 22 +- include/drat/string/xf.c | 378 +++---- include/drat/string/xf.h | 24 +- include/drat/time.c | 34 +- include/drat/time.h | 16 +- include/drat/utilities.c | 266 ++--- include/drat/utilities.h | 76 +- pull.sh | 102 +- remove-all-in-filelist.sh | 48 +- src/commands.h | 156 +-- src/commands/README.md | 112 +- src/commands/dumpfiles.c | 606 +++++------ src/commands/list-raw.c | 950 ++++++++--------- src/commands/modify.c | 1382 ++++++++++++------------- src/commands/recover-raw.c | 1022 +++++++++--------- src/commands/search-last-btree-node.c | 274 ++--- src/commands/search.c | 1012 +++++++++--------- src/commands/version.c | 28 +- src/drat.c | 86 +- src/legal.h | 30 +- 90 files changed, 9239 insertions(+), 9239 deletions(-) diff --git a/.gitignore b/.gitignore index f1fc71e..2211581 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.DS_Store -.vscode/ - -/drat -/out/ - -/docs/_build/ -/docs/_ext/__pycache__/ +.DS_Store +.vscode/ + +/drat +/out/ + +/docs/_build/ +/docs/_ext/__pycache__/ diff --git a/.readthedocs.yml b/.readthedocs.yml index 572d87c..a3ba938 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,9 +1,9 @@ -version: 2 - -sphinx: - configuration: docs/conf.py - -python: - version: 3.8 - install: - - requirements: docs/requirements.txt +version: 2 + +sphinx: + configuration: docs/conf.py + +python: + version: 3.8 + install: + - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cf66cb..26199f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,323 +1,323 @@ -# Drat changelog - -## v0.1.3 (2021-08-29) - -### Functional changes - -- Linux support added (tested under Ubuntu 20.04.3). From this version onwards, - binaries for Linux on x86-64 will also be provided for versioned releases. - Some more general code portability provisions have also been made. - -- `drat inspect` now automatically detects the APFS block size, based on the - block size value provided by the container superblock found at block 0. - -- Extended fields (xfields) are now supported. - -- `drat recover` now gets the actual file size and thus outputs files of the - correct size rather than a multiple of the APFS block size. The tools in - `/supplemental-tools` have thus been removed. ([Issue #2](https://github.com/jivanpal/drat/issues/2)). - -- UUIDs are now printed in standard format. - -- Fixed bug in `get_nx_incompatible_features_string()` and `get_nx_flags_string()` - that caused these functions to analyse the `nx_features` field instead. - -- `drat version` now outputs legal info (copyright notice, warranty, and - licence). - -### Other changes - -- Refactored libraries: - - - `/include` has been added to the compilation include path to avoid use of - relative paths. - - - `/src/apfs/struct` has moved to `/include/apfs`, and can thus be included - as ``. This library contains headers that define all data structures - detailed in Apple's APFS specfication. - - - The remainder of `/src/apfs` has moved to `/include/drat`, and can thus be - included as ``. This library contains functionality specific to Drat: - - - Headers in `` define functions that facilitate common - operations on data strctures provided by ``; - - - Headers in `` define functions that produce human-readable - strings/output detailing the data contained in data structures provided - by ``; and - - - Headers in `` define miscellaneous functions. - -- Refactored code: - - - String generation functions that operate on enum fields have had their - shared code factored into `` as - `get_single_enum_string()` and `get_flags_enum_string()`. - - - Use of `ctime()` for printing timestamps has been factored into - `get_apfs_timestamp_string()`. - -- Documentation for v0.2.x has been drafted in `/docs` using - [Sphinx](https://www.sphinx-doc.org/en/master/). - -## v0.1.2 (2021-02-05) - -- Renamed `src/apfs/struct/const.h` to `src/apfs/struct/jconst.h` - -- Updated symbol/struct definitions in `src/apfs/struct` to align with the - latest APFS spec revision (2020-06-22). This included adding a new file - `sealed.h` with the definitions contained in the new chapter of the spec, - §16 Sealed Volumes. A detailed list of changes follows: - - - §3 Objects — `object.h` - - - §3.1 Object Types - - - `OBJECT_TYPE_ER_RECOVERY_BLOCK` - - `OBJECT_TYPE_SNAP_META_EXT` - - `OBJECT_TYPE_INTEGRITY_META` - - `OBJECT_TYPE_FEXT_TREE` - - `OBJECT_TYPE_RESERVED_20` - - `OBJECT_TYPE_MEDIA_KEYBAG` - - - §5 Container — `nx.h` - - - §5.2 `nx_superblock_t` - - - `nx_newest_mounted_version` - - `nx_mkb_locker` - - - §6 Object Maps — `omap.h` - - - §6.7 Object Map Flags - - - `OMAP_VALID_FLAGS` - - - §7 Volumes — `fs.h` - - - §7.1 `apfs_superblock_t` - - - `apfs_cloneinfo_id_epoch` - - `apfs_cloneinfo_xid` - - `apfs_snap_meta_ext_oid` - - `apfs_volume_group_id` - - `apfs_integrity_meta_oid` - - `apfs_fext_tree_oid` - - `apfs_fext_tree_type` - - `reserved_type` - - `reserved_oid` - - - §7.3 Volume Flags - - - `APFS_FS_RESERVED_80` - - `APFS_FS_RESERVED_100` - - `APFS_FS_FLAGS_VALID_MASK` (name corrected from erroneous `APFS_FS_FALGS_VALID_MASK`) - - - §7.4 Volume Roles - - - `APFS_VOL_ROLE_UPDATE` - - `APFS_VOL_ROLE_XART` - - `APFS_VOL_ROLE_HARDWARE` - - `APFS_VOL_ROLE_BACKUP` - - `APFS_VOL_ROLE_RESERVED_7` - - `APFS_VOL_ROLE_RESERVED_8` (name changed from `APFS_VOL_ROLE_RESERVED_200`) - - `APFS_VOL_ROLE_ENTERPRISE` - - `APFS_VOL_ROLE_RESERVED_10` - - `APFS_VOL_ROLE_PRELOGIN` - - - §7.5 Optional Volume Feature Flags - - - `APFS_FEATURE_STRICTATIME` - - `APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE` - - - §7.7 Incompatible Volume Feature Flags - - - `APFS_INCOMPAT_INCOMPLETE_RESTORE` - - `APFS_INCOMPAT_SEALED_VOLUME` - - - §8 File-System Objects — `j.h` - - - §8.1 `j_key_t` - - - `SYSTEM_OBJ_ID_MARK` - - - §8.3 `j_inode_val_t` - - - `uncompressed_size` - - - §9 File-System Constants — `jconst.h` - - - §9.1 `j_obj_types` - - - `APFS_TYPE_FILE_INFO` - - - §9.3 `j_inode_flags` - - - `INODE_FAST_PROMOTE` - - `INODE_HAS_UNCOMPRESSED_SIZE` - - `INODE_IS_PURGEABLE` - - `INODE_WANTS_TO_BE_PURGEABLE` - - `INODE_IS_SYNC_ROOT` - - `INODE_SNAPSHOT_COW_EXEMPTION` - - - §9.6 Inode Numbers - - - `PURGEABLE_DIR_INO_NUM` - - `UNIFIED_ID_SPACE_MARK` - - - §9.7 Extended Attributes Constants - - - `FIRMLINK_EA_NAME` - - `APFS_COW_EXEMPT_COUNT_NAME` - - - §11 Extended Fields — `xf.h` - - - §11.3 Extended-Field Types - - - `INO_EXT_TYPE_PURGEABLE_FLAGS` - - `INO_EXT_TYPE_ORIG_SYNC_ROOT_ID` - - - §13 Snapshot Metadata — `snap.h` - - - *New datatype*: §13.6 `snap_meta_ext_obj_phys_t` - - *New datatype*: §13.7 `snap_meta_ext_t` - - - §14 B-Trees — `btree.h` - - - *New datatype*: §14.4 `btn_index_node_val_t` - - - §14.8 B-Tree Flags - - - `BTREE_HASHED` - - `BTREE_NOHEADER` - - - §14.10 B-Tree Node Flags - - - `BTNODE_HASHED` - - `BTNODE_NOHEADER` - - - §15 Encryption — `crypto.h` - - - §15.7 Protection Classes - - - `PROTECTION_CLASS_M` - - - §15.8 Encryption Identifiers - - - `APFS_UNASSIGNED_CRYPTO_ID` - - - §15.12 Keybag Tags - - - `KB_TAG_WRAPPING_M_KEY` - - `KB_TAG_VOLUME_M_KEY` - - - *New chapter*: §16 Sealed Volumes — `sealed.h` - - - *New datatype*: §16.1 `integrity_meta_phys_t` - - - §16.2 Integrity Metadata Version Constants - - - `INTEGRITY_META_VERSION_INVALID` - - `INTEGRITY_META_VERSION_1` - - `INTEGRITY_META_VERSION_2` - - `INTEGRITY_META_VERSION_HIGHEST` - - - §16.3 Integrity Metadata Flags - - - `APFS_SEAL_BROKEN` - - - §16.4 `apfs_hash_type_t` - - - `APFS_HASH_INVALID` - - `APFS_HASH_SHA256` - - `APFS_HASH_SHA512_256` - - `APFS_HASH_SHA384` - - `APFS_HASH_SHA512` - - `APFS_HASH_MIN` - - `APFS_HASH_MAX` - - `APFS_HASH_DEFAULT` - - - *New datatype*: §16.5 `fext_tree_key_t` - - - *New datatype*: §16.6 `fext_tree_val_t` - - - *New datatype*: §16.7 `j_file_info_key_t` - - - `J_FILE_INFO_LBA_MASK` - - `J_FILE_INFO_TYPE_MASK` - - `J_FILE_INFO_TYPE_SHIFT` - - - *New datatype*: §16.8 `j_file_info_val_t` - - - §16.9 `j_obj_file_info_type` - - - `APFS_FILE_INFO_DATA_HASH` - - - *New datatype*: §16.10 `j_file_data_hash_val_t` - - - §17 Space Manager — `spaceman.h` - - - *New datatype*: §17.4 `spaceman_free_queue_entry_t` - -- Updated existing functions in `src/apfs/string` to handle new symbols: - - - In `btree.c`: - - `get_btn_flags_string()` - - `get_bt_info_flags_string()` - - - In `fs.c`: - - `get_apfs_features_string()` - - `get_apfs_incompatible_features_string()` - - `get_apfs_fs_flags_string()` - - `get_apfs_role_string()` - - `print_apfs_superblock()` - - - In `j.c` - - `get_j_inode_internal_flags()` - - `print_j_inode_val()` - - - In `nx.c`: - - `get_nx_flags_string()` - - `print_nx_superblock()` - - - In `object.c`: - - `get_o_type_string()` - - `get_o_subtype_string()` - -## v0.1.1 (2021-02-02) - -- Fixed bug in `get_o_type_string()` where the "Unknown type" output did not - correctly display the hex value of the type field - (`o_type & OBJECT_TYPE_MASK`). - -- Re-implemented (and hopefully fixed all bugs in) the B-tree routines in - `src/apfs/func/btree.c`. Of particular note: - - - `get_btree_phys_omap_val()` is deprecated and replaced with - `get_btree_phys_omap_entry()`, which allows the caller to see the XID of - the returned entry. - - - `get_fs_records()` is now more efficient, as it no longer makes multiple - descents from the root node just to access multiple records in the same leaf - node. Instead, we now just walk along the leaf node, making new descents - only when we need to access a different node. - -## v0.1.0 (2021-01-26) - -Initial release, basically the same as the prior `apfs-tools` commands, but packaged into a single `drat` command. Recognised commands and their functionality are highly subject to change for now. The current list of commands is: - -``` -List of commands: - explore-fs-tree Explore filesystem B-tree - explore-omap-tree Explore object map B-tree - inspect Inspect APFS partition - list-raw List directory contents or file info based on its filesystem OID - list List directory contents or file info based on its filepath - read Read a block and display information about it - recover-raw Recover a file based on its filesystem OID - recover Recover a file bsaed on its filepath - resolver Check if given Virtual OIDs resolve to given Physical OIDs - search-last-btree-node Search the partition for B-tree nodes, reporting the Physical OID of the last one discovered - search Search the partition for blocks with certain features/properties - version Display Drat's version info -``` +# Drat changelog + +## v0.1.3 (2021-08-29) + +### Functional changes + +- Linux support added (tested under Ubuntu 20.04.3). From this version onwards, + binaries for Linux on x86-64 will also be provided for versioned releases. + Some more general code portability provisions have also been made. + +- `drat inspect` now automatically detects the APFS block size, based on the + block size value provided by the container superblock found at block 0. + +- Extended fields (xfields) are now supported. + +- `drat recover` now gets the actual file size and thus outputs files of the + correct size rather than a multiple of the APFS block size. The tools in + `/supplemental-tools` have thus been removed. ([Issue #2](https://github.com/jivanpal/drat/issues/2)). + +- UUIDs are now printed in standard format. + +- Fixed bug in `get_nx_incompatible_features_string()` and `get_nx_flags_string()` + that caused these functions to analyse the `nx_features` field instead. + +- `drat version` now outputs legal info (copyright notice, warranty, and + licence). + +### Other changes + +- Refactored libraries: + + - `/include` has been added to the compilation include path to avoid use of + relative paths. + + - `/src/apfs/struct` has moved to `/include/apfs`, and can thus be included + as ``. This library contains headers that define all data structures + detailed in Apple's APFS specfication. + + - The remainder of `/src/apfs` has moved to `/include/drat`, and can thus be + included as ``. This library contains functionality specific to Drat: + + - Headers in `` define functions that facilitate common + operations on data strctures provided by ``; + + - Headers in `` define functions that produce human-readable + strings/output detailing the data contained in data structures provided + by ``; and + + - Headers in `` define miscellaneous functions. + +- Refactored code: + + - String generation functions that operate on enum fields have had their + shared code factored into `` as + `get_single_enum_string()` and `get_flags_enum_string()`. + + - Use of `ctime()` for printing timestamps has been factored into + `get_apfs_timestamp_string()`. + +- Documentation for v0.2.x has been drafted in `/docs` using + [Sphinx](https://www.sphinx-doc.org/en/master/). + +## v0.1.2 (2021-02-05) + +- Renamed `src/apfs/struct/const.h` to `src/apfs/struct/jconst.h` + +- Updated symbol/struct definitions in `src/apfs/struct` to align with the + latest APFS spec revision (2020-06-22). This included adding a new file + `sealed.h` with the definitions contained in the new chapter of the spec, + §16 Sealed Volumes. A detailed list of changes follows: + + - §3 Objects — `object.h` + + - §3.1 Object Types + + - `OBJECT_TYPE_ER_RECOVERY_BLOCK` + - `OBJECT_TYPE_SNAP_META_EXT` + - `OBJECT_TYPE_INTEGRITY_META` + - `OBJECT_TYPE_FEXT_TREE` + - `OBJECT_TYPE_RESERVED_20` + - `OBJECT_TYPE_MEDIA_KEYBAG` + + - §5 Container — `nx.h` + + - §5.2 `nx_superblock_t` + + - `nx_newest_mounted_version` + - `nx_mkb_locker` + + - §6 Object Maps — `omap.h` + + - §6.7 Object Map Flags + + - `OMAP_VALID_FLAGS` + + - §7 Volumes — `fs.h` + + - §7.1 `apfs_superblock_t` + + - `apfs_cloneinfo_id_epoch` + - `apfs_cloneinfo_xid` + - `apfs_snap_meta_ext_oid` + - `apfs_volume_group_id` + - `apfs_integrity_meta_oid` + - `apfs_fext_tree_oid` + - `apfs_fext_tree_type` + - `reserved_type` + - `reserved_oid` + + - §7.3 Volume Flags + + - `APFS_FS_RESERVED_80` + - `APFS_FS_RESERVED_100` + - `APFS_FS_FLAGS_VALID_MASK` (name corrected from erroneous `APFS_FS_FALGS_VALID_MASK`) + + - §7.4 Volume Roles + + - `APFS_VOL_ROLE_UPDATE` + - `APFS_VOL_ROLE_XART` + - `APFS_VOL_ROLE_HARDWARE` + - `APFS_VOL_ROLE_BACKUP` + - `APFS_VOL_ROLE_RESERVED_7` + - `APFS_VOL_ROLE_RESERVED_8` (name changed from `APFS_VOL_ROLE_RESERVED_200`) + - `APFS_VOL_ROLE_ENTERPRISE` + - `APFS_VOL_ROLE_RESERVED_10` + - `APFS_VOL_ROLE_PRELOGIN` + + - §7.5 Optional Volume Feature Flags + + - `APFS_FEATURE_STRICTATIME` + - `APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE` + + - §7.7 Incompatible Volume Feature Flags + + - `APFS_INCOMPAT_INCOMPLETE_RESTORE` + - `APFS_INCOMPAT_SEALED_VOLUME` + + - §8 File-System Objects — `j.h` + + - §8.1 `j_key_t` + + - `SYSTEM_OBJ_ID_MARK` + + - §8.3 `j_inode_val_t` + + - `uncompressed_size` + + - §9 File-System Constants — `jconst.h` + + - §9.1 `j_obj_types` + + - `APFS_TYPE_FILE_INFO` + + - §9.3 `j_inode_flags` + + - `INODE_FAST_PROMOTE` + - `INODE_HAS_UNCOMPRESSED_SIZE` + - `INODE_IS_PURGEABLE` + - `INODE_WANTS_TO_BE_PURGEABLE` + - `INODE_IS_SYNC_ROOT` + - `INODE_SNAPSHOT_COW_EXEMPTION` + + - §9.6 Inode Numbers + + - `PURGEABLE_DIR_INO_NUM` + - `UNIFIED_ID_SPACE_MARK` + + - §9.7 Extended Attributes Constants + + - `FIRMLINK_EA_NAME` + - `APFS_COW_EXEMPT_COUNT_NAME` + + - §11 Extended Fields — `xf.h` + + - §11.3 Extended-Field Types + + - `INO_EXT_TYPE_PURGEABLE_FLAGS` + - `INO_EXT_TYPE_ORIG_SYNC_ROOT_ID` + + - §13 Snapshot Metadata — `snap.h` + + - *New datatype*: §13.6 `snap_meta_ext_obj_phys_t` + - *New datatype*: §13.7 `snap_meta_ext_t` + + - §14 B-Trees — `btree.h` + + - *New datatype*: §14.4 `btn_index_node_val_t` + + - §14.8 B-Tree Flags + + - `BTREE_HASHED` + - `BTREE_NOHEADER` + + - §14.10 B-Tree Node Flags + + - `BTNODE_HASHED` + - `BTNODE_NOHEADER` + + - §15 Encryption — `crypto.h` + + - §15.7 Protection Classes + + - `PROTECTION_CLASS_M` + + - §15.8 Encryption Identifiers + + - `APFS_UNASSIGNED_CRYPTO_ID` + + - §15.12 Keybag Tags + + - `KB_TAG_WRAPPING_M_KEY` + - `KB_TAG_VOLUME_M_KEY` + + - *New chapter*: §16 Sealed Volumes — `sealed.h` + + - *New datatype*: §16.1 `integrity_meta_phys_t` + + - §16.2 Integrity Metadata Version Constants + + - `INTEGRITY_META_VERSION_INVALID` + - `INTEGRITY_META_VERSION_1` + - `INTEGRITY_META_VERSION_2` + - `INTEGRITY_META_VERSION_HIGHEST` + + - §16.3 Integrity Metadata Flags + + - `APFS_SEAL_BROKEN` + + - §16.4 `apfs_hash_type_t` + + - `APFS_HASH_INVALID` + - `APFS_HASH_SHA256` + - `APFS_HASH_SHA512_256` + - `APFS_HASH_SHA384` + - `APFS_HASH_SHA512` + - `APFS_HASH_MIN` + - `APFS_HASH_MAX` + - `APFS_HASH_DEFAULT` + + - *New datatype*: §16.5 `fext_tree_key_t` + + - *New datatype*: §16.6 `fext_tree_val_t` + + - *New datatype*: §16.7 `j_file_info_key_t` + + - `J_FILE_INFO_LBA_MASK` + - `J_FILE_INFO_TYPE_MASK` + - `J_FILE_INFO_TYPE_SHIFT` + + - *New datatype*: §16.8 `j_file_info_val_t` + + - §16.9 `j_obj_file_info_type` + + - `APFS_FILE_INFO_DATA_HASH` + + - *New datatype*: §16.10 `j_file_data_hash_val_t` + + - §17 Space Manager — `spaceman.h` + + - *New datatype*: §17.4 `spaceman_free_queue_entry_t` + +- Updated existing functions in `src/apfs/string` to handle new symbols: + + - In `btree.c`: + - `get_btn_flags_string()` + - `get_bt_info_flags_string()` + + - In `fs.c`: + - `get_apfs_features_string()` + - `get_apfs_incompatible_features_string()` + - `get_apfs_fs_flags_string()` + - `get_apfs_role_string()` + - `print_apfs_superblock()` + + - In `j.c` + - `get_j_inode_internal_flags()` + - `print_j_inode_val()` + + - In `nx.c`: + - `get_nx_flags_string()` + - `print_nx_superblock()` + + - In `object.c`: + - `get_o_type_string()` + - `get_o_subtype_string()` + +## v0.1.1 (2021-02-02) + +- Fixed bug in `get_o_type_string()` where the "Unknown type" output did not + correctly display the hex value of the type field + (`o_type & OBJECT_TYPE_MASK`). + +- Re-implemented (and hopefully fixed all bugs in) the B-tree routines in + `src/apfs/func/btree.c`. Of particular note: + + - `get_btree_phys_omap_val()` is deprecated and replaced with + `get_btree_phys_omap_entry()`, which allows the caller to see the XID of + the returned entry. + + - `get_fs_records()` is now more efficient, as it no longer makes multiple + descents from the root node just to access multiple records in the same leaf + node. Instead, we now just walk along the leaf node, making new descents + only when we need to access a different node. + +## v0.1.0 (2021-01-26) + +Initial release, basically the same as the prior `apfs-tools` commands, but packaged into a single `drat` command. Recognised commands and their functionality are highly subject to change for now. The current list of commands is: + +``` +List of commands: + explore-fs-tree Explore filesystem B-tree + explore-omap-tree Explore object map B-tree + inspect Inspect APFS partition + list-raw List directory contents or file info based on its filesystem OID + list List directory contents or file info based on its filepath + read Read a block and display information about it + recover-raw Recover a file based on its filesystem OID + recover Recover a file bsaed on its filepath + resolver Check if given Virtual OIDs resolve to given Physical OIDs + search-last-btree-node Search the partition for B-tree nodes, reporting the Physical OID of the last one discovered + search Search the partition for blocks with certain features/properties + version Display Drat's version info +``` diff --git a/LICENSE.txt b/LICENSE.txt index f288702..3877ae0 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index ef213bc..ec49679 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,110 @@ -# Drat (formerly apfs-tools) - -Drat is a tool for analysing and recovering data from [APFS (Apple File System)](https://en.wikipedia.org/wiki/Apple_File_System) -partitions. Its creation was inspired by a [personal data loss incident](https://apple.stackexchange.com/questions/373718) -and [Jonathan Levin's](https://twitter.com/Morpheus______) closed-source -`fsleuth` tool, which he demonstrated in [this lecture](http://docs.macsysadmin.se/2018/video/Day4Session2.mp4). - -The name "Drat" is a loose acronym for "Disaster Recovery APFS Tools", and a bad -pun on how one might say "drat!" after discovering that their data is corrupted. - -This software is currently in development, and is being implemented with reference -to [Apple's official APFS specification (PDF)](https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf). -Copies of various versions of this spec are included in the `spec` directory for -archival purposes, particularly in case the online version of the document changes. - -Currently, all of Drat's commands (except `modify`, which is currently disabled -as it is not fit for use) operate in a read-only fashion, as they are intended -to be used in situations involving data recovery or data forensics. - -### Running the software - -If you're using an Intel machine that's running macOS or Linux, you can find -binaries for versioned releases on the [releases page](https://github.com/jivanpal/drat/releases). - -Documentation for versioned releases and as generated from the `main` branch -[can be viewed online](https://drat.readthedocs.io/). - -### Compiling the software - -#### Requirements - -- GNU C Compiler (`gcc`) — Required because we use `__attribute__((packed))`. - -- GNU Make (`make`). - -- GNU Argp library (``) — Part of the GNU C Library (glibc): - - - On Ubuntu, ensure that the package `libc6-dev` is installed. - - - On macOS, you can install just Argp via the [Homebrew](https://brew.sh) - package `argp-standalone`. The Makefile will handle this configuration - automatically. If you acquire Argp any other way, such as by installing - glibc in its entirety, you may need to configure `CFLAGS` and `LDFLAGS` as - appropriate. - -#### Instructions - -- Ensure that `gcc` is in your `$PATH`, or configure `CC` and `LD` as appropriate. - -- Run `make` from the project root (where this `README.md` file resides). An - `out` directory will be created in which the object files will be stored. The - final binary `drat` will be stored in the project root. - -- Run `make clean` to remove the compiled binary (`drat`) and other output files - (`out` directory). - -#### Tested platforms - -Compilation and execution has been tested on the following platforms: - -- macOS Catalina 10.15.7 (19H524) on an Intel x86-64 machine (MacBookPro9,2), using: - - - GCC 11.2.0 (Homebrew GCC 11.2.0) - - GNU Make 3.81 (as included in Xcode Command Line Tools) - - Homebrew package `argp-standalone`, version 1.3 - -- Ubuntu 20.04.3 on an Intel x86-64 machine (Intel Core i5-4288U), using: - - - GCC 9.3.0 - - GNU Make 4.2.1 - - GNU C Library (glibc) 2.31 - -### Generating the documentation - -[Sphinx](https://www.sphinx-doc.org/en/master/) is used to manage the -documentation. This facilitates usage of [Read the Docs](https://readthedocs.org/), -which hosts the documentation online for you to read easily, both for all -versioned releases and as generated from the `main` branch. - -We use a variant of Markdown called [MyST](https://myst-parser.readthedocs.io/en/latest/) -that supports all of the features of reStructuredText. - -#### Requirements - -Sphinx requires Python and its `sphinx` package. We also require the -Read the Docs theme (`sphinx_rtd_theme`) and the MyST parser (`myst-parser`). -If/when you have Python installed, you can install the required packages all at -once with the following: `pip install sphinx sphinx_rtd_theme myst-parser`. - - - -#### Instructions - -- From the project root (the directory where this `README.md` file resides): - - - Run `make docs` to generate HTML documentation in `docs/_build/html`. - Open `docs/_build/html/index.html` in your browser to view the generated - documentation. - - - Run `make clean-docs` to remove the generated documentation (`docs/_build` - directory). - -- From the `docs` directory: - - - Run `make ` to generate the documentation in a format other than - HTML, where `` is any of the formats listed in the output of - `make help`. You may need to install other software to generate - documentation in these other formats. - - - Run `make clean` to remove the generated documentation. +# Drat (formerly apfs-tools) + +Drat is a tool for analysing and recovering data from [APFS (Apple File System)](https://en.wikipedia.org/wiki/Apple_File_System) +partitions. Its creation was inspired by a [personal data loss incident](https://apple.stackexchange.com/questions/373718) +and [Jonathan Levin's](https://twitter.com/Morpheus______) closed-source +`fsleuth` tool, which he demonstrated in [this lecture](http://docs.macsysadmin.se/2018/video/Day4Session2.mp4). + +The name "Drat" is a loose acronym for "Disaster Recovery APFS Tools", and a bad +pun on how one might say "drat!" after discovering that their data is corrupted. + +This software is currently in development, and is being implemented with reference +to [Apple's official APFS specification (PDF)](https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf). +Copies of various versions of this spec are included in the `spec` directory for +archival purposes, particularly in case the online version of the document changes. + +Currently, all of Drat's commands (except `modify`, which is currently disabled +as it is not fit for use) operate in a read-only fashion, as they are intended +to be used in situations involving data recovery or data forensics. + +### Running the software + +If you're using an Intel machine that's running macOS or Linux, you can find +binaries for versioned releases on the [releases page](https://github.com/jivanpal/drat/releases). + +Documentation for versioned releases and as generated from the `main` branch +[can be viewed online](https://drat.readthedocs.io/). + +### Compiling the software + +#### Requirements + +- GNU C Compiler (`gcc`) — Required because we use `__attribute__((packed))`. + +- GNU Make (`make`). + +- GNU Argp library (``) — Part of the GNU C Library (glibc): + + - On Ubuntu, ensure that the package `libc6-dev` is installed. + + - On macOS, you can install just Argp via the [Homebrew](https://brew.sh) + package `argp-standalone`. The Makefile will handle this configuration + automatically. If you acquire Argp any other way, such as by installing + glibc in its entirety, you may need to configure `CFLAGS` and `LDFLAGS` as + appropriate. + +#### Instructions + +- Ensure that `gcc` is in your `$PATH`, or configure `CC` and `LD` as appropriate. + +- Run `make` from the project root (where this `README.md` file resides). An + `out` directory will be created in which the object files will be stored. The + final binary `drat` will be stored in the project root. + +- Run `make clean` to remove the compiled binary (`drat`) and other output files + (`out` directory). + +#### Tested platforms + +Compilation and execution has been tested on the following platforms: + +- macOS Catalina 10.15.7 (19H524) on an Intel x86-64 machine (MacBookPro9,2), using: + + - GCC 11.2.0 (Homebrew GCC 11.2.0) + - GNU Make 3.81 (as included in Xcode Command Line Tools) + - Homebrew package `argp-standalone`, version 1.3 + +- Ubuntu 20.04.3 on an Intel x86-64 machine (Intel Core i5-4288U), using: + + - GCC 9.3.0 + - GNU Make 4.2.1 + - GNU C Library (glibc) 2.31 + +### Generating the documentation + +[Sphinx](https://www.sphinx-doc.org/en/master/) is used to manage the +documentation. This facilitates usage of [Read the Docs](https://readthedocs.org/), +which hosts the documentation online for you to read easily, both for all +versioned releases and as generated from the `main` branch. + +We use a variant of Markdown called [MyST](https://myst-parser.readthedocs.io/en/latest/) +that supports all of the features of reStructuredText. + +#### Requirements + +Sphinx requires Python and its `sphinx` package. We also require the +Read the Docs theme (`sphinx_rtd_theme`) and the MyST parser (`myst-parser`). +If/when you have Python installed, you can install the required packages all at +once with the following: `pip install sphinx sphinx_rtd_theme myst-parser`. + + + +#### Instructions + +- From the project root (the directory where this `README.md` file resides): + + - Run `make docs` to generate HTML documentation in `docs/_build/html`. + Open `docs/_build/html/index.html` in your browser to view the generated + documentation. + + - Run `make clean-docs` to remove the generated documentation (`docs/_build` + directory). + +- From the `docs` directory: + + - Run `make ` to generate the documentation in a format other than + HTML, where `` is any of the formats listed in the output of + `make help`. You may need to install other software to generate + documentation in these other formats. + + - Run `make clean` to remove the generated documentation. diff --git a/docs/Makefile b/docs/Makefile index 4c64235..5c0e202 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,24 +1,24 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: clean -clean: - rm -rf _build +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: clean +clean: + rm -rf _build diff --git a/docs/_ext/drat.py b/docs/_ext/drat.py index efd1cd2..47cce4f 100644 --- a/docs/_ext/drat.py +++ b/docs/_ext/drat.py @@ -1,32 +1,32 @@ -from docutils import nodes -from docutils.parsers.rst import Directive -from sphinx.util.docutils import SphinxDirective - -def drat_role_app(name, rawtext, text, lineno, inliner, options={}, content=[]): - # TODO: Maybe make this link to a description of the app? - node = nodes.strong(None, text) - return [node], [] - -def drat_role_argument(name, rawtext, text, lineno, inliner,options={}, content=[]): - # TODO: Create directive to use at top of each `argument` page, - # and make this role create a backlink to that reference - node = nodes.literal(None, '--' + text) - return [node], [] - -def drat_role_datatype(name, rawtext, text, lineno, inliner, options={}, content=[]): - # TODO: Maybe make this link to a definition of the dataype? - node = nodes.literal(None, text) - return [node], [] - -def drat_role_drat_command(name, rawtext, text, lineno, inliner, options={}, content=[]): - # TODO: Create directive to use at top of each `drat-command` page, - # and make this role create a backlink to that reference - node = nodes.literal(None, text) - return [node], [] - -def setup(app): - app.add_role('app', drat_role_app) - app.add_role('argument', drat_role_argument) - app.add_role('datatype', drat_role_datatype) - app.add_role('drat-command', drat_role_drat_command) - return +from docutils import nodes +from docutils.parsers.rst import Directive +from sphinx.util.docutils import SphinxDirective + +def drat_role_app(name, rawtext, text, lineno, inliner, options={}, content=[]): + # TODO: Maybe make this link to a description of the app? + node = nodes.strong(None, text) + return [node], [] + +def drat_role_argument(name, rawtext, text, lineno, inliner,options={}, content=[]): + # TODO: Create directive to use at top of each `argument` page, + # and make this role create a backlink to that reference + node = nodes.literal(None, '--' + text) + return [node], [] + +def drat_role_datatype(name, rawtext, text, lineno, inliner, options={}, content=[]): + # TODO: Maybe make this link to a definition of the dataype? + node = nodes.literal(None, text) + return [node], [] + +def drat_role_drat_command(name, rawtext, text, lineno, inliner, options={}, content=[]): + # TODO: Create directive to use at top of each `drat-command` page, + # and make this role create a backlink to that reference + node = nodes.literal(None, text) + return [node], [] + +def setup(app): + app.add_role('app', drat_role_app) + app.add_role('argument', drat_role_argument) + app.add_role('datatype', drat_role_datatype) + app.add_role('drat-command', drat_role_drat_command) + return diff --git a/docs/commands/create-index.md b/docs/commands/create-index.md index 1816e56..003e998 100644 --- a/docs/commands/create-index.md +++ b/docs/commands/create-index.md @@ -1,20 +1,20 @@ -(command_create-index)= - -# {drat-command}`create-index` - -The {drat-command}`create-index` command will scan the APFS container and create -an index of the blocks within the container, such as the location of blocks of -certain types or blocks holding data on certain objects. This index takes the -form of a SQLite 3 database, and can be passed to {drat-command}`search` to make -search operations much quicker by greatly reducing the size of the search space. - -## Example usage and output - -``` -$ drat --container /dev/disk0s2 create-index drat-index.db -``` - -## Schema - -```{todo} Describe the database schema used by the index. -``` +(command_create-index)= + +# {drat-command}`create-index` + +The {drat-command}`create-index` command will scan the APFS container and create +an index of the blocks within the container, such as the location of blocks of +certain types or blocks holding data on certain objects. This index takes the +form of a SQLite 3 database, and can be passed to {drat-command}`search` to make +search operations much quicker by greatly reducing the size of the search space. + +## Example usage and output + +``` +$ drat --container /dev/disk0s2 create-index drat-index.db +``` + +## Schema + +```{todo} Describe the database schema used by the index. +``` diff --git a/docs/commands/explore-fs-tree.md b/docs/commands/explore-fs-tree.md index 4e20777..b392e5c 100644 --- a/docs/commands/explore-fs-tree.md +++ b/docs/commands/explore-fs-tree.md @@ -1,347 +1,347 @@ -(command_explore-fs-tree)= - -# {drat-command}`explore-fs-tree` - -## Description - -The {drat-command}`explore-fs-tree` command provides an interface via which you -can explore a filesystem B-tree (or subtree). You specify the B-tree either: - -- directly, by specifying the block address of a filesystem B-tree (root) node - via {argument}`fs`; or - -- indirectly, by specifying sufficient info for the block address of a B-tree - root node to be determined. That is, specify either: - - - the block address of a volume superblock via {argument}`fs`; or - - - a volume via {argument}`volume` or {argument}`volume-name`, optionally - along with a maximum transaction ID via {argument}`max-xid` (the most - recent filesystem tree with a transaction ID that doesn't exceed the one - specified will be explored). - -You must also specify an object map so that the Virtual object IDs used in the -filesystem tree to refer to other nodes within it can be resolved to block -addresses in order to locate the nodes. You specify the object map either -directly or indirectly as described in {drat-command}`explore-omap-tree`. - -Direct and indirect specifications may be mixed, in which case direct -specifications take precedence. In other words, if a volume is specified via -{argument}`volume` or {argument}`volume-name`, the implied choice of volume -superblock can be overriden via {argument}`fs`, and the implied choice of object -map can be overridden via {argument}`omap`. - -If both the filesystem tree and object map are unspecified, the default is -`--volume 1`; that is, the most recent filesystem tree of the first volume -will be explored using the corresponding object map. If one or the other is -specified, but not both, that's an error. - -When the command is run, Drat will display the entries within the specified -B-tree (root) node, each of which is a key given by an (FSOID, FSRT) pair. -You will then be prompted to choose an entry by entering its index. Drat will -then read the corresponding child node and display its entries. You can repeat -this process of entry selection until you select an entry in a leaf node, after -which Drat will display info about the corresponding record in the tree. At any -point, you can enter `..` to go back to the parent node, or enter nothing to -exit Drat. - -When viewing a non-leaf node, an extra set of columns, *Target child node*, is -shown, which displays the Virtual *OID* of the child node that the entry points -to, as well as the the *Block address* that the object map resolves that OID to, -and the *Actual OID* that appears in the data contained in that block. Following -each *Actual OID* is a checkbox `( )`, which will be checked `(X)` if that value -does not match the value in the *OID* column, indicating that the mapping is -bad. A well-formed object map will not result in any checkboxes being checked. - -When viewing a leaf node, human-readable descriptions of the record type are -shown in the *FSRT* column, and extra data is shown for convenience based on the -record type. For example, if the record is a dentry, then the corresponding -inode's FSOID, type, and name are shown. - -## Example usage and output - -``` -$ drat --container /dev/disk2s2 explore-fs-tree --fs 0xd297a --omap 0xd0146 - -Opening file at `/dev/disk0s2` in read-only mode ... OK. - -Finding object mappings tree via block address: 0xd0146 -Reading block 0xd0146 ... validating ... OK. -This is a B-tree root node for an object map. - -Finding filesystem tree via block address: 0xd297a -Reading block 0xd297a ... validating ... OK. -This is a B-tree root node for a filesystem. -Height of tree is 4. -Node has 13 entries, as follows: -+-------+------------------+-----------------------------------------+ -| | | | Target child node | -| Index | FSOID | FSRT |----------+---------------+--------------+ -| | | | OID | Block address | Actual OID | -+-------+-----------+------+----------+---------------+--------------+ -| 0 | 0x1 | 0x9 | 0xdd29 | 0xed993 | 0xdd29 ( ) | -| 1 | 0xaf3a4 | 0x5 | 0x2f77e | 0xe0a41 | 0x2f77e ( ) | -| 2 | 0xa43cf2 | 0x3 | 0x243fa9 | 0xe2645 | 0x243fa9 ( ) | -| 3 | 0xd20da9 | 0x8 | 0x38715f | 0xd6fb4 | 0x38715f ( ) | -| 4 | 0xf4ff0c | 0x8 | 0x41fe57 | 0xe3780 | 0x41fe57 ( ) | -| 5 | 0x11f65da | 0x3 | 0x4f9bec | 0xdd44b | 0x4f9bec ( ) | -| 6 | 0x1314268 | 0x9 | 0x5629f5 | 0xe5a00 | 0x5629f5 ( ) | -| 7 | 0x1428c41 | 0x3 | 0x5cce9b | 0xeb99e | 0x5cce9b ( ) | -| 8 | 0x1499c33 | 0x4 | 0x5e8b16 | 0xebeec | 0x5e8b16 ( ) | -| 9 | 0x153f3be | 0x4 | 0x620615 | 0xdb928 | 0x620615 ( ) | -| 10 | 0x15f71df | 0x3 | 0x6627d0 | 0xd5e71 | 0x6627d0 ( ) | -| 11 | 0x1678e4e | 0x3 | 0x67e91f | 0xda643 | 0x67e91f ( ) | -| 12 | 0x16e1f95 | 0x3 | 0x690b02 | 0xd5c8f | 0x690b02 ( ) | -+-------+-----------+------+----------+---------------+--------------+ -Current path: (_, _, _, _) -Choose an entry [.., 0-12]: 1 - -Child node has Virtual OID 0x2f77e, maps to block 0xe0a41. -Reading block 0xe0a41 ... validating ... OK. -Node has 94 entries, as follows: -+-------+-----------------+-----------------------------------------+ -| | | | Target child node | -| Index | FSOID | FSRT |----------+---------------+--------------+ -| | | | OID | Block address | Actual OID | -+-------+----------+------+----------+---------------+--------------+ -| 0 | 0xaf3a4 | 0x5 | 0x18ef1 | 0xd894c | 0x18ef1 ( ) | -| 1 | 0xaf9d9 | 0x8 | 0x18f71 | 0xdfe02 | 0x18f71 ( ) | -| 2 | 0xafcf1 | 0x3 | 0xdb001 | 0xe1c51 | 0xdb001 ( ) | -| 3 | 0xaffcc | 0x5 | 0x18ff2 | 0xe1c57 | 0x18ff2 ( ) | -| 4 | 0xb02b1 | 0x5 | 0xdb096 | 0xd8bd5 | 0xdb096 ( ) | -| 5 | 0xb06fe | 0x3 | 0x19070 | 0xd85aa | 0x19070 ( ) | -| 6 | 0xb0d79 | 0x4 | 0x190e4 | 0xd6ecd | 0x190e4 ( ) | -| 7 | 0xcb602 | 0x4 | 0x1975d | 0xd1b20 | 0x1975d ( ) | -| 8 | 0xce5e4 | 0x3 | 0x19a56 | 0xe3903 | 0x19a56 ( ) | -| 9 | 0xd7090 | 0x4 | 0x1cf1a | 0xe48b1 | 0x1cf1a ( ) | -| 10 | 0xd76b2 | 0x3 | 0x1cf35 | 0xe0160 | 0x1cf35 ( ) | -| 11 | 0xd96d8 | 0x9 | 0x1a7b7 | 0xe0167 | 0x1a7b7 ( ) | -| 12 | 0xda81f | 0x3 | 0x1a8af | 0xe0177 | 0x1a8af ( ) | -| 13 | 0xdb811 | 0x9 | 0x1e15b | 0xe01b0 | 0x1e15b ( ) | -| 14 | 0xdd711 | 0x4 | 0x22fbf | 0xdfbd3 | 0x22fbf ( ) | -| 15 | 0xddd50 | 0x3 | 0x1eb31 | 0xd9473 | 0x1eb31 ( ) | -| 16 | 0xde2f0 | 0x3 | 0x1ecaf | 0xe01cb | 0x1ecaf ( ) | -| 17 | 0xdf371 | 0x3 | 0x1f100 | 0xe01f0 | 0x1f100 ( ) | -| 18 | 0xe2866 | 0x3 | 0x1f4a1 | 0xdd298 | 0x1f4a1 ( ) | -| 19 | 0x143365 | 0x3 | 0x22bf3 | 0xd2e56 | 0x22bf3 ( ) | -| 20 | 0x1478f9 | 0x5 | 0x236c8 | 0xd54f0 | 0x236c8 ( ) | -| 21 | 0x1595c1 | 0x3 | 0x2c6eb | 0xd4c43 | 0x2c6eb ( ) | -| 22 | 0x15a089 | 0x9 | 0x2c78a | 0xe05f4 | 0x2c78a ( ) | -| 23 | 0x15af6b | 0x9 | 0x2cb92 | 0xe0654 | 0x2cb92 ( ) | -| 24 | 0x1799fd | 0x3 | 0x31de9 | 0xda18e | 0x31de9 ( ) | -| 25 | 0x179fcd | 0x3 | 0x31f0d | 0xdfca9 | 0x31f0d ( ) | -| 26 | 0x17a00a | 0x3 | 0x31e6c | 0xdf530 | 0x31e6c ( ) | -| 27 | 0x17a7c8 | 0x3 | 0x31ef9 | 0xda57f | 0x31ef9 ( ) | -| 28 | 0x1823fc | 0x9 | 0x32ddd | 0xe1075 | 0x32ddd ( ) | -| 29 | 0x182924 | 0x3 | 0x32e55 | 0xd2f36 | 0x32e55 ( ) | -| 30 | 0x183213 | 0x3 | 0x32f01 | 0xd89df | 0x32f01 ( ) | -| 31 | 0x1d683c | 0x4 | 0x5623a | 0xdf38d | 0x5623a ( ) | -| 32 | 0x22adf7 | 0x4 | 0x8e042 | 0xda2bc | 0x8e042 ( ) | -| 33 | 0x287b71 | 0x3 | 0x943cb | 0xe015b | 0x943cb ( ) | -| 34 | 0x2b7e2f | 0x4 | 0xb1dd7 | 0xe29e8 | 0xb1dd7 ( ) | -| 35 | 0x2d4ed2 | 0x6 | 0xb4769 | 0xda2c4 | 0xb4769 ( ) | -| 36 | 0x2e49cf | 0x3 | 0xbb071 | 0xd2541 | 0xbb071 ( ) | -| 37 | 0x2e4b7e | 0x9 | 0xba460 | 0xd250b | 0xba460 ( ) | -| 38 | 0x2ea142 | 0x8 | 0xbb558 | 0xe4aef | 0xbb558 ( ) | -| 39 | 0x2eb39d | 0x3 | 0xbbe75 | 0xdd2b8 | 0xbbe75 ( ) | -| 40 | 0x2eb86f | 0x3 | 0xbc079 | 0xd0355 | 0xbc079 ( ) | -| 41 | 0x2f7f6d | 0x3 | 0xc05f4 | 0xe76a6 | 0xc05f4 ( ) | -| 42 | 0x2f840b | 0x4 | 0xc0668 | 0xe74c2 | 0xc0668 ( ) | -| 43 | 0x2f89c5 | 0x4 | 0xc06de | 0xe73e0 | 0xc06de ( ) | -| 44 | 0x2f8f79 | 0x4 | 0xc0750 | 0xd9c21 | 0xc0750 ( ) | -| 45 | 0x2f956b | 0x4 | 0xc07ce | 0xe78c1 | 0xc07ce ( ) | -| 46 | 0x2f9af8 | 0x3 | 0xc0843 | 0xd2774 | 0xc0843 ( ) | -| 47 | 0x32815a | 0x3 | 0xc11b2 | 0xd394f | 0xc11b2 ( ) | -| 48 | 0x333c07 | 0x3 | 0xd0fd0 | 0xdf268 | 0xd0fd0 ( ) | -| 49 | 0x37fb42 | 0x3 | 0xe94a8 | 0xd9978 | 0xe94a8 ( ) | -| 50 | 0x3c4c65 | 0x3 | 0xffb5f | 0xda92e | 0xffb5f ( ) | -| 51 | 0x43e4d8 | 0x3 | 0x120fdb | 0xdfa52 | 0x120fdb ( ) | -| 52 | 0x47457f | 0x3 | 0x13ac2d | 0xdfbc9 | 0x13ac2d ( ) | -| 53 | 0x47491b | 0x9 | 0x13ac9b | 0xd78c1 | 0x13ac9b ( ) | -| 54 | 0x4afc54 | 0x8 | 0x13be67 | 0xd5ba8 | 0x13be67 ( ) | -| 55 | 0x4f0f95 | 0x3 | 0x160f9d | 0xdbfd5 | 0x160f9d ( ) | -| 56 | 0x4f168d | 0x3 | 0x161026 | 0xd526f | 0x161026 ( ) | -| 57 | 0x4f28d2 | 0x3 | 0x1610ae | 0xe5380 | 0x1610ae ( ) | -| 58 | 0x57c70b | 0x3 | 0x1843ab | 0xd9b9d | 0x1843ab ( ) | -| 59 | 0x57cba2 | 0x3 | 0x184a2b | 0xd9f1c | 0x184a2b ( ) | -| 60 | 0x5d8aca | 0x9 | 0x1a476d | 0xe630d | 0x1a476d ( ) | -| 61 | 0x5df9f8 | 0x9 | 0x1a53a9 | 0xe4202 | 0x1a53a9 ( ) | -| 62 | 0x5f4745 | 0x3 | 0x1a4f80 | 0xe43a2 | 0x1a4f80 ( ) | -| 63 | 0x5fdd5c | 0x3 | 0x1a538d | 0xe1940 | 0x1a538d ( ) | -| 64 | 0x6080bc | 0x3 | 0x1a5b6d | 0xd6b14 | 0x1a5b6d ( ) | -| 65 | 0x649eb8 | 0x9 | 0x1b82aa | 0xdf7a5 | 0x1b82aa ( ) | -| 66 | 0x66912c | 0x4 | 0x1bf13d | 0xe3927 | 0x1bf13d ( ) | -| 67 | 0x6b9ddc | 0x3 | 0x25c9fa | 0xd7e9f | 0x25c9fa ( ) | -| 68 | 0x72baa8 | 0x3 | 0x1fef96 | 0xdcdb8 | 0x1fef96 ( ) | -| 69 | 0x744bc1 | 0x3 | 0x20a607 | 0xd3c88 | 0x20a607 ( ) | -| 70 | 0x744dce | 0x3 | 0x20b9ca | 0xdd729 | 0x20b9ca ( ) | -| 71 | 0x744e02 | 0x8 | 0x209e5a | 0xd80ee | 0x209e5a ( ) | -| 72 | 0x7451fc | 0x3 | 0x209ec3 | 0xef591 | 0x209ec3 ( ) | -| 73 | 0x745a15 | 0x9 | 0x20a00a | 0xe5107 | 0x20a00a ( ) | -| 74 | 0x745ee1 | 0x8 | 0x20a08b | 0xe44dd | 0x20a08b ( ) | -| 75 | 0x746977 | 0x3 | 0x20a15b | 0xed8c4 | 0x20a15b ( ) | -| 76 | 0x7828dc | 0x3 | 0x216ad0 | 0xd81e7 | 0x216ad0 ( ) | -| 77 | 0x7ce9f1 | 0x3 | 0x22b142 | 0xd9644 | 0x22b142 ( ) | -| 78 | 0x803df5 | 0x3 | 0x2310fe | 0xdcab4 | 0x2310fe ( ) | -| 79 | 0x84846b | 0x9 | 0x25129a | 0xd413e | 0x25129a ( ) | -| 80 | 0x86d1b5 | 0x3 | 0x257c99 | 0xd7480 | 0x257c99 ( ) | -| 81 | 0x87b65b | 0x9 | 0x25bd0d | 0xdd6aa | 0x25bd0d ( ) | -| 82 | 0x88b49e | 0x3 | 0x25ca62 | 0xea7e0 | 0x25ca62 ( ) | -| 83 | 0x8fd89b | 0x9 | 0x2710ad | 0xe27aa | 0x2710ad ( ) | -| 84 | 0x8fdb17 | 0x4 | 0x2712c9 | 0xd1936 | 0x2712c9 ( ) | -| 85 | 0x8fdb44 | 0x3 | 0x271117 | 0xd6d76 | 0x271117 ( ) | -| 86 | 0x8fe224 | 0x3 | 0x2711a9 | 0xd518c | 0x2711a9 ( ) | -| 87 | 0x8fe3d4 | 0x4 | 0x27121e | 0xd5be7 | 0x27121e ( ) | -| 88 | 0x8feaff | 0x8 | 0x2712a2 | 0xd470d | 0x2712a2 ( ) | -| 89 | 0x94bf69 | 0x3 | 0x2818d9 | 0xd4c80 | 0x2818d9 ( ) | -| 90 | 0x995688 | 0x3 | 0x28875a | 0xdf433 | 0x28875a ( ) | -| 91 | 0x9cbb60 | 0x9 | 0x28be2b | 0xde6bd | 0x28be2b ( ) | -| 92 | 0x9e8db6 | 0x3 | 0x2a1ecf | 0xe0039 | 0x2a1ecf ( ) | -| 93 | 0xa21883 | 0x3 | 0x2a728c | 0xd9965 | 0x2a728c ( ) | -+-------+----------+------+----------+---------------+--------------+ -Current path: (1, _, _, _) -Choose an entry [.., 0-93]: 30 - -Child node has Virtual OID 0x32f01, maps to block 0xd89df. -Reading block 0xd89df ... validating ... OK. -Node has 65 entries, as follows: -+-------+-----------------+-----------------------------------------+ -| | | | Target child node | -| Index | FSOID | FSRT |----------+---------------+--------------+ -| | | | OID | Block address | Actual OID | -+-------+----------+------+----------+---------------+--------------+ -| 0 | 0x183213 | 0x3 | 0x32efd | 0xd6494 | 0x32efd ( ) | -| 1 | 0x183bf7 | 0x3 | 0x32f89 | 0xd128c | 0x32f89 ( ) | -| 2 | 0x185464 | 0x3 | 0x33457 | 0xd9748 | 0x33457 ( ) | -| 3 | 0x18552b | 0x6 | 0x33564 | 0xe115b | 0x33564 ( ) | -| 4 | 0x187983 | 0x3 | 0x37adb | 0xd8f96 | 0x37adb ( ) | -| 5 | 0x18a6a4 | 0x6 | 0x3b3a0 | 0xe9e4c | 0x3b3a0 ( ) | -| 6 | 0x18a962 | 0x9 | 0x3b3ac | 0xde0dd | 0x3b3ac ( ) | -| 7 | 0x18a964 | 0x9 | 0x3b3f6 | 0xd32e1 | 0x3b3f6 ( ) | -| 8 | 0x18a96f | 0x3 | 0x3b3f9 | 0xe308d | 0x3b3f9 ( ) | -| 9 | 0x18a971 | 0x3 | 0x3b3f7 | 0xe3c1f | 0x3b3f7 ( ) | -| 10 | 0x18a985 | 0x3 | 0x3b3f8 | 0xe5deb | 0x3b3f8 ( ) | -| 11 | 0x18a999 | 0x3 | 0x3b3fc | 0xe57c0 | 0x3b3fc ( ) | -| 12 | 0x18ba67 | 0x9 | 0x3d295 | 0xd10e7 | 0x3d295 ( ) | -| 13 | 0x18ba6b | 0x3 | 0x3d293 | 0xd10e5 | 0x3d293 ( ) | -| 14 | 0x18ba80 | 0x3 | 0x3d294 | 0xd10e6 | 0x3d294 ( ) | -| 15 | 0x18ba93 | 0x4 | 0x3d296 | 0xe5a2d | 0x3d296 ( ) | -| 16 | 0x18e7c3 | 0x3 | 0x3d2ca | 0xea935 | 0x3d2ca ( ) | -| 17 | 0x18e7cb | 0x4 | 0x3d79b | 0xeae76 | 0x3d79b ( ) | -| 18 | 0x18e827 | 0x6 | 0x3d7b4 | 0xea93e | 0x3d7b4 ( ) | -| 19 | 0x18e8fb | 0x9 | 0x3d7b6 | 0xd13a6 | 0x3d7b6 ( ) | -| 20 | 0x18e8fb | 0x9 | 0x209c65 | 0xea942 | 0x209c65 ( ) | -| 21 | 0x18e91d | 0x3 | 0x3d7b9 | 0xeaa15 | 0x3d7b9 ( ) | -| 22 | 0x18e96c | 0x4 | 0x3d7c1 | 0xe4931 | 0x3d7c1 ( ) | -| 23 | 0x18e98f | 0x4 | 0x3d7ce | 0xe2c98 | 0x3d7ce ( ) | -| 24 | 0x1918c7 | 0x8 | 0x3ddca | 0xe6e13 | 0x3ddca ( ) | -| 25 | 0x19196b | 0x9 | 0x3de4d | 0xe3073 | 0x3de4d ( ) | -| 26 | 0x191973 | 0x4 | 0x3de50 | 0xdde67 | 0x3de50 ( ) | -| 27 | 0x191975 | 0x4 | 0x3de4e | 0xe3074 | 0x3de4e ( ) | -| 28 | 0x191989 | 0x4 | 0x3de4f | 0xe33b0 | 0x3de4f ( ) | -| 29 | 0x19199f | 0x3 | 0x3de51 | 0xeb69d | 0x3de51 ( ) | -| 30 | 0x191b1c | 0x3 | 0x3de81 | 0xd0831 | 0x3de81 ( ) | -| 31 | 0x191d0a | 0x6 | 0x3de9c | 0xda6a8 | 0x3de9c ( ) | -| 32 | 0x192004 | 0x6 | 0x3df11 | 0xd250c | 0x3df11 ( ) | -| 33 | 0x192170 | 0x3 | 0x3df2c | 0xd60aa | 0x3df2c ( ) | -| 34 | 0x193c56 | 0x8 | 0x42396 | 0xd1306 | 0x42396 ( ) | -| 35 | 0x197686 | 0x3 | 0x43f58 | 0xd08db | 0x43f58 ( ) | -| 36 | 0x197cd9 | 0x9 | 0x44247 | 0xde436 | 0x44247 ( ) | -| 37 | 0x19bacf | 0x9 | 0x446f7 | 0xe00c7 | 0x446f7 ( ) | -| 38 | 0x19badc | 0x9 | 0x44a7e | 0xe86f1 | 0x44a7e ( ) | -| 39 | 0x19baed | 0x6 | 0x44a7f | 0xd2a15 | 0x44a7f ( ) | -| 40 | 0x1a1150 | 0x3 | 0x453b0 | 0xd1322 | 0x453b0 ( ) | -| 41 | 0x1a2b18 | 0x3 | 0x4819d | 0xd15c1 | 0x4819d ( ) | -| 42 | 0x1a71f9 | 0x6 | 0x49491 | 0xf239e | 0x49491 ( ) | -| 43 | 0x1a71fe | 0x4 | 0x4c416 | 0xd10f9 | 0x4c416 ( ) | -| 44 | 0x1a720e | 0x3 | 0x4c417 | 0xd10fb | 0x4c417 ( ) | -| 45 | 0x1a7221 | 0x4 | 0x4c419 | 0xe6c90 | 0x4c419 ( ) | -| 46 | 0x1a7761 | 0x3 | 0x4c481 | 0xdefda | 0x4c481 ( ) | -| 47 | 0x1ae04f | 0x3 | 0x547cb | 0xdbf66 | 0x547cb ( ) | -| 48 | 0x1ae061 | 0x6 | 0x54c62 | 0xda59d | 0x54c62 ( ) | -| 49 | 0x1b2e00 | 0x3 | 0x55c2c | 0xd1737 | 0x55c2c ( ) | -| 50 | 0x1b33aa | 0x4 | 0x57d8b | 0xd6332 | 0x57d8b ( ) | -| 51 | 0x1b38a9 | 0x3 | 0x57e66 | 0xda582 | 0x57e66 ( ) | -| 52 | 0x1b3d5e | 0x3 | 0x57e67 | 0xe0759 | 0x57e67 ( ) | -| 53 | 0x1b5399 | 0x4 | 0x58248 | 0xe20e8 | 0x58248 ( ) | -| 54 | 0x1bb366 | 0x3 | 0x5d1dd | 0xd027e | 0x5d1dd ( ) | -| 55 | 0x1bf693 | 0x8 | 0x62440 | 0xea807 | 0x62440 ( ) | -| 56 | 0x1d401c | 0x6 | 0x658b6 | 0xd7efd | 0x658b6 ( ) | -| 57 | 0x1d4029 | 0x8 | 0x65f22 | 0xea6ef | 0x65f22 ( ) | -| 58 | 0x1d4037 | 0x8 | 0x65f23 | 0xd7fc2 | 0x65f23 ( ) | -| 59 | 0x1d4299 | 0x3 | 0x65f29 | 0xdf4a7 | 0x65f29 ( ) | -| 60 | 0x1d45a8 | 0x4 | 0x65f82 | 0xddb2a | 0x65f82 ( ) | -| 61 | 0x1d45ae | 0x3 | 0x65fae | 0xea73e | 0x65fae ( ) | -| 62 | 0x1d45b9 | 0x4 | 0x65faf | 0xde792 | 0x65faf ( ) | -| 63 | 0x1d45d2 | 0x8 | 0x65fb1 | 0xe961e | 0x65fb1 ( ) | -| 64 | 0x1d5e9c | 0x3 | 0x67303 | 0xe852e | 0x67303 ( ) | -+-------+----------+------+----------+---------------+--------------+ -Current path: (1, 30, _, _) -Choose an entry [.., 0-64]: 17 - -Child node has Virtual OID 0x3d79b, maps to block 0xeae76. -Reading block 0xeae76 ... validating ... OK. -Node has 30 entries, as follows: -+-------+----------+------------------+ -| Index | FSOID | FSRT | -+-------+----------+------------------+ -| 0 | 0x18e7cb | 0x4, xattr | -+-------+----------+------------------+ -| 1 | 0x18e7cc | 0x3, inode | -| 2 | 0x18e7cc | 0x4, xattr | -| 3 | 0x18e7cc | 0x6, dstream | -| 4 | 0x18e7cc | 0x8, file extent | -+-------+----------+------------------+ -| 5 | 0x18e7cd | 0x3, inode | -| 6 | 0x18e7cd | 0x4, xattr | -| 7 | 0x18e7cd | 0x6, dstream | -| 8 | 0x18e7cd | 0x8, file extent | -+-------+----------+------------------+ -| 9 | 0x18e825 | 0x3, inode | -| 10 | 0x18e825 | 0x4, xattr | -| | | +-------------+----------------+--------------------+ -| | | | Inode FSOID | Inode type | Inode name | -| | | +-------------+----------------+--------------------+ -| 11 | 0x18e825 | 0x9, dentry | 0x169da65 | 0x4, directory | 83bc0baa3881f265_0 | -| 12 | 0x18e825 | 0x9, dentry | 0x169da72 | 0x4, directory | d3c975fa1a9b20f6_0 | -| 13 | 0x18e825 | 0x9, dentry | 0x169da6b | 0x4, directory | df7b00085ba675ef_0 | -| 14 | 0x18e825 | 0x9, dentry | 0x18e828 | 0x4, directory | index-dir | -| 15 | 0x18e825 | 0x9, dentry | 0x169da6f | 0x4, directory | f3faf2ae921d9bf5_0 | -| 16 | 0x18e825 | 0x9, dentry | 0x169da71 | 0x4, directory | 41a6c187467c47e1_0 | -| 17 | 0x18e825 | 0x9, dentry | 0x169da6c | 0x4, directory | 9b7fc9e7916c2c7c_0 | -| 18 | 0x18e825 | 0x9, dentry | 0x169da73 | 0x4, directory | a20bc29b43958149_0 | -| 19 | 0x18e825 | 0x9, dentry | 0x169da70 | 0x4, directory | 9bffce5feb2725f3_0 | -| 20 | 0x18e825 | 0x9, dentry | 0x169da67 | 0x4, directory | fd78b4182656630a_0 | -| 21 | 0x18e825 | 0x9, dentry | 0x169da66 | 0x4, directory | fc39fc87aa0a667a_0 | -| 22 | 0x18e825 | 0x9, dentry | 0x169da6a | 0x4, directory | 083f607b0ffe8511_0 | -| 23 | 0x18e825 | 0x9, dentry | 0x169da64 | 0x4, directory | d4e4cca6484c1eb2_0 | -| 24 | 0x18e825 | 0x9, dentry | 0x169da68 | 0x4, directory | c15c8b176db7917c_0 | -| 25 | 0x18e825 | 0x9, dentry | 0x18e827 | 0x4, directory | index | -| 26 | 0x18e825 | 0x9, dentry | 0x169da69 | 0x4, directory | 1b12bbc9cb4a5267_0 | -| 27 | 0x18e825 | 0x9, dentry | 0x169da74 | 0x4, directory | a28d674c00625ded_0 | -+-------+----------+------------------+-------------+----------------+--------------------+ -| 28 | 0x18e827 | 0x3, inode | -| 29 | 0x18e827 | 0x4, xattr | -+-------+----------+------------------+ -Current path: (1, 30, 17, _) -Choose an entry [.., 0-29]: 13 - -Record data for entry 13: - METADATA: - - Key size: 31 bytes - - Value size: 18 bytes - - ID and type field: 0x900000000018e825 - KEY: - - Virtual OID: 0x18e825 - - Object type: Directory entry - VALUE: - - Dentry name length: 19 UTF-8 bytes (including terminating NULL (U+0000) byte) - - Dentry name hash: 0x06afe3 - - Dentry name: df7b00085ba675ef_0 - - Dentry Virtual OID: 0x169da6b - - Time added: Wed Mar 3 15:17:33 2021 - - Dentry type: Regular file - - No. extended fields: 0 -Current path: (1, 30, 17, 31) -Choose an entry [..]: -``` +(command_explore-fs-tree)= + +# {drat-command}`explore-fs-tree` + +## Description + +The {drat-command}`explore-fs-tree` command provides an interface via which you +can explore a filesystem B-tree (or subtree). You specify the B-tree either: + +- directly, by specifying the block address of a filesystem B-tree (root) node + via {argument}`fs`; or + +- indirectly, by specifying sufficient info for the block address of a B-tree + root node to be determined. That is, specify either: + + - the block address of a volume superblock via {argument}`fs`; or + + - a volume via {argument}`volume` or {argument}`volume-name`, optionally + along with a maximum transaction ID via {argument}`max-xid` (the most + recent filesystem tree with a transaction ID that doesn't exceed the one + specified will be explored). + +You must also specify an object map so that the Virtual object IDs used in the +filesystem tree to refer to other nodes within it can be resolved to block +addresses in order to locate the nodes. You specify the object map either +directly or indirectly as described in {drat-command}`explore-omap-tree`. + +Direct and indirect specifications may be mixed, in which case direct +specifications take precedence. In other words, if a volume is specified via +{argument}`volume` or {argument}`volume-name`, the implied choice of volume +superblock can be overriden via {argument}`fs`, and the implied choice of object +map can be overridden via {argument}`omap`. + +If both the filesystem tree and object map are unspecified, the default is +`--volume 1`; that is, the most recent filesystem tree of the first volume +will be explored using the corresponding object map. If one or the other is +specified, but not both, that's an error. + +When the command is run, Drat will display the entries within the specified +B-tree (root) node, each of which is a key given by an (FSOID, FSRT) pair. +You will then be prompted to choose an entry by entering its index. Drat will +then read the corresponding child node and display its entries. You can repeat +this process of entry selection until you select an entry in a leaf node, after +which Drat will display info about the corresponding record in the tree. At any +point, you can enter `..` to go back to the parent node, or enter nothing to +exit Drat. + +When viewing a non-leaf node, an extra set of columns, *Target child node*, is +shown, which displays the Virtual *OID* of the child node that the entry points +to, as well as the the *Block address* that the object map resolves that OID to, +and the *Actual OID* that appears in the data contained in that block. Following +each *Actual OID* is a checkbox `( )`, which will be checked `(X)` if that value +does not match the value in the *OID* column, indicating that the mapping is +bad. A well-formed object map will not result in any checkboxes being checked. + +When viewing a leaf node, human-readable descriptions of the record type are +shown in the *FSRT* column, and extra data is shown for convenience based on the +record type. For example, if the record is a dentry, then the corresponding +inode's FSOID, type, and name are shown. + +## Example usage and output + +``` +$ drat --container /dev/disk2s2 explore-fs-tree --fs 0xd297a --omap 0xd0146 + +Opening file at `/dev/disk0s2` in read-only mode ... OK. + +Finding object mappings tree via block address: 0xd0146 +Reading block 0xd0146 ... validating ... OK. +This is a B-tree root node for an object map. + +Finding filesystem tree via block address: 0xd297a +Reading block 0xd297a ... validating ... OK. +This is a B-tree root node for a filesystem. +Height of tree is 4. +Node has 13 entries, as follows: ++-------+------------------+-----------------------------------------+ +| | | | Target child node | +| Index | FSOID | FSRT |----------+---------------+--------------+ +| | | | OID | Block address | Actual OID | ++-------+-----------+------+----------+---------------+--------------+ +| 0 | 0x1 | 0x9 | 0xdd29 | 0xed993 | 0xdd29 ( ) | +| 1 | 0xaf3a4 | 0x5 | 0x2f77e | 0xe0a41 | 0x2f77e ( ) | +| 2 | 0xa43cf2 | 0x3 | 0x243fa9 | 0xe2645 | 0x243fa9 ( ) | +| 3 | 0xd20da9 | 0x8 | 0x38715f | 0xd6fb4 | 0x38715f ( ) | +| 4 | 0xf4ff0c | 0x8 | 0x41fe57 | 0xe3780 | 0x41fe57 ( ) | +| 5 | 0x11f65da | 0x3 | 0x4f9bec | 0xdd44b | 0x4f9bec ( ) | +| 6 | 0x1314268 | 0x9 | 0x5629f5 | 0xe5a00 | 0x5629f5 ( ) | +| 7 | 0x1428c41 | 0x3 | 0x5cce9b | 0xeb99e | 0x5cce9b ( ) | +| 8 | 0x1499c33 | 0x4 | 0x5e8b16 | 0xebeec | 0x5e8b16 ( ) | +| 9 | 0x153f3be | 0x4 | 0x620615 | 0xdb928 | 0x620615 ( ) | +| 10 | 0x15f71df | 0x3 | 0x6627d0 | 0xd5e71 | 0x6627d0 ( ) | +| 11 | 0x1678e4e | 0x3 | 0x67e91f | 0xda643 | 0x67e91f ( ) | +| 12 | 0x16e1f95 | 0x3 | 0x690b02 | 0xd5c8f | 0x690b02 ( ) | ++-------+-----------+------+----------+---------------+--------------+ +Current path: (_, _, _, _) +Choose an entry [.., 0-12]: 1 + +Child node has Virtual OID 0x2f77e, maps to block 0xe0a41. +Reading block 0xe0a41 ... validating ... OK. +Node has 94 entries, as follows: ++-------+-----------------+-----------------------------------------+ +| | | | Target child node | +| Index | FSOID | FSRT |----------+---------------+--------------+ +| | | | OID | Block address | Actual OID | ++-------+----------+------+----------+---------------+--------------+ +| 0 | 0xaf3a4 | 0x5 | 0x18ef1 | 0xd894c | 0x18ef1 ( ) | +| 1 | 0xaf9d9 | 0x8 | 0x18f71 | 0xdfe02 | 0x18f71 ( ) | +| 2 | 0xafcf1 | 0x3 | 0xdb001 | 0xe1c51 | 0xdb001 ( ) | +| 3 | 0xaffcc | 0x5 | 0x18ff2 | 0xe1c57 | 0x18ff2 ( ) | +| 4 | 0xb02b1 | 0x5 | 0xdb096 | 0xd8bd5 | 0xdb096 ( ) | +| 5 | 0xb06fe | 0x3 | 0x19070 | 0xd85aa | 0x19070 ( ) | +| 6 | 0xb0d79 | 0x4 | 0x190e4 | 0xd6ecd | 0x190e4 ( ) | +| 7 | 0xcb602 | 0x4 | 0x1975d | 0xd1b20 | 0x1975d ( ) | +| 8 | 0xce5e4 | 0x3 | 0x19a56 | 0xe3903 | 0x19a56 ( ) | +| 9 | 0xd7090 | 0x4 | 0x1cf1a | 0xe48b1 | 0x1cf1a ( ) | +| 10 | 0xd76b2 | 0x3 | 0x1cf35 | 0xe0160 | 0x1cf35 ( ) | +| 11 | 0xd96d8 | 0x9 | 0x1a7b7 | 0xe0167 | 0x1a7b7 ( ) | +| 12 | 0xda81f | 0x3 | 0x1a8af | 0xe0177 | 0x1a8af ( ) | +| 13 | 0xdb811 | 0x9 | 0x1e15b | 0xe01b0 | 0x1e15b ( ) | +| 14 | 0xdd711 | 0x4 | 0x22fbf | 0xdfbd3 | 0x22fbf ( ) | +| 15 | 0xddd50 | 0x3 | 0x1eb31 | 0xd9473 | 0x1eb31 ( ) | +| 16 | 0xde2f0 | 0x3 | 0x1ecaf | 0xe01cb | 0x1ecaf ( ) | +| 17 | 0xdf371 | 0x3 | 0x1f100 | 0xe01f0 | 0x1f100 ( ) | +| 18 | 0xe2866 | 0x3 | 0x1f4a1 | 0xdd298 | 0x1f4a1 ( ) | +| 19 | 0x143365 | 0x3 | 0x22bf3 | 0xd2e56 | 0x22bf3 ( ) | +| 20 | 0x1478f9 | 0x5 | 0x236c8 | 0xd54f0 | 0x236c8 ( ) | +| 21 | 0x1595c1 | 0x3 | 0x2c6eb | 0xd4c43 | 0x2c6eb ( ) | +| 22 | 0x15a089 | 0x9 | 0x2c78a | 0xe05f4 | 0x2c78a ( ) | +| 23 | 0x15af6b | 0x9 | 0x2cb92 | 0xe0654 | 0x2cb92 ( ) | +| 24 | 0x1799fd | 0x3 | 0x31de9 | 0xda18e | 0x31de9 ( ) | +| 25 | 0x179fcd | 0x3 | 0x31f0d | 0xdfca9 | 0x31f0d ( ) | +| 26 | 0x17a00a | 0x3 | 0x31e6c | 0xdf530 | 0x31e6c ( ) | +| 27 | 0x17a7c8 | 0x3 | 0x31ef9 | 0xda57f | 0x31ef9 ( ) | +| 28 | 0x1823fc | 0x9 | 0x32ddd | 0xe1075 | 0x32ddd ( ) | +| 29 | 0x182924 | 0x3 | 0x32e55 | 0xd2f36 | 0x32e55 ( ) | +| 30 | 0x183213 | 0x3 | 0x32f01 | 0xd89df | 0x32f01 ( ) | +| 31 | 0x1d683c | 0x4 | 0x5623a | 0xdf38d | 0x5623a ( ) | +| 32 | 0x22adf7 | 0x4 | 0x8e042 | 0xda2bc | 0x8e042 ( ) | +| 33 | 0x287b71 | 0x3 | 0x943cb | 0xe015b | 0x943cb ( ) | +| 34 | 0x2b7e2f | 0x4 | 0xb1dd7 | 0xe29e8 | 0xb1dd7 ( ) | +| 35 | 0x2d4ed2 | 0x6 | 0xb4769 | 0xda2c4 | 0xb4769 ( ) | +| 36 | 0x2e49cf | 0x3 | 0xbb071 | 0xd2541 | 0xbb071 ( ) | +| 37 | 0x2e4b7e | 0x9 | 0xba460 | 0xd250b | 0xba460 ( ) | +| 38 | 0x2ea142 | 0x8 | 0xbb558 | 0xe4aef | 0xbb558 ( ) | +| 39 | 0x2eb39d | 0x3 | 0xbbe75 | 0xdd2b8 | 0xbbe75 ( ) | +| 40 | 0x2eb86f | 0x3 | 0xbc079 | 0xd0355 | 0xbc079 ( ) | +| 41 | 0x2f7f6d | 0x3 | 0xc05f4 | 0xe76a6 | 0xc05f4 ( ) | +| 42 | 0x2f840b | 0x4 | 0xc0668 | 0xe74c2 | 0xc0668 ( ) | +| 43 | 0x2f89c5 | 0x4 | 0xc06de | 0xe73e0 | 0xc06de ( ) | +| 44 | 0x2f8f79 | 0x4 | 0xc0750 | 0xd9c21 | 0xc0750 ( ) | +| 45 | 0x2f956b | 0x4 | 0xc07ce | 0xe78c1 | 0xc07ce ( ) | +| 46 | 0x2f9af8 | 0x3 | 0xc0843 | 0xd2774 | 0xc0843 ( ) | +| 47 | 0x32815a | 0x3 | 0xc11b2 | 0xd394f | 0xc11b2 ( ) | +| 48 | 0x333c07 | 0x3 | 0xd0fd0 | 0xdf268 | 0xd0fd0 ( ) | +| 49 | 0x37fb42 | 0x3 | 0xe94a8 | 0xd9978 | 0xe94a8 ( ) | +| 50 | 0x3c4c65 | 0x3 | 0xffb5f | 0xda92e | 0xffb5f ( ) | +| 51 | 0x43e4d8 | 0x3 | 0x120fdb | 0xdfa52 | 0x120fdb ( ) | +| 52 | 0x47457f | 0x3 | 0x13ac2d | 0xdfbc9 | 0x13ac2d ( ) | +| 53 | 0x47491b | 0x9 | 0x13ac9b | 0xd78c1 | 0x13ac9b ( ) | +| 54 | 0x4afc54 | 0x8 | 0x13be67 | 0xd5ba8 | 0x13be67 ( ) | +| 55 | 0x4f0f95 | 0x3 | 0x160f9d | 0xdbfd5 | 0x160f9d ( ) | +| 56 | 0x4f168d | 0x3 | 0x161026 | 0xd526f | 0x161026 ( ) | +| 57 | 0x4f28d2 | 0x3 | 0x1610ae | 0xe5380 | 0x1610ae ( ) | +| 58 | 0x57c70b | 0x3 | 0x1843ab | 0xd9b9d | 0x1843ab ( ) | +| 59 | 0x57cba2 | 0x3 | 0x184a2b | 0xd9f1c | 0x184a2b ( ) | +| 60 | 0x5d8aca | 0x9 | 0x1a476d | 0xe630d | 0x1a476d ( ) | +| 61 | 0x5df9f8 | 0x9 | 0x1a53a9 | 0xe4202 | 0x1a53a9 ( ) | +| 62 | 0x5f4745 | 0x3 | 0x1a4f80 | 0xe43a2 | 0x1a4f80 ( ) | +| 63 | 0x5fdd5c | 0x3 | 0x1a538d | 0xe1940 | 0x1a538d ( ) | +| 64 | 0x6080bc | 0x3 | 0x1a5b6d | 0xd6b14 | 0x1a5b6d ( ) | +| 65 | 0x649eb8 | 0x9 | 0x1b82aa | 0xdf7a5 | 0x1b82aa ( ) | +| 66 | 0x66912c | 0x4 | 0x1bf13d | 0xe3927 | 0x1bf13d ( ) | +| 67 | 0x6b9ddc | 0x3 | 0x25c9fa | 0xd7e9f | 0x25c9fa ( ) | +| 68 | 0x72baa8 | 0x3 | 0x1fef96 | 0xdcdb8 | 0x1fef96 ( ) | +| 69 | 0x744bc1 | 0x3 | 0x20a607 | 0xd3c88 | 0x20a607 ( ) | +| 70 | 0x744dce | 0x3 | 0x20b9ca | 0xdd729 | 0x20b9ca ( ) | +| 71 | 0x744e02 | 0x8 | 0x209e5a | 0xd80ee | 0x209e5a ( ) | +| 72 | 0x7451fc | 0x3 | 0x209ec3 | 0xef591 | 0x209ec3 ( ) | +| 73 | 0x745a15 | 0x9 | 0x20a00a | 0xe5107 | 0x20a00a ( ) | +| 74 | 0x745ee1 | 0x8 | 0x20a08b | 0xe44dd | 0x20a08b ( ) | +| 75 | 0x746977 | 0x3 | 0x20a15b | 0xed8c4 | 0x20a15b ( ) | +| 76 | 0x7828dc | 0x3 | 0x216ad0 | 0xd81e7 | 0x216ad0 ( ) | +| 77 | 0x7ce9f1 | 0x3 | 0x22b142 | 0xd9644 | 0x22b142 ( ) | +| 78 | 0x803df5 | 0x3 | 0x2310fe | 0xdcab4 | 0x2310fe ( ) | +| 79 | 0x84846b | 0x9 | 0x25129a | 0xd413e | 0x25129a ( ) | +| 80 | 0x86d1b5 | 0x3 | 0x257c99 | 0xd7480 | 0x257c99 ( ) | +| 81 | 0x87b65b | 0x9 | 0x25bd0d | 0xdd6aa | 0x25bd0d ( ) | +| 82 | 0x88b49e | 0x3 | 0x25ca62 | 0xea7e0 | 0x25ca62 ( ) | +| 83 | 0x8fd89b | 0x9 | 0x2710ad | 0xe27aa | 0x2710ad ( ) | +| 84 | 0x8fdb17 | 0x4 | 0x2712c9 | 0xd1936 | 0x2712c9 ( ) | +| 85 | 0x8fdb44 | 0x3 | 0x271117 | 0xd6d76 | 0x271117 ( ) | +| 86 | 0x8fe224 | 0x3 | 0x2711a9 | 0xd518c | 0x2711a9 ( ) | +| 87 | 0x8fe3d4 | 0x4 | 0x27121e | 0xd5be7 | 0x27121e ( ) | +| 88 | 0x8feaff | 0x8 | 0x2712a2 | 0xd470d | 0x2712a2 ( ) | +| 89 | 0x94bf69 | 0x3 | 0x2818d9 | 0xd4c80 | 0x2818d9 ( ) | +| 90 | 0x995688 | 0x3 | 0x28875a | 0xdf433 | 0x28875a ( ) | +| 91 | 0x9cbb60 | 0x9 | 0x28be2b | 0xde6bd | 0x28be2b ( ) | +| 92 | 0x9e8db6 | 0x3 | 0x2a1ecf | 0xe0039 | 0x2a1ecf ( ) | +| 93 | 0xa21883 | 0x3 | 0x2a728c | 0xd9965 | 0x2a728c ( ) | ++-------+----------+------+----------+---------------+--------------+ +Current path: (1, _, _, _) +Choose an entry [.., 0-93]: 30 + +Child node has Virtual OID 0x32f01, maps to block 0xd89df. +Reading block 0xd89df ... validating ... OK. +Node has 65 entries, as follows: ++-------+-----------------+-----------------------------------------+ +| | | | Target child node | +| Index | FSOID | FSRT |----------+---------------+--------------+ +| | | | OID | Block address | Actual OID | ++-------+----------+------+----------+---------------+--------------+ +| 0 | 0x183213 | 0x3 | 0x32efd | 0xd6494 | 0x32efd ( ) | +| 1 | 0x183bf7 | 0x3 | 0x32f89 | 0xd128c | 0x32f89 ( ) | +| 2 | 0x185464 | 0x3 | 0x33457 | 0xd9748 | 0x33457 ( ) | +| 3 | 0x18552b | 0x6 | 0x33564 | 0xe115b | 0x33564 ( ) | +| 4 | 0x187983 | 0x3 | 0x37adb | 0xd8f96 | 0x37adb ( ) | +| 5 | 0x18a6a4 | 0x6 | 0x3b3a0 | 0xe9e4c | 0x3b3a0 ( ) | +| 6 | 0x18a962 | 0x9 | 0x3b3ac | 0xde0dd | 0x3b3ac ( ) | +| 7 | 0x18a964 | 0x9 | 0x3b3f6 | 0xd32e1 | 0x3b3f6 ( ) | +| 8 | 0x18a96f | 0x3 | 0x3b3f9 | 0xe308d | 0x3b3f9 ( ) | +| 9 | 0x18a971 | 0x3 | 0x3b3f7 | 0xe3c1f | 0x3b3f7 ( ) | +| 10 | 0x18a985 | 0x3 | 0x3b3f8 | 0xe5deb | 0x3b3f8 ( ) | +| 11 | 0x18a999 | 0x3 | 0x3b3fc | 0xe57c0 | 0x3b3fc ( ) | +| 12 | 0x18ba67 | 0x9 | 0x3d295 | 0xd10e7 | 0x3d295 ( ) | +| 13 | 0x18ba6b | 0x3 | 0x3d293 | 0xd10e5 | 0x3d293 ( ) | +| 14 | 0x18ba80 | 0x3 | 0x3d294 | 0xd10e6 | 0x3d294 ( ) | +| 15 | 0x18ba93 | 0x4 | 0x3d296 | 0xe5a2d | 0x3d296 ( ) | +| 16 | 0x18e7c3 | 0x3 | 0x3d2ca | 0xea935 | 0x3d2ca ( ) | +| 17 | 0x18e7cb | 0x4 | 0x3d79b | 0xeae76 | 0x3d79b ( ) | +| 18 | 0x18e827 | 0x6 | 0x3d7b4 | 0xea93e | 0x3d7b4 ( ) | +| 19 | 0x18e8fb | 0x9 | 0x3d7b6 | 0xd13a6 | 0x3d7b6 ( ) | +| 20 | 0x18e8fb | 0x9 | 0x209c65 | 0xea942 | 0x209c65 ( ) | +| 21 | 0x18e91d | 0x3 | 0x3d7b9 | 0xeaa15 | 0x3d7b9 ( ) | +| 22 | 0x18e96c | 0x4 | 0x3d7c1 | 0xe4931 | 0x3d7c1 ( ) | +| 23 | 0x18e98f | 0x4 | 0x3d7ce | 0xe2c98 | 0x3d7ce ( ) | +| 24 | 0x1918c7 | 0x8 | 0x3ddca | 0xe6e13 | 0x3ddca ( ) | +| 25 | 0x19196b | 0x9 | 0x3de4d | 0xe3073 | 0x3de4d ( ) | +| 26 | 0x191973 | 0x4 | 0x3de50 | 0xdde67 | 0x3de50 ( ) | +| 27 | 0x191975 | 0x4 | 0x3de4e | 0xe3074 | 0x3de4e ( ) | +| 28 | 0x191989 | 0x4 | 0x3de4f | 0xe33b0 | 0x3de4f ( ) | +| 29 | 0x19199f | 0x3 | 0x3de51 | 0xeb69d | 0x3de51 ( ) | +| 30 | 0x191b1c | 0x3 | 0x3de81 | 0xd0831 | 0x3de81 ( ) | +| 31 | 0x191d0a | 0x6 | 0x3de9c | 0xda6a8 | 0x3de9c ( ) | +| 32 | 0x192004 | 0x6 | 0x3df11 | 0xd250c | 0x3df11 ( ) | +| 33 | 0x192170 | 0x3 | 0x3df2c | 0xd60aa | 0x3df2c ( ) | +| 34 | 0x193c56 | 0x8 | 0x42396 | 0xd1306 | 0x42396 ( ) | +| 35 | 0x197686 | 0x3 | 0x43f58 | 0xd08db | 0x43f58 ( ) | +| 36 | 0x197cd9 | 0x9 | 0x44247 | 0xde436 | 0x44247 ( ) | +| 37 | 0x19bacf | 0x9 | 0x446f7 | 0xe00c7 | 0x446f7 ( ) | +| 38 | 0x19badc | 0x9 | 0x44a7e | 0xe86f1 | 0x44a7e ( ) | +| 39 | 0x19baed | 0x6 | 0x44a7f | 0xd2a15 | 0x44a7f ( ) | +| 40 | 0x1a1150 | 0x3 | 0x453b0 | 0xd1322 | 0x453b0 ( ) | +| 41 | 0x1a2b18 | 0x3 | 0x4819d | 0xd15c1 | 0x4819d ( ) | +| 42 | 0x1a71f9 | 0x6 | 0x49491 | 0xf239e | 0x49491 ( ) | +| 43 | 0x1a71fe | 0x4 | 0x4c416 | 0xd10f9 | 0x4c416 ( ) | +| 44 | 0x1a720e | 0x3 | 0x4c417 | 0xd10fb | 0x4c417 ( ) | +| 45 | 0x1a7221 | 0x4 | 0x4c419 | 0xe6c90 | 0x4c419 ( ) | +| 46 | 0x1a7761 | 0x3 | 0x4c481 | 0xdefda | 0x4c481 ( ) | +| 47 | 0x1ae04f | 0x3 | 0x547cb | 0xdbf66 | 0x547cb ( ) | +| 48 | 0x1ae061 | 0x6 | 0x54c62 | 0xda59d | 0x54c62 ( ) | +| 49 | 0x1b2e00 | 0x3 | 0x55c2c | 0xd1737 | 0x55c2c ( ) | +| 50 | 0x1b33aa | 0x4 | 0x57d8b | 0xd6332 | 0x57d8b ( ) | +| 51 | 0x1b38a9 | 0x3 | 0x57e66 | 0xda582 | 0x57e66 ( ) | +| 52 | 0x1b3d5e | 0x3 | 0x57e67 | 0xe0759 | 0x57e67 ( ) | +| 53 | 0x1b5399 | 0x4 | 0x58248 | 0xe20e8 | 0x58248 ( ) | +| 54 | 0x1bb366 | 0x3 | 0x5d1dd | 0xd027e | 0x5d1dd ( ) | +| 55 | 0x1bf693 | 0x8 | 0x62440 | 0xea807 | 0x62440 ( ) | +| 56 | 0x1d401c | 0x6 | 0x658b6 | 0xd7efd | 0x658b6 ( ) | +| 57 | 0x1d4029 | 0x8 | 0x65f22 | 0xea6ef | 0x65f22 ( ) | +| 58 | 0x1d4037 | 0x8 | 0x65f23 | 0xd7fc2 | 0x65f23 ( ) | +| 59 | 0x1d4299 | 0x3 | 0x65f29 | 0xdf4a7 | 0x65f29 ( ) | +| 60 | 0x1d45a8 | 0x4 | 0x65f82 | 0xddb2a | 0x65f82 ( ) | +| 61 | 0x1d45ae | 0x3 | 0x65fae | 0xea73e | 0x65fae ( ) | +| 62 | 0x1d45b9 | 0x4 | 0x65faf | 0xde792 | 0x65faf ( ) | +| 63 | 0x1d45d2 | 0x8 | 0x65fb1 | 0xe961e | 0x65fb1 ( ) | +| 64 | 0x1d5e9c | 0x3 | 0x67303 | 0xe852e | 0x67303 ( ) | ++-------+----------+------+----------+---------------+--------------+ +Current path: (1, 30, _, _) +Choose an entry [.., 0-64]: 17 + +Child node has Virtual OID 0x3d79b, maps to block 0xeae76. +Reading block 0xeae76 ... validating ... OK. +Node has 30 entries, as follows: ++-------+----------+------------------+ +| Index | FSOID | FSRT | ++-------+----------+------------------+ +| 0 | 0x18e7cb | 0x4, xattr | ++-------+----------+------------------+ +| 1 | 0x18e7cc | 0x3, inode | +| 2 | 0x18e7cc | 0x4, xattr | +| 3 | 0x18e7cc | 0x6, dstream | +| 4 | 0x18e7cc | 0x8, file extent | ++-------+----------+------------------+ +| 5 | 0x18e7cd | 0x3, inode | +| 6 | 0x18e7cd | 0x4, xattr | +| 7 | 0x18e7cd | 0x6, dstream | +| 8 | 0x18e7cd | 0x8, file extent | ++-------+----------+------------------+ +| 9 | 0x18e825 | 0x3, inode | +| 10 | 0x18e825 | 0x4, xattr | +| | | +-------------+----------------+--------------------+ +| | | | Inode FSOID | Inode type | Inode name | +| | | +-------------+----------------+--------------------+ +| 11 | 0x18e825 | 0x9, dentry | 0x169da65 | 0x4, directory | 83bc0baa3881f265_0 | +| 12 | 0x18e825 | 0x9, dentry | 0x169da72 | 0x4, directory | d3c975fa1a9b20f6_0 | +| 13 | 0x18e825 | 0x9, dentry | 0x169da6b | 0x4, directory | df7b00085ba675ef_0 | +| 14 | 0x18e825 | 0x9, dentry | 0x18e828 | 0x4, directory | index-dir | +| 15 | 0x18e825 | 0x9, dentry | 0x169da6f | 0x4, directory | f3faf2ae921d9bf5_0 | +| 16 | 0x18e825 | 0x9, dentry | 0x169da71 | 0x4, directory | 41a6c187467c47e1_0 | +| 17 | 0x18e825 | 0x9, dentry | 0x169da6c | 0x4, directory | 9b7fc9e7916c2c7c_0 | +| 18 | 0x18e825 | 0x9, dentry | 0x169da73 | 0x4, directory | a20bc29b43958149_0 | +| 19 | 0x18e825 | 0x9, dentry | 0x169da70 | 0x4, directory | 9bffce5feb2725f3_0 | +| 20 | 0x18e825 | 0x9, dentry | 0x169da67 | 0x4, directory | fd78b4182656630a_0 | +| 21 | 0x18e825 | 0x9, dentry | 0x169da66 | 0x4, directory | fc39fc87aa0a667a_0 | +| 22 | 0x18e825 | 0x9, dentry | 0x169da6a | 0x4, directory | 083f607b0ffe8511_0 | +| 23 | 0x18e825 | 0x9, dentry | 0x169da64 | 0x4, directory | d4e4cca6484c1eb2_0 | +| 24 | 0x18e825 | 0x9, dentry | 0x169da68 | 0x4, directory | c15c8b176db7917c_0 | +| 25 | 0x18e825 | 0x9, dentry | 0x18e827 | 0x4, directory | index | +| 26 | 0x18e825 | 0x9, dentry | 0x169da69 | 0x4, directory | 1b12bbc9cb4a5267_0 | +| 27 | 0x18e825 | 0x9, dentry | 0x169da74 | 0x4, directory | a28d674c00625ded_0 | ++-------+----------+------------------+-------------+----------------+--------------------+ +| 28 | 0x18e827 | 0x3, inode | +| 29 | 0x18e827 | 0x4, xattr | ++-------+----------+------------------+ +Current path: (1, 30, 17, _) +Choose an entry [.., 0-29]: 13 + +Record data for entry 13: + METADATA: + - Key size: 31 bytes + - Value size: 18 bytes + - ID and type field: 0x900000000018e825 + KEY: + - Virtual OID: 0x18e825 + - Object type: Directory entry + VALUE: + - Dentry name length: 19 UTF-8 bytes (including terminating NULL (U+0000) byte) + - Dentry name hash: 0x06afe3 + - Dentry name: df7b00085ba675ef_0 + - Dentry Virtual OID: 0x169da6b + - Time added: Wed Mar 3 15:17:33 2021 + - Dentry type: Regular file + - No. extended fields: 0 +Current path: (1, 30, 17, 31) +Choose an entry [..]: +``` diff --git a/docs/commands/explore-fs.md b/docs/commands/explore-fs.md index 3a039cf..c43d0c6 100644 --- a/docs/commands/explore-fs.md +++ b/docs/commands/explore-fs.md @@ -1,245 +1,245 @@ -(command_explore-fs)= - -# {drat-command}`explore-fs` - -## Description - -The {drat-command}`explore-fs` command provides an interface via which you can -explore a volume's filesystem. That is, you can interactively navigate through -the filesystem, viewing the filesystem records for any filesystem object. When -you run {drat-command}`explore-fs`, you must specify: - -- a filesystem tree in the same manner as {drat-command}`explore-fs-tree`; and - -- an object map in the same manner as {drat-command}`explore-fs-tree`; and - -- an entry point, which is either: - - - an absolute filesystem path via {argument}`path`, like `/`, - `/Users/john`, `/Users/john/Documents/my document.txt`, or `/..`; or - - - a filesystem object ID via {argument}`fsoid`, like `0x7563`. - -If no filesystem tree is specified, the default is `--volume 1`. -If no entrypoint is specified, the default is `--path /`. - -When the command is run, Drat will display the filesystem records of the entry -point in the order they appear in the filesystem B-tree, which should be by -FSRT, then by name for xattrs and dentries. In order to navigate the filesystem, -you will be prompted to enter: - -- a record index; -- `/` followed by a dentry name; -- `@` followed by an xattr name; or -- `..`, which denotes the parent filesystem object. - -At any time, you can enter nothing to exit Drat. - -## Example usage and output - -``` -$ drat explore-fs --container /dev/disk0s2 --volume 1 --path /Users/john - -Opening file at `/dev/disk0s2` in read-only mode ... OK. - -Determining block size ... 4096 bytes. - -Finding volume 0: -- Finding most recent container superblock: - - Reading block 0x0 ... validating ... OK. - - Loading checkpoint descriptor area ... OK. - - Searching checkpoint descriptor area ... found most recent container superblock at index 2, its XID is 0x629c38. -- Finding container's object mappings tree: - - Container's object map has Physical OID 0xd25cd. - - Reading block 0xd25cd ... validating ... OK. - - Container's object mappings tree has Physical OID 0xdb5ce. - - Reading block 0xdb5ce ... validating ... OK. -- Finding volume 0's superblock: - - Volume 0 has Virtual OID 0x402, maps to block 0xd8e59. - - Reading block 0xd8e59 ... validating ... OK. - - Volume name: macOS - Data - -Finding volume's object mappings tree: -- Volume's object map has Physical OID 0xd66c6. -- Reading block 0xd66c6 ... validating ... OK. -- Volume's object mappings tree has Physical OID 0xd196a. -- Reading block 0xd196a ... validating ... OK. - -Finding volume's filesystem tree: -- Filesystem tree has Virtual OID 0x404, maps to block 0xcfcf3. -- Reading block 0xcfcf3 ... validating ... OK. - -Navigating to path `/Users/john` ... its FSOID is 0x7563. -Finding records for FSOID 0x7563 ... OK. -Filesystem object has 25 records, as follows: -+-------+-------------+ -| Index | FSRT | -+-------+-------------+ -| 0 | 0x3, inode | Parent FSOID = 0x558b -| 1 | 0x4, xattr | Name = com.apple.system.Security -| | +-------------+-------------------+---------------------+ -| | | Inode FSOID | Inode type | Inode name | -| | +-------------+-------------------+---------------------+ -| 2 | 0x9, dentry | 0x1476c2 | 0x4, directory | .config | -| 3 | 0x9, dentry | 0x7564 | 0x4, directory | Music | -| 4 | 0x9, dentry | 0x5a288d | 0x8, regular file | .DS_Store | -| 5 | 0x9, dentry | 0x759a | 0x8, regular file | .CFUserTextEncoding | -| 6 | 0x9, dentry | 0x37239 | 0x4, directory | bin | -| 7 | 0x9, dentry | 0x3723d | 0x8, regular file | .zshrc | -| 8 | 0x9, dentry | 0x9cf93 | 0x4, directory | .local | -| 9 | 0x9, dentry | 0x7566 | 0x4, directory | Pictures | -| 10 | 0x9, dentry | 0x16f5f01 | 0x8, regular file | .zsh_history | -| 11 | 0x9, dentry | 0x7568 | 0x4, directory | Desktop | -| 12 | 0x9, dentry | 0x756a | 0x4, directory | Library | -| 13 | 0x9, dentry | 0x182e7e | 0x4, directory | .cups | -| 14 | 0x9, dentry | 0x36fc6 | 0x4, directory | .ssh | -| 15 | 0x9, dentry | 0x7592 | 0x4, directory | Movies | -| 16 | 0x9, dentry | 0x3297d | 0x4, directory | Applications | -| 17 | 0x9, dentry | 0x16f2b3e | 0x4, directory | .Trash | -| 18 | 0x9, dentry | 0x153fffe | 0x8, regular file | .zcompdump | -| 19 | 0x9, dentry | 0x7594 | 0x4, directory | Documents | -| 20 | 0x9, dentry | 0xaf5a6c | 0x4, directory | .zcompcache | -| 21 | 0x9, dentry | 0xc2297 | 0x8, regular file | .Xauthority | -| 22 | 0x9, dentry | 0x7596 | 0x4, directory | Downloads | -| 23 | 0x9, dentry | 0xc12c9 | 0x4, directory | .cache | -| 24 | 0x9, dentry | 0x144d51 | 0x8, regular file | .aliases | -+-------+-------------+-------------+-------------------+---------------------+ -Current path: /Users/john -Choose a record [.., 0-24, /, @]: 4 - -Finding records for FSOID 0x5a288d ... OK. -Filesystem object has 5 records, as follows: -+-------+------------------+ -| Index | FSRT | -+-------+------------------+ -| 0 | 0x3, inode | Parent FSOID = 0x7563 -| 1 | 0x4, xattr | Name = com.apple.FinderInfo -| 2 | 0x6, dstream | Reference count = 1 -| | +-----------------+----------------+---------------+ -| | | Logical address | Length (bytes) | Block address | -| | +-----------------+----------------+---------------+ -| 3 | 0x8, file extent | 0 | 4096 | 0x359223c | -| 4 | 0x8, file extent | 0x1000 | 8192 | 0x2c69bcd | -+-------+------------------+-----------------+----------------+---------------+ -Current path: /Users/john/.DS_Store -Choose a record [.., 0-4, @]: .. - -Finding records for FSOID 0x7563 ... OK -Object has 25 records, as follows: -+-------+-------------+ -| Index | FSRT | -+-------+-------------+ -| 0 | 0x3, inode | Parent FSOID = 0x558b -| 1 | 0x4, xattr | Name = com.apple.system.Security -| | +-------------+-------------------+---------------------+ -| | | Inode FSOID | Inode type | Inode name | -| | +-------------+-------------------+---------------------+ -| 2 | 0x9, dentry | 0x1476c2 | 0x4, directory | .config | -| 3 | 0x9, dentry | 0x7564 | 0x4, directory | Music | -| 4 | 0x9, dentry | 0x5a288d | 0x8, regular file | .DS_Store | -| 5 | 0x9, dentry | 0x759a | 0x8, regular file | .CFUserTextEncoding | -| 6 | 0x9, dentry | 0x37239 | 0x4, directory | bin | -| 7 | 0x9, dentry | 0x3723d | 0x8, regular file | .zshrc | -| 8 | 0x9, dentry | 0x9cf93 | 0x4, directory | .local | -| 9 | 0x9, dentry | 0x7566 | 0x4, directory | Pictures | -| 10 | 0x9, dentry | 0x16f5f01 | 0x8, regular file | .zsh_history | -| 11 | 0x9, dentry | 0x7568 | 0x4, directory | Desktop | -| 12 | 0x9, dentry | 0x756a | 0x4, directory | Library | -| 13 | 0x9, dentry | 0x182e7e | 0x4, directory | .cups | -| 14 | 0x9, dentry | 0x36fc6 | 0x4, directory | .ssh | -| 15 | 0x9, dentry | 0x7592 | 0x4, directory | Movies | -| 16 | 0x9, dentry | 0x3297d | 0x4, directory | Applications | -| 17 | 0x9, dentry | 0x16f2b3e | 0x4, directory | .Trash | -| 18 | 0x9, dentry | 0x153fffe | 0x8, regular file | .zcompdump | -| 19 | 0x9, dentry | 0x7594 | 0x4, directory | Documents | -| 20 | 0x9, dentry | 0xaf5a6c | 0x4, directory | .zcompcache | -| 21 | 0x9, dentry | 0xc2297 | 0x8, regular file | .Xauthority | -| 22 | 0x9, dentry | 0x7596 | 0x4, directory | Downloads | -| 23 | 0x9, dentry | 0xc12c9 | 0x4, directory | .cache | -| 24 | 0x9, dentry | 0x144d51 | 0x8, regular file | .aliases | -+-------+-------------+-------------+-------------------+---------------------+ -Current path: /Users/john -Choose a record [.., 0-24, /, @]: /Library - -Finding records for FSOID 0x756a ... OK. -Object has 70 records, as follows: -+-------+-------------+ -| Index | FSRT | -+-------+-------------+ -| 0 | 0x3, inode | Parent FSOID = 0x7563 -| 1 | 0x4, xattr | Name = com.apple.FinderInfo -| 2 | 0x4, xattr | Name = com.apple.system.Security -| | +-------------+-------------------+-------------------------+ -| | | Inode FSOID | Inode type | Inode name | -| | +-------------+-------------------+-------------------------+ -| 3 | 0x9, dentry | 0x756b | 0x4, directory | Application Support | -| 4 | 0x9, dentry | 0x81a6 | 0x4, directory | Maps | -| 5 | 0x9, dentry | 0x7980 | 0x4, directory | Assistant | -| 6 | 0x9, dentry | 0x756c | 0x4, directory | Autosave Information | -| 7 | 0x9, dentry | 0x8952 | 0x4, directory | Saved Application State | -| 8 | 0x9, dentry | 0x8044 | 0x4, directory | IdentityServices | -| 9 | 0x9, dentry | 0x8b74 | 0x4, directory | WebKit | -| 10 | 0x9, dentry | 0x14308d | 0x4, directory | Developer | -| 11 | 0x9, dentry | 0x5299e3 | 0x4, directory | Mozilla | -| 12 | 0x9, dentry | 0x7964 | 0x4, directory | Calendars | -| 13 | 0x9, dentry | 0x756d | 0x4, directory | Preferences | -| 14 | 0x9, dentry | 0x8946 | 0x4, directory | studentd | -| 15 | 0x9, dentry | 0x7751 | 0x4, directory | Messages | -| 16 | 0x9, dentry | 0x8ff5 | 0x4, directory | HomeKit | -| 17 | 0x9, dentry | 0x2f777a | 0x4, directory | DES | -| 18 | 0x9, dentry | 0x75ef | 0x4, directory | Keychains | -| 19 | 0x9, dentry | 0x8093 | 0x4, directory | Sharing | -| 20 | 0x9, dentry | 0x7571 | 0x4, directory | ColorPickers | -| 21 | 0x9, dentry | 0x1538ca | 0x4, directory | ColorSync | -| 22 | 0x9, dentry | 0x782e | 0x4, directory | Application Scripts | -| 23 | 0x9, dentry | 0x7572 | 0x4, directory | Assistants | -| 24 | 0x9, dentry | 0x759d | 0x8, regular file | .localized | -| 25 | 0x9, dentry | 0x776f | 0x4, directory | Mail | -| 26 | 0x9, dentry | 0x99335 | 0x4, directory | UIKitSystem | -| 27 | 0x9, dentry | 0x7573 | 0x4, directory | Compositions | -| 28 | 0x9, dentry | 0x846f5e | 0x4, directory | GameKit | -| 29 | 0x9, dentry | 0x323ad | 0x4, directory | Google | -| 30 | 0x9, dentry | 0x8bb2 | 0x4, directory | LanguageModeling | -| 31 | 0x9, dentry | 0x7575 | 0x4, directory | Favorites | -| 32 | 0x9, dentry | 0x8e95 | 0x4, directory | Passes | -| 33 | 0x9, dentry | 0x759e | 0x4, directory | FontCollections | -| 34 | 0x9, dentry | 0x7577 | 0x4, directory | Sounds | -| 35 | 0x9, dentry | 0x82f4 | 0x4, directory | com.apple.internal.ck | -| 36 | 0x9, dentry | 0x7578 | 0x4, directory | Printers | -| 37 | 0x9, dentry | 0x768a | 0x4, directory | SyncedPreferences | -| 38 | 0x9, dentry | 0x7579 | 0x4, directory | Audio | -| 39 | 0x9, dentry | 0x7582 | 0x4, directory | Keyboard Layouts | -| 40 | 0x9, dentry | 0x7583 | 0x4, directory | Logs | -| 41 | 0x9, dentry | 0x7584 | 0x4, directory | Internet Plug-Ins | -| 42 | 0x9, dentry | 0x96d34 | 0x4, directory | FrontBoard | -| 43 | 0x9, dentry | 0x9220 | 0x4, directory | News | -| 44 | 0x9, dentry | 0x767d | 0x4, directory | Accounts | -| 45 | 0x9, dentry | 0x7783 | 0x4, directory | Safari | -| 46 | 0x9, dentry | 0x7585 | 0x4, directory | Colors | -| 47 | 0x9, dentry | 0x8cce | 0x4, directory | MediaStream | -| 48 | 0x9, dentry | 0x7586 | 0x4, directory | PreferencePanes | -| 49 | 0x9, dentry | 0x9bce | 0x4, directory | Mobile Documents | -| 50 | 0x9, dentry | 0x7666 | 0x4, directory | Suggestions | -| 51 | 0x9, dentry | 0x324a7 | 0x4, directory | LaunchAgents | -| 52 | 0x9, dentry | 0x7fc8 | 0x4, directory | Group Containers | -| 53 | 0x9, dentry | 0x8b86 | 0x4, directory | Dictionaries | -| 54 | 0x9, dentry | 0x76b8 | 0x4, directory | Containers | -| 55 | 0x9, dentry | 0x767b | 0x4, directory | PersonalizationPortrait | -| 56 | 0x9, dentry | 0x7587 | 0x4, directory | Fonts | -| 57 | 0x9, dentry | 0x8b7f | 0x4, directory | KeyboardServices | -| 58 | 0x9, dentry | 0x7628 | 0x4, directory | Metadata | -| 59 | 0x9, dentry | 0x7588 | 0x4, directory | Screen Savers | -| 60 | 0x9, dentry | 0x8078 | 0x4, directory | CallServices | -| 61 | 0x9, dentry | 0x7589 | 0x4, directory | Spelling | -| 62 | 0x9, dentry | 0x9cea | 0x4, directory | SafariSafeBrowsing | -| 63 | 0x9, dentry | 0x760f | 0x4, directory | Reminders | -| 64 | 0x9, dentry | 0x758a | 0x4, directory | Input Methods | -| 65 | 0x9, dentry | 0x9bea | 0x4, directory | Cookies | -| 66 | 0x9, dentry | 0x758c | 0x4, directory | Services | -| 67 | 0x9, dentry | 0x294760 | 0x4, directory | Keyboard | -| 68 | 0x9, dentry | 0x881f | 0x4, directory | CoreFollowUp | -| 69 | 0x9, dentry | 0x758d | 0x4, directory | Caches | -+-------+-------------+-------------+-------------------+-------------------------+ -Current path: /Users/john/Library -Choose a record [.., 0-69, /, @]: -``` +(command_explore-fs)= + +# {drat-command}`explore-fs` + +## Description + +The {drat-command}`explore-fs` command provides an interface via which you can +explore a volume's filesystem. That is, you can interactively navigate through +the filesystem, viewing the filesystem records for any filesystem object. When +you run {drat-command}`explore-fs`, you must specify: + +- a filesystem tree in the same manner as {drat-command}`explore-fs-tree`; and + +- an object map in the same manner as {drat-command}`explore-fs-tree`; and + +- an entry point, which is either: + + - an absolute filesystem path via {argument}`path`, like `/`, + `/Users/john`, `/Users/john/Documents/my document.txt`, or `/..`; or + + - a filesystem object ID via {argument}`fsoid`, like `0x7563`. + +If no filesystem tree is specified, the default is `--volume 1`. +If no entrypoint is specified, the default is `--path /`. + +When the command is run, Drat will display the filesystem records of the entry +point in the order they appear in the filesystem B-tree, which should be by +FSRT, then by name for xattrs and dentries. In order to navigate the filesystem, +you will be prompted to enter: + +- a record index; +- `/` followed by a dentry name; +- `@` followed by an xattr name; or +- `..`, which denotes the parent filesystem object. + +At any time, you can enter nothing to exit Drat. + +## Example usage and output + +``` +$ drat explore-fs --container /dev/disk0s2 --volume 1 --path /Users/john + +Opening file at `/dev/disk0s2` in read-only mode ... OK. + +Determining block size ... 4096 bytes. + +Finding volume 0: +- Finding most recent container superblock: + - Reading block 0x0 ... validating ... OK. + - Loading checkpoint descriptor area ... OK. + - Searching checkpoint descriptor area ... found most recent container superblock at index 2, its XID is 0x629c38. +- Finding container's object mappings tree: + - Container's object map has Physical OID 0xd25cd. + - Reading block 0xd25cd ... validating ... OK. + - Container's object mappings tree has Physical OID 0xdb5ce. + - Reading block 0xdb5ce ... validating ... OK. +- Finding volume 0's superblock: + - Volume 0 has Virtual OID 0x402, maps to block 0xd8e59. + - Reading block 0xd8e59 ... validating ... OK. + - Volume name: macOS - Data + +Finding volume's object mappings tree: +- Volume's object map has Physical OID 0xd66c6. +- Reading block 0xd66c6 ... validating ... OK. +- Volume's object mappings tree has Physical OID 0xd196a. +- Reading block 0xd196a ... validating ... OK. + +Finding volume's filesystem tree: +- Filesystem tree has Virtual OID 0x404, maps to block 0xcfcf3. +- Reading block 0xcfcf3 ... validating ... OK. + +Navigating to path `/Users/john` ... its FSOID is 0x7563. +Finding records for FSOID 0x7563 ... OK. +Filesystem object has 25 records, as follows: ++-------+-------------+ +| Index | FSRT | ++-------+-------------+ +| 0 | 0x3, inode | Parent FSOID = 0x558b +| 1 | 0x4, xattr | Name = com.apple.system.Security +| | +-------------+-------------------+---------------------+ +| | | Inode FSOID | Inode type | Inode name | +| | +-------------+-------------------+---------------------+ +| 2 | 0x9, dentry | 0x1476c2 | 0x4, directory | .config | +| 3 | 0x9, dentry | 0x7564 | 0x4, directory | Music | +| 4 | 0x9, dentry | 0x5a288d | 0x8, regular file | .DS_Store | +| 5 | 0x9, dentry | 0x759a | 0x8, regular file | .CFUserTextEncoding | +| 6 | 0x9, dentry | 0x37239 | 0x4, directory | bin | +| 7 | 0x9, dentry | 0x3723d | 0x8, regular file | .zshrc | +| 8 | 0x9, dentry | 0x9cf93 | 0x4, directory | .local | +| 9 | 0x9, dentry | 0x7566 | 0x4, directory | Pictures | +| 10 | 0x9, dentry | 0x16f5f01 | 0x8, regular file | .zsh_history | +| 11 | 0x9, dentry | 0x7568 | 0x4, directory | Desktop | +| 12 | 0x9, dentry | 0x756a | 0x4, directory | Library | +| 13 | 0x9, dentry | 0x182e7e | 0x4, directory | .cups | +| 14 | 0x9, dentry | 0x36fc6 | 0x4, directory | .ssh | +| 15 | 0x9, dentry | 0x7592 | 0x4, directory | Movies | +| 16 | 0x9, dentry | 0x3297d | 0x4, directory | Applications | +| 17 | 0x9, dentry | 0x16f2b3e | 0x4, directory | .Trash | +| 18 | 0x9, dentry | 0x153fffe | 0x8, regular file | .zcompdump | +| 19 | 0x9, dentry | 0x7594 | 0x4, directory | Documents | +| 20 | 0x9, dentry | 0xaf5a6c | 0x4, directory | .zcompcache | +| 21 | 0x9, dentry | 0xc2297 | 0x8, regular file | .Xauthority | +| 22 | 0x9, dentry | 0x7596 | 0x4, directory | Downloads | +| 23 | 0x9, dentry | 0xc12c9 | 0x4, directory | .cache | +| 24 | 0x9, dentry | 0x144d51 | 0x8, regular file | .aliases | ++-------+-------------+-------------+-------------------+---------------------+ +Current path: /Users/john +Choose a record [.., 0-24, /, @]: 4 + +Finding records for FSOID 0x5a288d ... OK. +Filesystem object has 5 records, as follows: ++-------+------------------+ +| Index | FSRT | ++-------+------------------+ +| 0 | 0x3, inode | Parent FSOID = 0x7563 +| 1 | 0x4, xattr | Name = com.apple.FinderInfo +| 2 | 0x6, dstream | Reference count = 1 +| | +-----------------+----------------+---------------+ +| | | Logical address | Length (bytes) | Block address | +| | +-----------------+----------------+---------------+ +| 3 | 0x8, file extent | 0 | 4096 | 0x359223c | +| 4 | 0x8, file extent | 0x1000 | 8192 | 0x2c69bcd | ++-------+------------------+-----------------+----------------+---------------+ +Current path: /Users/john/.DS_Store +Choose a record [.., 0-4, @]: .. + +Finding records for FSOID 0x7563 ... OK +Object has 25 records, as follows: ++-------+-------------+ +| Index | FSRT | ++-------+-------------+ +| 0 | 0x3, inode | Parent FSOID = 0x558b +| 1 | 0x4, xattr | Name = com.apple.system.Security +| | +-------------+-------------------+---------------------+ +| | | Inode FSOID | Inode type | Inode name | +| | +-------------+-------------------+---------------------+ +| 2 | 0x9, dentry | 0x1476c2 | 0x4, directory | .config | +| 3 | 0x9, dentry | 0x7564 | 0x4, directory | Music | +| 4 | 0x9, dentry | 0x5a288d | 0x8, regular file | .DS_Store | +| 5 | 0x9, dentry | 0x759a | 0x8, regular file | .CFUserTextEncoding | +| 6 | 0x9, dentry | 0x37239 | 0x4, directory | bin | +| 7 | 0x9, dentry | 0x3723d | 0x8, regular file | .zshrc | +| 8 | 0x9, dentry | 0x9cf93 | 0x4, directory | .local | +| 9 | 0x9, dentry | 0x7566 | 0x4, directory | Pictures | +| 10 | 0x9, dentry | 0x16f5f01 | 0x8, regular file | .zsh_history | +| 11 | 0x9, dentry | 0x7568 | 0x4, directory | Desktop | +| 12 | 0x9, dentry | 0x756a | 0x4, directory | Library | +| 13 | 0x9, dentry | 0x182e7e | 0x4, directory | .cups | +| 14 | 0x9, dentry | 0x36fc6 | 0x4, directory | .ssh | +| 15 | 0x9, dentry | 0x7592 | 0x4, directory | Movies | +| 16 | 0x9, dentry | 0x3297d | 0x4, directory | Applications | +| 17 | 0x9, dentry | 0x16f2b3e | 0x4, directory | .Trash | +| 18 | 0x9, dentry | 0x153fffe | 0x8, regular file | .zcompdump | +| 19 | 0x9, dentry | 0x7594 | 0x4, directory | Documents | +| 20 | 0x9, dentry | 0xaf5a6c | 0x4, directory | .zcompcache | +| 21 | 0x9, dentry | 0xc2297 | 0x8, regular file | .Xauthority | +| 22 | 0x9, dentry | 0x7596 | 0x4, directory | Downloads | +| 23 | 0x9, dentry | 0xc12c9 | 0x4, directory | .cache | +| 24 | 0x9, dentry | 0x144d51 | 0x8, regular file | .aliases | ++-------+-------------+-------------+-------------------+---------------------+ +Current path: /Users/john +Choose a record [.., 0-24, /, @]: /Library + +Finding records for FSOID 0x756a ... OK. +Object has 70 records, as follows: ++-------+-------------+ +| Index | FSRT | ++-------+-------------+ +| 0 | 0x3, inode | Parent FSOID = 0x7563 +| 1 | 0x4, xattr | Name = com.apple.FinderInfo +| 2 | 0x4, xattr | Name = com.apple.system.Security +| | +-------------+-------------------+-------------------------+ +| | | Inode FSOID | Inode type | Inode name | +| | +-------------+-------------------+-------------------------+ +| 3 | 0x9, dentry | 0x756b | 0x4, directory | Application Support | +| 4 | 0x9, dentry | 0x81a6 | 0x4, directory | Maps | +| 5 | 0x9, dentry | 0x7980 | 0x4, directory | Assistant | +| 6 | 0x9, dentry | 0x756c | 0x4, directory | Autosave Information | +| 7 | 0x9, dentry | 0x8952 | 0x4, directory | Saved Application State | +| 8 | 0x9, dentry | 0x8044 | 0x4, directory | IdentityServices | +| 9 | 0x9, dentry | 0x8b74 | 0x4, directory | WebKit | +| 10 | 0x9, dentry | 0x14308d | 0x4, directory | Developer | +| 11 | 0x9, dentry | 0x5299e3 | 0x4, directory | Mozilla | +| 12 | 0x9, dentry | 0x7964 | 0x4, directory | Calendars | +| 13 | 0x9, dentry | 0x756d | 0x4, directory | Preferences | +| 14 | 0x9, dentry | 0x8946 | 0x4, directory | studentd | +| 15 | 0x9, dentry | 0x7751 | 0x4, directory | Messages | +| 16 | 0x9, dentry | 0x8ff5 | 0x4, directory | HomeKit | +| 17 | 0x9, dentry | 0x2f777a | 0x4, directory | DES | +| 18 | 0x9, dentry | 0x75ef | 0x4, directory | Keychains | +| 19 | 0x9, dentry | 0x8093 | 0x4, directory | Sharing | +| 20 | 0x9, dentry | 0x7571 | 0x4, directory | ColorPickers | +| 21 | 0x9, dentry | 0x1538ca | 0x4, directory | ColorSync | +| 22 | 0x9, dentry | 0x782e | 0x4, directory | Application Scripts | +| 23 | 0x9, dentry | 0x7572 | 0x4, directory | Assistants | +| 24 | 0x9, dentry | 0x759d | 0x8, regular file | .localized | +| 25 | 0x9, dentry | 0x776f | 0x4, directory | Mail | +| 26 | 0x9, dentry | 0x99335 | 0x4, directory | UIKitSystem | +| 27 | 0x9, dentry | 0x7573 | 0x4, directory | Compositions | +| 28 | 0x9, dentry | 0x846f5e | 0x4, directory | GameKit | +| 29 | 0x9, dentry | 0x323ad | 0x4, directory | Google | +| 30 | 0x9, dentry | 0x8bb2 | 0x4, directory | LanguageModeling | +| 31 | 0x9, dentry | 0x7575 | 0x4, directory | Favorites | +| 32 | 0x9, dentry | 0x8e95 | 0x4, directory | Passes | +| 33 | 0x9, dentry | 0x759e | 0x4, directory | FontCollections | +| 34 | 0x9, dentry | 0x7577 | 0x4, directory | Sounds | +| 35 | 0x9, dentry | 0x82f4 | 0x4, directory | com.apple.internal.ck | +| 36 | 0x9, dentry | 0x7578 | 0x4, directory | Printers | +| 37 | 0x9, dentry | 0x768a | 0x4, directory | SyncedPreferences | +| 38 | 0x9, dentry | 0x7579 | 0x4, directory | Audio | +| 39 | 0x9, dentry | 0x7582 | 0x4, directory | Keyboard Layouts | +| 40 | 0x9, dentry | 0x7583 | 0x4, directory | Logs | +| 41 | 0x9, dentry | 0x7584 | 0x4, directory | Internet Plug-Ins | +| 42 | 0x9, dentry | 0x96d34 | 0x4, directory | FrontBoard | +| 43 | 0x9, dentry | 0x9220 | 0x4, directory | News | +| 44 | 0x9, dentry | 0x767d | 0x4, directory | Accounts | +| 45 | 0x9, dentry | 0x7783 | 0x4, directory | Safari | +| 46 | 0x9, dentry | 0x7585 | 0x4, directory | Colors | +| 47 | 0x9, dentry | 0x8cce | 0x4, directory | MediaStream | +| 48 | 0x9, dentry | 0x7586 | 0x4, directory | PreferencePanes | +| 49 | 0x9, dentry | 0x9bce | 0x4, directory | Mobile Documents | +| 50 | 0x9, dentry | 0x7666 | 0x4, directory | Suggestions | +| 51 | 0x9, dentry | 0x324a7 | 0x4, directory | LaunchAgents | +| 52 | 0x9, dentry | 0x7fc8 | 0x4, directory | Group Containers | +| 53 | 0x9, dentry | 0x8b86 | 0x4, directory | Dictionaries | +| 54 | 0x9, dentry | 0x76b8 | 0x4, directory | Containers | +| 55 | 0x9, dentry | 0x767b | 0x4, directory | PersonalizationPortrait | +| 56 | 0x9, dentry | 0x7587 | 0x4, directory | Fonts | +| 57 | 0x9, dentry | 0x8b7f | 0x4, directory | KeyboardServices | +| 58 | 0x9, dentry | 0x7628 | 0x4, directory | Metadata | +| 59 | 0x9, dentry | 0x7588 | 0x4, directory | Screen Savers | +| 60 | 0x9, dentry | 0x8078 | 0x4, directory | CallServices | +| 61 | 0x9, dentry | 0x7589 | 0x4, directory | Spelling | +| 62 | 0x9, dentry | 0x9cea | 0x4, directory | SafariSafeBrowsing | +| 63 | 0x9, dentry | 0x760f | 0x4, directory | Reminders | +| 64 | 0x9, dentry | 0x758a | 0x4, directory | Input Methods | +| 65 | 0x9, dentry | 0x9bea | 0x4, directory | Cookies | +| 66 | 0x9, dentry | 0x758c | 0x4, directory | Services | +| 67 | 0x9, dentry | 0x294760 | 0x4, directory | Keyboard | +| 68 | 0x9, dentry | 0x881f | 0x4, directory | CoreFollowUp | +| 69 | 0x9, dentry | 0x758d | 0x4, directory | Caches | ++-------+-------------+-------------+-------------------+-------------------------+ +Current path: /Users/john/Library +Choose a record [.., 0-69, /, @]: +``` diff --git a/docs/commands/explore-omap-tree.md b/docs/commands/explore-omap-tree.md index ee5c835..eee3285 100644 --- a/docs/commands/explore-omap-tree.md +++ b/docs/commands/explore-omap-tree.md @@ -1,301 +1,301 @@ -(command_explore-omap-tree)= - -# {drat-command}`explore-omap-tree` - -## Description - -The {drat-command}`explore-omap-tree` command provides an interface via which you -can explore an object map B-tree (or subtree). You specify the B-tree either: - -- directly, by specifying the block address of a B-tree (root) node via - {argument}`omap`; or - -- indirectly, by specifying sufficient info for the block address of a B-tree - root node to be determined. That is, specify either: - - - the block address of an object map (a data structure of type - {datatype}`omap_phys_t`), volume superblock, or container superblock via - {argument}`omap`; or - - - a volume or the container itself via {argument}`volume` or - {argument}`volume-name`, optionally along with a maximum transaction ID via - {argument}`max-xid` (the most recent object map with a transaction ID that - doesn't exceed the one specified will be explored). - -If no such arguments are specified, the default is `--volume 0 --max-xid -1`. -That is, Drat will explore the most recent container object map B-tree. - -When the command is run, Drat will display the entries within the specified -B-tree (root) node, each of which is a key given by an (OID, XID) pair. You -will then be prompted to choose an entry by specifying its index and pressing -ENTER. Drat will then read the corresponding child node and display its -entries. You can repeat this process of entry selection until you select an -entry in a leaf node, after which Drat will display info about the -corresponding record in the tree. Pressing ENTER at this point will exit Drat. -At any point, you can specify `..` instead of a valid entry index to go back to -the parent node. - -When viewing a non-leaf node, an extra column, *Target child node*, is shown -for convenience, which displays the address of the child node that the entry -points to. - -When viewing a leaf node, an extra set of columns, *Target block*, is shown, -which displays the *Address* of the block that each (OID, XID) pair maps to, as -well as the *Actual OID* and *Actual XID* that appear in the data contained in -that block. Following each *Actual OID* and *Actual XID* is a checkbox `( )`, -which will be checked `(X)` if that value does not match the value in the *OID* -or *XID* column, respectively, indicating that the mapping is bad. A well-formed -object map will not result in any checkboxes being checked. - -## Example usage - -``` -$ drat --container /dev/disk2s2 explore-omap-tree - -$ drat --container partition-dump.bin explore-omap-tree --volume 3 - -$ drat --container /dev/disk2s2 explore-omap-tree --volume-name Preboot --max-xid 0x56f2e00 - -$ drat --container partition-dump.bin explore-omap-tree --omap 0xd1395 -``` - -## Example output - -``` -$ drat --container partition-dump.bin explore-omap-tree --omap 0xd1395 - -Opening file at `partition-dump.bin` in read-only mode ... OK. - -Reading block 0xd1395 ... validating ... OK. -This is a B-tree root node. -Height of tree is 3. -Node has 11 entries, as follows: -+-------+----------+----------+-------------------+ -| Index | OID | XID | Target child node | -+-------+----------+----------+-------------------+ -| 0 | 0x404 | 0x620846 | 0x100000000012 | -| 1 | 0x1a62b | 0x1ac2 | 0xd8384 | -| 2 | 0x1a42d1 | 0x5fe4fc | 0xd3dff | -| 3 | 0x3a3770 | 0x615572 | 0xd6d35 | -| 4 | 0x421d21 | 0x305a6f | 0xd7c77 | -| 5 | 0x52f2b1 | 0x4034b2 | 0x8ffff | -| 6 | 0x56cae8 | 0x49bb2b | 0x80028 | -| 7 | 0x5ea464 | 0x5ef837 | 0xd6445 | -| 8 | 0x636ea0 | 0x5e61d0 | 0xd30cc | -| 9 | 0x66b83b | 0x62031d | 0xd3dec | -| 10 | 0x67fea8 | 0x5fe60c | 0xd6ac4 | -+-------+----------+----------+-------------------+ -Current path: (_, _, _) -Choose an entry [0-10]: 1 - -Child node is block 0xd663a. -Reading block 0xd663a ... validating ... OK. -Node has 91 entries, as follows: -+-------+----------+----------+-------------------+ -| Index | OID | XID | Target child node | -+-------+----------+----------+-------------------+ -| 0 | 0x1a62b | 0x1ac2 | 0xd6e95 | -| 1 | 0x1a921 | 0x1a97 | 0x80188 | -| 2 | 0x1abbf | 0x1ad0 | 0x80470 | -| 3 | 0x1adae | 0x1ad0 | 0xd6e47 | -| 4 | 0x1ae09 | 0x1a97 | 0xd6e4a | -| 5 | 0x1ae78 | 0x1a97 | 0xd6e53 | -| 6 | 0x1af64 | 0x5d5d2b | 0x80368 | -| 7 | 0x1b1dc | 0x1d3c | 0xd6e91 | -| 8 | 0x1dd86 | 0x1ad0 | 0xe2803 | -| 9 | 0x1ee21 | 0x1acb | 0xeb460 | -| 10 | 0x22bc3 | 0x1d38 | 0xe4433 | -| 11 | 0x22c80 | 0x222913 | 0x80420 | -| 12 | 0x28c5c | 0x3b52cd | 0x80230 | -| 13 | 0x2c70f | 0x6a29 | 0x803e0 | -| 14 | 0x2c930 | 0x5881b1 | 0xd6a38 | -| 15 | 0x2cc08 | 0x620345 | 0xd5df1 | -| 16 | 0x31de5 | 0x4b5fc | 0xdb797 | -| 17 | 0x31e20 | 0x4b5fc | 0xcfe3e | -| 18 | 0x31e90 | 0x4b5fc | 0xd5533 | -| 19 | 0x31efe | 0x4b5fc | 0x800f0 | -| 20 | 0x32d0b | 0x615572 | 0xd6029 | -| 21 | 0x32e1b | 0xaac2 | 0xe0a07 | -| 22 | 0x32e60 | 0x5d5d2a | 0xd672a | -| 23 | 0x32efd | 0x3c0651 | 0xe74f4 | -| 24 | 0x4c481 | 0x5fa6c4 | 0x80460 | -| 25 | 0x799da | 0x61d90c | 0x80450 | -| 26 | 0x92a7f | 0x3c0550 | 0x803f0 | -| 27 | 0xb137a | 0x34a74c | 0x800e8 | -| 28 | 0xb7074 | 0x61da8a | 0xcfd16 | -| 29 | 0xbb0e4 | 0x4c663 | 0xd55d1 | -| 30 | 0xbb413 | 0x4ce5e | 0xe029f | -| 31 | 0xbb4d3 | 0x4ce45 | 0xd5adb | -| 32 | 0xbbcc3 | 0x4c947 | 0xda3c0 | -| 33 | 0xbbffa | 0x4ce72 | 0x80238 | -| 34 | 0xbc03b | 0x4ce45 | 0xe6aee | -| 35 | 0xbda1c | 0x3c066c | 0xd0084 | -| 36 | 0xc060b | 0x4b5fc | 0x80468 | -| 37 | 0xc0648 | 0x4b5fc | 0xe71a1 | -| 38 | 0xc06a6 | 0x4b5fc | 0x80170 | -| 39 | 0xc06e7 | 0x31a219 | 0xe2b32 | -| 40 | 0xc0726 | 0x4b5fc | 0xeb5db | -| 41 | 0xc0784 | 0x31a219 | 0xe3b9a | -| 42 | 0xc07c3 | 0x4b5fc | 0x80030 | -| 43 | 0xc0808 | 0x31a219 | 0x800c8 | -| 44 | 0xc0916 | 0x5d6438 | 0xe6e7e | -| 45 | 0xd0f7e | 0x5a086 | 0xeac65 | -| 46 | 0xd1162 | 0xa3f40 | 0xd111e | -| 47 | 0xda823 | 0x25b862 | 0x803b8 | -| 48 | 0xdaa44 | 0x25a034 | 0xe21da | -| 49 | 0xdaab5 | 0x25a034 | 0xeaa39 | -| 50 | 0xdab25 | 0x25a034 | 0xda0ff | -| 51 | 0xdab95 | 0x25a034 | 0xd5eea | -| 52 | 0xdac07 | 0x25a034 | 0xe46f7 | -| 53 | 0xdac78 | 0x25a034 | 0x800b8 | -| 54 | 0xdad85 | 0x25a034 | 0x80370 | -| 55 | 0xdae19 | 0x25a034 | 0xea541 | -| 56 | 0xdaec9 | 0x25a034 | 0xcfe68 | -| 57 | 0xdafbf | 0x25a034 | 0x80440 | -| 58 | 0xdb02c | 0x25b862 | 0xd5a49 | -| 59 | 0xdb104 | 0x25a034 | 0xe6d17 | -| 60 | 0xdb20b | 0x25a034 | 0xd035b | -| 61 | 0xdb377 | 0x25a034 | 0xea989 | -| 62 | 0xdb423 | 0x25a034 | 0xe6ea3 | -| 63 | 0xdb4fd | 0x25a034 | 0xeabd7 | -| 64 | 0xdb570 | 0x25a034 | 0xec05d | -| 65 | 0xdb5e0 | 0x25a034 | 0xd6019 | -| 66 | 0xdb6c2 | 0x25a034 | 0xe156f | -| 67 | 0xdb736 | 0x25a034 | 0x80180 | -| 68 | 0xdb838 | 0x25a034 | 0xd3174 | -| 69 | 0xdb8ca | 0x25a034 | 0xdc724 | -| 70 | 0xdb965 | 0x25a034 | 0xd9424 | -| 71 | 0xdb9d3 | 0x25a034 | 0xe5395 | -| 72 | 0xdba43 | 0x25a034 | 0xeb231 | -| 73 | 0xdbab5 | 0x25a034 | 0xda65d | -| 74 | 0xdbb23 | 0x25a034 | 0xd4503 | -| 75 | 0xdbc3d | 0x25a034 | 0x80160 | -| 76 | 0xdbdde | 0x25a034 | 0xd6e8b | -| 77 | 0xe9997 | 0x5ef8a2 | 0x80418 | -| 78 | 0xff747 | 0x5d5d22 | 0x80348 | -| 79 | 0x11c3f8 | 0x19f5ed | 0xd555d | -| 80 | 0x13ac36 | 0x4bb20d | 0x80280 | -| 81 | 0x13ac78 | 0x4bbe9d | 0xd671c | -| 82 | 0x13fb9f | 0x61d911 | 0xe0310 | -| 83 | 0x160f7d | 0xae209 | 0xd55ef | -| 84 | 0x160fc8 | 0xae209 | 0x80448 | -| 85 | 0x160fca | 0xae209 | 0xd7871 | -| 86 | 0x161038 | 0xae209 | 0xda1eb | -| 87 | 0x1610ea | 0x5ff4f4 | 0x80360 | -| 88 | 0x177bbe | 0x2245b7 | 0xd98d3 | -| 89 | 0x1849d0 | 0x34a74c | 0xea81d | -| 90 | 0x1856f7 | 0x174fe3 | 0xda035 | -+-------+----------+----------+-------------------+ -Current path: (1, _, _) -Choose an entry [.., 0-90]: 40 - -Child node is block 0xe6ea3. -Reading block 0xe6ea3 ... validating ... OK. -Node has 88 entries, as follows: -+-------+---------+----------+--------------------------------------+ -| | | | Target block | -| Index | OID | XID +---------+-------------+--------------+ -| | | | Address | Actual OID | Actual XID | -+-------+---------+----------+---------+-------------+--------------+ -| 0 | 0xc0726 | 0x4b5fc | 0xd73e8 | 0xc0726 ( ) | 0x4b5fc ( ) | -| 1 | 0xc0727 | 0x3185ab | 0xde42e | 0xc0727 ( ) | 0x3185ab ( ) | -| 2 | 0xc0728 | 0x4b5fc | 0xd73d7 | 0xc0728 ( ) | 0x4b5fc ( ) | -| 3 | 0xc0729 | 0x31a219 | 0xec455 | 0xc0729 ( ) | 0x31a219 ( ) | -| 4 | 0xc072a | 0x4b5fc | 0xd73d9 | 0xc072a ( ) | 0x4b5fc ( ) | -| 5 | 0xc072b | 0x4b5fc | 0xd73d3 | 0xc072b ( ) | 0x4b5fc ( ) | -| 6 | 0xc072c | 0x4b5fc | 0xd73d2 | 0xc072c ( ) | 0x4b5fc ( ) | -| 7 | 0xc072d | 0x4b5fc | 0xd73d4 | 0xc072d ( ) | 0x4b5fc ( ) | -| 8 | 0xc072e | 0x31a219 | 0xe6f1a | 0xc072e ( ) | 0x31a219 ( ) | -| 9 | 0xc072f | 0x4942b | 0xd41eb | 0xc072f ( ) | 0x4942b ( ) | -| 10 | 0xc0730 | 0x4b5fc | 0xd73ce | 0xc0730 ( ) | 0x4b5fc ( ) | -| 11 | 0xc0731 | 0x4b5fc | 0xd73cd | 0xc0731 ( ) | 0x4b5fc ( ) | -| 12 | 0xc0732 | 0x4942b | 0xd41fe | 0xc0732 ( ) | 0x4942b ( ) | -| 13 | 0xc0733 | 0x31a219 | 0xe6f13 | 0xc0733 ( ) | 0x31a219 ( ) | -| 14 | 0xc0734 | 0x4b5fc | 0xd73ca | 0xc0734 ( ) | 0x4b5fc ( ) | -| 15 | 0xc0735 | 0x4b5fc | 0xd73cb | 0xc0735 ( ) | 0x4b5fc ( ) | -| 16 | 0xc0736 | 0x4b5fc | 0xd73c9 | 0xc0736 ( ) | 0x4b5fc ( ) | -| 17 | 0xc0737 | 0x31a219 | 0xe6f10 | 0xc0737 ( ) | 0x31a219 ( ) | -| 18 | 0xc0738 | 0x4b5fc | 0xd73c6 | 0xc0738 ( ) | 0x4b5fc ( ) | -| 19 | 0xc0739 | 0x4b5fc | 0xd73c5 | 0xc0739 ( ) | 0x4b5fc ( ) | -| 20 | 0xc073a | 0x4b1fd | 0xd4d0b | 0xc073a ( ) | 0x4b1fd ( ) | -| 21 | 0xc073b | 0x31a219 | 0xe6f04 | 0xc073b ( ) | 0x31a219 ( ) | -| 22 | 0xc073c | 0x4b5fc | 0xd73c1 | 0xc073c ( ) | 0x4b5fc ( ) | -| 23 | 0xc073d | 0x4b5fc | 0xd73c0 | 0xc073d ( ) | 0x4b5fc ( ) | -| 24 | 0xc073f | 0x31a219 | 0xe6f02 | 0xc073f ( ) | 0x31a219 ( ) | -| 25 | 0xc0741 | 0x4b5fc | 0xd73bd | 0xc0741 ( ) | 0x4b5fc ( ) | -| 26 | 0xc0742 | 0x4b5fc | 0xd73bc | 0xc0742 ( ) | 0x4b5fc ( ) | -| 27 | 0xc0743 | 0x4b5fc | 0xd73be | 0xc0743 ( ) | 0x4b5fc ( ) | -| 28 | 0xc0744 | 0x31a219 | 0xe6f00 | 0xc0744 ( ) | 0x31a219 ( ) | -| 29 | 0xc0745 | 0x4b5fc | 0xd73ba | 0xc0745 ( ) | 0x4b5fc ( ) | -| 30 | 0xc0746 | 0x4b5fc | 0xd73b9 | 0xc0746 ( ) | 0x4b5fc ( ) | -| 31 | 0xc0747 | 0x4942b | 0xd4296 | 0xc0747 ( ) | 0x4942b ( ) | -| 32 | 0xc0748 | 0x31a219 | 0xe6ef9 | 0xc0748 ( ) | 0x31a219 ( ) | -| 33 | 0xc0749 | 0x4b5fc | 0xd73b7 | 0xc0749 ( ) | 0x4b5fc ( ) | -| 34 | 0xc074a | 0xa4f9d | 0xdb89b | 0xc074a ( ) | 0xa4f9d ( ) | -| 35 | 0xc074b | 0x4942b | 0xd429c | 0xc074b ( ) | 0x4942b ( ) | -| 36 | 0xc074c | 0x31a219 | 0xec59c | 0xc074c ( ) | 0x31a219 ( ) | -| 37 | 0xc074d | 0x4b5fc | 0xd73b4 | 0xc074d ( ) | 0x4b5fc ( ) | -| 38 | 0xc074e | 0x4b5fc | 0xd73b3 | 0xc074e ( ) | 0x4b5fc ( ) | -| 39 | 0xc074f | 0x4942b | 0xd42b4 | 0xc074f ( ) | 0x4942b ( ) | -| 40 | 0xc0750 | 0x8f0aa | 0xd9c21 | 0xc0750 ( ) | 0x8f0aa ( ) | -| 41 | 0xc0751 | 0x31a219 | 0xe6ef3 | 0xc0751 ( ) | 0x31a219 ( ) | -| 42 | 0xc0752 | 0x4b5fc | 0xd73b1 | 0xc0752 ( ) | 0x4b5fc ( ) | -| 43 | 0xc0753 | 0x7f795 | 0xd8648 | 0xc0753 ( ) | 0x7f795 ( ) | -| 44 | 0xc0754 | 0x4942b | 0xd42bf | 0xc0754 ( ) | 0x4942b ( ) | -| 45 | 0xc0755 | 0x31a219 | 0xec598 | 0xc0755 ( ) | 0x31a219 ( ) | -| 46 | 0xc0758 | 0x4b5fc | 0xd73ad | 0xc0758 ( ) | 0x4b5fc ( ) | -| 47 | 0xc0759 | 0x4b5fc | 0xd73ac | 0xc0759 ( ) | 0x4b5fc ( ) | -| 48 | 0xc075a | 0x4942c | 0xd438a | 0xc075a ( ) | 0x4942c ( ) | -| 49 | 0xc075c | 0x31a219 | 0xe6eda | 0xc075c ( ) | 0x31a219 ( ) | -| 50 | 0xc075d | 0x178060 | 0xe6955 | 0xc075d ( ) | 0x178060 ( ) | -| 51 | 0xc075e | 0x7f795 | 0xd8699 | 0xc075e ( ) | 0x7f795 ( ) | -| 52 | 0xc075f | 0x4942d | 0xd4512 | 0xc075f ( ) | 0x4942d ( ) | -| 53 | 0xc0760 | 0x31a219 | 0xe6ed4 | 0xc0760 ( ) | 0x31a219 ( ) | -| 54 | 0xc0761 | 0x4b5fc | 0xd73a5 | 0xc0761 ( ) | 0x4b5fc ( ) | -| 55 | 0xc0762 | 0x7f795 | 0xd86d2 | 0xc0762 ( ) | 0x7f795 ( ) | -| 56 | 0xc0763 | 0x4942d | 0xd455c | 0xc0763 ( ) | 0x4942d ( ) | -| 57 | 0xc0764 | 0x31a219 | 0xe6ec0 | 0xc0764 ( ) | 0x31a219 ( ) | -| 58 | 0xc0765 | 0x4b5fc | 0xd7389 | 0xc0765 ( ) | 0x4b5fc ( ) | -| 59 | 0xc0766 | 0x84819 | 0xdbee0 | 0xc0766 ( ) | 0x84819 ( ) | -| 60 | 0xc0768 | 0x31a219 | 0xe6eb9 | 0xc0768 ( ) | 0x31a219 ( ) | -| 61 | 0xc0769 | 0x4b5fc | 0xd7385 | 0xc0769 ( ) | 0x4b5fc ( ) | -| 62 | 0xc076a | 0x4b5fc | 0xd7386 | 0xc076a ( ) | 0x4b5fc ( ) | -| 63 | 0xc076b | 0x4b5fc | 0xd7384 | 0xc076b ( ) | 0x4b5fc ( ) | -| 64 | 0xc076c | 0x31a219 | 0xe6eab | 0xc076c ( ) | 0x31a219 ( ) | -| 65 | 0xc076d | 0xf1ab8 | 0xd319b | 0xc076d ( ) | 0xf1ab8 ( ) | -| 66 | 0xc076e | 0x4b5fc | 0xd7378 | 0xc076e ( ) | 0x4b5fc ( ) | -| 67 | 0xc076f | 0x4b5fc | 0xd7370 | 0xc076f ( ) | 0x4b5fc ( ) | -| 68 | 0xc0770 | 0x4b5fc | 0xd7379 | 0xc0770 ( ) | 0x4b5fc ( ) | -| 69 | 0xc0771 | 0x31a219 | 0xe6ea9 | 0xc0771 ( ) | 0x31a219 ( ) | -| 70 | 0xc0772 | 0x4b5fc | 0xd736e | 0xc0772 ( ) | 0x4b5fc ( ) | -| 71 | 0xc0773 | 0x4b5fc | 0xd736d | 0xc0773 ( ) | 0x4b5fc ( ) | -| 72 | 0xc0774 | 0x4942e | 0xd4662 | 0xc0774 ( ) | 0x4942e ( ) | -| 73 | 0xc0775 | 0x2fb2c5 | 0xec704 | 0xc0775 ( ) | 0x2fb2c5 ( ) | -| 74 | 0xc0776 | 0x31a219 | 0xe6ea6 | 0xc0776 ( ) | 0x31a219 ( ) | -| 75 | 0xc0777 | 0x4b5fc | 0xd736b | 0xc0777 ( ) | 0x4b5fc ( ) | -| 76 | 0xc0778 | 0x7f795 | 0xd875f | 0xc0778 ( ) | 0x7f795 ( ) | -| 77 | 0xc0779 | 0x4942e | 0xd466e | 0xc0779 ( ) | 0x4942e ( ) | -| 78 | 0xc077a | 0x31a219 | 0xe6ea5 | 0xc077a ( ) | 0x31a219 ( ) | -| 79 | 0xc077b | 0x4b5fc | 0xd7367 | 0xc077b ( ) | 0x4b5fc ( ) | -| 80 | 0xc077c | 0x84819 | 0xdb73f | 0xc077c ( ) | 0x84819 ( ) | -| 81 | 0xc077d | 0x4942e | 0xd467a | 0xc077d ( ) | 0x4942e ( ) | -| 82 | 0xc077e | 0x31a219 | 0xe6ea4 | 0xc077e ( ) | 0x31a219 ( ) | -| 83 | 0xc077f | 0x4b5fc | 0xd735c | 0xc077f ( ) | 0x4b5fc ( ) | -| 84 | 0xc0780 | 0x31a219 | 0xe6e8e | 0xc0780 ( ) | 0x31a219 ( ) | -| 85 | 0xc0781 | 0x4942e | 0xd4681 | 0xc0781 ( ) | 0x4942e ( ) | -| 86 | 0xc0782 | 0x4b5fc | 0xd7356 | 0xc0782 ( ) | 0x4b5fc ( ) | -| 87 | 0xc0783 | 0x4b5fc | 0xd7352 | 0xc0783 ( ) | 0x4b5fc ( ) | -+-------+---------+----------+---------+-------------+--------------+ -Current path: (1, 40, _) -Choose an entry [.., 0-87]: 31 - -Record data for entry 31: - KEY: - - OID: 0xc0747 - - XID: 0x4942b - VALUE: - - Object size: 4096 bytes - - Object address: 0xd4296 -Current path: (1, 40, 31) -Choose an entry [..]: -``` +(command_explore-omap-tree)= + +# {drat-command}`explore-omap-tree` + +## Description + +The {drat-command}`explore-omap-tree` command provides an interface via which you +can explore an object map B-tree (or subtree). You specify the B-tree either: + +- directly, by specifying the block address of a B-tree (root) node via + {argument}`omap`; or + +- indirectly, by specifying sufficient info for the block address of a B-tree + root node to be determined. That is, specify either: + + - the block address of an object map (a data structure of type + {datatype}`omap_phys_t`), volume superblock, or container superblock via + {argument}`omap`; or + + - a volume or the container itself via {argument}`volume` or + {argument}`volume-name`, optionally along with a maximum transaction ID via + {argument}`max-xid` (the most recent object map with a transaction ID that + doesn't exceed the one specified will be explored). + +If no such arguments are specified, the default is `--volume 0 --max-xid -1`. +That is, Drat will explore the most recent container object map B-tree. + +When the command is run, Drat will display the entries within the specified +B-tree (root) node, each of which is a key given by an (OID, XID) pair. You +will then be prompted to choose an entry by specifying its index and pressing +ENTER. Drat will then read the corresponding child node and display its +entries. You can repeat this process of entry selection until you select an +entry in a leaf node, after which Drat will display info about the +corresponding record in the tree. Pressing ENTER at this point will exit Drat. +At any point, you can specify `..` instead of a valid entry index to go back to +the parent node. + +When viewing a non-leaf node, an extra column, *Target child node*, is shown +for convenience, which displays the address of the child node that the entry +points to. + +When viewing a leaf node, an extra set of columns, *Target block*, is shown, +which displays the *Address* of the block that each (OID, XID) pair maps to, as +well as the *Actual OID* and *Actual XID* that appear in the data contained in +that block. Following each *Actual OID* and *Actual XID* is a checkbox `( )`, +which will be checked `(X)` if that value does not match the value in the *OID* +or *XID* column, respectively, indicating that the mapping is bad. A well-formed +object map will not result in any checkboxes being checked. + +## Example usage + +``` +$ drat --container /dev/disk2s2 explore-omap-tree + +$ drat --container partition-dump.bin explore-omap-tree --volume 3 + +$ drat --container /dev/disk2s2 explore-omap-tree --volume-name Preboot --max-xid 0x56f2e00 + +$ drat --container partition-dump.bin explore-omap-tree --omap 0xd1395 +``` + +## Example output + +``` +$ drat --container partition-dump.bin explore-omap-tree --omap 0xd1395 + +Opening file at `partition-dump.bin` in read-only mode ... OK. + +Reading block 0xd1395 ... validating ... OK. +This is a B-tree root node. +Height of tree is 3. +Node has 11 entries, as follows: ++-------+----------+----------+-------------------+ +| Index | OID | XID | Target child node | ++-------+----------+----------+-------------------+ +| 0 | 0x404 | 0x620846 | 0x100000000012 | +| 1 | 0x1a62b | 0x1ac2 | 0xd8384 | +| 2 | 0x1a42d1 | 0x5fe4fc | 0xd3dff | +| 3 | 0x3a3770 | 0x615572 | 0xd6d35 | +| 4 | 0x421d21 | 0x305a6f | 0xd7c77 | +| 5 | 0x52f2b1 | 0x4034b2 | 0x8ffff | +| 6 | 0x56cae8 | 0x49bb2b | 0x80028 | +| 7 | 0x5ea464 | 0x5ef837 | 0xd6445 | +| 8 | 0x636ea0 | 0x5e61d0 | 0xd30cc | +| 9 | 0x66b83b | 0x62031d | 0xd3dec | +| 10 | 0x67fea8 | 0x5fe60c | 0xd6ac4 | ++-------+----------+----------+-------------------+ +Current path: (_, _, _) +Choose an entry [0-10]: 1 + +Child node is block 0xd663a. +Reading block 0xd663a ... validating ... OK. +Node has 91 entries, as follows: ++-------+----------+----------+-------------------+ +| Index | OID | XID | Target child node | ++-------+----------+----------+-------------------+ +| 0 | 0x1a62b | 0x1ac2 | 0xd6e95 | +| 1 | 0x1a921 | 0x1a97 | 0x80188 | +| 2 | 0x1abbf | 0x1ad0 | 0x80470 | +| 3 | 0x1adae | 0x1ad0 | 0xd6e47 | +| 4 | 0x1ae09 | 0x1a97 | 0xd6e4a | +| 5 | 0x1ae78 | 0x1a97 | 0xd6e53 | +| 6 | 0x1af64 | 0x5d5d2b | 0x80368 | +| 7 | 0x1b1dc | 0x1d3c | 0xd6e91 | +| 8 | 0x1dd86 | 0x1ad0 | 0xe2803 | +| 9 | 0x1ee21 | 0x1acb | 0xeb460 | +| 10 | 0x22bc3 | 0x1d38 | 0xe4433 | +| 11 | 0x22c80 | 0x222913 | 0x80420 | +| 12 | 0x28c5c | 0x3b52cd | 0x80230 | +| 13 | 0x2c70f | 0x6a29 | 0x803e0 | +| 14 | 0x2c930 | 0x5881b1 | 0xd6a38 | +| 15 | 0x2cc08 | 0x620345 | 0xd5df1 | +| 16 | 0x31de5 | 0x4b5fc | 0xdb797 | +| 17 | 0x31e20 | 0x4b5fc | 0xcfe3e | +| 18 | 0x31e90 | 0x4b5fc | 0xd5533 | +| 19 | 0x31efe | 0x4b5fc | 0x800f0 | +| 20 | 0x32d0b | 0x615572 | 0xd6029 | +| 21 | 0x32e1b | 0xaac2 | 0xe0a07 | +| 22 | 0x32e60 | 0x5d5d2a | 0xd672a | +| 23 | 0x32efd | 0x3c0651 | 0xe74f4 | +| 24 | 0x4c481 | 0x5fa6c4 | 0x80460 | +| 25 | 0x799da | 0x61d90c | 0x80450 | +| 26 | 0x92a7f | 0x3c0550 | 0x803f0 | +| 27 | 0xb137a | 0x34a74c | 0x800e8 | +| 28 | 0xb7074 | 0x61da8a | 0xcfd16 | +| 29 | 0xbb0e4 | 0x4c663 | 0xd55d1 | +| 30 | 0xbb413 | 0x4ce5e | 0xe029f | +| 31 | 0xbb4d3 | 0x4ce45 | 0xd5adb | +| 32 | 0xbbcc3 | 0x4c947 | 0xda3c0 | +| 33 | 0xbbffa | 0x4ce72 | 0x80238 | +| 34 | 0xbc03b | 0x4ce45 | 0xe6aee | +| 35 | 0xbda1c | 0x3c066c | 0xd0084 | +| 36 | 0xc060b | 0x4b5fc | 0x80468 | +| 37 | 0xc0648 | 0x4b5fc | 0xe71a1 | +| 38 | 0xc06a6 | 0x4b5fc | 0x80170 | +| 39 | 0xc06e7 | 0x31a219 | 0xe2b32 | +| 40 | 0xc0726 | 0x4b5fc | 0xeb5db | +| 41 | 0xc0784 | 0x31a219 | 0xe3b9a | +| 42 | 0xc07c3 | 0x4b5fc | 0x80030 | +| 43 | 0xc0808 | 0x31a219 | 0x800c8 | +| 44 | 0xc0916 | 0x5d6438 | 0xe6e7e | +| 45 | 0xd0f7e | 0x5a086 | 0xeac65 | +| 46 | 0xd1162 | 0xa3f40 | 0xd111e | +| 47 | 0xda823 | 0x25b862 | 0x803b8 | +| 48 | 0xdaa44 | 0x25a034 | 0xe21da | +| 49 | 0xdaab5 | 0x25a034 | 0xeaa39 | +| 50 | 0xdab25 | 0x25a034 | 0xda0ff | +| 51 | 0xdab95 | 0x25a034 | 0xd5eea | +| 52 | 0xdac07 | 0x25a034 | 0xe46f7 | +| 53 | 0xdac78 | 0x25a034 | 0x800b8 | +| 54 | 0xdad85 | 0x25a034 | 0x80370 | +| 55 | 0xdae19 | 0x25a034 | 0xea541 | +| 56 | 0xdaec9 | 0x25a034 | 0xcfe68 | +| 57 | 0xdafbf | 0x25a034 | 0x80440 | +| 58 | 0xdb02c | 0x25b862 | 0xd5a49 | +| 59 | 0xdb104 | 0x25a034 | 0xe6d17 | +| 60 | 0xdb20b | 0x25a034 | 0xd035b | +| 61 | 0xdb377 | 0x25a034 | 0xea989 | +| 62 | 0xdb423 | 0x25a034 | 0xe6ea3 | +| 63 | 0xdb4fd | 0x25a034 | 0xeabd7 | +| 64 | 0xdb570 | 0x25a034 | 0xec05d | +| 65 | 0xdb5e0 | 0x25a034 | 0xd6019 | +| 66 | 0xdb6c2 | 0x25a034 | 0xe156f | +| 67 | 0xdb736 | 0x25a034 | 0x80180 | +| 68 | 0xdb838 | 0x25a034 | 0xd3174 | +| 69 | 0xdb8ca | 0x25a034 | 0xdc724 | +| 70 | 0xdb965 | 0x25a034 | 0xd9424 | +| 71 | 0xdb9d3 | 0x25a034 | 0xe5395 | +| 72 | 0xdba43 | 0x25a034 | 0xeb231 | +| 73 | 0xdbab5 | 0x25a034 | 0xda65d | +| 74 | 0xdbb23 | 0x25a034 | 0xd4503 | +| 75 | 0xdbc3d | 0x25a034 | 0x80160 | +| 76 | 0xdbdde | 0x25a034 | 0xd6e8b | +| 77 | 0xe9997 | 0x5ef8a2 | 0x80418 | +| 78 | 0xff747 | 0x5d5d22 | 0x80348 | +| 79 | 0x11c3f8 | 0x19f5ed | 0xd555d | +| 80 | 0x13ac36 | 0x4bb20d | 0x80280 | +| 81 | 0x13ac78 | 0x4bbe9d | 0xd671c | +| 82 | 0x13fb9f | 0x61d911 | 0xe0310 | +| 83 | 0x160f7d | 0xae209 | 0xd55ef | +| 84 | 0x160fc8 | 0xae209 | 0x80448 | +| 85 | 0x160fca | 0xae209 | 0xd7871 | +| 86 | 0x161038 | 0xae209 | 0xda1eb | +| 87 | 0x1610ea | 0x5ff4f4 | 0x80360 | +| 88 | 0x177bbe | 0x2245b7 | 0xd98d3 | +| 89 | 0x1849d0 | 0x34a74c | 0xea81d | +| 90 | 0x1856f7 | 0x174fe3 | 0xda035 | ++-------+----------+----------+-------------------+ +Current path: (1, _, _) +Choose an entry [.., 0-90]: 40 + +Child node is block 0xe6ea3. +Reading block 0xe6ea3 ... validating ... OK. +Node has 88 entries, as follows: ++-------+---------+----------+--------------------------------------+ +| | | | Target block | +| Index | OID | XID +---------+-------------+--------------+ +| | | | Address | Actual OID | Actual XID | ++-------+---------+----------+---------+-------------+--------------+ +| 0 | 0xc0726 | 0x4b5fc | 0xd73e8 | 0xc0726 ( ) | 0x4b5fc ( ) | +| 1 | 0xc0727 | 0x3185ab | 0xde42e | 0xc0727 ( ) | 0x3185ab ( ) | +| 2 | 0xc0728 | 0x4b5fc | 0xd73d7 | 0xc0728 ( ) | 0x4b5fc ( ) | +| 3 | 0xc0729 | 0x31a219 | 0xec455 | 0xc0729 ( ) | 0x31a219 ( ) | +| 4 | 0xc072a | 0x4b5fc | 0xd73d9 | 0xc072a ( ) | 0x4b5fc ( ) | +| 5 | 0xc072b | 0x4b5fc | 0xd73d3 | 0xc072b ( ) | 0x4b5fc ( ) | +| 6 | 0xc072c | 0x4b5fc | 0xd73d2 | 0xc072c ( ) | 0x4b5fc ( ) | +| 7 | 0xc072d | 0x4b5fc | 0xd73d4 | 0xc072d ( ) | 0x4b5fc ( ) | +| 8 | 0xc072e | 0x31a219 | 0xe6f1a | 0xc072e ( ) | 0x31a219 ( ) | +| 9 | 0xc072f | 0x4942b | 0xd41eb | 0xc072f ( ) | 0x4942b ( ) | +| 10 | 0xc0730 | 0x4b5fc | 0xd73ce | 0xc0730 ( ) | 0x4b5fc ( ) | +| 11 | 0xc0731 | 0x4b5fc | 0xd73cd | 0xc0731 ( ) | 0x4b5fc ( ) | +| 12 | 0xc0732 | 0x4942b | 0xd41fe | 0xc0732 ( ) | 0x4942b ( ) | +| 13 | 0xc0733 | 0x31a219 | 0xe6f13 | 0xc0733 ( ) | 0x31a219 ( ) | +| 14 | 0xc0734 | 0x4b5fc | 0xd73ca | 0xc0734 ( ) | 0x4b5fc ( ) | +| 15 | 0xc0735 | 0x4b5fc | 0xd73cb | 0xc0735 ( ) | 0x4b5fc ( ) | +| 16 | 0xc0736 | 0x4b5fc | 0xd73c9 | 0xc0736 ( ) | 0x4b5fc ( ) | +| 17 | 0xc0737 | 0x31a219 | 0xe6f10 | 0xc0737 ( ) | 0x31a219 ( ) | +| 18 | 0xc0738 | 0x4b5fc | 0xd73c6 | 0xc0738 ( ) | 0x4b5fc ( ) | +| 19 | 0xc0739 | 0x4b5fc | 0xd73c5 | 0xc0739 ( ) | 0x4b5fc ( ) | +| 20 | 0xc073a | 0x4b1fd | 0xd4d0b | 0xc073a ( ) | 0x4b1fd ( ) | +| 21 | 0xc073b | 0x31a219 | 0xe6f04 | 0xc073b ( ) | 0x31a219 ( ) | +| 22 | 0xc073c | 0x4b5fc | 0xd73c1 | 0xc073c ( ) | 0x4b5fc ( ) | +| 23 | 0xc073d | 0x4b5fc | 0xd73c0 | 0xc073d ( ) | 0x4b5fc ( ) | +| 24 | 0xc073f | 0x31a219 | 0xe6f02 | 0xc073f ( ) | 0x31a219 ( ) | +| 25 | 0xc0741 | 0x4b5fc | 0xd73bd | 0xc0741 ( ) | 0x4b5fc ( ) | +| 26 | 0xc0742 | 0x4b5fc | 0xd73bc | 0xc0742 ( ) | 0x4b5fc ( ) | +| 27 | 0xc0743 | 0x4b5fc | 0xd73be | 0xc0743 ( ) | 0x4b5fc ( ) | +| 28 | 0xc0744 | 0x31a219 | 0xe6f00 | 0xc0744 ( ) | 0x31a219 ( ) | +| 29 | 0xc0745 | 0x4b5fc | 0xd73ba | 0xc0745 ( ) | 0x4b5fc ( ) | +| 30 | 0xc0746 | 0x4b5fc | 0xd73b9 | 0xc0746 ( ) | 0x4b5fc ( ) | +| 31 | 0xc0747 | 0x4942b | 0xd4296 | 0xc0747 ( ) | 0x4942b ( ) | +| 32 | 0xc0748 | 0x31a219 | 0xe6ef9 | 0xc0748 ( ) | 0x31a219 ( ) | +| 33 | 0xc0749 | 0x4b5fc | 0xd73b7 | 0xc0749 ( ) | 0x4b5fc ( ) | +| 34 | 0xc074a | 0xa4f9d | 0xdb89b | 0xc074a ( ) | 0xa4f9d ( ) | +| 35 | 0xc074b | 0x4942b | 0xd429c | 0xc074b ( ) | 0x4942b ( ) | +| 36 | 0xc074c | 0x31a219 | 0xec59c | 0xc074c ( ) | 0x31a219 ( ) | +| 37 | 0xc074d | 0x4b5fc | 0xd73b4 | 0xc074d ( ) | 0x4b5fc ( ) | +| 38 | 0xc074e | 0x4b5fc | 0xd73b3 | 0xc074e ( ) | 0x4b5fc ( ) | +| 39 | 0xc074f | 0x4942b | 0xd42b4 | 0xc074f ( ) | 0x4942b ( ) | +| 40 | 0xc0750 | 0x8f0aa | 0xd9c21 | 0xc0750 ( ) | 0x8f0aa ( ) | +| 41 | 0xc0751 | 0x31a219 | 0xe6ef3 | 0xc0751 ( ) | 0x31a219 ( ) | +| 42 | 0xc0752 | 0x4b5fc | 0xd73b1 | 0xc0752 ( ) | 0x4b5fc ( ) | +| 43 | 0xc0753 | 0x7f795 | 0xd8648 | 0xc0753 ( ) | 0x7f795 ( ) | +| 44 | 0xc0754 | 0x4942b | 0xd42bf | 0xc0754 ( ) | 0x4942b ( ) | +| 45 | 0xc0755 | 0x31a219 | 0xec598 | 0xc0755 ( ) | 0x31a219 ( ) | +| 46 | 0xc0758 | 0x4b5fc | 0xd73ad | 0xc0758 ( ) | 0x4b5fc ( ) | +| 47 | 0xc0759 | 0x4b5fc | 0xd73ac | 0xc0759 ( ) | 0x4b5fc ( ) | +| 48 | 0xc075a | 0x4942c | 0xd438a | 0xc075a ( ) | 0x4942c ( ) | +| 49 | 0xc075c | 0x31a219 | 0xe6eda | 0xc075c ( ) | 0x31a219 ( ) | +| 50 | 0xc075d | 0x178060 | 0xe6955 | 0xc075d ( ) | 0x178060 ( ) | +| 51 | 0xc075e | 0x7f795 | 0xd8699 | 0xc075e ( ) | 0x7f795 ( ) | +| 52 | 0xc075f | 0x4942d | 0xd4512 | 0xc075f ( ) | 0x4942d ( ) | +| 53 | 0xc0760 | 0x31a219 | 0xe6ed4 | 0xc0760 ( ) | 0x31a219 ( ) | +| 54 | 0xc0761 | 0x4b5fc | 0xd73a5 | 0xc0761 ( ) | 0x4b5fc ( ) | +| 55 | 0xc0762 | 0x7f795 | 0xd86d2 | 0xc0762 ( ) | 0x7f795 ( ) | +| 56 | 0xc0763 | 0x4942d | 0xd455c | 0xc0763 ( ) | 0x4942d ( ) | +| 57 | 0xc0764 | 0x31a219 | 0xe6ec0 | 0xc0764 ( ) | 0x31a219 ( ) | +| 58 | 0xc0765 | 0x4b5fc | 0xd7389 | 0xc0765 ( ) | 0x4b5fc ( ) | +| 59 | 0xc0766 | 0x84819 | 0xdbee0 | 0xc0766 ( ) | 0x84819 ( ) | +| 60 | 0xc0768 | 0x31a219 | 0xe6eb9 | 0xc0768 ( ) | 0x31a219 ( ) | +| 61 | 0xc0769 | 0x4b5fc | 0xd7385 | 0xc0769 ( ) | 0x4b5fc ( ) | +| 62 | 0xc076a | 0x4b5fc | 0xd7386 | 0xc076a ( ) | 0x4b5fc ( ) | +| 63 | 0xc076b | 0x4b5fc | 0xd7384 | 0xc076b ( ) | 0x4b5fc ( ) | +| 64 | 0xc076c | 0x31a219 | 0xe6eab | 0xc076c ( ) | 0x31a219 ( ) | +| 65 | 0xc076d | 0xf1ab8 | 0xd319b | 0xc076d ( ) | 0xf1ab8 ( ) | +| 66 | 0xc076e | 0x4b5fc | 0xd7378 | 0xc076e ( ) | 0x4b5fc ( ) | +| 67 | 0xc076f | 0x4b5fc | 0xd7370 | 0xc076f ( ) | 0x4b5fc ( ) | +| 68 | 0xc0770 | 0x4b5fc | 0xd7379 | 0xc0770 ( ) | 0x4b5fc ( ) | +| 69 | 0xc0771 | 0x31a219 | 0xe6ea9 | 0xc0771 ( ) | 0x31a219 ( ) | +| 70 | 0xc0772 | 0x4b5fc | 0xd736e | 0xc0772 ( ) | 0x4b5fc ( ) | +| 71 | 0xc0773 | 0x4b5fc | 0xd736d | 0xc0773 ( ) | 0x4b5fc ( ) | +| 72 | 0xc0774 | 0x4942e | 0xd4662 | 0xc0774 ( ) | 0x4942e ( ) | +| 73 | 0xc0775 | 0x2fb2c5 | 0xec704 | 0xc0775 ( ) | 0x2fb2c5 ( ) | +| 74 | 0xc0776 | 0x31a219 | 0xe6ea6 | 0xc0776 ( ) | 0x31a219 ( ) | +| 75 | 0xc0777 | 0x4b5fc | 0xd736b | 0xc0777 ( ) | 0x4b5fc ( ) | +| 76 | 0xc0778 | 0x7f795 | 0xd875f | 0xc0778 ( ) | 0x7f795 ( ) | +| 77 | 0xc0779 | 0x4942e | 0xd466e | 0xc0779 ( ) | 0x4942e ( ) | +| 78 | 0xc077a | 0x31a219 | 0xe6ea5 | 0xc077a ( ) | 0x31a219 ( ) | +| 79 | 0xc077b | 0x4b5fc | 0xd7367 | 0xc077b ( ) | 0x4b5fc ( ) | +| 80 | 0xc077c | 0x84819 | 0xdb73f | 0xc077c ( ) | 0x84819 ( ) | +| 81 | 0xc077d | 0x4942e | 0xd467a | 0xc077d ( ) | 0x4942e ( ) | +| 82 | 0xc077e | 0x31a219 | 0xe6ea4 | 0xc077e ( ) | 0x31a219 ( ) | +| 83 | 0xc077f | 0x4b5fc | 0xd735c | 0xc077f ( ) | 0x4b5fc ( ) | +| 84 | 0xc0780 | 0x31a219 | 0xe6e8e | 0xc0780 ( ) | 0x31a219 ( ) | +| 85 | 0xc0781 | 0x4942e | 0xd4681 | 0xc0781 ( ) | 0x4942e ( ) | +| 86 | 0xc0782 | 0x4b5fc | 0xd7356 | 0xc0782 ( ) | 0x4b5fc ( ) | +| 87 | 0xc0783 | 0x4b5fc | 0xd7352 | 0xc0783 ( ) | 0x4b5fc ( ) | ++-------+---------+----------+---------+-------------+--------------+ +Current path: (1, 40, _) +Choose an entry [.., 0-87]: 31 + +Record data for entry 31: + KEY: + - OID: 0xc0747 + - XID: 0x4942b + VALUE: + - Object size: 4096 bytes + - Object address: 0xd4296 +Current path: (1, 40, 31) +Choose an entry [..]: +``` diff --git a/docs/commands/index.md b/docs/commands/index.md index 5ad4787..06a3809 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -1,33 +1,33 @@ -# Commands - -To use Drat, you must specify what you want to do by using one of the following -commands. Click the name of the command to go to the corresponding section, -which provides a detailed description of the command and its functionality: - -| Command | Summary | -| :-- | :-- | -| {ref}`command_create-index` | Create an index of the filesystem to aid searching | -| {ref}`command_explore-fs` | Explore a filesystem, starting from a particular path or FSOID | -| {ref}`command_explore-fs-tree` | Explore a filesystem B-tree (or subtree) | -| {ref}`command_explore-omap-tree` | Explore an object map B-tree (or subtree) | -| {ref}`command_inspect` | Inspect an APFS container | -| {ref}`command_read` | Read a block a display information about it | -| {ref}`command_recover` | Recover/undelete a file | -| {ref}`command_resolve-virtual-oids` | Resolve a set of Virtual OIDs to their corresponding physical block addresses | -| {ref}`command_search` | Search an APFS container for blocks with certain features/properties | -| {ref}`command_version` | Display Drat's version number along with legal info | - -```{toctree} -:hidden: - -create-index -explore-fs -explore-fs-tree -explore-omap-tree -inspect -read -recover -resolve-virtual-oids -search -version -``` +# Commands + +To use Drat, you must specify what you want to do by using one of the following +commands. Click the name of the command to go to the corresponding section, +which provides a detailed description of the command and its functionality: + +| Command | Summary | +| :-- | :-- | +| {ref}`command_create-index` | Create an index of the filesystem to aid searching | +| {ref}`command_explore-fs` | Explore a filesystem, starting from a particular path or FSOID | +| {ref}`command_explore-fs-tree` | Explore a filesystem B-tree (or subtree) | +| {ref}`command_explore-omap-tree` | Explore an object map B-tree (or subtree) | +| {ref}`command_inspect` | Inspect an APFS container | +| {ref}`command_read` | Read a block a display information about it | +| {ref}`command_recover` | Recover/undelete a file | +| {ref}`command_resolve-virtual-oids` | Resolve a set of Virtual OIDs to their corresponding physical block addresses | +| {ref}`command_search` | Search an APFS container for blocks with certain features/properties | +| {ref}`command_version` | Display Drat's version number along with legal info | + +```{toctree} +:hidden: + +create-index +explore-fs +explore-fs-tree +explore-omap-tree +inspect +read +recover +resolve-virtual-oids +search +version +``` diff --git a/docs/commands/inspect.md b/docs/commands/inspect.md index 29dd200..72192ff 100644 --- a/docs/commands/inspect.md +++ b/docs/commands/inspect.md @@ -1,576 +1,576 @@ -(command_inspect)= - -# {drat-command}`inspect` - -## Description - -The {drat-command}`inspect` command inspects all aspects of an APFS container, -primarily by simulating the process of mounting it. It displays info about the -inspection process, reporting info, warnings, and errors about the container's -state. - -## Example usage and output - -``` -$ drat --container /dev/disk2s2 inspect - -Opening file at `/dev/disk2s2` in read-only mode ... OK. -Simulating a mount of the APFS container. -Validating checksum of block 0x0 ... OK. - -Details of block 0x0: --------------------------------------------------------------------------------- -Stored checksum: 0xe25ef5e409032d45 -OID: 0x1 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Container superblock -Subtype: (none/invalid) -Magic string: NXSB -Block size: 4096 bytes -Block count: 117161676 -Supported features: -- No feature flags are set. -Supported read-only compatible features: -- No read-only compatible feature flags are set. -Backward-incompatible features: -- No backward-incompatible feature flags are set. -UUID: 0x07531d831bda8f8c9944ee600d55fd37 -Next OID: 0x28709a -Next XID: 0x1bca25 -Space manager Ephemeral OID: 0x87d46 -Object map Physical OID: 0xcfc19 -Reaper Ephemeral OID: 0x401 -Other flags: -- No other flags are set. --------------------------------------------------------------------------------- - -Locating the checkpoint descriptor area: -- Its length is 280 blocks. -- It is contiguous. -- The address of its first block is 0x3629fe4. -Loading the checkpoint descriptor area into memory ... OK. -Locating the most recent well-formed container superblock in the checkpoint descriptor area: -- It lies at index 271 within the checkpoint descriptor area. - -Details of this container superblock: --------------------------------------------------------------------------------- -Stored checksum: 0xe25eecc609033666 -OID: 0x1 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Container superblock -Subtype: (none/invalid) -Magic string: NXSB -Block size: 4096 bytes -Block count: 117161676 -Supported features: -- No feature flags are set. -Supported read-only compatible features: -- No read-only compatible feature flags are set. -Backward-incompatible features: -- No backward-incompatible feature flags are set. -UUID: 0x07531d831bda8f8c9944ee600d55fd37 -Next OID: 0x28709a -Next XID: 0x1bca25 -Space manager Ephemeral OID: 0x87d46 -Object map Physical OID: 0xcfc19 -Reaper Ephemeral OID: 0x401 -Other flags: -- No other flags are set. --------------------------------------------------------------------------------- -- The corresponding checkpoint starts at index 270 within the checkpoint descriptor area, and spans 2 blocks. - -Loading the corresponding checkpoint ... OK. - -Details of each block in this checkpoint: --------------------------------------------------------------------------------- -Stored checksum: 0xe63bc1554547b687 -OID: 0x362a0f2 -XID: 0x1bca24 -Storage type: Physical -Type flags: (none) -Type: Checkpoint map -Subtype: (none/invalid) -Flags: -- Last checkpoint-mapping block in the corresponding checkpoint. -Number of mappings: 5 --------------------------------------------------------------------------------- -Stored checksum: 0xe25eecc609033666 -OID: 0x1 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Container superblock -Subtype: (none/invalid) -Magic string: NXSB -Block size: 4096 bytes -Block count: 117161676 -Supported features: -- No feature flags are set. -Supported read-only compatible features: -- No read-only compatible feature flags are set. -Backward-incompatible features: -- No backward-incompatible feature flags are set. -UUID: 0x07531d831bda8f8c9944ee600d55fd37 -Next OID: 0x28709a -Next XID: 0x1bca25 -Space manager Ephemeral OID: 0x87d46 -Object map Physical OID: 0xcfc19 -Reaper Ephemeral OID: 0x401 -Other flags: -- No other flags are set. --------------------------------------------------------------------------------- - -Details of each checkpoint-mapping in this checkpoint: --------------------------------------------------------------------------------- -Ephemeral OID: 0x87d46 -Logical block address on disk: 0x3624379 -Object type: Space manager -Object subtype: (none/invalid) -Object size: 4096 bytes -Associated volume OID (virtual): 0x0 --------------------------------------------------------------------------------- -Ephemeral OID: 0x87d47 -Logical block address on disk: 0x362437a -Object type: B-tree (root node) -Object subtype: Space manager free-space queue -Object size: 4096 bytes -Associated volume OID (virtual): 0x0 --------------------------------------------------------------------------------- -Ephemeral OID: 0x405 -Logical block address on disk: 0x362437b -Object type: B-tree (root node) -Object subtype: Space manager free-space queue -Object size: 4096 bytes -Associated volume OID (virtual): 0x0 --------------------------------------------------------------------------------- -Ephemeral OID: 0x401 -Logical block address on disk: 0x362437c -Object type: Container reaper -Object subtype: (none/invalid) -Object size: 4096 bytes -Associated volume OID (virtual): 0x0 --------------------------------------------------------------------------------- -Ephemeral OID: 0x178c0 -Logical block address on disk: 0x362437d -Object type: Container reaper list -Object subtype: (none/invalid) -Object size: 4096 bytes -Associated volume OID (virtual): 0x0 --------------------------------------------------------------------------------- -- There are 5 checkpoint-mappings in this checkpoint. - -Reading the Ephemeral objects used by this checkpoint ... OK. -Validating the Ephemeral objects ... OK. - -Details of the Ephemeral objects: --------------------------------------------------------------------------------- -Stored checksum: 0x25850498fb2a64c7 -OID: 0x87d46 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Space manager -Subtype: (none/invalid) --------------------------------------------------------------------------------- -Stored checksum: 0x5d65ceccea5e5cca -OID: 0x87d47 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: B-tree (root node) -Subtype: Space manager free-space queue --------------------------------------------------------------------------------- -Stored checksum: 0x6b1aa7c5bd428590 -OID: 0x405 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: B-tree (root node) -Subtype: Space manager free-space queue --------------------------------------------------------------------------------- -Stored checksum: 0xfaf5a52944eb7745 -OID: 0x401 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Container reaper -Subtype: (none/invalid) --------------------------------------------------------------------------------- -Stored checksum: 0x5f93fce79faa616f -OID: 0x178c0 -XID: 0x1bca24 -Storage type: Ephemeral -Type flags: (none) -Type: Container reaper list -Subtype: (none/invalid) --------------------------------------------------------------------------------- - -The container superblock states that the container object map has Physical OID 0xcfc19. -Loading the container object map ... OK. -Validating the container object map ... OK. - -Details of the container object map: --------------------------------------------------------------------------------- -Stored checksum: 0x1602d10c29c764ec -OID: 0xcfc19 -XID: 0x1bca24 -Storage type: Physical -Type flags: (none) -Type: Object map -Subtype: (none/invalid) -Flags: -- No snapshot support -Object mappings tree: -- Storage type: Physical -- Type flags: (none) -- Type: B-tree (root node) -- Object ID: 0xd03ba -Snapshots tree: -- Storage type: Physical -- Type flags: (none) -- Type: B-tree (root node) -- Object ID: 0x0 -- Number of snapshots: 0 snapshots -- Latest snapshot XID: 0x0 -In-progress revert: -- Minimum XID: 0x0 -- Maximum XID: 0x0 --------------------------------------------------------------------------------- - -Reading the root node of the container object map B-tree ... OK. -Validating the root node of the container object map B-tree ... OK. - -Details of the container object map B-tree: --------------------------------------------------------------------------------- -Stored checksum: 0xccda4948e1be42ff -OID: 0xd03ba -XID: 0x1bca24 -Storage type: Physical -Type flags: (none) -Type: B-tree (root node) -Subtype: Object map -Flags: Root node, Leaf node, Fixed size for keys and values -Number of child levels: 0 -Number of keys in this node: 4 -Location of table of contents: -- Offset from start of node data area: 0x0 = 0 -- Length (bytes): 0x1c0 = 448 -Location of key–value shared free space: -- Offset from start of keys area: 0x50 = 80 -- Length (bytes): 0xd40 = 3392 - -Info relating to the entire B-tree: -- Flags: - - This B-tree is currently undergoing a series of sequential inserts --- optimise operations if possible - - Child nodes are referred to using Physical OIDs -- Node size: 4096 bytes -- Key size: 16 bytes -- Value size: 16 bytes - -- Length of longest key: 16 bytes -- Length of longest value: 16 bytes -- Number of keys: 4 -- Number of nodes: 1 --------------------------------------------------------------------------------- - -The container superblock lists 4 APFS volumes, whose superblocks have the following Virtual OIDs: -- 0x402 -- 0x408 -- 0x40a -- 0xa2cd - -Reading the APFS volume superblocks ... OK. -Validating the APFS volume superblocks ... OK. - -Details of these volume superblocks: --------------------------------------------------------------------------------- -Stored checksum: 0xb5b1a8822f1d487c -OID: 0x402 -XID: 0x1bca24 -Storage type: Virtual -Type flags: (none) -Type: APFS volume -Subtype: (none/invalid) - -Magic string: APSB -Index within container volume array: 0 - -Volume name: ### macOS ### - -Flags: -- Volume is unencrypted. -Supported features: -- This volume has hardlink map records. -Supported read-only compatible features: -- No read-only compatible volume feature flags are set. -Backward-incompatible features: -- Filenames on this volume are case-insensitive. -Roles: -- This volume has no defined roles - -Last unmount time: Mon Oct 28 00:39:51 2019 -Last modification time: Mon Oct 28 00:39:51 2019 - -Reserved blocks: 0 blocks -Block quota: 0 blocks -Allocated blocks: 95004267 blocks - -Volume object map Physical OID: 0xd0c91 - -Root tree info: -- Physical OID: 0x404 -- Storage type: Virtual -- Type flags: (none) -- Object type: B-tree (root node) - -Extent-reference tree info: -- Physical OID: 0xcfbc1 -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -Snapshot metadata tree info: -- Physical OID: 0xd19b3 -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -On next mount, revert to: -- snapshot with this XID: 0x0 -- APFS volume superblock with this Physical OID: 0x0 - -Next file-system object ID that will be assigned: 0xb7b2d2 -Next document ID that will be assigned: 0x39 - -Number of: - -- regular files: 818284 -- directories: 149371 -- symbolic links: 33513 -- other file-system objects: 259 - -- snapshots: 0 -- block allocations ever made: 527393597 -- block liberations ever made: 432520964 - -UUID: 0x6e8c1b74febf029f874ce36cbf63236e --------------------------------------------------------------------------------- -Stored checksum: 0x89a5ff1f4028c3dc -OID: 0x408 -XID: 0x1ac206 -Storage type: Virtual -Type flags: (none) -Type: APFS volume -Subtype: (none/invalid) - -Magic string: APSB -Index within container volume array: 1 - -Volume name: ### Preboot ### - -Flags: -- Volume is unencrypted. -Supported features: -- This volume has hardlink map records. -Supported read-only compatible features: -- No read-only compatible volume feature flags are set. -Backward-incompatible features: -- Filenames on this volume are case-insensitive. -Roles: -- Preboot volume (contains files needed to boot from an encrypted volumes) - -Last unmount time: Thu Jan 1 01:00:00 1970 -Last modification time: Tue Oct 1 22:09:26 2019 - -Reserved blocks: 0 blocks -Block quota: 0 blocks -Allocated blocks: 5774 blocks - -Volume object map Physical OID: 0xdfec2 - -Root tree info: -- Physical OID: 0x409 -- Storage type: Virtual -- Type flags: (none) -- Object type: B-tree (root node) - -Extent-reference tree info: -- Physical OID: 0xd0dc1 -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -Snapshot metadata tree info: -- Physical OID: 0xd047a -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -On next mount, revert to: -- snapshot with this XID: 0x0 -- APFS volume superblock with this Physical OID: 0x0 - -Next file-system object ID that will be assigned: 0x66d -Next document ID that will be assigned: 0x3 - -Number of: - -- regular files: 85 -- directories: 19 -- symbolic links: 0 -- other file-system objects: 0 - -- snapshots: 0 -- block allocations ever made: 340093 -- block liberations ever made: 334334 - -UUID: 0x4c6f78180f2d3fa5654347f662a09011 --------------------------------------------------------------------------------- -Stored checksum: 0xcd5302d537353713 -OID: 0x40a -XID: 0x1bc8be -Storage type: Virtual -Type flags: (none) -Type: APFS volume -Subtype: (none/invalid) - -Magic string: APSB -Index within container volume array: 2 - -Volume name: ### Recovery ### - -Flags: -- Volume is unencrypted. -Supported features: -- This volume has hardlink map records. -Supported read-only compatible features: -- No read-only compatible volume feature flags are set. -Backward-incompatible features: -- Filenames on this volume are case-insensitive. -Roles: -- Recovery volume (contains a recovery system) - -Last unmount time: Thu Jan 1 01:00:00 1970 -Last modification time: Sun Oct 6 22:32:29 2019 - -Reserved blocks: 0 blocks -Block quota: 0 blocks -Allocated blocks: 123872 blocks - -Volume object map Physical OID: 0xd01da - -Root tree info: -- Physical OID: 0x40b -- Storage type: Virtual -- Type flags: (none) -- Object type: B-tree (root node) - -Extent-reference tree info: -- Physical OID: 0xd142f -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -Snapshot metadata tree info: -- Physical OID: 0xd04ed -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -On next mount, revert to: -- snapshot with this XID: 0x0 -- APFS volume superblock with this Physical OID: 0x0 - -Next file-system object ID that will be assigned: 0xc6 -Next document ID that will be assigned: 0x3 - -Number of: - -- regular files: 29 -- directories: 3 -- symbolic links: 0 -- other file-system objects: 0 - -- snapshots: 0 -- block allocations ever made: 873153 -- block liberations ever made: 749291 - -UUID: 0x4c9d52dcc5ca25ad34422a50db9597fd --------------------------------------------------------------------------------- -Stored checksum: 0xa040ad7c9b0dcaef -OID: 0xa2cd -XID: 0x1bca08 -Storage type: Virtual -Type flags: (none) -Type: APFS volume -Subtype: (none/invalid) - -Magic string: APSB -Index within container volume array: 3 - -Volume name: ### VM ### - -Flags: -- Volume is unencrypted. -Supported features: -- This volume has hardlink map records. -Supported read-only compatible features: -- No read-only compatible volume feature flags are set. -Backward-incompatible features: -- Filenames on this volume are case-insensitive. -Roles: -- Swap volume (used as swap space for virtual memory) - -Last unmount time: Thu Jan 1 01:00:00 1970 -Last modification time: Sun Oct 6 22:59:23 2019 - -Reserved blocks: 0 blocks -Block quota: 0 blocks -Allocated blocks: 786438 blocks - -Volume object map Physical OID: 0xd607d - -Root tree info: -- Physical OID: 0xa2ce -- Storage type: Virtual -- Type flags: (none) -- Object type: B-tree (root node) - -Extent-reference tree info: -- Physical OID: 0xd2365 -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -Snapshot metadata tree info: -- Physical OID: 0xd053e -- Storage type: Physical -- Type flags: (none) -- Object type: B-tree (root node) - -On next mount, revert to: -- snapshot with this XID: 0x0 -- APFS volume superblock with this Physical OID: 0x0 - -Next file-system object ID that will be assigned: 0x251 -Next document ID that will be assigned: 0x3 - -Number of: - -- regular files: 3 -- directories: 0 -- symbolic links: 0 -- other file-system objects: 0 - -- snapshots: 0 -- block allocations ever made: 130023424 -- block liberations ever made: 129236992 - -UUID: 0x63c4ccbb1c4f6685084c983199a4a324 --------------------------------------------------------------------------------- -END: All done. -``` +(command_inspect)= + +# {drat-command}`inspect` + +## Description + +The {drat-command}`inspect` command inspects all aspects of an APFS container, +primarily by simulating the process of mounting it. It displays info about the +inspection process, reporting info, warnings, and errors about the container's +state. + +## Example usage and output + +``` +$ drat --container /dev/disk2s2 inspect + +Opening file at `/dev/disk2s2` in read-only mode ... OK. +Simulating a mount of the APFS container. +Validating checksum of block 0x0 ... OK. + +Details of block 0x0: +-------------------------------------------------------------------------------- +Stored checksum: 0xe25ef5e409032d45 +OID: 0x1 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Container superblock +Subtype: (none/invalid) +Magic string: NXSB +Block size: 4096 bytes +Block count: 117161676 +Supported features: +- No feature flags are set. +Supported read-only compatible features: +- No read-only compatible feature flags are set. +Backward-incompatible features: +- No backward-incompatible feature flags are set. +UUID: 0x07531d831bda8f8c9944ee600d55fd37 +Next OID: 0x28709a +Next XID: 0x1bca25 +Space manager Ephemeral OID: 0x87d46 +Object map Physical OID: 0xcfc19 +Reaper Ephemeral OID: 0x401 +Other flags: +- No other flags are set. +-------------------------------------------------------------------------------- + +Locating the checkpoint descriptor area: +- Its length is 280 blocks. +- It is contiguous. +- The address of its first block is 0x3629fe4. +Loading the checkpoint descriptor area into memory ... OK. +Locating the most recent well-formed container superblock in the checkpoint descriptor area: +- It lies at index 271 within the checkpoint descriptor area. + +Details of this container superblock: +-------------------------------------------------------------------------------- +Stored checksum: 0xe25eecc609033666 +OID: 0x1 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Container superblock +Subtype: (none/invalid) +Magic string: NXSB +Block size: 4096 bytes +Block count: 117161676 +Supported features: +- No feature flags are set. +Supported read-only compatible features: +- No read-only compatible feature flags are set. +Backward-incompatible features: +- No backward-incompatible feature flags are set. +UUID: 0x07531d831bda8f8c9944ee600d55fd37 +Next OID: 0x28709a +Next XID: 0x1bca25 +Space manager Ephemeral OID: 0x87d46 +Object map Physical OID: 0xcfc19 +Reaper Ephemeral OID: 0x401 +Other flags: +- No other flags are set. +-------------------------------------------------------------------------------- +- The corresponding checkpoint starts at index 270 within the checkpoint descriptor area, and spans 2 blocks. + +Loading the corresponding checkpoint ... OK. + +Details of each block in this checkpoint: +-------------------------------------------------------------------------------- +Stored checksum: 0xe63bc1554547b687 +OID: 0x362a0f2 +XID: 0x1bca24 +Storage type: Physical +Type flags: (none) +Type: Checkpoint map +Subtype: (none/invalid) +Flags: +- Last checkpoint-mapping block in the corresponding checkpoint. +Number of mappings: 5 +-------------------------------------------------------------------------------- +Stored checksum: 0xe25eecc609033666 +OID: 0x1 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Container superblock +Subtype: (none/invalid) +Magic string: NXSB +Block size: 4096 bytes +Block count: 117161676 +Supported features: +- No feature flags are set. +Supported read-only compatible features: +- No read-only compatible feature flags are set. +Backward-incompatible features: +- No backward-incompatible feature flags are set. +UUID: 0x07531d831bda8f8c9944ee600d55fd37 +Next OID: 0x28709a +Next XID: 0x1bca25 +Space manager Ephemeral OID: 0x87d46 +Object map Physical OID: 0xcfc19 +Reaper Ephemeral OID: 0x401 +Other flags: +- No other flags are set. +-------------------------------------------------------------------------------- + +Details of each checkpoint-mapping in this checkpoint: +-------------------------------------------------------------------------------- +Ephemeral OID: 0x87d46 +Logical block address on disk: 0x3624379 +Object type: Space manager +Object subtype: (none/invalid) +Object size: 4096 bytes +Associated volume OID (virtual): 0x0 +-------------------------------------------------------------------------------- +Ephemeral OID: 0x87d47 +Logical block address on disk: 0x362437a +Object type: B-tree (root node) +Object subtype: Space manager free-space queue +Object size: 4096 bytes +Associated volume OID (virtual): 0x0 +-------------------------------------------------------------------------------- +Ephemeral OID: 0x405 +Logical block address on disk: 0x362437b +Object type: B-tree (root node) +Object subtype: Space manager free-space queue +Object size: 4096 bytes +Associated volume OID (virtual): 0x0 +-------------------------------------------------------------------------------- +Ephemeral OID: 0x401 +Logical block address on disk: 0x362437c +Object type: Container reaper +Object subtype: (none/invalid) +Object size: 4096 bytes +Associated volume OID (virtual): 0x0 +-------------------------------------------------------------------------------- +Ephemeral OID: 0x178c0 +Logical block address on disk: 0x362437d +Object type: Container reaper list +Object subtype: (none/invalid) +Object size: 4096 bytes +Associated volume OID (virtual): 0x0 +-------------------------------------------------------------------------------- +- There are 5 checkpoint-mappings in this checkpoint. + +Reading the Ephemeral objects used by this checkpoint ... OK. +Validating the Ephemeral objects ... OK. + +Details of the Ephemeral objects: +-------------------------------------------------------------------------------- +Stored checksum: 0x25850498fb2a64c7 +OID: 0x87d46 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Space manager +Subtype: (none/invalid) +-------------------------------------------------------------------------------- +Stored checksum: 0x5d65ceccea5e5cca +OID: 0x87d47 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: B-tree (root node) +Subtype: Space manager free-space queue +-------------------------------------------------------------------------------- +Stored checksum: 0x6b1aa7c5bd428590 +OID: 0x405 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: B-tree (root node) +Subtype: Space manager free-space queue +-------------------------------------------------------------------------------- +Stored checksum: 0xfaf5a52944eb7745 +OID: 0x401 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Container reaper +Subtype: (none/invalid) +-------------------------------------------------------------------------------- +Stored checksum: 0x5f93fce79faa616f +OID: 0x178c0 +XID: 0x1bca24 +Storage type: Ephemeral +Type flags: (none) +Type: Container reaper list +Subtype: (none/invalid) +-------------------------------------------------------------------------------- + +The container superblock states that the container object map has Physical OID 0xcfc19. +Loading the container object map ... OK. +Validating the container object map ... OK. + +Details of the container object map: +-------------------------------------------------------------------------------- +Stored checksum: 0x1602d10c29c764ec +OID: 0xcfc19 +XID: 0x1bca24 +Storage type: Physical +Type flags: (none) +Type: Object map +Subtype: (none/invalid) +Flags: +- No snapshot support +Object mappings tree: +- Storage type: Physical +- Type flags: (none) +- Type: B-tree (root node) +- Object ID: 0xd03ba +Snapshots tree: +- Storage type: Physical +- Type flags: (none) +- Type: B-tree (root node) +- Object ID: 0x0 +- Number of snapshots: 0 snapshots +- Latest snapshot XID: 0x0 +In-progress revert: +- Minimum XID: 0x0 +- Maximum XID: 0x0 +-------------------------------------------------------------------------------- + +Reading the root node of the container object map B-tree ... OK. +Validating the root node of the container object map B-tree ... OK. + +Details of the container object map B-tree: +-------------------------------------------------------------------------------- +Stored checksum: 0xccda4948e1be42ff +OID: 0xd03ba +XID: 0x1bca24 +Storage type: Physical +Type flags: (none) +Type: B-tree (root node) +Subtype: Object map +Flags: Root node, Leaf node, Fixed size for keys and values +Number of child levels: 0 +Number of keys in this node: 4 +Location of table of contents: +- Offset from start of node data area: 0x0 = 0 +- Length (bytes): 0x1c0 = 448 +Location of key–value shared free space: +- Offset from start of keys area: 0x50 = 80 +- Length (bytes): 0xd40 = 3392 + +Info relating to the entire B-tree: +- Flags: + - This B-tree is currently undergoing a series of sequential inserts --- optimise operations if possible + - Child nodes are referred to using Physical OIDs +- Node size: 4096 bytes +- Key size: 16 bytes +- Value size: 16 bytes + +- Length of longest key: 16 bytes +- Length of longest value: 16 bytes +- Number of keys: 4 +- Number of nodes: 1 +-------------------------------------------------------------------------------- + +The container superblock lists 4 APFS volumes, whose superblocks have the following Virtual OIDs: +- 0x402 +- 0x408 +- 0x40a +- 0xa2cd + +Reading the APFS volume superblocks ... OK. +Validating the APFS volume superblocks ... OK. + +Details of these volume superblocks: +-------------------------------------------------------------------------------- +Stored checksum: 0xb5b1a8822f1d487c +OID: 0x402 +XID: 0x1bca24 +Storage type: Virtual +Type flags: (none) +Type: APFS volume +Subtype: (none/invalid) + +Magic string: APSB +Index within container volume array: 0 + +Volume name: ### macOS ### + +Flags: +- Volume is unencrypted. +Supported features: +- This volume has hardlink map records. +Supported read-only compatible features: +- No read-only compatible volume feature flags are set. +Backward-incompatible features: +- Filenames on this volume are case-insensitive. +Roles: +- This volume has no defined roles + +Last unmount time: Mon Oct 28 00:39:51 2019 +Last modification time: Mon Oct 28 00:39:51 2019 + +Reserved blocks: 0 blocks +Block quota: 0 blocks +Allocated blocks: 95004267 blocks + +Volume object map Physical OID: 0xd0c91 + +Root tree info: +- Physical OID: 0x404 +- Storage type: Virtual +- Type flags: (none) +- Object type: B-tree (root node) + +Extent-reference tree info: +- Physical OID: 0xcfbc1 +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +Snapshot metadata tree info: +- Physical OID: 0xd19b3 +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +On next mount, revert to: +- snapshot with this XID: 0x0 +- APFS volume superblock with this Physical OID: 0x0 + +Next file-system object ID that will be assigned: 0xb7b2d2 +Next document ID that will be assigned: 0x39 + +Number of: + +- regular files: 818284 +- directories: 149371 +- symbolic links: 33513 +- other file-system objects: 259 + +- snapshots: 0 +- block allocations ever made: 527393597 +- block liberations ever made: 432520964 + +UUID: 0x6e8c1b74febf029f874ce36cbf63236e +-------------------------------------------------------------------------------- +Stored checksum: 0x89a5ff1f4028c3dc +OID: 0x408 +XID: 0x1ac206 +Storage type: Virtual +Type flags: (none) +Type: APFS volume +Subtype: (none/invalid) + +Magic string: APSB +Index within container volume array: 1 + +Volume name: ### Preboot ### + +Flags: +- Volume is unencrypted. +Supported features: +- This volume has hardlink map records. +Supported read-only compatible features: +- No read-only compatible volume feature flags are set. +Backward-incompatible features: +- Filenames on this volume are case-insensitive. +Roles: +- Preboot volume (contains files needed to boot from an encrypted volumes) + +Last unmount time: Thu Jan 1 01:00:00 1970 +Last modification time: Tue Oct 1 22:09:26 2019 + +Reserved blocks: 0 blocks +Block quota: 0 blocks +Allocated blocks: 5774 blocks + +Volume object map Physical OID: 0xdfec2 + +Root tree info: +- Physical OID: 0x409 +- Storage type: Virtual +- Type flags: (none) +- Object type: B-tree (root node) + +Extent-reference tree info: +- Physical OID: 0xd0dc1 +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +Snapshot metadata tree info: +- Physical OID: 0xd047a +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +On next mount, revert to: +- snapshot with this XID: 0x0 +- APFS volume superblock with this Physical OID: 0x0 + +Next file-system object ID that will be assigned: 0x66d +Next document ID that will be assigned: 0x3 + +Number of: + +- regular files: 85 +- directories: 19 +- symbolic links: 0 +- other file-system objects: 0 + +- snapshots: 0 +- block allocations ever made: 340093 +- block liberations ever made: 334334 + +UUID: 0x4c6f78180f2d3fa5654347f662a09011 +-------------------------------------------------------------------------------- +Stored checksum: 0xcd5302d537353713 +OID: 0x40a +XID: 0x1bc8be +Storage type: Virtual +Type flags: (none) +Type: APFS volume +Subtype: (none/invalid) + +Magic string: APSB +Index within container volume array: 2 + +Volume name: ### Recovery ### + +Flags: +- Volume is unencrypted. +Supported features: +- This volume has hardlink map records. +Supported read-only compatible features: +- No read-only compatible volume feature flags are set. +Backward-incompatible features: +- Filenames on this volume are case-insensitive. +Roles: +- Recovery volume (contains a recovery system) + +Last unmount time: Thu Jan 1 01:00:00 1970 +Last modification time: Sun Oct 6 22:32:29 2019 + +Reserved blocks: 0 blocks +Block quota: 0 blocks +Allocated blocks: 123872 blocks + +Volume object map Physical OID: 0xd01da + +Root tree info: +- Physical OID: 0x40b +- Storage type: Virtual +- Type flags: (none) +- Object type: B-tree (root node) + +Extent-reference tree info: +- Physical OID: 0xd142f +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +Snapshot metadata tree info: +- Physical OID: 0xd04ed +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +On next mount, revert to: +- snapshot with this XID: 0x0 +- APFS volume superblock with this Physical OID: 0x0 + +Next file-system object ID that will be assigned: 0xc6 +Next document ID that will be assigned: 0x3 + +Number of: + +- regular files: 29 +- directories: 3 +- symbolic links: 0 +- other file-system objects: 0 + +- snapshots: 0 +- block allocations ever made: 873153 +- block liberations ever made: 749291 + +UUID: 0x4c9d52dcc5ca25ad34422a50db9597fd +-------------------------------------------------------------------------------- +Stored checksum: 0xa040ad7c9b0dcaef +OID: 0xa2cd +XID: 0x1bca08 +Storage type: Virtual +Type flags: (none) +Type: APFS volume +Subtype: (none/invalid) + +Magic string: APSB +Index within container volume array: 3 + +Volume name: ### VM ### + +Flags: +- Volume is unencrypted. +Supported features: +- This volume has hardlink map records. +Supported read-only compatible features: +- No read-only compatible volume feature flags are set. +Backward-incompatible features: +- Filenames on this volume are case-insensitive. +Roles: +- Swap volume (used as swap space for virtual memory) + +Last unmount time: Thu Jan 1 01:00:00 1970 +Last modification time: Sun Oct 6 22:59:23 2019 + +Reserved blocks: 0 blocks +Block quota: 0 blocks +Allocated blocks: 786438 blocks + +Volume object map Physical OID: 0xd607d + +Root tree info: +- Physical OID: 0xa2ce +- Storage type: Virtual +- Type flags: (none) +- Object type: B-tree (root node) + +Extent-reference tree info: +- Physical OID: 0xd2365 +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +Snapshot metadata tree info: +- Physical OID: 0xd053e +- Storage type: Physical +- Type flags: (none) +- Object type: B-tree (root node) + +On next mount, revert to: +- snapshot with this XID: 0x0 +- APFS volume superblock with this Physical OID: 0x0 + +Next file-system object ID that will be assigned: 0x251 +Next document ID that will be assigned: 0x3 + +Number of: + +- regular files: 3 +- directories: 0 +- symbolic links: 0 +- other file-system objects: 0 + +- snapshots: 0 +- block allocations ever made: 130023424 +- block liberations ever made: 129236992 + +UUID: 0x63c4ccbb1c4f6685084c983199a4a324 +-------------------------------------------------------------------------------- +END: All done. +``` diff --git a/docs/commands/read.md b/docs/commands/read.md index 30536d3..962bfe1 100644 --- a/docs/commands/read.md +++ b/docs/commands/read.md @@ -1,25 +1,25 @@ -(command_read)= - -# {drat-command}`read` - -## Description - -The {drat-command}`read` command prints a nicely formatted, human-readable -description of a given block in an APFS container. - -## Example usage and output - -``` -$ drat --container /dev/disk2s2 read --block 0x3af2 - -Opening file at `/dev/disk0s2` in read-only mode ... OK. - -- Details of block 0x3af2: -Stored checksum: 0x8fb5c6176f907f7e -OID: 0x0000000000000403 -XID: 0x000000000000bd46 -Storage type: Ephemeral -Type flags: (none) -Type: B-tree (root node) -Subtype: Space manager free-space queue -``` +(command_read)= + +# {drat-command}`read` + +## Description + +The {drat-command}`read` command prints a nicely formatted, human-readable +description of a given block in an APFS container. + +## Example usage and output + +``` +$ drat --container /dev/disk2s2 read --block 0x3af2 + +Opening file at `/dev/disk0s2` in read-only mode ... OK. + +- Details of block 0x3af2: +Stored checksum: 0x8fb5c6176f907f7e +OID: 0x0000000000000403 +XID: 0x000000000000bd46 +Storage type: Ephemeral +Type flags: (none) +Type: B-tree (root node) +Subtype: Space manager free-space queue +``` diff --git a/docs/commands/recover.md b/docs/commands/recover.md index f2c92dc..78a418d 100644 --- a/docs/commands/recover.md +++ b/docs/commands/recover.md @@ -1,73 +1,73 @@ -(command_recover)= - -# {drat-command}`recover` - -The {drat-command}`recover` command allows you to easily recover a file or -entire directory tree and all the files within it. You must specify the same set -of parameters required by {drat-command}`explore-fs`, namely: - -- a filesystem tree in the same manner as {drat-command}`explore-fs-tree`; and - -- an object map in the same manner as {drat-command}`explore-fs-tree`; and - -- an entry point, which is the item you wish to recover, and is either: - - - an absolute filesystem path via {argument}`path`, like `/`, - `/Users/john`, `/Users/john/Documents/my document.txt`, or `/..`; or - - - a filesystem object ID via {argument}`fsoid`, like `0x7563`. - -If no filesystem tree or object map is specified, the default is `--volume 1`. -The simplest usage thus involves only specifying {argument}`path`, in which case -the item at that path within the first volume will be recovered. You can -additionally specify {argument}`volume` or {argument}`volume-name` if the item -you wish to recover is not within the first volume. - -Specify the output path (the path to write the recovered data to) with -{argument}`output`. If the output path is a directory and ends with `/`, the -item you wish to recover will be stored within that directory, and the item name -as determined by {argument}`path` or {argument}`fsoid` will be used as the -recovered item's name. The default is `--output ./`, so the recovered item will -be written to the current working directory. Specifying `--output -` when the -entry point is a file will result in the file data being sent to {file}`stdout`, -and all normal informational output will be sent to {file}`stderr` rather than -{file}`stdout`. - -Whilst files are being recovered, they are temporarily named -`_com.dratapp.recover_`, and then renamed to `` once -they have been fully recovered. - -If the item you wish to recover is a file, and a directory with the target name -already exists, an error will occur. If a file with the target name already -exists, it will not be overwritten unless you specify {argument}`overwrite`. - -If the item you wish to recover is a directory, and a file with the target name -already exists, an error will occur. If a directory with the target name already -exists, the recovered directoy tree will be merged with the existing directory -tree. Any existing files that have the same relative path as recovered files -will not be overwritten unless you specify {argument}`overwrite`. This behaviour -means that if a directory tree recovery process gets interrupted or encounters -an error, you can issue the same command *without* {argument}`overwrite` to -effectively resume the recovery process from where it stopped, or *with* -{argument}`overwrite` to effectively restart the process from scratch. - -## Example usage and output - -``` -$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt - -$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt --output recovered-file.txt - -$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt --output - | grep 'lorem ipsum' - -$ drat --container /dev/disk0s2 recover --path /Users/john - -$ drat --container /dev/disk0s2 recover --path /Users/john --output recovered-john --overwrite - -$ drat --container /dev/disk0s2 recover --volume-name macOS --path /System - -$ drat --container /dev/disk0s2 recover --volume 5 --path /System -``` - -```{todo} Provide example output +(command_recover)= + +# {drat-command}`recover` + +The {drat-command}`recover` command allows you to easily recover a file or +entire directory tree and all the files within it. You must specify the same set +of parameters required by {drat-command}`explore-fs`, namely: + +- a filesystem tree in the same manner as {drat-command}`explore-fs-tree`; and + +- an object map in the same manner as {drat-command}`explore-fs-tree`; and + +- an entry point, which is the item you wish to recover, and is either: + + - an absolute filesystem path via {argument}`path`, like `/`, + `/Users/john`, `/Users/john/Documents/my document.txt`, or `/..`; or + + - a filesystem object ID via {argument}`fsoid`, like `0x7563`. + +If no filesystem tree or object map is specified, the default is `--volume 1`. +The simplest usage thus involves only specifying {argument}`path`, in which case +the item at that path within the first volume will be recovered. You can +additionally specify {argument}`volume` or {argument}`volume-name` if the item +you wish to recover is not within the first volume. + +Specify the output path (the path to write the recovered data to) with +{argument}`output`. If the output path is a directory and ends with `/`, the +item you wish to recover will be stored within that directory, and the item name +as determined by {argument}`path` or {argument}`fsoid` will be used as the +recovered item's name. The default is `--output ./`, so the recovered item will +be written to the current working directory. Specifying `--output -` when the +entry point is a file will result in the file data being sent to {file}`stdout`, +and all normal informational output will be sent to {file}`stderr` rather than +{file}`stdout`. + +Whilst files are being recovered, they are temporarily named +`_com.dratapp.recover_`, and then renamed to `` once +they have been fully recovered. + +If the item you wish to recover is a file, and a directory with the target name +already exists, an error will occur. If a file with the target name already +exists, it will not be overwritten unless you specify {argument}`overwrite`. + +If the item you wish to recover is a directory, and a file with the target name +already exists, an error will occur. If a directory with the target name already +exists, the recovered directoy tree will be merged with the existing directory +tree. Any existing files that have the same relative path as recovered files +will not be overwritten unless you specify {argument}`overwrite`. This behaviour +means that if a directory tree recovery process gets interrupted or encounters +an error, you can issue the same command *without* {argument}`overwrite` to +effectively resume the recovery process from where it stopped, or *with* +{argument}`overwrite` to effectively restart the process from scratch. + +## Example usage and output + +``` +$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt + +$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt --output recovered-file.txt + +$ drat --container /dev/disk0s2 recover --path /Users/john/Desktop/my-file.txt --output - | grep 'lorem ipsum' + +$ drat --container /dev/disk0s2 recover --path /Users/john + +$ drat --container /dev/disk0s2 recover --path /Users/john --output recovered-john --overwrite + +$ drat --container /dev/disk0s2 recover --volume-name macOS --path /System + +$ drat --container /dev/disk0s2 recover --volume 5 --path /System +``` + +```{todo} Provide example output ``` \ No newline at end of file diff --git a/docs/commands/resolve-virtual-oids.md b/docs/commands/resolve-virtual-oids.md index b33ac2e..d223779 100644 --- a/docs/commands/resolve-virtual-oids.md +++ b/docs/commands/resolve-virtual-oids.md @@ -1,47 +1,47 @@ -(command_resolve-virtual-oids)= - -# {drat-command}`resolve-virtual-oids` - -## Description - -The {drat-command}`resolve-virtual-oids` command takes one or more Virtual OIDs and -uses one or more object maps to resolve each Virtual OID to one or more block -addresses. It can also be used to programatically check whether a given Virtual -OID maps to a given block address, or do such checks in a batch — that is, -check whether a specific set of Virtual OIDs maps directly to a specific -set of block addresses. - -## Example usage and output - -``` -$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oid 0x247 -0x247 -> 0x1167a - -$ drat --container /dev/disk2s2 resolve-virtual-oids --block 0x1d623e --oid 0x247 -0x247 -> 0x1167a - -$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3,0x10a -0x247 -> 0x1167a -0x11f3 -> 0x20ad6 -0x10a -> 0x17d59 -``` - -``` -$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3 --expected-addresses 0x1167a,0x20ad6,0x17d59 -0x247 -> 0x1167a ? OK -0x11f3 -> 0x20ad6 ? OK -0x10a -> 0x17d59 ? OK - -$ echo $? -0 -``` - -``` -$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3 --expected-addresses 0x1167a,0x123,0x17d59 -0x247 -> 0x1167a ? OK -0x11f3 -> 0x123 ? FAILED ; 0x11f3 -> 0x20ad6 -0x10a -> 0x17d59 ? OK - -$ echo $? -1 +(command_resolve-virtual-oids)= + +# {drat-command}`resolve-virtual-oids` + +## Description + +The {drat-command}`resolve-virtual-oids` command takes one or more Virtual OIDs and +uses one or more object maps to resolve each Virtual OID to one or more block +addresses. It can also be used to programatically check whether a given Virtual +OID maps to a given block address, or do such checks in a batch — that is, +check whether a specific set of Virtual OIDs maps directly to a specific +set of block addresses. + +## Example usage and output + +``` +$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oid 0x247 +0x247 -> 0x1167a + +$ drat --container /dev/disk2s2 resolve-virtual-oids --block 0x1d623e --oid 0x247 +0x247 -> 0x1167a + +$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3,0x10a +0x247 -> 0x1167a +0x11f3 -> 0x20ad6 +0x10a -> 0x17d59 +``` + +``` +$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3 --expected-addresses 0x1167a,0x20ad6,0x17d59 +0x247 -> 0x1167a ? OK +0x11f3 -> 0x20ad6 ? OK +0x10a -> 0x17d59 ? OK + +$ echo $? +0 +``` + +``` +$ drat --container /dev/disk2s2 resolve-virtual-oids --volume 1 --oids 0x247,0x11f3 --expected-addresses 0x1167a,0x123,0x17d59 +0x247 -> 0x1167a ? OK +0x11f3 -> 0x123 ? FAILED ; 0x11f3 -> 0x20ad6 +0x10a -> 0x17d59 ? OK + +$ echo $? +1 ``` \ No newline at end of file diff --git a/docs/commands/search.md b/docs/commands/search.md index 80b2aa4..a7a8d50 100644 --- a/docs/commands/search.md +++ b/docs/commands/search.md @@ -1,193 +1,193 @@ -(command_search)= - -# {drat-command}`search` - -The {drat-command}`search` command allows you to scan the filesystem for -particular data, such as blocks of particular types or with particular IDs, or -blocks containing specific data, such as object maps nodes containing particular -mappings, or filesystem records for particular FSOIDs or item names. - -Performing a search involves specifying one or more search criteria/parameters, -ecah of which has its own flag — these are detailed in the rest of this page. -Some flags are only defined/make sense in certain contexts, so if that context -is not explicitly specified, it is implied; or if a conflicting contxt is -specified, an error occurs. For example, searching for particular object -mappings only makes sense if we're looking at blocks that are nodes of an object -map B-tree (`omap-tree`). Thus, specifying `--omap-key-oid 0x123` implies -`--type omap-tree`. Specifying conflicting contexts, as in -`drat search --type fs --omap-key-oid 0x123`, results in an error. - -The list of values passed to a parameter can be comma-delimited -(e.g. `--type omap,fs`), which indicates that any of the values must match -(logical OR). When multiple parameters are specified -(e.g. `--omap-key-oid 0x123 --omap-key-xid 0x456`), all of the values must match -(logical AND). - -Drat can create an index of the filesystem in advance in order to make searching -quicker (see {drat-command}`create-index`). This index is stored in a file, and -you pass it to Drat with {argument}`index`. If no index is specified or if the -index doesn't contain required data, Drat will scan through the filesystem -itself to get this data. - -## Search parameters - -### Top-level parameters - -A list of top-level search parameters follows: - -- `--oid` — The object ID. Use `--storage-type` to filter to Physical OIDs, - Ephemeral OIDs, or Virtual OIDs. - -- `--xid` — The transaction ID. This value is matched exactly. For a wider - search, specify a comma-delimited list of IDs (e.g. `--xid 0x1,0x3,0x1b`) or - a range in the form `[min xid]-[max xid]` (e.g. `--xid 0x1b-0x2c`), or a - combination of these (e.g. `--xid 0x1-0x3,0x5,0x1b-0x2c`). - -- `--storage-type` — The object storage type. Valid values are `physical`, - `ephemeral`, and `virtual`. - -- `--type` — The object type/subtype. See *Object types* below for a list of - valid keyword values. This can also be a numeric value (decimal or - hexadecimal), which can be useful in order to search for reserved types - (e.g. `0x20`, `24`), or types that this version of Drat is not aware of. Some - of the supported keyword values actually reference *subtypes*, but all of the - currently defined subtypes imply a (super-)type of `btree`, so in practice - there is no need for a separate search parameter. If, for whatever reason, you - want to specify a hexadecimal subtype, you can use a slash `/` to delimit it - from the (super-)type. For example: - - - `--type 0x20` is an object with (super-)type `0x20`. - - `--type 0x20/0x30` is an object with (super-)type `0x20` and subtype `0x30`. - - `--type /0x30` is an object with subtype `0x30` and any (super-)type. - -### Object types - -A table of valid keyword values for the `--type` parameter follows: - -| Keyword | Equivalent value | Description | -| :-- | :-- | :-- | -| `invalid` | `0x0` | The "invalid" object type | -| `test` | `0xff` | The reserved "testing" object type | -| `nxsb` | `0x1` | Container superblock | -| `btree` | `btree-root,btree-nonroot` | Any B-tree node | -| `btree-root` | `0x2` | B-tree root node | -| `btree-nonroot` | `0x3` | B-tree non-root node | -| `btree-leaf` | `--type btree --btree-flags leaf` | B-tree leaf node | -| `spaceman` | `0x5` | Spaceman (space manager) block | -| `spaceman-cab` | `0x6` | Spaceman chunk-info address block | -| `spaceman-cib` | `0x7` | Spaceman chunk-info block | -| `spaceman-bitmap` | `0x8` | Spaceman free-space bitmap | -| `spaceman-free-queue-tree` | `btree/0x9` | Spaceman free-space queue | -| `extent-list-tree` | `btree/0xa` | Extents-list tree | -| `omap` | `0xb` | Omap (object map) | -| `omap-tree` | `btree/0xb` | Any omap tree node | -| `omap-tree-leaf` | `btree-leaf/0xb` | Omap tree leaf node | -| `xp-map` | `0xc` | Checkpoint map | -| `fs` | `0xd` | Volume superblock | -| `fs-tree` | `btree/0xe` | Any filesystem records tree node | -| `fs-tree-leaf` | `btree-leaf/0xe` | Filesystem records tree leaf node | -| `block-ref-tree` | `btree/0xf` | Extent reference tree | -| `snap-meta-tree` | `btree/0x10` | Volume snapshot metadata tree | -| `reaper` | `0x11` | Reaper | -| `reaper-list` | `0x12` | Reaper list | -| `omap-snapshot-tree` | `btree/0x13` | Omap snapshot tree | -| `efi-jumpstart` | `0x14` | EFI jumpstart block | -| `fusion-middle-tree` | `btree/0x15` | Fusion drive middle-tree (tracks HDD blocks that are cached on SSD) | -| `fusion-wbc` | `0x16` | Fusion drive write-back cache state | -| `fusion-wbc-list` | `0x17` | Fusion drive write-back cache list | -| `er-state` | `0x18` | Encryption rolling state | -| `gbitmap` | `0x19` | General-purpose bitmap | -| `gbitmap-tree` | `btree/0x1a` | B-tree of general-purpose bitmaps | -| `gbitmap-block` | `0x1b` | Block containing a general-purpose bitmap | -| `er-recovery-block` | `0x1c` | Encryption-rolling recovery block | -| `snap-meta-ext` | `0x1d` | Additional snapshot metadata | -| `integrity-meta` | `0x1e` | Integrity metadata object | -| `fext-tree` | `btree/0x1f` | B-tree of file extents | -| `container-keybag` | `0x6b657973` (`'keys'`) | Container keybag | -| `volume-keybag` | `0x72656373` (`'recs'`) | Volume keybag | -| `media-keybag` | `0x6d6b6579` (`'mkey'`) | Media keybag | - -### Context-specific parameters - -The following sections list search parameters that are only valid in certain contexts. - -#### B-tree nodes (`--type btree`) - -```{todo} Describe this -``` - -#### B-tree root nodes (`--type btree-root`) - -```{todo} Describe this -``` - -#### Omap tree nodes (`--type omap-tree`) - -The following parameters can be used with `--type omap-tree` in general, but -it's usually only useful to use them with `--type omap-tree-leaf`, since you're -probably intersted in finding particular object mappings (pointers to objects), -not pointers to other omap tree nodes: - -- `--omap-key-oid` — A Virtual OID that makes up an (OID, XID) key-pair. - -- `--omap-key-xid` — An XID that makes up an (OID, XID) key-pair. - -##### Omap tree leaves (`--type omap-tree-leaf`) - -The following parameters can only be used with `--type omap-tree-leaf`: - -- `--omap-val-paddr` — The block address that some (OID, XID) key-pair maps to. - -#### Filesystem trees (`--type fs-tree`) - -The following parameters can be used with `--type fs-tree` in general, but it's -usually only useful to use them with `--type fs-tree-leaf`, since you're -probably interested in finding particular filesystem records, not the -Virtual OIDs of other filesystem tree nodes: - -- `--fsoid` — Filesystem object ID (a.k.a inode number). The ID of a filesystem - object whose records you wish to search for. - -- `--fsrt` — Filesystem record type. The type of filesystem records that you - wish to search for. See *Filesystem record types* below for a list of valid - keyword values. - -##### Filesystem record types - -| Keyword | Equivalent value | Description | -| :-- | :-- | :-- | -| `any` | `0`, `0x0` | Any type | -| `snap-meta` | `1`, `0x1` | Snapshot metadata | -| `extent` | `2`, `0x2` | Physical extent record | -| `inode` | `3`, `0x3` | Inode | -| `xattr` | `4`, `0x4` | Extended attribute | -| `sibling-link` | `5`, `0x5` | Sibling link; a mapping from target inode to source hard links which point to that target | -| `dstream` | `6`, `0x6` | Data stream | -| `crypto` | `7`, `0x7` | Per-file encryption state | -| `file-extent` | `8`, `0x8` | File extent record | -| `dentry` | `9`, `0x9` | Dentry (directory entry) | -| `dir-stats` | `10`, `0xa` | Directory information/statistics | -| `snap-name` | `11`, `0xb` | Snapshot name | -| `sibling-map` | `12`, `0xc` | Sibling map; a mapping from hard link to target inode | -| `file-info` | `13`, `0xd` | Additional info about file data | -| `invalid` | `15`, `0xf` | The "invalid" record type | - -##### Filesystem tree leaves (`--type fs-tree-leaf`) - -This section describes parameters specific to each record type. They can only be -used in conjunction with `--type fs-tree-leaf`. - -###### Dentries (`--fsrt dentry`) - -The following search parameters can only be used in conjunction with -`--fsrt dentry`, since they are specific to dentries (directory entry records): - -- `--dentry-name` — The name of the item. Be careful to escape any commas in the - name with backslash `\`, else they will be interpreted as logical OR - operators. For example, compare `--dentry-name 'Martian, The'` and - `--dentry-name 'Martian\, The'`: the latter will search for items named - `Martian, The`, whereas the former will search for items named `Martian` or - ` The` (note also the leading space in ` The`, since leading and trailing - whitespace is not ignored). - -- `--dentry-fsoid` — The FSOID (a.k.a. inode number, file ID) of the item. +(command_search)= + +# {drat-command}`search` + +The {drat-command}`search` command allows you to scan the filesystem for +particular data, such as blocks of particular types or with particular IDs, or +blocks containing specific data, such as object maps nodes containing particular +mappings, or filesystem records for particular FSOIDs or item names. + +Performing a search involves specifying one or more search criteria/parameters, +ecah of which has its own flag — these are detailed in the rest of this page. +Some flags are only defined/make sense in certain contexts, so if that context +is not explicitly specified, it is implied; or if a conflicting contxt is +specified, an error occurs. For example, searching for particular object +mappings only makes sense if we're looking at blocks that are nodes of an object +map B-tree (`omap-tree`). Thus, specifying `--omap-key-oid 0x123` implies +`--type omap-tree`. Specifying conflicting contexts, as in +`drat search --type fs --omap-key-oid 0x123`, results in an error. + +The list of values passed to a parameter can be comma-delimited +(e.g. `--type omap,fs`), which indicates that any of the values must match +(logical OR). When multiple parameters are specified +(e.g. `--omap-key-oid 0x123 --omap-key-xid 0x456`), all of the values must match +(logical AND). + +Drat can create an index of the filesystem in advance in order to make searching +quicker (see {drat-command}`create-index`). This index is stored in a file, and +you pass it to Drat with {argument}`index`. If no index is specified or if the +index doesn't contain required data, Drat will scan through the filesystem +itself to get this data. + +## Search parameters + +### Top-level parameters + +A list of top-level search parameters follows: + +- `--oid` — The object ID. Use `--storage-type` to filter to Physical OIDs, + Ephemeral OIDs, or Virtual OIDs. + +- `--xid` — The transaction ID. This value is matched exactly. For a wider + search, specify a comma-delimited list of IDs (e.g. `--xid 0x1,0x3,0x1b`) or + a range in the form `[min xid]-[max xid]` (e.g. `--xid 0x1b-0x2c`), or a + combination of these (e.g. `--xid 0x1-0x3,0x5,0x1b-0x2c`). + +- `--storage-type` — The object storage type. Valid values are `physical`, + `ephemeral`, and `virtual`. + +- `--type` — The object type/subtype. See *Object types* below for a list of + valid keyword values. This can also be a numeric value (decimal or + hexadecimal), which can be useful in order to search for reserved types + (e.g. `0x20`, `24`), or types that this version of Drat is not aware of. Some + of the supported keyword values actually reference *subtypes*, but all of the + currently defined subtypes imply a (super-)type of `btree`, so in practice + there is no need for a separate search parameter. If, for whatever reason, you + want to specify a hexadecimal subtype, you can use a slash `/` to delimit it + from the (super-)type. For example: + + - `--type 0x20` is an object with (super-)type `0x20`. + - `--type 0x20/0x30` is an object with (super-)type `0x20` and subtype `0x30`. + - `--type /0x30` is an object with subtype `0x30` and any (super-)type. + +### Object types + +A table of valid keyword values for the `--type` parameter follows: + +| Keyword | Equivalent value | Description | +| :-- | :-- | :-- | +| `invalid` | `0x0` | The "invalid" object type | +| `test` | `0xff` | The reserved "testing" object type | +| `nxsb` | `0x1` | Container superblock | +| `btree` | `btree-root,btree-nonroot` | Any B-tree node | +| `btree-root` | `0x2` | B-tree root node | +| `btree-nonroot` | `0x3` | B-tree non-root node | +| `btree-leaf` | `--type btree --btree-flags leaf` | B-tree leaf node | +| `spaceman` | `0x5` | Spaceman (space manager) block | +| `spaceman-cab` | `0x6` | Spaceman chunk-info address block | +| `spaceman-cib` | `0x7` | Spaceman chunk-info block | +| `spaceman-bitmap` | `0x8` | Spaceman free-space bitmap | +| `spaceman-free-queue-tree` | `btree/0x9` | Spaceman free-space queue | +| `extent-list-tree` | `btree/0xa` | Extents-list tree | +| `omap` | `0xb` | Omap (object map) | +| `omap-tree` | `btree/0xb` | Any omap tree node | +| `omap-tree-leaf` | `btree-leaf/0xb` | Omap tree leaf node | +| `xp-map` | `0xc` | Checkpoint map | +| `fs` | `0xd` | Volume superblock | +| `fs-tree` | `btree/0xe` | Any filesystem records tree node | +| `fs-tree-leaf` | `btree-leaf/0xe` | Filesystem records tree leaf node | +| `block-ref-tree` | `btree/0xf` | Extent reference tree | +| `snap-meta-tree` | `btree/0x10` | Volume snapshot metadata tree | +| `reaper` | `0x11` | Reaper | +| `reaper-list` | `0x12` | Reaper list | +| `omap-snapshot-tree` | `btree/0x13` | Omap snapshot tree | +| `efi-jumpstart` | `0x14` | EFI jumpstart block | +| `fusion-middle-tree` | `btree/0x15` | Fusion drive middle-tree (tracks HDD blocks that are cached on SSD) | +| `fusion-wbc` | `0x16` | Fusion drive write-back cache state | +| `fusion-wbc-list` | `0x17` | Fusion drive write-back cache list | +| `er-state` | `0x18` | Encryption rolling state | +| `gbitmap` | `0x19` | General-purpose bitmap | +| `gbitmap-tree` | `btree/0x1a` | B-tree of general-purpose bitmaps | +| `gbitmap-block` | `0x1b` | Block containing a general-purpose bitmap | +| `er-recovery-block` | `0x1c` | Encryption-rolling recovery block | +| `snap-meta-ext` | `0x1d` | Additional snapshot metadata | +| `integrity-meta` | `0x1e` | Integrity metadata object | +| `fext-tree` | `btree/0x1f` | B-tree of file extents | +| `container-keybag` | `0x6b657973` (`'keys'`) | Container keybag | +| `volume-keybag` | `0x72656373` (`'recs'`) | Volume keybag | +| `media-keybag` | `0x6d6b6579` (`'mkey'`) | Media keybag | + +### Context-specific parameters + +The following sections list search parameters that are only valid in certain contexts. + +#### B-tree nodes (`--type btree`) + +```{todo} Describe this +``` + +#### B-tree root nodes (`--type btree-root`) + +```{todo} Describe this +``` + +#### Omap tree nodes (`--type omap-tree`) + +The following parameters can be used with `--type omap-tree` in general, but +it's usually only useful to use them with `--type omap-tree-leaf`, since you're +probably intersted in finding particular object mappings (pointers to objects), +not pointers to other omap tree nodes: + +- `--omap-key-oid` — A Virtual OID that makes up an (OID, XID) key-pair. + +- `--omap-key-xid` — An XID that makes up an (OID, XID) key-pair. + +##### Omap tree leaves (`--type omap-tree-leaf`) + +The following parameters can only be used with `--type omap-tree-leaf`: + +- `--omap-val-paddr` — The block address that some (OID, XID) key-pair maps to. + +#### Filesystem trees (`--type fs-tree`) + +The following parameters can be used with `--type fs-tree` in general, but it's +usually only useful to use them with `--type fs-tree-leaf`, since you're +probably interested in finding particular filesystem records, not the +Virtual OIDs of other filesystem tree nodes: + +- `--fsoid` — Filesystem object ID (a.k.a inode number). The ID of a filesystem + object whose records you wish to search for. + +- `--fsrt` — Filesystem record type. The type of filesystem records that you + wish to search for. See *Filesystem record types* below for a list of valid + keyword values. + +##### Filesystem record types + +| Keyword | Equivalent value | Description | +| :-- | :-- | :-- | +| `any` | `0`, `0x0` | Any type | +| `snap-meta` | `1`, `0x1` | Snapshot metadata | +| `extent` | `2`, `0x2` | Physical extent record | +| `inode` | `3`, `0x3` | Inode | +| `xattr` | `4`, `0x4` | Extended attribute | +| `sibling-link` | `5`, `0x5` | Sibling link; a mapping from target inode to source hard links which point to that target | +| `dstream` | `6`, `0x6` | Data stream | +| `crypto` | `7`, `0x7` | Per-file encryption state | +| `file-extent` | `8`, `0x8` | File extent record | +| `dentry` | `9`, `0x9` | Dentry (directory entry) | +| `dir-stats` | `10`, `0xa` | Directory information/statistics | +| `snap-name` | `11`, `0xb` | Snapshot name | +| `sibling-map` | `12`, `0xc` | Sibling map; a mapping from hard link to target inode | +| `file-info` | `13`, `0xd` | Additional info about file data | +| `invalid` | `15`, `0xf` | The "invalid" record type | + +##### Filesystem tree leaves (`--type fs-tree-leaf`) + +This section describes parameters specific to each record type. They can only be +used in conjunction with `--type fs-tree-leaf`. + +###### Dentries (`--fsrt dentry`) + +The following search parameters can only be used in conjunction with +`--fsrt dentry`, since they are specific to dentries (directory entry records): + +- `--dentry-name` — The name of the item. Be careful to escape any commas in the + name with backslash `\`, else they will be interpreted as logical OR + operators. For example, compare `--dentry-name 'Martian, The'` and + `--dentry-name 'Martian\, The'`: the latter will search for items named + `Martian, The`, whereas the former will search for items named `Martian` or + ` The` (note also the leading space in ` The`, since leading and trailing + whitespace is not ignored). + +- `--dentry-fsoid` — The FSOID (a.k.a. inode number, file ID) of the item. diff --git a/docs/commands/version.md b/docs/commands/version.md index 8e21a2a..362ac67 100644 --- a/docs/commands/version.md +++ b/docs/commands/version.md @@ -1,24 +1,24 @@ -(command_version)= - -# {drat-command}`version` - -## Description - -The {drat-command}`version` command displays Drat's version number along with -legal info (copyright notice, warranty notice, and license details). - -## Example usage and output - -``` -$ drat version - -Drat version 0.2.0 -Copyright (C) 2019-2021 Jivan Pal - - -This program comes with ABSOLUTELY NO WARRANTY; not even the implied warranty -of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This is free software: -you can modify and redistribute it under the terms of the GNU General Public -License, version 3 only, as published by the Free Software Foundation, which -can be found at . -``` +(command_version)= + +# {drat-command}`version` + +## Description + +The {drat-command}`version` command displays Drat's version number along with +legal info (copyright notice, warranty notice, and license details). + +## Example usage and output + +``` +$ drat version + +Drat version 0.2.0 +Copyright (C) 2019-2021 Jivan Pal + + +This program comes with ABSOLUTELY NO WARRANTY; not even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This is free software: +you can modify and redistribute it under the terms of the GNU General Public +License, version 3 only, as published by the Free Software Foundation, which +can be found at . +``` diff --git a/docs/conf.py b/docs/conf.py index e857778..f5b3c27 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,59 +1,59 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -import os -import sys -sys.path.insert(0, os.path.abspath('./_ext')) - - -# -- Project information ----------------------------------------------------- - -project = 'Drat' -copyright = '2021, Jivan Pal' -author = 'Jivan Pal' - -# The full version, including alpha/beta/rc tags -release = '0.2.0' - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'myst_parser', - 'sphinx.ext.todo', - 'drat', -] - -todo_include_todos=True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +import os +import sys +sys.path.insert(0, os.path.abspath('./_ext')) + + +# -- Project information ----------------------------------------------------- + +project = 'Drat' +copyright = '2021, Jivan Pal' +author = 'Jivan Pal' + +# The full version, including alpha/beta/rc tags +release = '0.2.0' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'myst_parser', + 'sphinx.ext.todo', + 'drat', +] + +todo_include_todos=True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/docs/global-arguments/block-size.md b/docs/global-arguments/block-size.md index 703f083..22e6753 100644 --- a/docs/global-arguments/block-size.md +++ b/docs/global-arguments/block-size.md @@ -1,8 +1,8 @@ -(argument_block-size)= - -# {argument}`block-size` - -The {argument}`block-size` argument allows you to manually specify the block -size used by the APFS container in contexts where Drat cannot determine it -automatically. The block size is specified in bytes, and the default value is -`4096`. +(argument_block-size)= + +# {argument}`block-size` + +The {argument}`block-size` argument allows you to manually specify the block +size used by the APFS container in contexts where Drat cannot determine it +automatically. The block size is specified in bytes, and the default value is +`4096`. diff --git a/docs/global-arguments/container.md b/docs/global-arguments/container.md index 8673175..bb32788 100644 --- a/docs/global-arguments/container.md +++ b/docs/global-arguments/container.md @@ -1,26 +1,26 @@ -(argument_container)= - -# {argument}`container` - -## Description - -The {argument}`container` argument is used to specify the file (whether a -regular file or not) that represents the APFS partition/container to work on. -For example, this can be: - -- a device special file that represents: - - - an APFS partition (e.g. `/dev/disk2s3`, `/dev/sdb3`); or - - - a synthesized disk that represents a mounted APFS container - (e.g. `/dev/disk3`, `/dev/sdc`); or - -- a disk image, such as: - - - a copy of an APFS partition made with the likes of {command}`dd`; - - - an APFS filesystem image created with the likes of {command}`newfs_apfs`, - {command}`hdiutil` or via {app}`Disk Utility`. - -This argument is mandatory for all commands, else Drat will not know what APFS -container you want it to work on. +(argument_container)= + +# {argument}`container` + +## Description + +The {argument}`container` argument is used to specify the file (whether a +regular file or not) that represents the APFS partition/container to work on. +For example, this can be: + +- a device special file that represents: + + - an APFS partition (e.g. `/dev/disk2s3`, `/dev/sdb3`); or + + - a synthesized disk that represents a mounted APFS container + (e.g. `/dev/disk3`, `/dev/sdc`); or + +- a disk image, such as: + + - a copy of an APFS partition made with the likes of {command}`dd`; + + - an APFS filesystem image created with the likes of {command}`newfs_apfs`, + {command}`hdiutil` or via {app}`Disk Utility`. + +This argument is mandatory for all commands, else Drat will not know what APFS +container you want it to work on. diff --git a/docs/global-arguments/index.md b/docs/global-arguments/index.md index a5a668b..df04f51 100644 --- a/docs/global-arguments/index.md +++ b/docs/global-arguments/index.md @@ -1,22 +1,22 @@ -# Global arguments - -Many of the arguments passed to Drat are accepted by several of its commands. -Such arguments are called **global arguments**. A table of all global arguments -follows. Click the name of an argument to go to the corresponding section, -which provides a detailed description of the argument and its functionality: - -| Argument | Summary | -| :-- | :-- | -| {ref}`argument_container` | The path to the APFS partition/container to work with | -| {ref}`argument_block-size` | The block size of the APFS container | -| {ref}`argument_volume` | The volume to work with | -| {ref}`argument_max-xid` | The maximum transaction ID to consider | - -```{toctree} -:hidden: - -container -block-size -volume -max-xid -```` +# Global arguments + +Many of the arguments passed to Drat are accepted by several of its commands. +Such arguments are called **global arguments**. A table of all global arguments +follows. Click the name of an argument to go to the corresponding section, +which provides a detailed description of the argument and its functionality: + +| Argument | Summary | +| :-- | :-- | +| {ref}`argument_container` | The path to the APFS partition/container to work with | +| {ref}`argument_block-size` | The block size of the APFS container | +| {ref}`argument_volume` | The volume to work with | +| {ref}`argument_max-xid` | The maximum transaction ID to consider | + +```{toctree} +:hidden: + +container +block-size +volume +max-xid +```` diff --git a/docs/global-arguments/max-xid.md b/docs/global-arguments/max-xid.md index 725dbf8..ddb31c2 100644 --- a/docs/global-arguments/max-xid.md +++ b/docs/global-arguments/max-xid.md @@ -1,14 +1,14 @@ -(argument_max-xid)= - -# {argument}`max-xid` - -## Description - -The {argument}`max-xid` argument allows you to specify the maximum transaction -ID that should be considered when working with the APFS container. That is, any -objects with a transaction ID that exceeds the one specified will be ignored, -as if you are at the point in the time when the transaction with the specified -ID occurred, and thus have no knowledge of any future transactions. A value of -`-1` means "no maximum transaction ID", and is functionally equivalent to -a value of `0xffffffffffffffff`, which is the highest possible transaction ID, -since transaction IDs are 64-bit numbers. +(argument_max-xid)= + +# {argument}`max-xid` + +## Description + +The {argument}`max-xid` argument allows you to specify the maximum transaction +ID that should be considered when working with the APFS container. That is, any +objects with a transaction ID that exceeds the one specified will be ignored, +as if you are at the point in the time when the transaction with the specified +ID occurred, and thus have no knowledge of any future transactions. A value of +`-1` means "no maximum transaction ID", and is functionally equivalent to +a value of `0xffffffffffffffff`, which is the highest possible transaction ID, +since transaction IDs are 64-bit numbers. diff --git a/docs/global-arguments/volume.md b/docs/global-arguments/volume.md index 50bafa6..913a777 100644 --- a/docs/global-arguments/volume.md +++ b/docs/global-arguments/volume.md @@ -1,26 +1,26 @@ -(argument_volume)= - -# {argument}`volume` / {argument}`volume-name` - -## Description - -The {argument}`volume` argument allows you to specify the volume you want to -work with by its index within the container specified by {argument}`container`. -Volume indexing starts at 1, so `1` is the first volume, `2` is the second -volume, etc., and an index of `0` refers to the container itself, as opposed to -an actual volume. - -Alternatively, you can specify a volume name via the {argument}`volume-name` -argument. The specified volume name must match the name of precisely one of the -volumes within the container, else the name cannot be resolved to a specific -volume and an error will be reported, along with a list of the volumes and -their indexes, if possible. [Unicode canonical equivalence](https://unicode.org/reports/tr15/#Canon_Compat_Equivalence) -is used to compare strings for equality. - -## Example usage - -- `--volume 0` -- `--volume 1` -- `--volume 5` -- `--volume-name Preboot` -- `--volume-name 'macOS - Data'` +(argument_volume)= + +# {argument}`volume` / {argument}`volume-name` + +## Description + +The {argument}`volume` argument allows you to specify the volume you want to +work with by its index within the container specified by {argument}`container`. +Volume indexing starts at 1, so `1` is the first volume, `2` is the second +volume, etc., and an index of `0` refers to the container itself, as opposed to +an actual volume. + +Alternatively, you can specify a volume name via the {argument}`volume-name` +argument. The specified volume name must match the name of precisely one of the +volumes within the container, else the name cannot be resolved to a specific +volume and an error will be reported, along with a list of the volumes and +their indexes, if possible. [Unicode canonical equivalence](https://unicode.org/reports/tr15/#Canon_Compat_Equivalence) +is used to compare strings for equality. + +## Example usage + +- `--volume 0` +- `--volume 1` +- `--volume 5` +- `--volume-name Preboot` +- `--volume-name 'macOS - Data'` diff --git a/docs/index.md b/docs/index.md index fc62cac..09c2f5c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ -% Drat documentation master file, created by -% sphinx-quickstart on Fri Mar 5 14:40:50 2021. -% You can adapt this file completely to your liking, but it should at least -% contain the root `toctree` directive. - -# Drat documentation - -This is the documentation for [Drat](https://github.com/jivanpal/drat), a tool -for analysing and recovering data from [APFS (Apple File System)](https://en.wikipedia.org/wiki/Apple_File_System) -partitions. - -This documentation covers version 0.2.x of the software. - -```{toctree} -global-arguments/index -commands/index -``` +% Drat documentation master file, created by +% sphinx-quickstart on Fri Mar 5 14:40:50 2021. +% You can adapt this file completely to your liking, but it should at least +% contain the root `toctree` directive. + +# Drat documentation + +This is the documentation for [Drat](https://github.com/jivanpal/drat), a tool +for analysing and recovering data from [APFS (Apple File System)](https://en.wikipedia.org/wiki/Apple_File_System) +partitions. + +This documentation covers version 0.2.x of the software. + +```{toctree} +global-arguments/index +commands/index +``` diff --git a/docs/requirements.txt b/docs/requirements.txt index dd5e14f..a34d89d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ -sphinx==3.5.1 -sphinx_rtd_theme==0.5.1 -myst-parser==0.13.5 +sphinx==3.5.1 +sphinx_rtd_theme==0.5.1 +myst-parser==0.13.5 diff --git a/include/apfs/README.md b/include/apfs/README.md index f0d16c4..1011af2 100644 --- a/include/apfs/README.md +++ b/include/apfs/README.md @@ -1,4 +1,4 @@ -# `include/apfs` - -This directory contains data structure and flag/constant definitions as given in -the APFS specification. +# `include/apfs` + +This directory contains data structure and flag/constant definitions as given in +the APFS specification. diff --git a/include/apfs/btree.h b/include/apfs/btree.h index c784826..33825f7 100644 --- a/include/apfs/btree.h +++ b/include/apfs/btree.h @@ -1,108 +1,108 @@ -#ifndef APFS_BTREE_H -#define APFS_BTREE_H - -/** - * Structures and related items as defined in - * §14 B-Trees - */ - -#include -#include // obj_phys_t - -/** `nloc_t` --- forward declared for `btree_node_phys_t` **/ - -typedef struct { - uint16_t off; - uint16_t len; -} nloc_t; - -/** `btree_node_phys_t` **/ - -typedef struct { - obj_phys_t btn_o; - uint16_t btn_flags; - uint16_t btn_level; - uint32_t btn_nkeys; - nloc_t btn_table_space; - nloc_t btn_free_space; - nloc_t btn_key_free_list; - nloc_t btn_val_free_list; - uint64_t btn_data[]; -} btree_node_phys_t; - -/** `btree_info_fixed_t` **/ - -typedef struct { - uint32_t bt_flags; - uint32_t bt_node_size; - uint32_t bt_key_size; - uint32_t bt_val_size; -} btree_info_fixed_t; - -/** `btree_info_t` **/ - -typedef struct { - btree_info_fixed_t bt_fixed; - uint32_t bt_longest_key; - uint32_t bt_longest_val; - uint64_t bt_key_count; - uint64_t bt_node_count; -} btree_info_t; - -/** `btn_index_node_val_t` **/ - -#define BTREE_NODE_HASH_SIZE_MAX 64 - -typedef struct { - oid_t binv_child_oid; - uint8_t binv_child_hash[BTREE_NODE_HASH_SIZE_MAX]; -} btn_index_node_val_t; - -/** `kvloc_t` **/ - -typedef struct { - nloc_t k; - nloc_t v; -} kvloc_t; - -/** `kvoff_t` **/ - -typedef struct { - uint16_t k; - uint16_t v; -} kvoff_t; - -/** B-Tree Flags **/ - -#define BTREE_UINT64_KEYS 0x00000001 -#define BTREE_SEQUENTIAL_INSERT 0x00000002 -#define BTREE_ALLOW_GHOSTS 0x00000004 -#define BTREE_EPHEMERAL 0x00000008 -#define BTREE_PHYSICAL 0x00000010 -#define BTREE_NONPERSISTENT 0x00000020 -#define BTREE_KV_NONALIGNED 0x00000040 -#define BTREE_HASHED 0x00000080 -#define BTREE_NOHEADER 0x00000100 - -/** B-Tree Table of Contents Constants **/ - -#define BTREE_TOC_ENTRY_INCREMENT 8 -#define BTREE_TOC_ENTRY_MAX_UNUSED (2 * BTREE_TOC_ENTRY_INCREMENT) - -/** B-Tree Node Flags **/ - -#define BTNODE_ROOT 0x0001 -#define BTNODE_LEAF 0x0002 - -#define BTNODE_FIXED_KV_SIZE 0x0004 -#define BTNODE_HASHED 0x0008 -#define BTNODE_NOHEADER 0x0010 - -#define BTNODE_CHECK_KOFF_INVAL 0x8000 - -/** B-Tree Node Constants **/ - -#define BTREE_NODE_SIZE_DEFAULT 4096 // = 4 Ki -#define BTREE_NODE_MIN_ENTRY_COUNT 4 - -#endif // APFS_BTREE_H +#ifndef APFS_BTREE_H +#define APFS_BTREE_H + +/** + * Structures and related items as defined in + * §14 B-Trees + */ + +#include +#include // obj_phys_t + +/** `nloc_t` --- forward declared for `btree_node_phys_t` **/ + +typedef struct { + uint16_t off; + uint16_t len; +} nloc_t; + +/** `btree_node_phys_t` **/ + +typedef struct { + obj_phys_t btn_o; + uint16_t btn_flags; + uint16_t btn_level; + uint32_t btn_nkeys; + nloc_t btn_table_space; + nloc_t btn_free_space; + nloc_t btn_key_free_list; + nloc_t btn_val_free_list; + uint64_t btn_data[]; +} btree_node_phys_t; + +/** `btree_info_fixed_t` **/ + +typedef struct { + uint32_t bt_flags; + uint32_t bt_node_size; + uint32_t bt_key_size; + uint32_t bt_val_size; +} btree_info_fixed_t; + +/** `btree_info_t` **/ + +typedef struct { + btree_info_fixed_t bt_fixed; + uint32_t bt_longest_key; + uint32_t bt_longest_val; + uint64_t bt_key_count; + uint64_t bt_node_count; +} btree_info_t; + +/** `btn_index_node_val_t` **/ + +#define BTREE_NODE_HASH_SIZE_MAX 64 + +typedef struct { + oid_t binv_child_oid; + uint8_t binv_child_hash[BTREE_NODE_HASH_SIZE_MAX]; +} btn_index_node_val_t; + +/** `kvloc_t` **/ + +typedef struct { + nloc_t k; + nloc_t v; +} kvloc_t; + +/** `kvoff_t` **/ + +typedef struct { + uint16_t k; + uint16_t v; +} kvoff_t; + +/** B-Tree Flags **/ + +#define BTREE_UINT64_KEYS 0x00000001 +#define BTREE_SEQUENTIAL_INSERT 0x00000002 +#define BTREE_ALLOW_GHOSTS 0x00000004 +#define BTREE_EPHEMERAL 0x00000008 +#define BTREE_PHYSICAL 0x00000010 +#define BTREE_NONPERSISTENT 0x00000020 +#define BTREE_KV_NONALIGNED 0x00000040 +#define BTREE_HASHED 0x00000080 +#define BTREE_NOHEADER 0x00000100 + +/** B-Tree Table of Contents Constants **/ + +#define BTREE_TOC_ENTRY_INCREMENT 8 +#define BTREE_TOC_ENTRY_MAX_UNUSED (2 * BTREE_TOC_ENTRY_INCREMENT) + +/** B-Tree Node Flags **/ + +#define BTNODE_ROOT 0x0001 +#define BTNODE_LEAF 0x0002 + +#define BTNODE_FIXED_KV_SIZE 0x0004 +#define BTNODE_HASHED 0x0008 +#define BTNODE_NOHEADER 0x0010 + +#define BTNODE_CHECK_KOFF_INVAL 0x8000 + +/** B-Tree Node Constants **/ + +#define BTREE_NODE_SIZE_DEFAULT 4096 // = 4 Ki +#define BTREE_NODE_MIN_ENTRY_COUNT 4 + +#endif // APFS_BTREE_H diff --git a/include/apfs/crypto.h b/include/apfs/crypto.h index b9d0cda..825a75c 100644 --- a/include/apfs/crypto.h +++ b/include/apfs/crypto.h @@ -1,121 +1,121 @@ -#ifndef APFS_CRYPTO_H -#define APFS_CRYPTO_H - -/** - * Structures and related items as defined in - * §15 Encryption - */ - -#include -#include -#include // uuid_t -#include // j_key_t -#include // obj_phys_t - -/** `j_crypto_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_crypto_key_t; - -/** `wrapped_crypto_state_t` --- forward declared for `j_crypto_val_t` **/ - -typedef struct { - uint16_t major_version; - uint16_t minor_version; - crypto_flags_t cpflags; - cp_key_class_t persistent_class; - cp_key_os_version_t key_os_version; - cp_key_revision_t key_revision; - uint16_t key_len; - uint8_t persistent_key[0]; -} __attribute__((aligned(2), packed)) wrapped_crypto_state_t; - -#define CP_MAX_WRAPPEDKEYSIZE 128 - -/** `j_crypto_val_t` **/ - -typedef struct { - uint32_t refcnt; - wrapped_crypto_state_t state; -} __attribute__((aligned(4), packed)) j_crypto_val_t; - -/** `wrapped_meta_crypto_state_t` **/ - -typedef struct { - uint16_t major_version; - uint16_t minor_version; - crypto_flags_t cpflags; - cp_key_class_t persistent_class; - cp_key_os_version_t key_os_version; - cp_key_revision_t key_revision; -} __attribute__((aligned(2), packed)) wrapped_meta_crypto_state_t; - -/** Protection Classes **/ - -#define PROTECTION_CLASS_DIR_NONE 0 -#define PROTECTION_CLASS_A 1 -#define PROTECTION_CLASS_B 2 -#define PROTECTION_CLASS_C 3 -#define PROTECTION_CLASS_D 4 -#define PROTECTION_CLASS_F 6 -#define PROTECTION_CLASS_M 14 - -#define CP_EFFECTIVE_CLASSMASK 0x0000001f - -/** Encryption Identifiers **/ - -#define CRYPTO_SW_ID 4 -#define CRYPTO_RESERVED_5 5 - -#define APFS_UNASSIGNED_CRYPTO_ID (~0ULL) - -/** `keybag_entry_t` --- forward declared for `kb_locker_t` **/ - -typedef struct { - uuid_t ke_uuid; - uint16_t ke_tag; - uint16_t ke_keylen; - uint8_t padding[4]; - uint8_t ke_keydata[]; -} keybag_entry_t; - -#define APFS_VOL_KEYBAG_ENTRY_MAX_SIZE 512 -#define APFS_FV_PERSONAL_RECOVERY_KEY_UUID "EBC6C064-0000-11AA-AA11-00306543ECAC" - -/** `kb_locker_t` **/ - -typedef struct { - uint16_t kl_version; - uint16_t kl_nkeys; - uint32_t kl_nbytes; - uint8_t padding[8]; - keybag_entry_t kl_entries[]; -} kb_locker_t; - -#define APFS_KEYBAG_VERSION 2 - -/** `media_keybag_t` **/ - -typedef struct { - obj_phys_t mk_obj; - kb_locker_t mk_locker; -} media_keybag_t; - -/** Keybag Tags **/ - -enum { - KB_TAG_UNKNOWN = 0, - KB_TAG_RESERVED_1 = 1, - - KB_TAG_VOLUME_KEY = 2, - KB_TAG_VOLUME_UNLOCK_RECORDS = 3, - KB_TAG_VOLUME_PASSPHRASE_HINT = 4, - - KB_TAG_WRAPPING_M_KEY = 5, - KB_TAG_VOLUME_M_KEY = 6, - - KB_TAG_RESERVED_F8 = 0xf8, -}; - -#endif // APFS_CRYPTO_H +#ifndef APFS_CRYPTO_H +#define APFS_CRYPTO_H + +/** + * Structures and related items as defined in + * §15 Encryption + */ + +#include +#include +#include // uuid_t +#include // j_key_t +#include // obj_phys_t + +/** `j_crypto_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_crypto_key_t; + +/** `wrapped_crypto_state_t` --- forward declared for `j_crypto_val_t` **/ + +typedef struct { + uint16_t major_version; + uint16_t minor_version; + crypto_flags_t cpflags; + cp_key_class_t persistent_class; + cp_key_os_version_t key_os_version; + cp_key_revision_t key_revision; + uint16_t key_len; + uint8_t persistent_key[0]; +} __attribute__((aligned(2), packed)) wrapped_crypto_state_t; + +#define CP_MAX_WRAPPEDKEYSIZE 128 + +/** `j_crypto_val_t` **/ + +typedef struct { + uint32_t refcnt; + wrapped_crypto_state_t state; +} __attribute__((aligned(4), packed)) j_crypto_val_t; + +/** `wrapped_meta_crypto_state_t` **/ + +typedef struct { + uint16_t major_version; + uint16_t minor_version; + crypto_flags_t cpflags; + cp_key_class_t persistent_class; + cp_key_os_version_t key_os_version; + cp_key_revision_t key_revision; +} __attribute__((aligned(2), packed)) wrapped_meta_crypto_state_t; + +/** Protection Classes **/ + +#define PROTECTION_CLASS_DIR_NONE 0 +#define PROTECTION_CLASS_A 1 +#define PROTECTION_CLASS_B 2 +#define PROTECTION_CLASS_C 3 +#define PROTECTION_CLASS_D 4 +#define PROTECTION_CLASS_F 6 +#define PROTECTION_CLASS_M 14 + +#define CP_EFFECTIVE_CLASSMASK 0x0000001f + +/** Encryption Identifiers **/ + +#define CRYPTO_SW_ID 4 +#define CRYPTO_RESERVED_5 5 + +#define APFS_UNASSIGNED_CRYPTO_ID (~0ULL) + +/** `keybag_entry_t` --- forward declared for `kb_locker_t` **/ + +typedef struct { + uuid_t ke_uuid; + uint16_t ke_tag; + uint16_t ke_keylen; + uint8_t padding[4]; + uint8_t ke_keydata[]; +} keybag_entry_t; + +#define APFS_VOL_KEYBAG_ENTRY_MAX_SIZE 512 +#define APFS_FV_PERSONAL_RECOVERY_KEY_UUID "EBC6C064-0000-11AA-AA11-00306543ECAC" + +/** `kb_locker_t` **/ + +typedef struct { + uint16_t kl_version; + uint16_t kl_nkeys; + uint32_t kl_nbytes; + uint8_t padding[8]; + keybag_entry_t kl_entries[]; +} kb_locker_t; + +#define APFS_KEYBAG_VERSION 2 + +/** `media_keybag_t` **/ + +typedef struct { + obj_phys_t mk_obj; + kb_locker_t mk_locker; +} media_keybag_t; + +/** Keybag Tags **/ + +enum { + KB_TAG_UNKNOWN = 0, + KB_TAG_RESERVED_1 = 1, + + KB_TAG_VOLUME_KEY = 2, + KB_TAG_VOLUME_UNLOCK_RECORDS = 3, + KB_TAG_VOLUME_PASSPHRASE_HINT = 4, + + KB_TAG_WRAPPING_M_KEY = 5, + KB_TAG_VOLUME_M_KEY = 6, + + KB_TAG_RESERVED_F8 = 0xf8, +}; + +#endif // APFS_CRYPTO_H diff --git a/include/apfs/cryptorolling.h b/include/apfs/cryptorolling.h index 11e5be1..6876ce9 100644 --- a/include/apfs/cryptorolling.h +++ b/include/apfs/cryptorolling.h @@ -1,125 +1,125 @@ -#ifndef APFS_CRYPTOROLLING_H -#define APFS_CRYPTOROLLING_H - -/** - * Structures and related items as defined in - * §18 Encryption Rolling - */ - -#include -#include // obj_phys_t, oid_t, xid_t - -/** `er_state_phys_t` **/ - -// Forward declared for `er_state_phys[_v1]_t` -typedef struct { - obj_phys_t ersb_o; - uint32_t ersb_magic; - uint32_t ersb_version; -} er_state_phys_header_t; - -typedef struct { - er_state_phys_header_t ersb_header; - uint64_t ersb_flags; - uint64_t ersb_snap_xid; - uint64_t ersb_current_fext_obj_id; - uint64_t ersb_file_offset; - uint64_t ersb_progress; - uint64_t ersb_total_blk_to_encrypt; - oid_t ersb_blockmap_oid; - uint64_t ersb_tidemark_obj_id; - uint64_t ersb_recovery_extents_count; - oid_t ersb_recovery_list_oid; - uint64_t ersb_recovery_length; -} er_state_phys_t; - -typedef struct { - er_state_phys_header_t ersb_header; - uint64_t ersb_flags; - uint64_t ersb_snap_xid; - uint64_t ersb_current_fext_obj_id; - uint64_t ersb_file_offset; - uint64_t ersb_fext_pbn; - uint64_t ersb_paddr; - uint64_t ersb_progress; - uint64_t ersb_total_blk_to_encrypt; - uint64_t ersb_blockmap_oid; - uint64_t ersb_checksum_count; - uint64_t ersb_reserved; - uint64_t ersb_fext_cid; - uint8_t ersb_checksum[0]; -} er_state_phys_v1_t; - -/** `er_phase_t` **/ - -typedef enum { - ER_PHASE_OMAP_ROLL = 1, - ER_PHASE_DATA_ROLL = 2, - ER_PHASE_SNAP_ROLL = 3, -} er_phase_t; - -/** `er_recovery_block_phys_t` **/ - -typedef struct { - obj_phys_t erb_o; - uint64_t erb_offset; - oid_t erb_next_oid; - uint8_t erb_data[0]; -} er_recovery_block_phys_t; - -/** `gbitmap_block_phys_t` **/ - -typedef struct { - obj_phys_t bmb_o; - uint64_t bmb_field[0]; -} gbitmap_block_phys_t; - -/** `gbitmap_phys_t` **/ - -typedef struct { - obj_phys_t bm_o; - oid_t bm_tree_oid; - uint64_t bm_bit_count; - uint64_t bm_flags; -} gbitmap_phys_t; - -/** Encryption-Rolling Checksum Block Sizes **/ - -enum { - ER_512B_BLOCKSIZE = 0, - ER_2KiB_BLOCKSIZE = 1, - ER_4KiB_BLOCKSIZE = 2, - ER_8KiB_BLOCKSIZE = 3, - ER_16KiB_BLOCKSIZE = 4, - ER_32KiB_BLOCKSIZE = 5, - ER_64KiB_BLOCKSIZE = 6, -}; - -/** Encryption Rolling Flags **/ - -#define ERSB_FLAG_ENCRYPTING 0x00000001 -#define ERSB_FLAG_DECRYPTING 0x00000002 -#define ERSB_FLAG_KEYROLLING 0x00000004 -#define ERSB_FLAG_PAUSED 0x00000008 -#define ERSB_FLAG_FAILED 0x00000010 -#define ERSB_FLAG_CID_IS_TWEAK 0x00000020 -#define ERSB_FLAG_FREE_1 0x00000040 -#define ERSB_FLAG_FREE_2 0x00000080 - -#define ERSB_FLAG_CM_BLOCK_SIZE_MASK 0x00000F00 -#define ERSB_FLAG_CM_BLOCK_SIZE_SHIFT 8 - -#define ERSB_FLAG_ER_PHASE_MASK 0x00003000 -#define ERSB_FLAG_ER_PHASE_SHIFT 12 -#define ERSB_FLAG_FROM_ONEKEY 0x00004000 - -/** Encryption-Rolling Constants **/ - -#define ER_CHECKSUM_LENGTH 8 -#define ER_MAGIC 'FLAB' -#define ER_VERSION 1 - -#define ER_MAX_CHECKSUM_COUNT_SHIFT 16 -#define ER_CUR_CHECKSUM_COUNT_MASK 0x0000ffff - -#endif // APFS_CRYPTOROLLING_H +#ifndef APFS_CRYPTOROLLING_H +#define APFS_CRYPTOROLLING_H + +/** + * Structures and related items as defined in + * §18 Encryption Rolling + */ + +#include +#include // obj_phys_t, oid_t, xid_t + +/** `er_state_phys_t` **/ + +// Forward declared for `er_state_phys[_v1]_t` +typedef struct { + obj_phys_t ersb_o; + uint32_t ersb_magic; + uint32_t ersb_version; +} er_state_phys_header_t; + +typedef struct { + er_state_phys_header_t ersb_header; + uint64_t ersb_flags; + uint64_t ersb_snap_xid; + uint64_t ersb_current_fext_obj_id; + uint64_t ersb_file_offset; + uint64_t ersb_progress; + uint64_t ersb_total_blk_to_encrypt; + oid_t ersb_blockmap_oid; + uint64_t ersb_tidemark_obj_id; + uint64_t ersb_recovery_extents_count; + oid_t ersb_recovery_list_oid; + uint64_t ersb_recovery_length; +} er_state_phys_t; + +typedef struct { + er_state_phys_header_t ersb_header; + uint64_t ersb_flags; + uint64_t ersb_snap_xid; + uint64_t ersb_current_fext_obj_id; + uint64_t ersb_file_offset; + uint64_t ersb_fext_pbn; + uint64_t ersb_paddr; + uint64_t ersb_progress; + uint64_t ersb_total_blk_to_encrypt; + uint64_t ersb_blockmap_oid; + uint64_t ersb_checksum_count; + uint64_t ersb_reserved; + uint64_t ersb_fext_cid; + uint8_t ersb_checksum[0]; +} er_state_phys_v1_t; + +/** `er_phase_t` **/ + +typedef enum { + ER_PHASE_OMAP_ROLL = 1, + ER_PHASE_DATA_ROLL = 2, + ER_PHASE_SNAP_ROLL = 3, +} er_phase_t; + +/** `er_recovery_block_phys_t` **/ + +typedef struct { + obj_phys_t erb_o; + uint64_t erb_offset; + oid_t erb_next_oid; + uint8_t erb_data[0]; +} er_recovery_block_phys_t; + +/** `gbitmap_block_phys_t` **/ + +typedef struct { + obj_phys_t bmb_o; + uint64_t bmb_field[0]; +} gbitmap_block_phys_t; + +/** `gbitmap_phys_t` **/ + +typedef struct { + obj_phys_t bm_o; + oid_t bm_tree_oid; + uint64_t bm_bit_count; + uint64_t bm_flags; +} gbitmap_phys_t; + +/** Encryption-Rolling Checksum Block Sizes **/ + +enum { + ER_512B_BLOCKSIZE = 0, + ER_2KiB_BLOCKSIZE = 1, + ER_4KiB_BLOCKSIZE = 2, + ER_8KiB_BLOCKSIZE = 3, + ER_16KiB_BLOCKSIZE = 4, + ER_32KiB_BLOCKSIZE = 5, + ER_64KiB_BLOCKSIZE = 6, +}; + +/** Encryption Rolling Flags **/ + +#define ERSB_FLAG_ENCRYPTING 0x00000001 +#define ERSB_FLAG_DECRYPTING 0x00000002 +#define ERSB_FLAG_KEYROLLING 0x00000004 +#define ERSB_FLAG_PAUSED 0x00000008 +#define ERSB_FLAG_FAILED 0x00000010 +#define ERSB_FLAG_CID_IS_TWEAK 0x00000020 +#define ERSB_FLAG_FREE_1 0x00000040 +#define ERSB_FLAG_FREE_2 0x00000080 + +#define ERSB_FLAG_CM_BLOCK_SIZE_MASK 0x00000F00 +#define ERSB_FLAG_CM_BLOCK_SIZE_SHIFT 8 + +#define ERSB_FLAG_ER_PHASE_MASK 0x00003000 +#define ERSB_FLAG_ER_PHASE_SHIFT 12 +#define ERSB_FLAG_FROM_ONEKEY 0x00004000 + +/** Encryption-Rolling Constants **/ + +#define ER_CHECKSUM_LENGTH 8 +#define ER_MAGIC 'FLAB' +#define ER_VERSION 1 + +#define ER_MAX_CHECKSUM_COUNT_SHIFT 16 +#define ER_CUR_CHECKSUM_COUNT_MASK 0x0000ffff + +#endif // APFS_CRYPTOROLLING_H diff --git a/include/apfs/cryptotypes.h b/include/apfs/cryptotypes.h index 49e6456..3ec19b6 100644 --- a/include/apfs/cryptotypes.h +++ b/include/apfs/cryptotypes.h @@ -1,22 +1,22 @@ -#ifndef APFS_CRYPTOTYPES_H -#define APFS_CRYPTOTYPES_H - -/** - * Definitions of types used for encryption-related structures, as defined in - * §15.6 Encryption Types. - * - * These types are defined in their own header file rather than being included - * in `crypto.h`, else there would be a dependency cycle - * (`j.h` ==> `crypto.h` ==> `j.h`). - */ - -#include - -/** Encryption Types **/ - -typedef uint32_t cp_key_class_t; -typedef uint32_t cp_key_os_version_t; -typedef uint16_t cp_key_revision_t; -typedef uint32_t crypto_flags_t; - -#endif // APFS_CRYPTOTYPES_H +#ifndef APFS_CRYPTOTYPES_H +#define APFS_CRYPTOTYPES_H + +/** + * Definitions of types used for encryption-related structures, as defined in + * §15.6 Encryption Types. + * + * These types are defined in their own header file rather than being included + * in `crypto.h`, else there would be a dependency cycle + * (`j.h` ==> `crypto.h` ==> `j.h`). + */ + +#include + +/** Encryption Types **/ + +typedef uint32_t cp_key_class_t; +typedef uint32_t cp_key_os_version_t; +typedef uint16_t cp_key_revision_t; +typedef uint32_t crypto_flags_t; + +#endif // APFS_CRYPTOTYPES_H diff --git a/include/apfs/dstream.h b/include/apfs/dstream.h index f0c1425..3e3b038 100644 --- a/include/apfs/dstream.h +++ b/include/apfs/dstream.h @@ -1,78 +1,78 @@ -#ifndef APFS_DSTREAM_H -#define APFS_DSTREAM_H - -/** - * Structures and related items as defined in - * §10 Data Streams - */ - -#include -#include // j_key_t - -/** `j_phys_ext_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_phys_ext_key_t; - -/** `j_phys_ext_val_t` **/ - -typedef struct { - uint64_t len_and_kind; - uint64_t owning_obj_id; - int32_t refcnt; -} __attribute__((packed)) j_phys_ext_val_t; - -#define PEXT_LEN_MASK 0x0fffffffffffffffULL -#define PEXT_KIND_MASK 0xf000000000000000ULL -#define PEXT_KIND_SHIFT 60 - -/** `j_file_extent_key_t` **/ - -typedef struct { - j_key_t hdr; - uint64_t logical_addr; -} __attribute__((packed)) j_file_extent_key_t; - -/** `j_file_extent_val_t` **/ - -typedef struct { - uint64_t len_and_flags; - uint64_t phys_block_num; - uint64_t crypto_id; -} __attribute__((packed)) j_file_extent_val_t; - -#define J_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL -#define J_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL -#define J_FILE_EXTENT_FLAG_SHIFT 56 - -/** `j_dstream_id_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_dstream_id_key_t; - -/** `j_dstream_id_val_t` **/ - -typedef struct { - uint32_t refcnt; -} __attribute__((packed)) j_dstream_id_val_t; - -/** `j_dstream_t` --- forward declared for `j_xattr_dstream` **/ - -typedef struct { - uint64_t size; - uint64_t alloced_size; - uint64_t default_crypto_id; - uint64_t total_bytes_written; - uint64_t total_bytes_read; -} __attribute__((aligned(8),packed)) j_dstream_t; - -/** `j_xattr_dstream` **/ - -typedef struct { - uint64_t xattr_obj_id; - j_dstream_t dstream; -} j_xattr_dstream_t; - -#endif // APFS_DSTREAM_H +#ifndef APFS_DSTREAM_H +#define APFS_DSTREAM_H + +/** + * Structures and related items as defined in + * §10 Data Streams + */ + +#include +#include // j_key_t + +/** `j_phys_ext_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_phys_ext_key_t; + +/** `j_phys_ext_val_t` **/ + +typedef struct { + uint64_t len_and_kind; + uint64_t owning_obj_id; + int32_t refcnt; +} __attribute__((packed)) j_phys_ext_val_t; + +#define PEXT_LEN_MASK 0x0fffffffffffffffULL +#define PEXT_KIND_MASK 0xf000000000000000ULL +#define PEXT_KIND_SHIFT 60 + +/** `j_file_extent_key_t` **/ + +typedef struct { + j_key_t hdr; + uint64_t logical_addr; +} __attribute__((packed)) j_file_extent_key_t; + +/** `j_file_extent_val_t` **/ + +typedef struct { + uint64_t len_and_flags; + uint64_t phys_block_num; + uint64_t crypto_id; +} __attribute__((packed)) j_file_extent_val_t; + +#define J_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL +#define J_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL +#define J_FILE_EXTENT_FLAG_SHIFT 56 + +/** `j_dstream_id_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_dstream_id_key_t; + +/** `j_dstream_id_val_t` **/ + +typedef struct { + uint32_t refcnt; +} __attribute__((packed)) j_dstream_id_val_t; + +/** `j_dstream_t` --- forward declared for `j_xattr_dstream` **/ + +typedef struct { + uint64_t size; + uint64_t alloced_size; + uint64_t default_crypto_id; + uint64_t total_bytes_written; + uint64_t total_bytes_read; +} __attribute__((aligned(8),packed)) j_dstream_t; + +/** `j_xattr_dstream` **/ + +typedef struct { + uint64_t xattr_obj_id; + j_dstream_t dstream; +} j_xattr_dstream_t; + +#endif // APFS_DSTREAM_H diff --git a/include/apfs/fs.h b/include/apfs/fs.h index 3023c76..5dda533 100644 --- a/include/apfs/fs.h +++ b/include/apfs/fs.h @@ -1,205 +1,205 @@ -#ifndef APFS_FS_H -#define APFS_FS_H - -/** - * Structures and related items as defined in - * §7 Volumes - */ - -#include -#include // wrapped_meta_crypto_state_t -#include // uuid_t -#include // obj_phys_t, oid_t, xid_t - -/** `apfs_modified_by_t` --- forward declared for `apfs_superblock_t` **/ - -#define APFS_MODIFIED_NAMELEN 32 - -typedef struct { - uint8_t id[APFS_MODIFIED_NAMELEN]; - uint64_t timestamp; - xid_t last_xid; -} apfs_modified_by_t; - -/** `apfs_superblock_t` **/ - -#define APFS_MAGIC 'BSPA' -#define APFS_MAX_HIST 8 -#define APFS_VOLNAME_LEN 256 - -typedef struct { - obj_phys_t apfs_o; - - uint32_t apfs_magic; - uint32_t apfs_fs_index; - - uint64_t apfs_features; - uint64_t apfs_readonly_compatible_features; - uint64_t apfs_incompatible_features; - - uint64_t apfs_unmount_time; - - uint64_t apfs_fs_reserve_block_count; - uint64_t apfs_fs_quota_block_count; - uint64_t apfs_fs_alloc_count; - - wrapped_meta_crypto_state_t apfs_meta_crypto; - - uint32_t apfs_root_tree_type; - uint32_t apfs_extentref_tree_type; - uint32_t apfs_snap_meta_tree_type; - - oid_t apfs_omap_oid; - oid_t apfs_root_tree_oid; - oid_t apfs_extentref_tree_oid; - oid_t apfs_snap_meta_tree_oid; - - xid_t apfs_revert_to_xid; - oid_t apfs_revert_to_sblock_oid; - - uint64_t apfs_next_obj_id; - - uint64_t apfs_num_files; - uint64_t apfs_num_directories; - uint64_t apfs_num_symlinks; - uint64_t apfs_num_other_fsobjects; - uint64_t apfs_num_snapshots; - - uint64_t apfs_total_block_alloced; - uint64_t apfs_total_blocks_freed; - - uuid_t apfs_vol_uuid; - uint64_t apfs_last_mod_time; - - uint64_t apfs_fs_flags; - - apfs_modified_by_t apfs_formatted_by; - apfs_modified_by_t apfs_modified_by[APFS_MAX_HIST]; - - uint8_t apfs_volname[APFS_VOLNAME_LEN]; - uint32_t apfs_next_doc_id; - - uint16_t apfs_role; - uint16_t reserved; - - xid_t apfs_root_to_xid; - oid_t apfs_er_state_oid; - -// Fields introduced in revision 2020-05-15 - - // Fields supported on macOS 10.13.3+ - uint64_t apfs_cloneinfo_id_epoch; - uint64_t apfs_cloneinfo_xid; - - // Fields supported on macOS 10.15+ - oid_t apfs_snap_meta_ext_oid; - uuid_t apfs_volume_group_id; - -// Fields introduced in revision 2020-06-22 - - // Fields supported on macOS 11+ - oid_t apfs_integrity_meta_oid; - oid_t apfs_fext_tree_oid; - uint32_t apfs_fext_tree_type; - - uint32_t reserved_type; - oid_t reserved_oid; -} apfs_superblock_t; - -/** Volume Flags **/ - -#define APFS_FS_UNENCRYPTED 0x00000001LL -#define APFS_FS_RESERVED_2 0x00000002LL -#define APFS_FS_RESERVED_4 0x00000004LL -#define APFS_FS_ONEKEY 0x00000008LL -#define APFS_FS_SPILLEDOVER 0x00000010LL -#define APFS_FS_RUN_SPILLOVER_CLEANER 0x00000020LL -#define APFS_FS_ALWAYS_CHECK_EXTENTREF 0x00000040LL -#define APFS_FS_RESERVED_80 0x00000080LL -#define APFS_FS_RESERVED_100 0x00000100LL - -#define APFS_FS_FLAGS_VALID_MASK ( \ - APFS_FS_UNENCRYPTED \ - | APFS_FS_RESERVED_2 \ - | APFS_FS_RESERVED_4 \ - | APFS_FS_ONEKEY \ - | APFS_FS_SPILLEDOVER \ - | APFS_FS_RUN_SPILLOVER_CLEANER \ - | APFS_FS_ALWAYS_CHECK_EXTENTREF \ - | APFS_FS_RESERVED_80 \ - | APFS_FS_RESERVED_100 \ -) - -#define APFS_FS_CRYPTOFLAGS ( \ - APFS_FS_UNENCRYPTED \ - | APFS_FS_ONEKEY \ -) - -/** Volume Roles **/ - -#define APFS_VOL_ROLE_NONE 0x0000 - -#define APFS_VOL_ROLE_SYSTEM 0x0001 -#define APFS_VOL_ROLE_USER 0x0002 -#define APFS_VOL_ROLE_RECOVERY 0x0004 -#define APFS_VOL_ROLE_VM 0x0008 -#define APFS_VOL_ROLE_PREBOOT 0x0010 -#define APFS_VOL_ROLE_INSTALLER 0x0020 - -#define APFS_VOLUME_ENUM_SHIFT 6 - -#define APFS_VOL_ROLE_DATA ( 1 << APFS_VOLUME_ENUM_SHIFT) // = 0x0040 --- formerly defined explicitly as `0x0040` -#define APFS_VOL_ROLE_BASEBAND ( 2 << APFS_VOLUME_ENUM_SHIFT) // = 0x0080 --- formerly defined explicitly as `0x0080` - -// Roles supported since revision 2020-05-15 --- macOS 10.15+, iOS 13+ -#define APFS_VOL_ROLE_UPDATE ( 3 << APFS_VOLUME_ENUM_SHIFT) // = 0x00c0 -#define APFS_VOL_ROLE_XART ( 4 << APFS_VOLUME_ENUM_SHIFT) // = 0x0100 -#define APFS_VOL_ROLE_HARDWARE ( 5 << APFS_VOLUME_ENUM_SHIFT) // = 0x0140 -#define APFS_VOL_ROLE_BACKUP ( 6 << APFS_VOLUME_ENUM_SHIFT) // = 0x0180 -#define APFS_VOL_ROLE_RESERVED_7 ( 7 << APFS_VOLUME_ENUM_SHIFT) // = 0x01c0 --- Apple's spec also uses the name `APFS_VOL_ROLE_SIDECAR`, but that could be an error -#define APFS_VOL_ROLE_RESERVED_8 ( 8 << APFS_VOLUME_ENUM_SHIFT) // = 0x0200 --- formerly named `APFS_VOL_ROLE_RESERVED_200` -#define APFS_VOL_ROLE_ENTERPRISE ( 9 << APFS_VOLUME_ENUM_SHIFT) // = 0x0240 -#define APFS_VOL_ROLE_RESERVED_10 (10 << APFS_VOLUME_ENUM_SHIFT) // = 0x0280 -#define APFS_VOL_ROLE_PRELOGIN (11 << APFS_VOLUME_ENUM_SHIFT) // = 0x02c0 - -/** Optional Volume Feature Flags **/ - -#define APFS_FEATURE_DEFRAG_PRERELEASE 0x00000001LL -#define APFS_FEATURE_HARDLINK_MAP_RECORDS 0x00000002LL -#define APFS_FEATURE_DEFRAG 0x00000004LL -#define APFS_FEATURE_STRICTATIME 0x00000008LL -#define APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE 0x00000010LL - -#define APFS_SUPPORTED_FEATURES_MASK ( \ - APFS_FEATURE_DEFRAG \ - | APFS_FEATURE_DEFRAG_PRERELEASE \ - | APFS_FEATURE_HARDLINK_MAP_RECORDS \ - | APFS_FEATURE_STRICTATIME \ - | APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE \ -) - -/** Read-Only Comaptible Volume Feature Flags **/ - -#define APFS_SUPPORTED_ROCOMPAT_MASK 0x0ULL - -/** Incompatible Volume Feature Flags **/ - -#define APFS_INCOMPAT_CASE_INSENSITIVE 0x00000001LL -#define APFS_INCOMPAT_DATALESS_SNAPS 0x00000002LL -#define APFS_INCOMPAT_ENC_ROLLED 0x00000004LL -#define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE 0x00000008LL -#define APFS_INCOMPAT_INCOMPLETE_RESTORE 0x00000010LL -#define APFS_INCOMPAT_SEALED_VOLUME 0x00000020LL -#define APFS_INCOMPAT_RESERVED_40 0x00000040LL - -#define APFS_SUPPORTED_INCOMPAT_MASK ( \ - APFS_INCOMPAT_CASE_INSENSITIVE \ - | APFS_INCOMPAT_DATALESS_SNAPS \ - | APFS_INCOMPAT_ENC_ROLLED \ - | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \ - | APFS_INCOMPAT_INCOMPLETE_RESTORE \ - | APFS_INCOMPAT_SEALED_VOLUME \ - | APFS_INCOMPAT_RESERVED_40 \ -) - -#endif // APFS_FS_H +#ifndef APFS_FS_H +#define APFS_FS_H + +/** + * Structures and related items as defined in + * §7 Volumes + */ + +#include +#include // wrapped_meta_crypto_state_t +#include // uuid_t +#include // obj_phys_t, oid_t, xid_t + +/** `apfs_modified_by_t` --- forward declared for `apfs_superblock_t` **/ + +#define APFS_MODIFIED_NAMELEN 32 + +typedef struct { + uint8_t id[APFS_MODIFIED_NAMELEN]; + uint64_t timestamp; + xid_t last_xid; +} apfs_modified_by_t; + +/** `apfs_superblock_t` **/ + +#define APFS_MAGIC 'BSPA' +#define APFS_MAX_HIST 8 +#define APFS_VOLNAME_LEN 256 + +typedef struct { + obj_phys_t apfs_o; + + uint32_t apfs_magic; + uint32_t apfs_fs_index; + + uint64_t apfs_features; + uint64_t apfs_readonly_compatible_features; + uint64_t apfs_incompatible_features; + + uint64_t apfs_unmount_time; + + uint64_t apfs_fs_reserve_block_count; + uint64_t apfs_fs_quota_block_count; + uint64_t apfs_fs_alloc_count; + + wrapped_meta_crypto_state_t apfs_meta_crypto; + + uint32_t apfs_root_tree_type; + uint32_t apfs_extentref_tree_type; + uint32_t apfs_snap_meta_tree_type; + + oid_t apfs_omap_oid; + oid_t apfs_root_tree_oid; + oid_t apfs_extentref_tree_oid; + oid_t apfs_snap_meta_tree_oid; + + xid_t apfs_revert_to_xid; + oid_t apfs_revert_to_sblock_oid; + + uint64_t apfs_next_obj_id; + + uint64_t apfs_num_files; + uint64_t apfs_num_directories; + uint64_t apfs_num_symlinks; + uint64_t apfs_num_other_fsobjects; + uint64_t apfs_num_snapshots; + + uint64_t apfs_total_block_alloced; + uint64_t apfs_total_blocks_freed; + + uuid_t apfs_vol_uuid; + uint64_t apfs_last_mod_time; + + uint64_t apfs_fs_flags; + + apfs_modified_by_t apfs_formatted_by; + apfs_modified_by_t apfs_modified_by[APFS_MAX_HIST]; + + uint8_t apfs_volname[APFS_VOLNAME_LEN]; + uint32_t apfs_next_doc_id; + + uint16_t apfs_role; + uint16_t reserved; + + xid_t apfs_root_to_xid; + oid_t apfs_er_state_oid; + +// Fields introduced in revision 2020-05-15 + + // Fields supported on macOS 10.13.3+ + uint64_t apfs_cloneinfo_id_epoch; + uint64_t apfs_cloneinfo_xid; + + // Fields supported on macOS 10.15+ + oid_t apfs_snap_meta_ext_oid; + uuid_t apfs_volume_group_id; + +// Fields introduced in revision 2020-06-22 + + // Fields supported on macOS 11+ + oid_t apfs_integrity_meta_oid; + oid_t apfs_fext_tree_oid; + uint32_t apfs_fext_tree_type; + + uint32_t reserved_type; + oid_t reserved_oid; +} apfs_superblock_t; + +/** Volume Flags **/ + +#define APFS_FS_UNENCRYPTED 0x00000001LL +#define APFS_FS_RESERVED_2 0x00000002LL +#define APFS_FS_RESERVED_4 0x00000004LL +#define APFS_FS_ONEKEY 0x00000008LL +#define APFS_FS_SPILLEDOVER 0x00000010LL +#define APFS_FS_RUN_SPILLOVER_CLEANER 0x00000020LL +#define APFS_FS_ALWAYS_CHECK_EXTENTREF 0x00000040LL +#define APFS_FS_RESERVED_80 0x00000080LL +#define APFS_FS_RESERVED_100 0x00000100LL + +#define APFS_FS_FLAGS_VALID_MASK ( \ + APFS_FS_UNENCRYPTED \ + | APFS_FS_RESERVED_2 \ + | APFS_FS_RESERVED_4 \ + | APFS_FS_ONEKEY \ + | APFS_FS_SPILLEDOVER \ + | APFS_FS_RUN_SPILLOVER_CLEANER \ + | APFS_FS_ALWAYS_CHECK_EXTENTREF \ + | APFS_FS_RESERVED_80 \ + | APFS_FS_RESERVED_100 \ +) + +#define APFS_FS_CRYPTOFLAGS ( \ + APFS_FS_UNENCRYPTED \ + | APFS_FS_ONEKEY \ +) + +/** Volume Roles **/ + +#define APFS_VOL_ROLE_NONE 0x0000 + +#define APFS_VOL_ROLE_SYSTEM 0x0001 +#define APFS_VOL_ROLE_USER 0x0002 +#define APFS_VOL_ROLE_RECOVERY 0x0004 +#define APFS_VOL_ROLE_VM 0x0008 +#define APFS_VOL_ROLE_PREBOOT 0x0010 +#define APFS_VOL_ROLE_INSTALLER 0x0020 + +#define APFS_VOLUME_ENUM_SHIFT 6 + +#define APFS_VOL_ROLE_DATA ( 1 << APFS_VOLUME_ENUM_SHIFT) // = 0x0040 --- formerly defined explicitly as `0x0040` +#define APFS_VOL_ROLE_BASEBAND ( 2 << APFS_VOLUME_ENUM_SHIFT) // = 0x0080 --- formerly defined explicitly as `0x0080` + +// Roles supported since revision 2020-05-15 --- macOS 10.15+, iOS 13+ +#define APFS_VOL_ROLE_UPDATE ( 3 << APFS_VOLUME_ENUM_SHIFT) // = 0x00c0 +#define APFS_VOL_ROLE_XART ( 4 << APFS_VOLUME_ENUM_SHIFT) // = 0x0100 +#define APFS_VOL_ROLE_HARDWARE ( 5 << APFS_VOLUME_ENUM_SHIFT) // = 0x0140 +#define APFS_VOL_ROLE_BACKUP ( 6 << APFS_VOLUME_ENUM_SHIFT) // = 0x0180 +#define APFS_VOL_ROLE_RESERVED_7 ( 7 << APFS_VOLUME_ENUM_SHIFT) // = 0x01c0 --- Apple's spec also uses the name `APFS_VOL_ROLE_SIDECAR`, but that could be an error +#define APFS_VOL_ROLE_RESERVED_8 ( 8 << APFS_VOLUME_ENUM_SHIFT) // = 0x0200 --- formerly named `APFS_VOL_ROLE_RESERVED_200` +#define APFS_VOL_ROLE_ENTERPRISE ( 9 << APFS_VOLUME_ENUM_SHIFT) // = 0x0240 +#define APFS_VOL_ROLE_RESERVED_10 (10 << APFS_VOLUME_ENUM_SHIFT) // = 0x0280 +#define APFS_VOL_ROLE_PRELOGIN (11 << APFS_VOLUME_ENUM_SHIFT) // = 0x02c0 + +/** Optional Volume Feature Flags **/ + +#define APFS_FEATURE_DEFRAG_PRERELEASE 0x00000001LL +#define APFS_FEATURE_HARDLINK_MAP_RECORDS 0x00000002LL +#define APFS_FEATURE_DEFRAG 0x00000004LL +#define APFS_FEATURE_STRICTATIME 0x00000008LL +#define APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE 0x00000010LL + +#define APFS_SUPPORTED_FEATURES_MASK ( \ + APFS_FEATURE_DEFRAG \ + | APFS_FEATURE_DEFRAG_PRERELEASE \ + | APFS_FEATURE_HARDLINK_MAP_RECORDS \ + | APFS_FEATURE_STRICTATIME \ + | APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE \ +) + +/** Read-Only Comaptible Volume Feature Flags **/ + +#define APFS_SUPPORTED_ROCOMPAT_MASK 0x0ULL + +/** Incompatible Volume Feature Flags **/ + +#define APFS_INCOMPAT_CASE_INSENSITIVE 0x00000001LL +#define APFS_INCOMPAT_DATALESS_SNAPS 0x00000002LL +#define APFS_INCOMPAT_ENC_ROLLED 0x00000004LL +#define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE 0x00000008LL +#define APFS_INCOMPAT_INCOMPLETE_RESTORE 0x00000010LL +#define APFS_INCOMPAT_SEALED_VOLUME 0x00000020LL +#define APFS_INCOMPAT_RESERVED_40 0x00000040LL + +#define APFS_SUPPORTED_INCOMPAT_MASK ( \ + APFS_INCOMPAT_CASE_INSENSITIVE \ + | APFS_INCOMPAT_DATALESS_SNAPS \ + | APFS_INCOMPAT_ENC_ROLLED \ + | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \ + | APFS_INCOMPAT_INCOMPLETE_RESTORE \ + | APFS_INCOMPAT_SEALED_VOLUME \ + | APFS_INCOMPAT_RESERVED_40 \ +) + +#endif // APFS_FS_H diff --git a/include/apfs/fusion.h b/include/apfs/fusion.h index c7a095e..6044d9f 100644 --- a/include/apfs/fusion.h +++ b/include/apfs/fusion.h @@ -1,84 +1,84 @@ -#ifndef APFS_FUSION_H -#define APFS_FUSION_H - -/** - * Structures and related items as defined in - * §19 Fusion - */ - -#include -#include // paddr_t, prange_t -#include // obj_phys_t, oid_t, xid_t - -// NOTE: Apple's spec uses camel case for variable names, but in order to -// maintain consistency with the rest of the APFS structures defined in this -// repo, as well as abiding by de facto C conventions, we opt to continue using -// snake case (underscores separating words). - -/** `fusion_wbc_phys_t` **/ - -typedef struct { - obj_phys_t fwp_obj_hdr; - uint64_t fwp_version; - oid_t fwp_list_head_oid; - oid_t fwp_list_tail_oid; - uint64_t fwp_stable_head_offset; - uint64_t fwp_stable_tail_offset; - uint32_t fwp_list_blocks_count; - uint32_t fwp_reserved; - uint64_t fwp_used_by_rc; - prange_t fwp_rc_stash; -} fusion_wbc_phys_t; - -/** `fusion_wbc_list_entry_t` **/ - -typedef struct { - paddr_t fwle_wbc_lba; - paddr_t fwle_target_lba; - uint64_t fwle_length; -} fusion_wbc_list_entry_t; - -/** `fusion_wbc_list_phys_t` **/ - -typedef struct { - obj_phys_t fwlp_obj_hdr; - uint64_t fwlp_version; - uint64_t fwlp_tail_offset; - uint32_t fwlp_index_begin; - uint32_t fwlp_index_end; - uint32_t fwlp_index_max; - uint32_t fwlp_reserved; - fusion_wbc_list_entry_t fwlp_list_entries[]; -} fusion_wbc_list_phys_t; - -/** Address Markers **/ - -#define FUSION_TIER2_DEVICE_BYTE_ADDR 0x4000000000000000ULL - -#define FUSION_TIER2_DEVICE_BLOCK_ADDR(_blksize) \ - (FUSION_TIER2_DEVICE_BYTE_ADDR >> __builtin_ctzl(_blksize)) - -#define FUSION_BLKNO(_fusion_tier2, _blkno, _blksize) ( \ - (_fusion_tier2) \ - ? ( FUSION_TIER2_DEVICE_BLOCK_ADDR(_blksize) | (_blkno) ) \ - : (_blkno) \ -) - -/** `fusion_mt_key_t` **/ - -typedef paddr_t fusion_mt_key_t; - -/** `fusion_mt_val_t` **/ - -typedef struct { - paddr_t fmv_lba; - uint32_t fmv_length; - uint32_t fmv_flags; -} fusion_mt_val_t; - -/** Fusion Middle-Tree Flags **/ - -#define FUSION_MT_DIRTY (1 << 0) -#define FUSION_MT_TENANT (1 << 1) - -#endif // APFS_FUSION_H +#ifndef APFS_FUSION_H +#define APFS_FUSION_H + +/** + * Structures and related items as defined in + * §19 Fusion + */ + +#include +#include // paddr_t, prange_t +#include // obj_phys_t, oid_t, xid_t + +// NOTE: Apple's spec uses camel case for variable names, but in order to +// maintain consistency with the rest of the APFS structures defined in this +// repo, as well as abiding by de facto C conventions, we opt to continue using +// snake case (underscores separating words). + +/** `fusion_wbc_phys_t` **/ + +typedef struct { + obj_phys_t fwp_obj_hdr; + uint64_t fwp_version; + oid_t fwp_list_head_oid; + oid_t fwp_list_tail_oid; + uint64_t fwp_stable_head_offset; + uint64_t fwp_stable_tail_offset; + uint32_t fwp_list_blocks_count; + uint32_t fwp_reserved; + uint64_t fwp_used_by_rc; + prange_t fwp_rc_stash; +} fusion_wbc_phys_t; + +/** `fusion_wbc_list_entry_t` **/ + +typedef struct { + paddr_t fwle_wbc_lba; + paddr_t fwle_target_lba; + uint64_t fwle_length; +} fusion_wbc_list_entry_t; + +/** `fusion_wbc_list_phys_t` **/ + +typedef struct { + obj_phys_t fwlp_obj_hdr; + uint64_t fwlp_version; + uint64_t fwlp_tail_offset; + uint32_t fwlp_index_begin; + uint32_t fwlp_index_end; + uint32_t fwlp_index_max; + uint32_t fwlp_reserved; + fusion_wbc_list_entry_t fwlp_list_entries[]; +} fusion_wbc_list_phys_t; + +/** Address Markers **/ + +#define FUSION_TIER2_DEVICE_BYTE_ADDR 0x4000000000000000ULL + +#define FUSION_TIER2_DEVICE_BLOCK_ADDR(_blksize) \ + (FUSION_TIER2_DEVICE_BYTE_ADDR >> __builtin_ctzl(_blksize)) + +#define FUSION_BLKNO(_fusion_tier2, _blkno, _blksize) ( \ + (_fusion_tier2) \ + ? ( FUSION_TIER2_DEVICE_BLOCK_ADDR(_blksize) | (_blkno) ) \ + : (_blkno) \ +) + +/** `fusion_mt_key_t` **/ + +typedef paddr_t fusion_mt_key_t; + +/** `fusion_mt_val_t` **/ + +typedef struct { + paddr_t fmv_lba; + uint32_t fmv_length; + uint32_t fmv_flags; +} fusion_mt_val_t; + +/** Fusion Middle-Tree Flags **/ + +#define FUSION_MT_DIRTY (1 << 0) +#define FUSION_MT_TENANT (1 << 1) + +#endif // APFS_FUSION_H diff --git a/include/apfs/general.h b/include/apfs/general.h index 698219a..1f1138c 100644 --- a/include/apfs/general.h +++ b/include/apfs/general.h @@ -1,20 +1,20 @@ -#ifndef APFS_GENERAL_H -#define APFS_GENERAL_H - -/** - * Structures and related item as defined in - * §2 General-Purpose Types - */ - -#include - -typedef int64_t paddr_t; - -typedef struct { - paddr_t pr_start_paddr; - uint64_t pr_block_count; -} prange_t; - -typedef unsigned char uuid_t[16]; - -#endif // APFS_GENERAL_H +#ifndef APFS_GENERAL_H +#define APFS_GENERAL_H + +/** + * Structures and related item as defined in + * §2 General-Purpose Types + */ + +#include + +typedef int64_t paddr_t; + +typedef struct { + paddr_t pr_start_paddr; + uint64_t pr_block_count; +} prange_t; + +typedef unsigned char uuid_t[16]; + +#endif // APFS_GENERAL_H diff --git a/include/apfs/j.h b/include/apfs/j.h index 16899d7..e81ed1b 100644 --- a/include/apfs/j.h +++ b/include/apfs/j.h @@ -1,141 +1,141 @@ -#ifndef APFS_J_H -#define APFS_J_H - -/** - * Structures and related items as defined in - * §8 File-System Objects - */ - -#include -#include // apfs_mode_t -#include // cp_key_class_t - -/** `j_key_t` **/ - -typedef struct { - uint64_t obj_id_and_type; -} __attribute__((packed)) j_key_t; - -#define OBJ_ID_MASK 0x0fffffffffffffffULL -#define OBJ_TYPE_MASK 0xf000000000000000ULL -#define OBJ_TYPE_SHIFT 60 - -#define SYSTEM_OBJ_ID_MARK 0x0fffffff00000000ULL - -/** `j_inode_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_inode_key_t; - -/** `j_inode_val_t` **/ - -typedef uint32_t uid_t; -typedef uint32_t gid_t; - -typedef struct { - uint64_t parent_id; - uint64_t private_id; - - uint64_t create_time; - uint64_t mod_time; - uint64_t change_time; - uint64_t access_time; - - uint64_t internal_flags; - - union { - int32_t nchildren; - int32_t nlink; - }; - - cp_key_class_t default_protection_class; - uint32_t write_generation_counter; - uint32_t bsd_flags; - uid_t owner; - gid_t group; - apfs_mode_t mode; - uint16_t pad1; - uint64_t uncompressed_size; // formerly `pad2` - uint8_t xfields[]; -} __attribute__((packed)) j_inode_val_t; - -/** `j_drec_key_t` **/ - -typedef struct { - j_key_t hdr; - uint16_t name_len; // NOTE: Not `name_len_and_hash` as Apple's spec erroneously says. - uint8_t name[0]; -} __attribute__((packed)) j_drec_key_t; - -/** - * NOTE: Apple's spec says that if a file-system record is of type - * `APFS_TYPE_DIR_REC`, then the record's key is an instance of `j_drec_key_t`. - * However, the type `j_drec_hashed_key_t` (seen below) is defined in Apple's - * spec but not used anywhere in the spec; and upon closer inspection, the keys - * that I (Jivan Pal) have encountered in practice exclusively appear to be - * instances of `j_drec_hashed_key_t`. - * - * As such, either: - * (a) `j_drec_key_t` has been silently deprecated as of 2019-10-31 and replaced - * by `j_drec_hashed_key_t`; or - * (b) the specific type (`j_drec_key_t` vs. `j_drec_hashed_key_t`) must be - * determined by some convoluted means (i.e. case analysis of the data - * contained in the key). - * - * We assume that (a) is true, i.e. we exclusively use `j_drec_hashed_key_t`. - */ - -/** `j_drec_hashed_key_t` **/ - -typedef struct { - j_key_t hdr; - uint32_t name_len_and_hash; - uint8_t name[0]; -} __attribute__((packed)) j_drec_hashed_key_t; - -#define J_DREC_LEN_MASK 0x000003ff -#define J_DREC_HASH_MASK 0xfffffc00 // Apple's spec incorrectly says `0xfffff400` -#define J_DREC_HASH_SHIFT 10 - -/** `j_drec_val_t` **/ - -typedef struct { - uint64_t file_id; - uint64_t date_added; - uint64_t flags; - uint8_t xfields[]; -} __attribute__((packed)) j_drec_val_t; - -/** `j_dir_stats_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_dir_stats_key_t; - -/** `j_dir_stats_val_t` **/ - -typedef struct { - uint64_t num_children; - uint64_t total_size; - uint64_t chained_key; - uint64_t gen_count; -} __attribute__((packed)) j_dir_stats_val_t; - -/** `j_xattr_key_t` **/ - -typedef struct { - j_key_t hdr; - uint16_t name_len; - uint8_t name[0]; -} __attribute__((packed)) j_xattr_key_t; - -/** `j_xattr_val_t` **/ - -typedef struct { - uint16_t flags; - uint16_t xdata_len; - uint8_t xdata[0]; -} __attribute__((packed)) j_xattr_val_t; - -#endif // APFS_J_H +#ifndef APFS_J_H +#define APFS_J_H + +/** + * Structures and related items as defined in + * §8 File-System Objects + */ + +#include +#include // apfs_mode_t +#include // cp_key_class_t + +/** `j_key_t` **/ + +typedef struct { + uint64_t obj_id_and_type; +} __attribute__((packed)) j_key_t; + +#define OBJ_ID_MASK 0x0fffffffffffffffULL +#define OBJ_TYPE_MASK 0xf000000000000000ULL +#define OBJ_TYPE_SHIFT 60 + +#define SYSTEM_OBJ_ID_MARK 0x0fffffff00000000ULL + +/** `j_inode_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_inode_key_t; + +/** `j_inode_val_t` **/ + +typedef uint32_t uid_t; +typedef uint32_t gid_t; + +typedef struct { + uint64_t parent_id; + uint64_t private_id; + + uint64_t create_time; + uint64_t mod_time; + uint64_t change_time; + uint64_t access_time; + + uint64_t internal_flags; + + union { + int32_t nchildren; + int32_t nlink; + }; + + cp_key_class_t default_protection_class; + uint32_t write_generation_counter; + uint32_t bsd_flags; + uid_t owner; + gid_t group; + apfs_mode_t mode; + uint16_t pad1; + uint64_t uncompressed_size; // formerly `pad2` + uint8_t xfields[]; +} __attribute__((packed)) j_inode_val_t; + +/** `j_drec_key_t` **/ + +typedef struct { + j_key_t hdr; + uint16_t name_len; // NOTE: Not `name_len_and_hash` as Apple's spec erroneously says. + uint8_t name[0]; +} __attribute__((packed)) j_drec_key_t; + +/** + * NOTE: Apple's spec says that if a file-system record is of type + * `APFS_TYPE_DIR_REC`, then the record's key is an instance of `j_drec_key_t`. + * However, the type `j_drec_hashed_key_t` (seen below) is defined in Apple's + * spec but not used anywhere in the spec; and upon closer inspection, the keys + * that I (Jivan Pal) have encountered in practice exclusively appear to be + * instances of `j_drec_hashed_key_t`. + * + * As such, either: + * (a) `j_drec_key_t` has been silently deprecated as of 2019-10-31 and replaced + * by `j_drec_hashed_key_t`; or + * (b) the specific type (`j_drec_key_t` vs. `j_drec_hashed_key_t`) must be + * determined by some convoluted means (i.e. case analysis of the data + * contained in the key). + * + * We assume that (a) is true, i.e. we exclusively use `j_drec_hashed_key_t`. + */ + +/** `j_drec_hashed_key_t` **/ + +typedef struct { + j_key_t hdr; + uint32_t name_len_and_hash; + uint8_t name[0]; +} __attribute__((packed)) j_drec_hashed_key_t; + +#define J_DREC_LEN_MASK 0x000003ff +#define J_DREC_HASH_MASK 0xfffffc00 // Apple's spec incorrectly says `0xfffff400` +#define J_DREC_HASH_SHIFT 10 + +/** `j_drec_val_t` **/ + +typedef struct { + uint64_t file_id; + uint64_t date_added; + uint64_t flags; + uint8_t xfields[]; +} __attribute__((packed)) j_drec_val_t; + +/** `j_dir_stats_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_dir_stats_key_t; + +/** `j_dir_stats_val_t` **/ + +typedef struct { + uint64_t num_children; + uint64_t total_size; + uint64_t chained_key; + uint64_t gen_count; +} __attribute__((packed)) j_dir_stats_val_t; + +/** `j_xattr_key_t` **/ + +typedef struct { + j_key_t hdr; + uint16_t name_len; + uint8_t name[0]; +} __attribute__((packed)) j_xattr_key_t; + +/** `j_xattr_val_t` **/ + +typedef struct { + uint16_t flags; + uint16_t xdata_len; + uint8_t xdata[0]; +} __attribute__((packed)) j_xattr_val_t; + +#endif // APFS_J_H diff --git a/include/apfs/jconst.h b/include/apfs/jconst.h index 2556d1e..30f11bd 100644 --- a/include/apfs/jconst.h +++ b/include/apfs/jconst.h @@ -1,199 +1,199 @@ -#ifndef APFS_CONST_H -#define APFS_CONST_H - -/** - * Structures and related items as defined in - * §9 File-System Constants - */ - -#include - -/** `j_obj_types` **/ - -typedef enum { - APFS_TYPE_ANY = 0, - - APFS_TYPE_SNAP_METADATA = 1, - APFS_TYPE_EXTENT = 2, - APFS_TYPE_INODE = 3, - APFS_TYPE_XATTR = 4, - APFS_TYPE_SIBLING_LINK = 5, - APFS_TYPE_DSTREAM_ID = 6, - APFS_TYPE_CRYPTO_STATE = 7, - APFS_TYPE_FILE_EXTENT = 8, - APFS_TYPE_DIR_REC = 9, - APFS_TYPE_DIR_STATS = 10, // NOTE: The value is an instance of type `j_dir_stats_val_t`, not `j_drec_val_t` as Apple's spec incorrectly says. - APFS_TYPE_SNAP_NAME = 11, - APFS_TYPE_SIBLING_MAP = 12, - APFS_TYPE_FILE_INFO = 13, - - APFS_TYPE_MAX_VALID = 13, - APFS_TYPE_MAX = 15, - - APFS_TYPE_INVALID = 15, -} j_obj_types; - -/** `j_obj_kinds` **/ - -typedef enum { - APFS_KIND_ANY = 0, - APFS_KIND_NEW = 1, - APFS_KIND_UPDATE = 2, - APFS_KIND_DEAD = 3, - APFS_KIND_UPDATE_RECENT = 4, - - APFS_KIND_INVALID = 255, -} j_obj_kinds; - -/** `j_inode_flags` **/ - -typedef enum { - INODE_IS_APFS_PRIVATE = 0x00000001, - INODE_MAINTAIN_DIR_STATS = 0x00000002, - INODE_DIR_STATS_ORIGIN = 0x00000004, - INODE_PROT_CLASS_EXPLICIT = 0x00000008, - INODE_WAS_CLONED = 0x00000010, - INODE_FLAG_UNUSED = 0x00000020, - INODE_HAS_SECURITY_EA = 0x00000040, - INODE_BEING_TRUNCATED = 0x00000080, - INODE_HAS_FINDER_INFO = 0x00000100, - INODE_IS_SPARSE = 0x00000200, - INODE_WAS_EVER_CLONED = 0x00000400, - INODE_ACTIVE_FILE_TRIMMED = 0x00000800, - INODE_PINNED_TO_MAIN = 0x00001000, - INODE_PINNED_TO_TIER2 = 0x00002000, - INODE_HAS_RSRC_FORK = 0x00004000, - INODE_NO_RSRC_FORK = 0x00008000, - INODE_ALLOCATION_SPILLEDOVER = 0x00010000, - INODE_FAST_PROMOTE = 0x00020000, - INODE_HAS_UNCOMPRESSED_SIZE = 0x00040000, - INODE_IS_PURGEABLE = 0x00080000, - INODE_WANTS_TO_BE_PURGEABLE = 0x00100000, - INODE_IS_SYNC_ROOT = 0x00200000, - INODE_SNAPSHOT_COW_EXEMPTION = 0x00400000, - - INODE_INHERITED_INTERNAL_FLAGS = ( \ - INODE_MAINTAIN_DIR_STATS \ - | INODE_SNAPSHOT_COW_EXEMPTION \ - ), - - INODE_CLONED_INTERNAL_FLAGS = ( \ - INODE_HAS_RSRC_FORK \ - | INODE_NO_RSRC_FORK \ - | INODE_HAS_FINDER_INFO \ - | INODE_SNAPSHOT_COW_EXEMPTION \ - ), -} j_inode_flags; - -#define APFS_VALID_INTERNAL_INODE_FLAGS ( \ - INODE_IS_APFS_PRIVATE \ - | INODE_MAINTAIN_DIR_STATS \ - | INODE_DIR_STATS_ORIGIN \ - | INODE_PROT_CLASS_EXPLICIT \ - | INODE_WAS_CLONED \ - | INODE_HAS_SECURITY_EA \ - | INODE_BEING_TRUNCATED \ - | INODE_HAS_FINDER_INFO \ - | INODE_IS_SPARSE \ - | INODE_WAS_EVER_CLONED \ - | INODE_ACTIVE_FILE_TRIMMED \ - | INODE_PINNED_TO_MAIN \ - | INODE_PINNED_TO_TIER2 \ - | INODE_HAS_RSRC_FORK \ - | INODE_NO_RSRC_FORK \ - | INODE_ALLOCATION_SPILLEDOVER \ - | INODE_FAST_PROMOTE \ - | INODE_HAS_UNCOMPRESSED_SIZE \ - | INODE_IS_PURGEABLE \ - | INODE_WANTS_TO_BE_PURGEABLE \ - | INODE_IS_SYNC_ROOT \ - | INODE_SNAPSHOT_COW_EXEMPTION \ -) - -#define APFS_INODE_PINNED_MASK ( \ - INODE_PINNED_TO_MAIN \ - | INODE_PINNED_TO_TIER2 \ -) - -/** `j_xattr_flags` **/ - -typedef enum { - XATTR_DATA_STREAM = 0x00000001, - XATTR_DATA_EMBEDDED = 0x00000002, - XATTR_FILE_SYSTEM_OWNED = 0x00000004, - XATTR_RESERVED_8 = 0x00000008, -} j_xattr_flags; - -/** `dir_rec_flags` **/ - -typedef enum { - DREC_TYPE_MASK = 0x000f, - RESERVED_10 = 0x0010, -} dir_rec_flags; - -/** Inode Numbers **/ - -#define INVALID_INO_NUM 0 -#define ROOT_DIR_PARENT 1 -#define ROOT_DIR_INO_NUM 2 -#define PRIV_DIR_INO_NUM 3 -#define SNAP_DIR_INO_NUM 6 -#define PURGEABLE_DIR_INO_NUM 7 - -#define MIN_USER_INO_NUM 16 - -#define UNIFIED_ID_SPACE_MARK 0x0800000000000000ULL - -/** Extended Attributes Constants **/ - -#define XATTR_MAX_EMBEDDED_SIZE 3804 // = 3 Ki + 732 -#define SYMLINK_EA_NAME "com.apple.fs.symlink" -#define FIRMLINK_EA_NAME "com.apple.fs.firmlink" -#define APFS_COW_EXEMPT_COUNT_NAME "com.apple.fs.cow-exempt-file-count" - -/** File-System Object Constants **/ - -#define OWNING_OBJ_ID_INVALID ~0ULL -#define OWNING_OBJ_ID_UNKNOWN ~1ULL - -#define JOBJ_MAX_KEY_SIZE 832 -#define JOBJ_MAX_VALUE_SIZE 3808 // = 3 Ki + 736 - -#define MIN_DOC_ID 3 - -/** File Extent Constants **/ - -#define FEXT_CRYPTO_ID_IS_TWEAK 0x01 - -/** File Modes **/ - -/** - * Called `mode_t` in the spec, but this clashes with the GNU `mode_t` on - * non-Apple platforms, so we use a distinct name for portability. - */ -typedef uint16_t apfs_mode_t; - -#define S_IFMT 0170000 - -#define S_IFIFO 0010000 -#define S_IFCHR 0020000 -#define S_IFDIR 0040000 -#define S_IFBLK 0060000 -#define S_IFREG 0100000 -#define S_IFLNK 0120000 -#define S_IFSOCK 0140000 -#define S_IFWHT 0160000 - -/** Directory Entry File Types **/ - -#define DT_UNKNOWN 0 -#define DT_FIFO 1 -#define DT_CHR 2 -#define DT_DIR 4 -#define DT_BLK 6 -#define DT_REG 8 -#define DT_LNK 10 -#define DT_SOCK 12 -#define DT_WHT 14 - -#endif // APFS_CONST_H +#ifndef APFS_CONST_H +#define APFS_CONST_H + +/** + * Structures and related items as defined in + * §9 File-System Constants + */ + +#include + +/** `j_obj_types` **/ + +typedef enum { + APFS_TYPE_ANY = 0, + + APFS_TYPE_SNAP_METADATA = 1, + APFS_TYPE_EXTENT = 2, + APFS_TYPE_INODE = 3, + APFS_TYPE_XATTR = 4, + APFS_TYPE_SIBLING_LINK = 5, + APFS_TYPE_DSTREAM_ID = 6, + APFS_TYPE_CRYPTO_STATE = 7, + APFS_TYPE_FILE_EXTENT = 8, + APFS_TYPE_DIR_REC = 9, + APFS_TYPE_DIR_STATS = 10, // NOTE: The value is an instance of type `j_dir_stats_val_t`, not `j_drec_val_t` as Apple's spec incorrectly says. + APFS_TYPE_SNAP_NAME = 11, + APFS_TYPE_SIBLING_MAP = 12, + APFS_TYPE_FILE_INFO = 13, + + APFS_TYPE_MAX_VALID = 13, + APFS_TYPE_MAX = 15, + + APFS_TYPE_INVALID = 15, +} j_obj_types; + +/** `j_obj_kinds` **/ + +typedef enum { + APFS_KIND_ANY = 0, + APFS_KIND_NEW = 1, + APFS_KIND_UPDATE = 2, + APFS_KIND_DEAD = 3, + APFS_KIND_UPDATE_RECENT = 4, + + APFS_KIND_INVALID = 255, +} j_obj_kinds; + +/** `j_inode_flags` **/ + +typedef enum { + INODE_IS_APFS_PRIVATE = 0x00000001, + INODE_MAINTAIN_DIR_STATS = 0x00000002, + INODE_DIR_STATS_ORIGIN = 0x00000004, + INODE_PROT_CLASS_EXPLICIT = 0x00000008, + INODE_WAS_CLONED = 0x00000010, + INODE_FLAG_UNUSED = 0x00000020, + INODE_HAS_SECURITY_EA = 0x00000040, + INODE_BEING_TRUNCATED = 0x00000080, + INODE_HAS_FINDER_INFO = 0x00000100, + INODE_IS_SPARSE = 0x00000200, + INODE_WAS_EVER_CLONED = 0x00000400, + INODE_ACTIVE_FILE_TRIMMED = 0x00000800, + INODE_PINNED_TO_MAIN = 0x00001000, + INODE_PINNED_TO_TIER2 = 0x00002000, + INODE_HAS_RSRC_FORK = 0x00004000, + INODE_NO_RSRC_FORK = 0x00008000, + INODE_ALLOCATION_SPILLEDOVER = 0x00010000, + INODE_FAST_PROMOTE = 0x00020000, + INODE_HAS_UNCOMPRESSED_SIZE = 0x00040000, + INODE_IS_PURGEABLE = 0x00080000, + INODE_WANTS_TO_BE_PURGEABLE = 0x00100000, + INODE_IS_SYNC_ROOT = 0x00200000, + INODE_SNAPSHOT_COW_EXEMPTION = 0x00400000, + + INODE_INHERITED_INTERNAL_FLAGS = ( \ + INODE_MAINTAIN_DIR_STATS \ + | INODE_SNAPSHOT_COW_EXEMPTION \ + ), + + INODE_CLONED_INTERNAL_FLAGS = ( \ + INODE_HAS_RSRC_FORK \ + | INODE_NO_RSRC_FORK \ + | INODE_HAS_FINDER_INFO \ + | INODE_SNAPSHOT_COW_EXEMPTION \ + ), +} j_inode_flags; + +#define APFS_VALID_INTERNAL_INODE_FLAGS ( \ + INODE_IS_APFS_PRIVATE \ + | INODE_MAINTAIN_DIR_STATS \ + | INODE_DIR_STATS_ORIGIN \ + | INODE_PROT_CLASS_EXPLICIT \ + | INODE_WAS_CLONED \ + | INODE_HAS_SECURITY_EA \ + | INODE_BEING_TRUNCATED \ + | INODE_HAS_FINDER_INFO \ + | INODE_IS_SPARSE \ + | INODE_WAS_EVER_CLONED \ + | INODE_ACTIVE_FILE_TRIMMED \ + | INODE_PINNED_TO_MAIN \ + | INODE_PINNED_TO_TIER2 \ + | INODE_HAS_RSRC_FORK \ + | INODE_NO_RSRC_FORK \ + | INODE_ALLOCATION_SPILLEDOVER \ + | INODE_FAST_PROMOTE \ + | INODE_HAS_UNCOMPRESSED_SIZE \ + | INODE_IS_PURGEABLE \ + | INODE_WANTS_TO_BE_PURGEABLE \ + | INODE_IS_SYNC_ROOT \ + | INODE_SNAPSHOT_COW_EXEMPTION \ +) + +#define APFS_INODE_PINNED_MASK ( \ + INODE_PINNED_TO_MAIN \ + | INODE_PINNED_TO_TIER2 \ +) + +/** `j_xattr_flags` **/ + +typedef enum { + XATTR_DATA_STREAM = 0x00000001, + XATTR_DATA_EMBEDDED = 0x00000002, + XATTR_FILE_SYSTEM_OWNED = 0x00000004, + XATTR_RESERVED_8 = 0x00000008, +} j_xattr_flags; + +/** `dir_rec_flags` **/ + +typedef enum { + DREC_TYPE_MASK = 0x000f, + RESERVED_10 = 0x0010, +} dir_rec_flags; + +/** Inode Numbers **/ + +#define INVALID_INO_NUM 0 +#define ROOT_DIR_PARENT 1 +#define ROOT_DIR_INO_NUM 2 +#define PRIV_DIR_INO_NUM 3 +#define SNAP_DIR_INO_NUM 6 +#define PURGEABLE_DIR_INO_NUM 7 + +#define MIN_USER_INO_NUM 16 + +#define UNIFIED_ID_SPACE_MARK 0x0800000000000000ULL + +/** Extended Attributes Constants **/ + +#define XATTR_MAX_EMBEDDED_SIZE 3804 // = 3 Ki + 732 +#define SYMLINK_EA_NAME "com.apple.fs.symlink" +#define FIRMLINK_EA_NAME "com.apple.fs.firmlink" +#define APFS_COW_EXEMPT_COUNT_NAME "com.apple.fs.cow-exempt-file-count" + +/** File-System Object Constants **/ + +#define OWNING_OBJ_ID_INVALID ~0ULL +#define OWNING_OBJ_ID_UNKNOWN ~1ULL + +#define JOBJ_MAX_KEY_SIZE 832 +#define JOBJ_MAX_VALUE_SIZE 3808 // = 3 Ki + 736 + +#define MIN_DOC_ID 3 + +/** File Extent Constants **/ + +#define FEXT_CRYPTO_ID_IS_TWEAK 0x01 + +/** File Modes **/ + +/** + * Called `mode_t` in the spec, but this clashes with the GNU `mode_t` on + * non-Apple platforms, so we use a distinct name for portability. + */ +typedef uint16_t apfs_mode_t; + +#define S_IFMT 0170000 + +#define S_IFIFO 0010000 +#define S_IFCHR 0020000 +#define S_IFDIR 0040000 +#define S_IFBLK 0060000 +#define S_IFREG 0100000 +#define S_IFLNK 0120000 +#define S_IFSOCK 0140000 +#define S_IFWHT 0160000 + +/** Directory Entry File Types **/ + +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#endif // APFS_CONST_H diff --git a/include/apfs/jumpstart.h b/include/apfs/jumpstart.h index f47f065..2b35e5a 100644 --- a/include/apfs/jumpstart.h +++ b/include/apfs/jumpstart.h @@ -1,32 +1,32 @@ -#ifndef APFS_JUMPSTART_H -#define APFS_JUMPSTART_H - -/** - * Structures and related items as defined in - * §4 EFI Jumpstart - */ - -#include -#include // prange_t -#include // obj_phys_t - -/** `nx_efi_jumpstart_t` **/ - -typedef struct { - obj_phys_t nej_o; - uint32_t nej_magic; - uint32_t nej_version; - uint32_t nej_efi_file_len; - uint32_t nej_num_extents; - uint64_t nej_reserved[16]; - prange_t nej_rec_extents[]; -} nx_efi_jumpstart_t; - -#define NX_EFI_JUMPSTART_MAGIC 'RDSJ' -#define NX_EFI_JUMPSTART_VERSION 1 - -/** Partition UUIDs **/ - -#define APFS_GPT_PARTITION_UUID "7C3457EF-0000-11AA-AA11-00306543ECAC" - -#endif // APFS_JUMPSTART_H +#ifndef APFS_JUMPSTART_H +#define APFS_JUMPSTART_H + +/** + * Structures and related items as defined in + * §4 EFI Jumpstart + */ + +#include +#include // prange_t +#include // obj_phys_t + +/** `nx_efi_jumpstart_t` **/ + +typedef struct { + obj_phys_t nej_o; + uint32_t nej_magic; + uint32_t nej_version; + uint32_t nej_efi_file_len; + uint32_t nej_num_extents; + uint64_t nej_reserved[16]; + prange_t nej_rec_extents[]; +} nx_efi_jumpstart_t; + +#define NX_EFI_JUMPSTART_MAGIC 'RDSJ' +#define NX_EFI_JUMPSTART_VERSION 1 + +/** Partition UUIDs **/ + +#define APFS_GPT_PARTITION_UUID "7C3457EF-0000-11AA-AA11-00306543ECAC" + +#endif // APFS_JUMPSTART_H diff --git a/include/apfs/nx.h b/include/apfs/nx.h index 603c2a2..cc57a96 100644 --- a/include/apfs/nx.h +++ b/include/apfs/nx.h @@ -1,158 +1,158 @@ -#ifndef APFS_NX_H -#define APFS_NX_H - -/** - * Structures and related items as defined in - * §5 Container - */ - -#include -#include // paddr_t, prange_t, uuid_t -#include // obj_phys_t, oid_t, xid_t - -/** `nx_counter_id_t` --- forward declared for `nx_superblock_t` **/ - -typedef enum { - NX_CNTR_OBJ_CKSUM_SET = 0, - NX_CNTR_OBJ_CKSUM_FAIL = 1, - - NX_NUM_COUNTERS = 32, -} nx_counter_id_t; - -/** `nx_superblock_t` **/ - -#define NX_MAGIC 'BSXN' -#define NX_MAX_FILE_SYSTEMS 100 - -#define NX_EPH_INFO_COUNT 4 -#define NX_EPH_MIN_BLOCK_COUNT 8 -#define NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4 -#define NX_TX_MIN_CHECKPOINT_COUNT 4 -#define NX_EPH_INFO_VERSION 1 - -typedef struct { - obj_phys_t nx_o; - uint32_t nx_magic; - uint32_t nx_block_size; - uint64_t nx_block_count; - - uint64_t nx_features; - uint64_t nx_readonly_compatible_features; - uint64_t nx_incompatible_features; - - uuid_t nx_uuid; - - oid_t nx_next_oid; - xid_t nx_next_xid; - - uint32_t nx_xp_desc_blocks; - uint32_t nx_xp_data_blocks; - paddr_t nx_xp_desc_base; - paddr_t nx_xp_data_base; - uint32_t nx_xp_desc_next; - uint32_t nx_xp_data_next; - uint32_t nx_xp_desc_index; - uint32_t nx_xp_desc_len; - uint32_t nx_xp_data_index; - uint32_t nx_xp_data_len; - - oid_t nx_spaceman_oid; - oid_t nx_omap_oid; - oid_t nx_reaper_oid; - - uint32_t nx_test_type; - - uint32_t nx_max_file_systems; - oid_t nx_fs_oid[NX_MAX_FILE_SYSTEMS]; - uint64_t nx_counters[NX_NUM_COUNTERS]; - prange_t nx_blocked_out_prange; - oid_t nx_evict_mapping_tree_oid; - uint64_t nx_flags; - paddr_t nx_efi_jumpstart; - uuid_t nx_fusion_uuid; - prange_t nx_keylocker; - uint64_t nx_ephemeral_info[NX_EPH_INFO_COUNT]; - - oid_t nx_test_oid; - - oid_t nx_fusion_mt_oid; - oid_t nx_fusion_wbc_oid; - prange_t nx_fusion_wbc; - - uint64_t nx_newest_mounted_version; - - prange_t nx_mkb_locker; -} nx_superblock_t; - -/** Container Flags **/ - -#define NX_RESERVED_1 0x00000001LL -#define NX_RESERVED_2 0x00000002LL -#define NX_CRYPTO_SW 0x00000004LL - -/** Optional Container Feature Flags **/ - -#define NX_FEATURE_DEFRAG 0x0000000000000001ULL -#define NX_FEATURE_LCFD 0x0000000000000002ULL -#define NX_SUPPORTED_FEATURES_MASK (NX_FEATURE_DEFRAG | NX_FEATURE_LCFD) - -/** Read-Only Compatible Container Feature Flags **/ - -#define NX_SUPPORTED_ROCOMPAT_MASK 0x0ULL - -/** Incompatible Container Feature Flags **/ - -#define NX_INCOMPAT_VERSION1 0x0000000000000001ULL -#define NX_INCOMPAT_VERSION2 0x0000000000000002ULL -#define NX_INCOMPAT_FUSION 0x0000000000000100ULL - -/* - * NOTE: Apple's spec doesn't include `NX_INCOMPAT_VERSION1` in this mask, - * perhaps in error, so we include it here. - */ -#define NX_SUPPORTED_INCOMPAT_MASK ( \ - NX_INCOMPAT_VERSION1 \ - | NX_INCOMPAT_VERSION2 \ - | NX_INCOMPAT_FUSION \ -) - -/** Block and Container Size **/ - -#define NX_MINIMUM_BLOCK_SIZE 4096 // = 4 Ki -#define NX_DEFAULT_BLOCK_SIZE 4096 // = 4 Ki -#define NX_MAXIMUM_BLOCK_SIZE 65536 // = 64 Ki -#define NX_MINIMUM_CONTAINER_SIZE 1048576 // = 1024 Ki = 1 Mi - -/** `checkpoint_mapping_t` **/ - -typedef struct { - uint32_t cpm_type; - uint32_t cpm_subtype; - uint32_t cpm_size; - uint32_t cpm_pad; - oid_t cpm_fs_oid; - oid_t cpm_oid; - oid_t cpm_paddr; -} checkpoint_mapping_t; - -/** `checkpoint_map_phys_t` **/ - -typedef struct { - obj_phys_t cpm_o; - uint32_t cpm_flags; - uint32_t cpm_count; - checkpoint_mapping_t cpm_map[]; -} checkpoint_map_phys_t; - -/** Checkpoint Flags **/ - -#define CHECKPOINT_MAP_LAST 0x00000001 - -/** `evict_mapping_val_t` **/ - -typedef struct { - paddr_t dst_paddr; - uint64_t len; -} __attribute__((packed)) evict_mapping_val_t; - -#endif // APFS_NX_H +#ifndef APFS_NX_H +#define APFS_NX_H + +/** + * Structures and related items as defined in + * §5 Container + */ + +#include +#include // paddr_t, prange_t, uuid_t +#include // obj_phys_t, oid_t, xid_t + +/** `nx_counter_id_t` --- forward declared for `nx_superblock_t` **/ + +typedef enum { + NX_CNTR_OBJ_CKSUM_SET = 0, + NX_CNTR_OBJ_CKSUM_FAIL = 1, + + NX_NUM_COUNTERS = 32, +} nx_counter_id_t; + +/** `nx_superblock_t` **/ + +#define NX_MAGIC 'BSXN' +#define NX_MAX_FILE_SYSTEMS 100 + +#define NX_EPH_INFO_COUNT 4 +#define NX_EPH_MIN_BLOCK_COUNT 8 +#define NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4 +#define NX_TX_MIN_CHECKPOINT_COUNT 4 +#define NX_EPH_INFO_VERSION 1 + +typedef struct { + obj_phys_t nx_o; + uint32_t nx_magic; + uint32_t nx_block_size; + uint64_t nx_block_count; + + uint64_t nx_features; + uint64_t nx_readonly_compatible_features; + uint64_t nx_incompatible_features; + + uuid_t nx_uuid; + + oid_t nx_next_oid; + xid_t nx_next_xid; + + uint32_t nx_xp_desc_blocks; + uint32_t nx_xp_data_blocks; + paddr_t nx_xp_desc_base; + paddr_t nx_xp_data_base; + uint32_t nx_xp_desc_next; + uint32_t nx_xp_data_next; + uint32_t nx_xp_desc_index; + uint32_t nx_xp_desc_len; + uint32_t nx_xp_data_index; + uint32_t nx_xp_data_len; + + oid_t nx_spaceman_oid; + oid_t nx_omap_oid; + oid_t nx_reaper_oid; + + uint32_t nx_test_type; + + uint32_t nx_max_file_systems; + oid_t nx_fs_oid[NX_MAX_FILE_SYSTEMS]; + uint64_t nx_counters[NX_NUM_COUNTERS]; + prange_t nx_blocked_out_prange; + oid_t nx_evict_mapping_tree_oid; + uint64_t nx_flags; + paddr_t nx_efi_jumpstart; + uuid_t nx_fusion_uuid; + prange_t nx_keylocker; + uint64_t nx_ephemeral_info[NX_EPH_INFO_COUNT]; + + oid_t nx_test_oid; + + oid_t nx_fusion_mt_oid; + oid_t nx_fusion_wbc_oid; + prange_t nx_fusion_wbc; + + uint64_t nx_newest_mounted_version; + + prange_t nx_mkb_locker; +} nx_superblock_t; + +/** Container Flags **/ + +#define NX_RESERVED_1 0x00000001LL +#define NX_RESERVED_2 0x00000002LL +#define NX_CRYPTO_SW 0x00000004LL + +/** Optional Container Feature Flags **/ + +#define NX_FEATURE_DEFRAG 0x0000000000000001ULL +#define NX_FEATURE_LCFD 0x0000000000000002ULL +#define NX_SUPPORTED_FEATURES_MASK (NX_FEATURE_DEFRAG | NX_FEATURE_LCFD) + +/** Read-Only Compatible Container Feature Flags **/ + +#define NX_SUPPORTED_ROCOMPAT_MASK 0x0ULL + +/** Incompatible Container Feature Flags **/ + +#define NX_INCOMPAT_VERSION1 0x0000000000000001ULL +#define NX_INCOMPAT_VERSION2 0x0000000000000002ULL +#define NX_INCOMPAT_FUSION 0x0000000000000100ULL + +/* + * NOTE: Apple's spec doesn't include `NX_INCOMPAT_VERSION1` in this mask, + * perhaps in error, so we include it here. + */ +#define NX_SUPPORTED_INCOMPAT_MASK ( \ + NX_INCOMPAT_VERSION1 \ + | NX_INCOMPAT_VERSION2 \ + | NX_INCOMPAT_FUSION \ +) + +/** Block and Container Size **/ + +#define NX_MINIMUM_BLOCK_SIZE 4096 // = 4 Ki +#define NX_DEFAULT_BLOCK_SIZE 4096 // = 4 Ki +#define NX_MAXIMUM_BLOCK_SIZE 65536 // = 64 Ki +#define NX_MINIMUM_CONTAINER_SIZE 1048576 // = 1024 Ki = 1 Mi + +/** `checkpoint_mapping_t` **/ + +typedef struct { + uint32_t cpm_type; + uint32_t cpm_subtype; + uint32_t cpm_size; + uint32_t cpm_pad; + oid_t cpm_fs_oid; + oid_t cpm_oid; + oid_t cpm_paddr; +} checkpoint_mapping_t; + +/** `checkpoint_map_phys_t` **/ + +typedef struct { + obj_phys_t cpm_o; + uint32_t cpm_flags; + uint32_t cpm_count; + checkpoint_mapping_t cpm_map[]; +} checkpoint_map_phys_t; + +/** Checkpoint Flags **/ + +#define CHECKPOINT_MAP_LAST 0x00000001 + +/** `evict_mapping_val_t` **/ + +typedef struct { + paddr_t dst_paddr; + uint64_t len; +} __attribute__((packed)) evict_mapping_val_t; + +#endif // APFS_NX_H diff --git a/include/apfs/object.h b/include/apfs/object.h index 8a1127f..ccc0154 100644 --- a/include/apfs/object.h +++ b/include/apfs/object.h @@ -1,109 +1,109 @@ -#ifndef APFS_OBJECT_H -#define APFS_OBJECT_H - -/** - * Structures and related items as defined in - * §3 Objects - */ - -#include - -/** Supporting Data Types --- forward declared for `obj_phys_t` **/ - -typedef uint64_t oid_t; -typedef uint64_t xid_t; - -/** `obj_phys_t` **/ - -#define MAX_CKSUM_SIZE 8 - -typedef struct { - uint8_t o_cksum[MAX_CKSUM_SIZE]; - oid_t o_oid; - xid_t o_xid; - uint32_t o_type; - uint32_t o_subtype; -} obj_phys_t; - -/** Object Identifier Constants **/ - -#define OID_NX_SUPERBLOCK 1 - -#define OID_INVALID 0ULL -#define OID_RESERVED_COUNT 1024 - -/** Object Type Masks **/ - -#define OBJECT_TYPE_MASK 0x0000ffff -#define OBJECT_TYPE_FLAGS_MASK 0xffff0000 - -#define OBJ_STORAGETYPE_MASK 0xc0000000 -#define OBJECT_TYPE_FLAGS_DEFINED_MASK 0xf8000000 - -/** Object Types **/ - -#define OBJECT_TYPE_NX_SUPERBLOCK 0x00000001 - -#define OBJECT_TYPE_BTREE 0x00000002 -#define OBJECT_TYPE_BTREE_NODE 0x00000003 - -#define OBJECT_TYPE_SPACEMAN 0x00000005 -#define OBJECT_TYPE_SPACEMAN_CAB 0x00000006 -#define OBJECT_TYPE_SPACEMAN_CIB 0x00000007 -#define OBJECT_TYPE_SPACEMAN_BITMAP 0x00000008 -#define OBJECT_TYPE_SPACEMAN_FREE_QUEUE 0x00000009 - -#define OBJECT_TYPE_EXTENT_LIST_TREE 0x0000000a -#define OBJECT_TYPE_OMAP 0x0000000b -#define OBJECT_TYPE_CHECKPOINT_MAP 0x0000000c - -#define OBJECT_TYPE_FS 0x0000000d -#define OBJECT_TYPE_FSTREE 0x0000000e -#define OBJECT_TYPE_BLOCKREFTREE 0x0000000f -#define OBJECT_TYPE_SNAPMETATREE 0x00000010 - -#define OBJECT_TYPE_NX_REAPER 0x00000011 -#define OBJECT_TYPE_NX_REAP_LIST 0x00000012 -#define OBJECT_TYPE_OMAP_SNAPSHOT 0x00000013 -#define OBJECT_TYPE_EFI_JUMPSTART 0x00000014 -#define OBJECT_TYPE_FUSION_MIDDLE_TREE 0x00000015 -#define OBJECT_TYPE_NX_FUSION_WBC 0x00000016 -#define OBJECT_TYPE_NX_FUSION_WBC_LIST 0x00000017 -#define OBJECT_TYPE_ER_STATE 0x00000018 - -#define OBJECT_TYPE_GBITMAP 0x00000019 -#define OBJECT_TYPE_GBITMAP_TREE 0x0000001a -#define OBJECT_TYPE_GBITMAP_BLOCK 0x0000001b - -#define OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c -#define OBJECT_TYPE_SNAP_META_EXT 0x0000001d -#define OBJECT_TYPE_INTEGRITY_META 0x0000001e -#define OBJECT_TYPE_FEXT_TREE 0x0000001f -#define OBJECT_TYPE_RESERVED_20 0x00000020 - -#define OBJECT_TYPE_INVALID 0x00000000 -#define OBJECT_TYPE_TEST 0x000000ff - -/* - * Media keybag types (object types that correspond to an instance of - * `media_keybag_t`). The type values don't use only the low 16 bits of the - * `o_type` field. Instead, they use all 32 bits of that field, meaning these - * objects don't have type flags, and that comparing `o_type & OBJECT_TYPE_MASK` - * with these values doesn't work; you must compare `o_type` directly with these - * values instead. - */ -#define OBJECT_TYPE_CONTAINER_KEYBAG 'keys' -#define OBJECT_TYPE_VOLUME_KEYBAG 'recs' -#define OBJECT_TYPE_MEDIA_KEYBAG 'mkey' - -/** Object Type Flags **/ - -#define OBJ_VIRTUAL 0x00000000 -#define OBJ_EPHEMERAL 0x80000000 -#define OBJ_PHYSICAL 0x40000000 - -#define OBJ_NOHEADER 0x20000000 -#define OBJ_ENCRYPTED 0x10000000 -#define OBJ_NONPERSISTENT 0x08000000 - -#endif // APFS_OBJECT_H +#ifndef APFS_OBJECT_H +#define APFS_OBJECT_H + +/** + * Structures and related items as defined in + * §3 Objects + */ + +#include + +/** Supporting Data Types --- forward declared for `obj_phys_t` **/ + +typedef uint64_t oid_t; +typedef uint64_t xid_t; + +/** `obj_phys_t` **/ + +#define MAX_CKSUM_SIZE 8 + +typedef struct { + uint8_t o_cksum[MAX_CKSUM_SIZE]; + oid_t o_oid; + xid_t o_xid; + uint32_t o_type; + uint32_t o_subtype; +} obj_phys_t; + +/** Object Identifier Constants **/ + +#define OID_NX_SUPERBLOCK 1 + +#define OID_INVALID 0ULL +#define OID_RESERVED_COUNT 1024 + +/** Object Type Masks **/ + +#define OBJECT_TYPE_MASK 0x0000ffff +#define OBJECT_TYPE_FLAGS_MASK 0xffff0000 + +#define OBJ_STORAGETYPE_MASK 0xc0000000 +#define OBJECT_TYPE_FLAGS_DEFINED_MASK 0xf8000000 + +/** Object Types **/ + +#define OBJECT_TYPE_NX_SUPERBLOCK 0x00000001 + +#define OBJECT_TYPE_BTREE 0x00000002 +#define OBJECT_TYPE_BTREE_NODE 0x00000003 + +#define OBJECT_TYPE_SPACEMAN 0x00000005 +#define OBJECT_TYPE_SPACEMAN_CAB 0x00000006 +#define OBJECT_TYPE_SPACEMAN_CIB 0x00000007 +#define OBJECT_TYPE_SPACEMAN_BITMAP 0x00000008 +#define OBJECT_TYPE_SPACEMAN_FREE_QUEUE 0x00000009 + +#define OBJECT_TYPE_EXTENT_LIST_TREE 0x0000000a +#define OBJECT_TYPE_OMAP 0x0000000b +#define OBJECT_TYPE_CHECKPOINT_MAP 0x0000000c + +#define OBJECT_TYPE_FS 0x0000000d +#define OBJECT_TYPE_FSTREE 0x0000000e +#define OBJECT_TYPE_BLOCKREFTREE 0x0000000f +#define OBJECT_TYPE_SNAPMETATREE 0x00000010 + +#define OBJECT_TYPE_NX_REAPER 0x00000011 +#define OBJECT_TYPE_NX_REAP_LIST 0x00000012 +#define OBJECT_TYPE_OMAP_SNAPSHOT 0x00000013 +#define OBJECT_TYPE_EFI_JUMPSTART 0x00000014 +#define OBJECT_TYPE_FUSION_MIDDLE_TREE 0x00000015 +#define OBJECT_TYPE_NX_FUSION_WBC 0x00000016 +#define OBJECT_TYPE_NX_FUSION_WBC_LIST 0x00000017 +#define OBJECT_TYPE_ER_STATE 0x00000018 + +#define OBJECT_TYPE_GBITMAP 0x00000019 +#define OBJECT_TYPE_GBITMAP_TREE 0x0000001a +#define OBJECT_TYPE_GBITMAP_BLOCK 0x0000001b + +#define OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c +#define OBJECT_TYPE_SNAP_META_EXT 0x0000001d +#define OBJECT_TYPE_INTEGRITY_META 0x0000001e +#define OBJECT_TYPE_FEXT_TREE 0x0000001f +#define OBJECT_TYPE_RESERVED_20 0x00000020 + +#define OBJECT_TYPE_INVALID 0x00000000 +#define OBJECT_TYPE_TEST 0x000000ff + +/* + * Media keybag types (object types that correspond to an instance of + * `media_keybag_t`). The type values don't use only the low 16 bits of the + * `o_type` field. Instead, they use all 32 bits of that field, meaning these + * objects don't have type flags, and that comparing `o_type & OBJECT_TYPE_MASK` + * with these values doesn't work; you must compare `o_type` directly with these + * values instead. + */ +#define OBJECT_TYPE_CONTAINER_KEYBAG 'keys' +#define OBJECT_TYPE_VOLUME_KEYBAG 'recs' +#define OBJECT_TYPE_MEDIA_KEYBAG 'mkey' + +/** Object Type Flags **/ + +#define OBJ_VIRTUAL 0x00000000 +#define OBJ_EPHEMERAL 0x80000000 +#define OBJ_PHYSICAL 0x40000000 + +#define OBJ_NOHEADER 0x20000000 +#define OBJ_ENCRYPTED 0x10000000 +#define OBJ_NONPERSISTENT 0x08000000 + +#endif // APFS_OBJECT_H diff --git a/include/apfs/omap.h b/include/apfs/omap.h index 6072218..9c60e2c 100644 --- a/include/apfs/omap.h +++ b/include/apfs/omap.h @@ -1,83 +1,83 @@ -#ifndef APFS_OMAP_H -#define APFS_OMAP_H - -/** - * Structures and related items as defined in - * §6 Object Maps - */ - -#include -#include // paddr_t -#include // obj_phys_t, oid_t, xid_t - -/** `omap_phys_t` **/ - -typedef struct { - obj_phys_t om_o; - uint32_t om_flags; - uint32_t om_snap_count; - uint32_t om_tree_type; - uint32_t om_snapshot_tree_type; - oid_t om_tree_oid; - oid_t om_snapshot_tree_oid; - xid_t om_most_recent_snap; - xid_t om_pending_revert_min; - xid_t om_pending_revert_max; -} omap_phys_t; - -/** `omap_key_t` **/ - -typedef struct { - oid_t ok_oid; - xid_t ok_xid; -} omap_key_t; - -/** `omap_val_t` **/ - -typedef struct { - uint32_t ov_flags; - uint32_t ov_size; - paddr_t ov_paddr; -} omap_val_t; - -/** `omap_snapshot_t` **/ - -typedef struct { - uint32_t oms_flags; - uint32_t oms_pad; - oid_t oms_oid; -} omap_snapshot_t; - -/** Object Map Value Flags **/ - -#define OMAP_VAL_DELETED 0x00000001 -#define OMAP_VAL_SAVED 0x00000002 -#define OMAP_VAL_ENCRYPTED 0x00000004 -#define OMAP_VAL_NOHEADER 0x00000008 -#define OMAP_VAL_CRYPTO_GENERATION 0x00000010 - -/** Snapshot Flags **/ - -#define OMAP_SNAPSHOT_DELETED 0x00000001 -#define OMAP_SNAPSHOT_REVERTED 0x00000002 - -/** Object Map Flags **/ - -#define OMAP_MANUALLY_MANAGED 0x00000001 -#define OMAP_ENCRYPTING 0x00000002 -#define OMAP_DECRYPTING 0x00000004 -#define OMAP_KEYROLLING 0x00000008 -#define OMAP_CRYPTO_GENERATION 0x00000010 - -#define OMAP_VALID_FLAGS 0x0000001f - -/** Object Map Constants **/ - -#define OMAP_MAX_SNAP_COUNT UINT32_MAX - -/** Object Map Reaper Phases **/ - -#define OMAP_REAP_PHASE_MAP_TREE 1 -#define OMAP_REAP_PHASE_SNAPSHOT_TREE 2 - -#endif // APFS_OMAP_H +#ifndef APFS_OMAP_H +#define APFS_OMAP_H + +/** + * Structures and related items as defined in + * §6 Object Maps + */ + +#include +#include // paddr_t +#include // obj_phys_t, oid_t, xid_t + +/** `omap_phys_t` **/ + +typedef struct { + obj_phys_t om_o; + uint32_t om_flags; + uint32_t om_snap_count; + uint32_t om_tree_type; + uint32_t om_snapshot_tree_type; + oid_t om_tree_oid; + oid_t om_snapshot_tree_oid; + xid_t om_most_recent_snap; + xid_t om_pending_revert_min; + xid_t om_pending_revert_max; +} omap_phys_t; + +/** `omap_key_t` **/ + +typedef struct { + oid_t ok_oid; + xid_t ok_xid; +} omap_key_t; + +/** `omap_val_t` **/ + +typedef struct { + uint32_t ov_flags; + uint32_t ov_size; + paddr_t ov_paddr; +} omap_val_t; + +/** `omap_snapshot_t` **/ + +typedef struct { + uint32_t oms_flags; + uint32_t oms_pad; + oid_t oms_oid; +} omap_snapshot_t; + +/** Object Map Value Flags **/ + +#define OMAP_VAL_DELETED 0x00000001 +#define OMAP_VAL_SAVED 0x00000002 +#define OMAP_VAL_ENCRYPTED 0x00000004 +#define OMAP_VAL_NOHEADER 0x00000008 +#define OMAP_VAL_CRYPTO_GENERATION 0x00000010 + +/** Snapshot Flags **/ + +#define OMAP_SNAPSHOT_DELETED 0x00000001 +#define OMAP_SNAPSHOT_REVERTED 0x00000002 + +/** Object Map Flags **/ + +#define OMAP_MANUALLY_MANAGED 0x00000001 +#define OMAP_ENCRYPTING 0x00000002 +#define OMAP_DECRYPTING 0x00000004 +#define OMAP_KEYROLLING 0x00000008 +#define OMAP_CRYPTO_GENERATION 0x00000010 + +#define OMAP_VALID_FLAGS 0x0000001f + +/** Object Map Constants **/ + +#define OMAP_MAX_SNAP_COUNT UINT32_MAX + +/** Object Map Reaper Phases **/ + +#define OMAP_REAP_PHASE_MAP_TREE 1 +#define OMAP_REAP_PHASE_SNAPSHOT_TREE 2 + +#endif // APFS_OMAP_H diff --git a/include/apfs/reaper.h b/include/apfs/reaper.h index e6830eb..ff78c5c 100644 --- a/include/apfs/reaper.h +++ b/include/apfs/reaper.h @@ -1,113 +1,113 @@ -#ifndef APFS_REAPER_H -#define APFS_REAPER_H - -/** - * Structures and related items as defined in - * §17 Reaper - */ - -#include -#include // obj_phys_t, oid_t, xid_t -#include // omap_key_t - -/** `nx_repear_phys_t` **/ - -typedef struct { - obj_phys_t nr_o; - uint64_t nr_next_reap_id; - uint64_t nr_completed_id; - oid_t nr_head; - oid_t nr_tail; - uint32_t nr_flags; - uint32_t nr_rlcount; - uint32_t nr_type; - uint32_t nr_size; - oid_t nr_fs_oid; - oid_t nr_oid; - xid_t nr_xid; - uint32_t nr_nrle_flags; - uint32_t nr_state_buffer_size; - uint8_t nr_state_buffer[]; -} nx_repear_phys_t; - -/** `nx_reap_list_entry_t` --- forward declared for `nx_reap_list_phys_t` **/ - -typedef struct { - uint32_t nrle_next; - uint32_t nrle_flags; - uint32_t nrle_type; - uint32_t nrle_size; - oid_t nrle_fs_oid; - oid_t nrle_oid; - xid_t nrle_xid; -} nx_reap_list_entry_t; - -/** `nx_reap_list_phys_t` **/ - -typedef struct { - obj_phys_t nrl_o; - oid_t nrl_next; - uint32_t nrl_flags; - uint32_t nrl_max; - uint32_t nrl_count; - uint32_t nrl_first; - uint32_t nrl_last; - uint32_t nrl_free; - nx_reap_list_entry_t nrl_entries[]; -} nx_reap_list_phys_t; - -/** Volume Reaper States **/ - -enum { - APFS_REAP_PHASE_START = 0, - APFS_REAP_PHASE_SNAPSHOTS = 1, - APFS_REAP_PHASE_ACTIVE_FS = 2, - APFS_REAP_PHASE_DESTROY_OMAP = 3, - APFS_REAP_PHASE_DONE = 4, -}; - -/** Reaper Flags **/ - -#define NR_BHM_FLAG 0x00000001 -#define NR_CONTINUE 0x00000002 - -/** Reaper List Entry Flags **/ - -#define NRLE_VALID 0x00000001 -#define NRLE_REAP_ID_RECORD 0x00000002 -#define NRLE_CALL 0x00000004 -#define NRLE_COMPETITION 0x00000008 -#define NRLE_CLEANUP 0x00000010 - -/** Reaper List Flags **/ - -#define NRL_INDEX_INVALID 0xffffffff - -/** `omap_reap_state_t` **/ - -typedef struct { - uint32_t omr_phase; - omap_key_t omr_ok; -} omap_reap_state_t; - -/** `omap_cleanup_state_t` **/ - -typedef struct { - uint32_t omc_cleaning; - uint32_t omc_omsflags; - xid_t omc_sxidprev; - xid_t omc_sxidstart; - xid_t omc_sxidenf; - xid_t omc_sxidnext; - omap_key_t omc_curkey; -} omap_cleanup_state_t; - -/** `apfs_reap_state_t` **/ - -typedef struct { - uint64_t last_pbn; - xid_t cur_snap_xid; - uint32_t phase; -} __attribute__((packed)) apfs_reap_state_t; - -#endif // APFS_REAPER_H +#ifndef APFS_REAPER_H +#define APFS_REAPER_H + +/** + * Structures and related items as defined in + * §17 Reaper + */ + +#include +#include // obj_phys_t, oid_t, xid_t +#include // omap_key_t + +/** `nx_repear_phys_t` **/ + +typedef struct { + obj_phys_t nr_o; + uint64_t nr_next_reap_id; + uint64_t nr_completed_id; + oid_t nr_head; + oid_t nr_tail; + uint32_t nr_flags; + uint32_t nr_rlcount; + uint32_t nr_type; + uint32_t nr_size; + oid_t nr_fs_oid; + oid_t nr_oid; + xid_t nr_xid; + uint32_t nr_nrle_flags; + uint32_t nr_state_buffer_size; + uint8_t nr_state_buffer[]; +} nx_repear_phys_t; + +/** `nx_reap_list_entry_t` --- forward declared for `nx_reap_list_phys_t` **/ + +typedef struct { + uint32_t nrle_next; + uint32_t nrle_flags; + uint32_t nrle_type; + uint32_t nrle_size; + oid_t nrle_fs_oid; + oid_t nrle_oid; + xid_t nrle_xid; +} nx_reap_list_entry_t; + +/** `nx_reap_list_phys_t` **/ + +typedef struct { + obj_phys_t nrl_o; + oid_t nrl_next; + uint32_t nrl_flags; + uint32_t nrl_max; + uint32_t nrl_count; + uint32_t nrl_first; + uint32_t nrl_last; + uint32_t nrl_free; + nx_reap_list_entry_t nrl_entries[]; +} nx_reap_list_phys_t; + +/** Volume Reaper States **/ + +enum { + APFS_REAP_PHASE_START = 0, + APFS_REAP_PHASE_SNAPSHOTS = 1, + APFS_REAP_PHASE_ACTIVE_FS = 2, + APFS_REAP_PHASE_DESTROY_OMAP = 3, + APFS_REAP_PHASE_DONE = 4, +}; + +/** Reaper Flags **/ + +#define NR_BHM_FLAG 0x00000001 +#define NR_CONTINUE 0x00000002 + +/** Reaper List Entry Flags **/ + +#define NRLE_VALID 0x00000001 +#define NRLE_REAP_ID_RECORD 0x00000002 +#define NRLE_CALL 0x00000004 +#define NRLE_COMPETITION 0x00000008 +#define NRLE_CLEANUP 0x00000010 + +/** Reaper List Flags **/ + +#define NRL_INDEX_INVALID 0xffffffff + +/** `omap_reap_state_t` **/ + +typedef struct { + uint32_t omr_phase; + omap_key_t omr_ok; +} omap_reap_state_t; + +/** `omap_cleanup_state_t` **/ + +typedef struct { + uint32_t omc_cleaning; + uint32_t omc_omsflags; + xid_t omc_sxidprev; + xid_t omc_sxidstart; + xid_t omc_sxidenf; + xid_t omc_sxidnext; + omap_key_t omc_curkey; +} omap_cleanup_state_t; + +/** `apfs_reap_state_t` **/ + +typedef struct { + uint64_t last_pbn; + xid_t cur_snap_xid; + uint32_t phase; +} __attribute__((packed)) apfs_reap_state_t; + +#endif // APFS_REAPER_H diff --git a/include/apfs/sealed.h b/include/apfs/sealed.h index de13139..01b023d 100644 --- a/include/apfs/sealed.h +++ b/include/apfs/sealed.h @@ -1,111 +1,111 @@ -#ifndef APFS_SEALED_H -#define APFS_SEALED_H - -/** - * Structures and related items as defined in - * §16 Sealed Volumes - */ - -#include -#include -#include - -/** `apfs_hash_type_t` --- forward declared for `integrity_meta_phys_t` **/ - -typedef enum { - APFS_HASH_INVALID = 0, - APFS_HASH_SHA256 = 0x1, - APFS_HASH_SHA512_256 = 0x2, - APFS_HASH_SHA384 = 0x3, - APFS_HASH_SHA512 = 0x4, - - APFS_HASH_MIN = APFS_HASH_SHA256, - APFS_HASH_MAX = APFS_HASH_SHA512, - - APFS_HASH_DEFAULT = APFS_HASH_SHA256, -} apfs_hash_type_t; - -#define APFS_HASH_CCSHA256_SIZE 32 -#define APFS_HASH_CCSHA512_256_SIZE 32 -#define APFS_HASH_CCSHA384_SIZE 48 -#define APFS_HASH_CCSHA512_SIZE 64 - -#define APFS_HASH_MAX_SIZE 64 - -/** `integrity_meta_phys_t` **/ - -typedef struct { - obj_phys_t im_o; - uint32_t im_version; - - // Fields supported by `im_version` >= 1 - uint32_t im_flags; - apfs_hash_type_t im_hash_type; - uint32_t im_root_hash_offset; - xid_t im_broken_xid; - - // Fields supported by `im_version` >= 2 - uint64_t im_reserved[9]; -} __attribute__((packed)) integrity_meta_phys_t; - -/** Integrity Metadata Version Constants **/ - -enum { - INTEGRITY_META_VERSION_INVALID = 0, - INTEGRITY_META_VERSION_1 = 1, - INTEGRITY_META_VERSION_2 = 2, - INTEGRITY_META_VERSION_HIGHEST = INTEGRITY_META_VERSION_2, -}; - -/** Integrity Metadata Flags **/ - -#define APFS_SEAL_BROKEN (1U << 0) - -/** `fext_tree_key_t` **/ - -typedef struct { - uint64_t private_id; - uint64_t logical_addr; -} __attribute__((packed)) fext_tree_key_t; - -/** `fext_tree_val_t` **/ - -typedef struct { - uint64_t len_and_flags; - uint64_t phys_block_num; -} __attribute__((packed)) fext_tree_val_t; - -/** `j_file_info_key_t` **/ - -typedef struct { - j_key_t hdr; - uint64_t info_and_lba; -} __attribute__((packed)) j_file_info_key_t; - -#define J_FILE_INFO_LBA_MASK 0x00ffffffffffffffULL -#define J_FILE_INFO_TYPE_MASK 0xff00000000000000ULL -#define J_FILE_INFO_TYPE_SHIFT 56 - -/** `j_file_data_hash_val_t` --- forward declared for `j_file_info_val_t` **/ - -typedef struct { - uint16_t hashed_len; - uint8_t hash_size; - uint8_t hash[0]; -} __attribute__((packed)) j_file_data_hash_val_t; - -/** `j_file_info_val_t` **/ - -typedef struct { - union { - j_file_data_hash_val_t dhash; - }; -} __attribute__((packed)) j_file_info_val_t; - -/** `j_obj_file_info_type` **/ - -typedef enum { - APFS_FILE_INFO_DATA_HASH = 1, -} j_obj_file_info_type; - -#endif // APFS_SEALED_H +#ifndef APFS_SEALED_H +#define APFS_SEALED_H + +/** + * Structures and related items as defined in + * §16 Sealed Volumes + */ + +#include +#include +#include + +/** `apfs_hash_type_t` --- forward declared for `integrity_meta_phys_t` **/ + +typedef enum { + APFS_HASH_INVALID = 0, + APFS_HASH_SHA256 = 0x1, + APFS_HASH_SHA512_256 = 0x2, + APFS_HASH_SHA384 = 0x3, + APFS_HASH_SHA512 = 0x4, + + APFS_HASH_MIN = APFS_HASH_SHA256, + APFS_HASH_MAX = APFS_HASH_SHA512, + + APFS_HASH_DEFAULT = APFS_HASH_SHA256, +} apfs_hash_type_t; + +#define APFS_HASH_CCSHA256_SIZE 32 +#define APFS_HASH_CCSHA512_256_SIZE 32 +#define APFS_HASH_CCSHA384_SIZE 48 +#define APFS_HASH_CCSHA512_SIZE 64 + +#define APFS_HASH_MAX_SIZE 64 + +/** `integrity_meta_phys_t` **/ + +typedef struct { + obj_phys_t im_o; + uint32_t im_version; + + // Fields supported by `im_version` >= 1 + uint32_t im_flags; + apfs_hash_type_t im_hash_type; + uint32_t im_root_hash_offset; + xid_t im_broken_xid; + + // Fields supported by `im_version` >= 2 + uint64_t im_reserved[9]; +} __attribute__((packed)) integrity_meta_phys_t; + +/** Integrity Metadata Version Constants **/ + +enum { + INTEGRITY_META_VERSION_INVALID = 0, + INTEGRITY_META_VERSION_1 = 1, + INTEGRITY_META_VERSION_2 = 2, + INTEGRITY_META_VERSION_HIGHEST = INTEGRITY_META_VERSION_2, +}; + +/** Integrity Metadata Flags **/ + +#define APFS_SEAL_BROKEN (1U << 0) + +/** `fext_tree_key_t` **/ + +typedef struct { + uint64_t private_id; + uint64_t logical_addr; +} __attribute__((packed)) fext_tree_key_t; + +/** `fext_tree_val_t` **/ + +typedef struct { + uint64_t len_and_flags; + uint64_t phys_block_num; +} __attribute__((packed)) fext_tree_val_t; + +/** `j_file_info_key_t` **/ + +typedef struct { + j_key_t hdr; + uint64_t info_and_lba; +} __attribute__((packed)) j_file_info_key_t; + +#define J_FILE_INFO_LBA_MASK 0x00ffffffffffffffULL +#define J_FILE_INFO_TYPE_MASK 0xff00000000000000ULL +#define J_FILE_INFO_TYPE_SHIFT 56 + +/** `j_file_data_hash_val_t` --- forward declared for `j_file_info_val_t` **/ + +typedef struct { + uint16_t hashed_len; + uint8_t hash_size; + uint8_t hash[0]; +} __attribute__((packed)) j_file_data_hash_val_t; + +/** `j_file_info_val_t` **/ + +typedef struct { + union { + j_file_data_hash_val_t dhash; + }; +} __attribute__((packed)) j_file_info_val_t; + +/** `j_obj_file_info_type` **/ + +typedef enum { + APFS_FILE_INFO_DATA_HASH = 1, +} j_obj_file_info_type; + +#endif // APFS_SEALED_H diff --git a/include/apfs/sibling.h b/include/apfs/sibling.h index c798b7f..11cfd5c 100644 --- a/include/apfs/sibling.h +++ b/include/apfs/sibling.h @@ -1,39 +1,39 @@ -#ifndef APFS_SIBLING_H -#define APFS_SIBLING_H - -/** - * Structures and related items as defined in - * §12 Siblings - */ - -#include -#include // j_key_t - -/** `j_sibling_key_t` **/ - -typedef struct { - j_key_t hdr; - uint64_t sibling_id; -} __attribute__((packed)) j_sibling_key_t; - -/** `j_sibling_val_t` **/ - -typedef struct { - uint64_t parent_id; - uint16_t name_len; - uint8_t name[0]; -} __attribute__((packed)) j_sibling_val_t; - -/** `j_sibling_map_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_sibling_map_key_t; - -/** `j_sibling_map_val_t` **/ - -typedef struct { - uint64_t file_id; -} __attribute__((packed)) j_sibling_map_val_t; - -#endif // APFS_SIBLING_H +#ifndef APFS_SIBLING_H +#define APFS_SIBLING_H + +/** + * Structures and related items as defined in + * §12 Siblings + */ + +#include +#include // j_key_t + +/** `j_sibling_key_t` **/ + +typedef struct { + j_key_t hdr; + uint64_t sibling_id; +} __attribute__((packed)) j_sibling_key_t; + +/** `j_sibling_val_t` **/ + +typedef struct { + uint64_t parent_id; + uint16_t name_len; + uint8_t name[0]; +} __attribute__((packed)) j_sibling_val_t; + +/** `j_sibling_map_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_sibling_map_key_t; + +/** `j_sibling_map_val_t` **/ + +typedef struct { + uint64_t file_id; +} __attribute__((packed)) j_sibling_map_val_t; + +#endif // APFS_SIBLING_H diff --git a/include/apfs/snap.h b/include/apfs/snap.h index 83e6114..93e2921 100644 --- a/include/apfs/snap.h +++ b/include/apfs/snap.h @@ -1,73 +1,73 @@ -#ifndef APFS_SNAP_H -#define APFS_SNAP_H - -/** - * Structures and related items as defined in - * §13 Snapshot Metadata - */ - -#include -#include // uuid_t -#include // j_key_t -#include // oid_t, xid_t - -/** `j_snap_metadata_key_t` **/ - -typedef struct { - j_key_t hdr; -} __attribute__((packed)) j_snap_metadata_key_t; - -/** `j_snap_metadata_val_t` **/ - -typedef struct { - oid_t extentref_tree_oid; - oid_t sblock_oid; - uint64_t create_time; - uint64_t change_time; - uint64_t inum; - uint32_t extentref_tree_type; - uint32_t flags; - uint16_t name_len; - uint8_t name[0]; -} __attribute__((packed)) j_snap_metadata_val_t; - -/** `j_snap_name_key_t` **/ - -typedef struct { - j_key_t hdr; - uint16_t name_len; - uint8_t name[0]; -} __attribute__((packed)) j_snap_name_key_t; - -/** `j_snap_name_val_t` **/ - -typedef struct { - xid_t snap_xid; -} __attribute__((packed)) j_snap_name_val_t; - -/** `snap_meta_flags` **/ - -typedef enum { - SNAP_META_PENDING_DATALESS = 0x00000001, -} snap_meta_flags; - -/** `snap_meta_ext_t` --- forward declared for `snap_meta_ext_obj_phys_t` **/ - -typedef struct { - uint32_t sme_version; - - uint32_t sme_flags; - xid_t sme_snap_xid; - uuid_t sme_uuid; - - uint64_t sme_token; -} __attribute__((packed)) snap_meta_ext_t; - -/** `snap_meta_ext_obj_phys_t` **/ - -typedef struct { - obj_phys_t smeop_o; - snap_meta_ext_t smeop_sme; -} __attribute__((packed)) snap_meta_ext_obj_phys_t; - -#endif // APFS_SNAP_H +#ifndef APFS_SNAP_H +#define APFS_SNAP_H + +/** + * Structures and related items as defined in + * §13 Snapshot Metadata + */ + +#include +#include // uuid_t +#include // j_key_t +#include // oid_t, xid_t + +/** `j_snap_metadata_key_t` **/ + +typedef struct { + j_key_t hdr; +} __attribute__((packed)) j_snap_metadata_key_t; + +/** `j_snap_metadata_val_t` **/ + +typedef struct { + oid_t extentref_tree_oid; + oid_t sblock_oid; + uint64_t create_time; + uint64_t change_time; + uint64_t inum; + uint32_t extentref_tree_type; + uint32_t flags; + uint16_t name_len; + uint8_t name[0]; +} __attribute__((packed)) j_snap_metadata_val_t; + +/** `j_snap_name_key_t` **/ + +typedef struct { + j_key_t hdr; + uint16_t name_len; + uint8_t name[0]; +} __attribute__((packed)) j_snap_name_key_t; + +/** `j_snap_name_val_t` **/ + +typedef struct { + xid_t snap_xid; +} __attribute__((packed)) j_snap_name_val_t; + +/** `snap_meta_flags` **/ + +typedef enum { + SNAP_META_PENDING_DATALESS = 0x00000001, +} snap_meta_flags; + +/** `snap_meta_ext_t` --- forward declared for `snap_meta_ext_obj_phys_t` **/ + +typedef struct { + uint32_t sme_version; + + uint32_t sme_flags; + xid_t sme_snap_xid; + uuid_t sme_uuid; + + uint64_t sme_token; +} __attribute__((packed)) snap_meta_ext_t; + +/** `snap_meta_ext_obj_phys_t` **/ + +typedef struct { + obj_phys_t smeop_o; + snap_meta_ext_t smeop_sme; +} __attribute__((packed)) snap_meta_ext_obj_phys_t; + +#endif // APFS_SNAP_H diff --git a/include/apfs/spaceman.h b/include/apfs/spaceman.h index 2524a41..61a9c73 100644 --- a/include/apfs/spaceman.h +++ b/include/apfs/spaceman.h @@ -1,179 +1,179 @@ -#ifndef APFS_SPACEMAN_H -#define APFS_SPACEMAN_H - -/** - * Structures and related items as defined in - * §17 Space Manager - */ - -#include -#include // paddr_t -#include // obj_phys_t, xid_t - -/** `chunk_info_t` **/ - -typedef struct { - xid_t ci_xid; // Spec says `uint64_t`, but I assume it is meant to be `xid_t`; they're equivalent, anyway - uint64_t ci_addr; - uint32_t ci_block_count; - uint32_t ci_free_count; - paddr_t ci_bitmap_addr; -} chunk_info_t; - -/** `chunk_info_block_t` **/ - -typedef struct { - obj_phys_t cib_o; - uint32_t cib_index; - uint32_t cib_chunk_info_count; - chunk_info_t cib_chunk_info[]; -} chunk_info_block_t; - -/** `cib_addr_block` **/ - -typedef struct { - obj_phys_t cab_o; - uint32_t cab_index; - uint32_t cab_cib_count; - paddr_t cab_cib_addr[]; -} cib_addr_block_t; - -/** `spaceman_free_queue_key_t` --- forward declared for `spaceman_free_queue_entry_t` **/ - -typedef struct { - xid_t sfqk_xid; - paddr_t sfqk_paddr; -} spaceman_free_queue_key_t; - -/** `spaceman_free_queue_val_t` --- forward declared for `spaceman_free_queue_entry_t` **/ - -typedef uint64_t spaceman_free_queue_val_t; - -/** `spaceman_free_queue_entry_t` **/ - -typedef struct { - spaceman_free_queue_key_t sfqe_key; - spaceman_free_queue_val_t sfqe_count; -} spaceman_free_queue_entry_t; - -/** `spaceman_free_queue_t` **/ - -typedef struct { - uint64_t sfq_count; - oid_t sfq_tree_oid; - xid_t sfq_oldest_xid; - uint16_t sfq_tree_node_limit; - uint16_t sfq_pad16; - uint32_t sfq_pad32; - uint64_t sfq_reserved; -} spaceman_free_queue_t; - -/** `spaceman_device_t` **/ - -typedef struct { - uint64_t sm_block_count; - uint64_t sm_chunk_count; - uint32_t sm_cib_count; - uint32_t sm_cab_count; - uint64_t sm_free_count; - uint32_t sm_addr_offset; - uint32_t sm_reserved; - uint64_t sm_reserved2; -} spaceman_device_t; - -/** `spaceman_allocation_zone_boundaries_t` **/ - -typedef struct { - uint64_t saz_zone_start; - uint64_t saz_zone_end; -} spaceman_allocation_zone_boundaries_t; - -/** `spaceman_allocation_zone_info_phys_t` **/ - -#define SM_ALLOCZONE_INVALID_END_BOUNDARY 0 -#define SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES 7 - -typedef struct { - spaceman_allocation_zone_boundaries_t - saz_current_boundaries; - spaceman_allocation_zone_boundaries_t - saz_previous_boundaries[SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES]; - uint16_t saz_zone_id; - uint16_t saz_previous_boundary_index; - uint32_t saz_reserved; -} spaceman_allocation_zone_info_phys_t; - -/** `smdev` --- forward declared for `spaceman[_datazone_info]_phys_t` **/ - -enum smdev { - SD_MAIN = 0, - SD_TIER2 = 1, - SD_COUNT = 2, -}; - -/** `spaceman_datazone_info_phys_t` **/ - -#define SM_DATAZONE_ALLOCZONE_COUNT 8 - -typedef struct { - spaceman_allocation_zone_info_phys_t - sdz_allocation_zones[SD_COUNT][SM_DATAZONE_ALLOCZONE_COUNT]; -} spaceman_datazone_info_phys_t; - -/** `sfq` --- forward declared for `spaceman_phys_t` **/ - -enum sfq { - SFQ_IP = 0, - SFQ_MAIN = 1, - SFQ_TIER2 = 2, - SFQ_COUNT = 3, -}; - -/** `spaceman_phys_t` **/ - -typedef struct { - obj_phys_t sm_o; - uint32_t sm_block_size; - uint32_t sm_blocks_per_chunk; - uint32_t sm_chunks_per_cib; - uint32_t sm_cibs_per_cab; - - spaceman_device_t sm_dev[SD_COUNT]; - - uint32_t sm_flags; - uint32_t sm_ip_bm_tx_multiplier; - uint64_t sm_ip_block_count; - uint32_t sm_ip_bm_size_in_blocks; - uint32_t sm_ip_bm_block_count; - paddr_t sm_ip_bm_base; - paddr_t sm_ip_base; - uint64_t sm_fs_reserve_block_count; - uint64_t sm_fs_reserve_alloc_count; - - spaceman_free_queue_t sm_fq[SFQ_COUNT]; - - uint16_t sm_ip_bm_free_head; - uint16_t sm_ip_bm_free_tail; - uint32_t sm_ip_bm_xid_offset; - uint32_t sm_ip_bitmap_offset; - uint32_t sm_ip_bm_free_next_offset; - uint32_t sm_version; - uint32_t sm_struct_size; - - spaceman_datazone_info_phys_t sm_datazone; -} spaceman_phys_t; - -#define SM_FLAG_VERSIONED 0x00000001 - -/** Chunk Info Block Constants **/ - -#define CI_COUNT_MASK 0x000fffff -#define CI_COUNT_RESERVED_MASK 0xfff00000 - -/** Internal-Pool Bitmap **/ - -#define SPACEMAN_IP_BM_TX_MULTIPLIER 16 -#define SPACEMAN_IP_BM_INDEX_INVALID 0xfff -#define SPACEMAN_IP_BM_BLOCK_COUNT_MAX 0xffe - -#endif // APFS_SPACEMAN_H +#ifndef APFS_SPACEMAN_H +#define APFS_SPACEMAN_H + +/** + * Structures and related items as defined in + * §17 Space Manager + */ + +#include +#include // paddr_t +#include // obj_phys_t, xid_t + +/** `chunk_info_t` **/ + +typedef struct { + xid_t ci_xid; // Spec says `uint64_t`, but I assume it is meant to be `xid_t`; they're equivalent, anyway + uint64_t ci_addr; + uint32_t ci_block_count; + uint32_t ci_free_count; + paddr_t ci_bitmap_addr; +} chunk_info_t; + +/** `chunk_info_block_t` **/ + +typedef struct { + obj_phys_t cib_o; + uint32_t cib_index; + uint32_t cib_chunk_info_count; + chunk_info_t cib_chunk_info[]; +} chunk_info_block_t; + +/** `cib_addr_block` **/ + +typedef struct { + obj_phys_t cab_o; + uint32_t cab_index; + uint32_t cab_cib_count; + paddr_t cab_cib_addr[]; +} cib_addr_block_t; + +/** `spaceman_free_queue_key_t` --- forward declared for `spaceman_free_queue_entry_t` **/ + +typedef struct { + xid_t sfqk_xid; + paddr_t sfqk_paddr; +} spaceman_free_queue_key_t; + +/** `spaceman_free_queue_val_t` --- forward declared for `spaceman_free_queue_entry_t` **/ + +typedef uint64_t spaceman_free_queue_val_t; + +/** `spaceman_free_queue_entry_t` **/ + +typedef struct { + spaceman_free_queue_key_t sfqe_key; + spaceman_free_queue_val_t sfqe_count; +} spaceman_free_queue_entry_t; + +/** `spaceman_free_queue_t` **/ + +typedef struct { + uint64_t sfq_count; + oid_t sfq_tree_oid; + xid_t sfq_oldest_xid; + uint16_t sfq_tree_node_limit; + uint16_t sfq_pad16; + uint32_t sfq_pad32; + uint64_t sfq_reserved; +} spaceman_free_queue_t; + +/** `spaceman_device_t` **/ + +typedef struct { + uint64_t sm_block_count; + uint64_t sm_chunk_count; + uint32_t sm_cib_count; + uint32_t sm_cab_count; + uint64_t sm_free_count; + uint32_t sm_addr_offset; + uint32_t sm_reserved; + uint64_t sm_reserved2; +} spaceman_device_t; + +/** `spaceman_allocation_zone_boundaries_t` **/ + +typedef struct { + uint64_t saz_zone_start; + uint64_t saz_zone_end; +} spaceman_allocation_zone_boundaries_t; + +/** `spaceman_allocation_zone_info_phys_t` **/ + +#define SM_ALLOCZONE_INVALID_END_BOUNDARY 0 +#define SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES 7 + +typedef struct { + spaceman_allocation_zone_boundaries_t + saz_current_boundaries; + spaceman_allocation_zone_boundaries_t + saz_previous_boundaries[SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES]; + uint16_t saz_zone_id; + uint16_t saz_previous_boundary_index; + uint32_t saz_reserved; +} spaceman_allocation_zone_info_phys_t; + +/** `smdev` --- forward declared for `spaceman[_datazone_info]_phys_t` **/ + +enum smdev { + SD_MAIN = 0, + SD_TIER2 = 1, + SD_COUNT = 2, +}; + +/** `spaceman_datazone_info_phys_t` **/ + +#define SM_DATAZONE_ALLOCZONE_COUNT 8 + +typedef struct { + spaceman_allocation_zone_info_phys_t + sdz_allocation_zones[SD_COUNT][SM_DATAZONE_ALLOCZONE_COUNT]; +} spaceman_datazone_info_phys_t; + +/** `sfq` --- forward declared for `spaceman_phys_t` **/ + +enum sfq { + SFQ_IP = 0, + SFQ_MAIN = 1, + SFQ_TIER2 = 2, + SFQ_COUNT = 3, +}; + +/** `spaceman_phys_t` **/ + +typedef struct { + obj_phys_t sm_o; + uint32_t sm_block_size; + uint32_t sm_blocks_per_chunk; + uint32_t sm_chunks_per_cib; + uint32_t sm_cibs_per_cab; + + spaceman_device_t sm_dev[SD_COUNT]; + + uint32_t sm_flags; + uint32_t sm_ip_bm_tx_multiplier; + uint64_t sm_ip_block_count; + uint32_t sm_ip_bm_size_in_blocks; + uint32_t sm_ip_bm_block_count; + paddr_t sm_ip_bm_base; + paddr_t sm_ip_base; + uint64_t sm_fs_reserve_block_count; + uint64_t sm_fs_reserve_alloc_count; + + spaceman_free_queue_t sm_fq[SFQ_COUNT]; + + uint16_t sm_ip_bm_free_head; + uint16_t sm_ip_bm_free_tail; + uint32_t sm_ip_bm_xid_offset; + uint32_t sm_ip_bitmap_offset; + uint32_t sm_ip_bm_free_next_offset; + uint32_t sm_version; + uint32_t sm_struct_size; + + spaceman_datazone_info_phys_t sm_datazone; +} spaceman_phys_t; + +#define SM_FLAG_VERSIONED 0x00000001 + +/** Chunk Info Block Constants **/ + +#define CI_COUNT_MASK 0x000fffff +#define CI_COUNT_RESERVED_MASK 0xfff00000 + +/** Internal-Pool Bitmap **/ + +#define SPACEMAN_IP_BM_TX_MULTIPLIER 16 +#define SPACEMAN_IP_BM_INDEX_INVALID 0xfff +#define SPACEMAN_IP_BM_BLOCK_COUNT_MAX 0xffe + +#endif // APFS_SPACEMAN_H diff --git a/include/drat/asize.h b/include/drat/asize.h index 08528e1..d57c208 100644 --- a/include/drat/asize.h +++ b/include/drat/asize.h @@ -1,6 +1,6 @@ -#ifndef DRAT_ASIZE_H -#define DRAT_ASIZE_H - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - -#endif // DRAT_ASIZE_H +#ifndef DRAT_ASIZE_H +#define DRAT_ASIZE_H + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#endif // DRAT_ASIZE_H diff --git a/include/drat/func/README.md b/include/drat/func/README.md index 8fc0a7c..c1f9959 100644 --- a/include/drat/func/README.md +++ b/include/drat/func/README.md @@ -1,7 +1,7 @@ -# `include/drat/func` - -This directory contains functions which are useful to us, but which are neither: - -1. defined explcitly in the APFS spec; nor -2. which are used to produce human-readable descriptions of the data contained - within APFS data structures. +# `include/drat/func` + +This directory contains functions which are useful to us, but which are neither: + +1. defined explcitly in the APFS spec; nor +2. which are used to produce human-readable descriptions of the data contained + within APFS data structures. diff --git a/include/drat/func/boolean.c b/include/drat/func/boolean.c index 06b219b..e617d2a 100644 --- a/include/drat/func/boolean.c +++ b/include/drat/func/boolean.c @@ -1,80 +1,80 @@ -/** - * Boolean functions to check whether a given condition holds. - */ - -#include "boolean.h" - -/** - * Determine whether a given APFS object is of the Physical storage type. - */ -bool is_physical(obj_phys_t* obj) { - return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_PHYSICAL; -} - -/** - * Determine whether a given APFS object is of the Ephemeral storage type. - */ -bool is_ephemeral(obj_phys_t* obj) { - return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_EPHEMERAL; -} - -/** - * Determine whether a given APFS object is of the Virtual storage type. - */ -bool is_virtual(obj_phys_t* obj) { - return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_VIRTUAL; -} - -/** - * Determine whether a given APFS block is a container superblock based on the - * type flag in its header. - */ -bool is_nx_superblock(obj_phys_t* obj) { - return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_NX_SUPERBLOCK; -} - -/** - * Determine whether a given APFS block is a checkpoint mapping block based on - * the type flag in its header. - */ -bool is_checkpoint_map_phys(obj_phys_t* obj) { - return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_CHECKPOINT_MAP; -} - -/** - * Determine whether a given APFS object is the root node of a B-tree. - */ -bool is_btree_node_phys_root(obj_phys_t* obj) { - return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE; -} - -/** - * Determine whether a given APFS object is a non-root node of a B-tree. - */ -bool is_btree_node_phys_non_root(obj_phys_t* obj) { - return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE_NODE; -} - -/** - * Determine whether a given APFS object is a B-tree node (regardless of - * whether it is a root node or otherwise). - */ -bool is_btree_node_phys(obj_phys_t* obj) { - return is_btree_node_phys_root(obj) || is_btree_node_phys_non_root(obj); -} - -/** - * Determine whether a given APFS object has the subtype corresponding to an - * object map tree. - */ -bool is_omap_tree(obj_phys_t* obj) { - return obj->o_subtype == OBJECT_TYPE_OMAP; -} - -/** - * Determine whether a given APFS object has the subtype corresponding to a - * file-system records tree. - */ -bool is_fs_tree(obj_phys_t* obj) { - return obj->o_subtype == OBJECT_TYPE_FSTREE; -} +/** + * Boolean functions to check whether a given condition holds. + */ + +#include "boolean.h" + +/** + * Determine whether a given APFS object is of the Physical storage type. + */ +bool is_physical(obj_phys_t* obj) { + return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_PHYSICAL; +} + +/** + * Determine whether a given APFS object is of the Ephemeral storage type. + */ +bool is_ephemeral(obj_phys_t* obj) { + return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_EPHEMERAL; +} + +/** + * Determine whether a given APFS object is of the Virtual storage type. + */ +bool is_virtual(obj_phys_t* obj) { + return (obj->o_type & OBJ_STORAGETYPE_MASK) == OBJ_VIRTUAL; +} + +/** + * Determine whether a given APFS block is a container superblock based on the + * type flag in its header. + */ +bool is_nx_superblock(obj_phys_t* obj) { + return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_NX_SUPERBLOCK; +} + +/** + * Determine whether a given APFS block is a checkpoint mapping block based on + * the type flag in its header. + */ +bool is_checkpoint_map_phys(obj_phys_t* obj) { + return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_CHECKPOINT_MAP; +} + +/** + * Determine whether a given APFS object is the root node of a B-tree. + */ +bool is_btree_node_phys_root(obj_phys_t* obj) { + return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE; +} + +/** + * Determine whether a given APFS object is a non-root node of a B-tree. + */ +bool is_btree_node_phys_non_root(obj_phys_t* obj) { + return (obj->o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE_NODE; +} + +/** + * Determine whether a given APFS object is a B-tree node (regardless of + * whether it is a root node or otherwise). + */ +bool is_btree_node_phys(obj_phys_t* obj) { + return is_btree_node_phys_root(obj) || is_btree_node_phys_non_root(obj); +} + +/** + * Determine whether a given APFS object has the subtype corresponding to an + * object map tree. + */ +bool is_omap_tree(obj_phys_t* obj) { + return obj->o_subtype == OBJECT_TYPE_OMAP; +} + +/** + * Determine whether a given APFS object has the subtype corresponding to a + * file-system records tree. + */ +bool is_fs_tree(obj_phys_t* obj) { + return obj->o_subtype == OBJECT_TYPE_FSTREE; +} diff --git a/include/drat/func/boolean.h b/include/drat/func/boolean.h index a91a51a..08b9cd4 100644 --- a/include/drat/func/boolean.h +++ b/include/drat/func/boolean.h @@ -1,18 +1,18 @@ -#ifndef DRAT_FUNC_BOOLEAN_H -#define DRAT_FUNC_BOOLEAN_H - -#include -#include - -bool is_physical (obj_phys_t* obj); -bool is_ephemeral (obj_phys_t* obj); -bool is_virtual (obj_phys_t* obj); -bool is_nx_superblock (obj_phys_t* obj); -bool is_checkpoint_map_phys (obj_phys_t* obj); -bool is_btree_node_phys_root (obj_phys_t* obj); -bool is_btree_node_phys_non_root(obj_phys_t* obj); -bool is_btree_node_phys (obj_phys_t* obj); -bool is_omap_tree (obj_phys_t* obj); -bool is_fs_tree (obj_phys_t* obj); - -#endif // DRAT_FUNC_BOOLEAN_H +#ifndef DRAT_FUNC_BOOLEAN_H +#define DRAT_FUNC_BOOLEAN_H + +#include +#include + +bool is_physical (obj_phys_t* obj); +bool is_ephemeral (obj_phys_t* obj); +bool is_virtual (obj_phys_t* obj); +bool is_nx_superblock (obj_phys_t* obj); +bool is_checkpoint_map_phys (obj_phys_t* obj); +bool is_btree_node_phys_root (obj_phys_t* obj); +bool is_btree_node_phys_non_root(obj_phys_t* obj); +bool is_btree_node_phys (obj_phys_t* obj); +bool is_omap_tree (obj_phys_t* obj); +bool is_fs_tree (obj_phys_t* obj); + +#endif // DRAT_FUNC_BOOLEAN_H diff --git a/include/drat/func/btree.h b/include/drat/func/btree.h index e930dec..a512b68 100644 --- a/include/drat/func/btree.h +++ b/include/drat/func/btree.h @@ -1,49 +1,49 @@ -#ifndef DRAT_FUNC_BTREE_H -#define DRAT_FUNC_BTREE_H - -#include -#include - -/** - * Custom data structure used to store the key and value of an object map entry - * together. - */ -typedef struct { - omap_key_t key; - omap_val_t val; -} omap_entry_t; - -omap_entry_t* get_btree_phys_omap_entry(btree_node_phys_t* root_node, oid_t oid, xid_t max_xid); - -/** - * Custom data structure used to store a full file-system record (i.e. a single - * key–value pair from a file-system root tree) alongside each other for easier - * data access and manipulation. - * - * One can make use of an instance of this datatype by determining the strctures - * contained within its `data` field by appealing to the `obj_id_and_type` field - * of the `j_key_t` structure for the record, which is guaranteed to exist and - * start at `data[0]`. That is, a pointer to this instance of `j_key_t` can be - * obtained with `j_key_t* record_header = record->data`, where `record` is an - * instance of this type, `j_rec_t`. - * - * key_len: Length of the file-system record's key-part, in bytes. - * - * val_len: Length of the file-system record's value-part, in bytes. - * - * data: Array of `key_len + val_len` bytes of data, of which, - * index `0` through `key_len - 1` (inclusive) contain the - * key-part data, and index `key_len` through `key_len + val_len - 1` - * (inclusive) contain the value-part data. - */ -typedef struct { - uint16_t key_len; - uint16_t val_len; - char data[]; -} j_rec_t; - -void free_j_rec_array(j_rec_t** records_array); - -j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_t* vol_fs_root_node, oid_t oid, xid_t max_xid); - -#endif // DRAT_FUNC_BTREE_H +#ifndef DRAT_FUNC_BTREE_H +#define DRAT_FUNC_BTREE_H + +#include +#include + +/** + * Custom data structure used to store the key and value of an object map entry + * together. + */ +typedef struct { + omap_key_t key; + omap_val_t val; +} omap_entry_t; + +omap_entry_t* get_btree_phys_omap_entry(btree_node_phys_t* root_node, oid_t oid, xid_t max_xid); + +/** + * Custom data structure used to store a full file-system record (i.e. a single + * key–value pair from a file-system root tree) alongside each other for easier + * data access and manipulation. + * + * One can make use of an instance of this datatype by determining the strctures + * contained within its `data` field by appealing to the `obj_id_and_type` field + * of the `j_key_t` structure for the record, which is guaranteed to exist and + * start at `data[0]`. That is, a pointer to this instance of `j_key_t` can be + * obtained with `j_key_t* record_header = record->data`, where `record` is an + * instance of this type, `j_rec_t`. + * + * key_len: Length of the file-system record's key-part, in bytes. + * + * val_len: Length of the file-system record's value-part, in bytes. + * + * data: Array of `key_len + val_len` bytes of data, of which, + * index `0` through `key_len - 1` (inclusive) contain the + * key-part data, and index `key_len` through `key_len + val_len - 1` + * (inclusive) contain the value-part data. + */ +typedef struct { + uint16_t key_len; + uint16_t val_len; + char data[]; +} j_rec_t; + +void free_j_rec_array(j_rec_t** records_array); + +j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_t* vol_fs_root_node, oid_t oid, xid_t max_xid); + +#endif // DRAT_FUNC_BTREE_H diff --git a/include/drat/func/cksum.h b/include/drat/func/cksum.h index 6f3d53a..6b887df 100644 --- a/include/drat/func/cksum.h +++ b/include/drat/func/cksum.h @@ -1,11 +1,11 @@ -#ifndef DRAT_FUNC_CKSUM_H -#define DRAT_FUNC_CKSUM_H - -#include -#include - -uint64_t fletcher_cksum(uint32_t* block, bool compute); -uint64_t compute_block_cksum(uint32_t* block); -bool is_cksum_valid(uint32_t* block); - -#endif // DRAT_FUNC_CKSUM_H +#ifndef DRAT_FUNC_CKSUM_H +#define DRAT_FUNC_CKSUM_H + +#include +#include + +uint64_t fletcher_cksum(uint32_t* block, bool compute); +uint64_t compute_block_cksum(uint32_t* block); +bool is_cksum_valid(uint32_t* block); + +#endif // DRAT_FUNC_CKSUM_H diff --git a/include/drat/func/j.c b/include/drat/func/j.c index ccf1999..c7540e0 100644 --- a/include/drat/func/j.c +++ b/include/drat/func/j.c @@ -1,39 +1,39 @@ -#include "j.h" - -#include -#include - -#include -#include -#include - -#include - -uint64_t get_file_size(j_inode_val_t* inode, uint16_t inode_len) { - if (inode->internal_flags & INODE_HAS_UNCOMPRESSED_SIZE) { - return inode->uncompressed_size; - } - - // If inode has xfields, get file size from there - if (inode_len != sizeof(j_inode_val_t)) { - - xf_pair_t** xf_pairs = get_xf_pairs_array(inode->xfields); - if (!xf_pairs) { - // TODO: Error handling using `errno`? - fprintf("\nERROR: %s: Call to `get_xf_pairs_array()` failed.\n", __func__); - return 0; - } - - for (xf_pair_t** cursor = xf_pairs; *cursor; cursor++) { - xf_pair_t* xf_pair = *cursor; - if (xf_pair->key.x_type == INO_EXT_TYPE_DSTREAM) { - j_dstream_t* dstream = &(xf_pair->value); - return dstream->size; - } - } - } - - // TODO: Error handling using `errno`? - fprintf("\nERROR: %s: No file size found.\n", __func__); - return 0; -} +#include "j.h" + +#include +#include + +#include +#include +#include + +#include + +uint64_t get_file_size(j_inode_val_t* inode, uint16_t inode_len) { + if (inode->internal_flags & INODE_HAS_UNCOMPRESSED_SIZE) { + return inode->uncompressed_size; + } + + // If inode has xfields, get file size from there + if (inode_len != sizeof(j_inode_val_t)) { + + xf_pair_t** xf_pairs = get_xf_pairs_array(inode->xfields); + if (!xf_pairs) { + // TODO: Error handling using `errno`? + fprintf("\nERROR: %s: Call to `get_xf_pairs_array()` failed.\n", __func__); + return 0; + } + + for (xf_pair_t** cursor = xf_pairs; *cursor; cursor++) { + xf_pair_t* xf_pair = *cursor; + if (xf_pair->key.x_type == INO_EXT_TYPE_DSTREAM) { + j_dstream_t* dstream = &(xf_pair->value); + return dstream->size; + } + } + } + + // TODO: Error handling using `errno`? + fprintf("\nERROR: %s: No file size found.\n", __func__); + return 0; +} diff --git a/include/drat/func/j.h b/include/drat/func/j.h index 1f7001f..392094c 100644 --- a/include/drat/func/j.h +++ b/include/drat/func/j.h @@ -1,10 +1,10 @@ -#ifndef DRAT_FUNC_J_H -#define DRAT_FUNC_J_H - -#include - -#include - -uint64_t get_file_size(j_inode_val_t* inode, uint16_t inode_len); - -#endif // DRAT_FUNC_J_H +#ifndef DRAT_FUNC_J_H +#define DRAT_FUNC_J_H + +#include + +#include + +uint64_t get_file_size(j_inode_val_t* inode, uint16_t inode_len); + +#endif // DRAT_FUNC_J_H diff --git a/include/drat/func/xf.c b/include/drat/func/xf.c index c5cd45d..d201f62 100644 --- a/include/drat/func/xf.c +++ b/include/drat/func/xf.c @@ -1,94 +1,94 @@ -#include "xf.h" - -#include -#include -#include -#include - -/** - * Given an instance of `xf_blob_t` (such as `j_drec_val_t::xfields` or - * `j_inode_val_t::xfields`), get an array of instances of `xf_pair_t`, which - * allows you to easily access the xfield value for a given xfield key/type, - * since they are then stored directly alongside each other. - * - * xfields: - * A pointer to an instance of `xf_blob_t`, including the data in its - * `xf_data` field. - * - * RETURN VALUE: - * A pointer to the head of an array of pointers to instances of - * `xf_pair_t`. The length of this array is not returned to the caller, - * but the last element of this array will be a NULL pointer. If an error - * occurs, NULL is returned. - * - * When the data in the array is no longer needed, it is the caller's - * responsibility to free the associated memory by passing the pointer - * that was returned by this function to `free_xf_pairs_array()`. - */ -xf_pair_t** get_xf_pairs_array(xf_blob_t* xfields) { - /* - * Create the array to return. We use `calloc()` here so that early return - * in case of error does not cause our call to `free_xf_pairs_array()` to - * `free()` arbitrary pointers. In other words, it makes sure that - * `xf_pairs_array` is always NULL-terminated regardless of how many - * entries it has been populated with so far. - */ - xf_pair_t** xf_pairs_array = calloc(xfields->xf_num_exts + 1, sizeof(xf_pair_t*)); - if (!xf_pairs_array) { - fprintf(stderr, "\nERROR: %s: Couldn't create `xf_pairs_array`.\n", __func__); - return NULL; - } - - x_field_t* xf_keys = xfields->xf_data; - uint8_t* xf_values = xf_keys + xfields->xf_num_exts; - - /* - * Use this value to keep track of how many bytes into `xf_values` the - * current xfield's value starts. This will always be a multiple of 8 before - * we copy a value, since the values are 8-byte aligned. - */ - uint16_t xf_value_cursor_index = 0; - - // Populate the array - for (uint16_t i = 0; i < xfields->xf_num_exts; i++) { - // Move to the start of the next 8-byte aligned segment. - uint16_t remainder = xf_value_cursor_index % 8; - if (remainder != 0) { - xf_value_cursor_index += 8 - remainder; - } - - xf_pairs_array[i] = malloc(sizeof(x_field_t) + xf_keys[i].x_size); - if (!xf_pairs_array[i]) { - fprintf(stderr, "\nERROR: %s: Couldn't create `xf_pairs_array[%"PRIu16"].`\n", __func__, i); - free_xf_pairs_array(xf_pairs_array); - return NULL; - } - - memcpy(xf_pairs_array[i], xf_keys + i, sizeof(x_field_t)); - memcpy(&(xf_pairs_array[i]->value), xf_values + xf_value_cursor_index, xf_keys[i].x_size); - - xf_value_cursor_index += xf_keys[i].x_size; - } - xf_pairs_array[xfields->xf_num_exts] = NULL; - - return xf_pairs_array; -} - -/** - * Free memory allocated for a file-system records array that - * was created by a call to `get_xf_pairs_array()`. - * - * xf_pairs_array: - * A pointer to an array of pointers to instances of `xf_pair_t`, as - * returned by a call to `get_xfields_array()`. - */ -void free_xf_pairs_array(xf_pair_t** xf_pairs_array) { - if (!xf_pairs_array) { - return; - } - - for (xf_pair_t** cursor = xf_pairs_array; *cursor; cursor++) { - free(*cursor); - } - free(xf_pairs_array); -} +#include "xf.h" + +#include +#include +#include +#include + +/** + * Given an instance of `xf_blob_t` (such as `j_drec_val_t::xfields` or + * `j_inode_val_t::xfields`), get an array of instances of `xf_pair_t`, which + * allows you to easily access the xfield value for a given xfield key/type, + * since they are then stored directly alongside each other. + * + * xfields: + * A pointer to an instance of `xf_blob_t`, including the data in its + * `xf_data` field. + * + * RETURN VALUE: + * A pointer to the head of an array of pointers to instances of + * `xf_pair_t`. The length of this array is not returned to the caller, + * but the last element of this array will be a NULL pointer. If an error + * occurs, NULL is returned. + * + * When the data in the array is no longer needed, it is the caller's + * responsibility to free the associated memory by passing the pointer + * that was returned by this function to `free_xf_pairs_array()`. + */ +xf_pair_t** get_xf_pairs_array(xf_blob_t* xfields) { + /* + * Create the array to return. We use `calloc()` here so that early return + * in case of error does not cause our call to `free_xf_pairs_array()` to + * `free()` arbitrary pointers. In other words, it makes sure that + * `xf_pairs_array` is always NULL-terminated regardless of how many + * entries it has been populated with so far. + */ + xf_pair_t** xf_pairs_array = calloc(xfields->xf_num_exts + 1, sizeof(xf_pair_t*)); + if (!xf_pairs_array) { + fprintf(stderr, "\nERROR: %s: Couldn't create `xf_pairs_array`.\n", __func__); + return NULL; + } + + x_field_t* xf_keys = xfields->xf_data; + uint8_t* xf_values = xf_keys + xfields->xf_num_exts; + + /* + * Use this value to keep track of how many bytes into `xf_values` the + * current xfield's value starts. This will always be a multiple of 8 before + * we copy a value, since the values are 8-byte aligned. + */ + uint16_t xf_value_cursor_index = 0; + + // Populate the array + for (uint16_t i = 0; i < xfields->xf_num_exts; i++) { + // Move to the start of the next 8-byte aligned segment. + uint16_t remainder = xf_value_cursor_index % 8; + if (remainder != 0) { + xf_value_cursor_index += 8 - remainder; + } + + xf_pairs_array[i] = malloc(sizeof(x_field_t) + xf_keys[i].x_size); + if (!xf_pairs_array[i]) { + fprintf(stderr, "\nERROR: %s: Couldn't create `xf_pairs_array[%"PRIu16"].`\n", __func__, i); + free_xf_pairs_array(xf_pairs_array); + return NULL; + } + + memcpy(xf_pairs_array[i], xf_keys + i, sizeof(x_field_t)); + memcpy(&(xf_pairs_array[i]->value), xf_values + xf_value_cursor_index, xf_keys[i].x_size); + + xf_value_cursor_index += xf_keys[i].x_size; + } + xf_pairs_array[xfields->xf_num_exts] = NULL; + + return xf_pairs_array; +} + +/** + * Free memory allocated for a file-system records array that + * was created by a call to `get_xf_pairs_array()`. + * + * xf_pairs_array: + * A pointer to an array of pointers to instances of `xf_pair_t`, as + * returned by a call to `get_xfields_array()`. + */ +void free_xf_pairs_array(xf_pair_t** xf_pairs_array) { + if (!xf_pairs_array) { + return; + } + + for (xf_pair_t** cursor = xf_pairs_array; *cursor; cursor++) { + free(*cursor); + } + free(xf_pairs_array); +} diff --git a/include/drat/func/xf.h b/include/drat/func/xf.h index d216fc9..1c9f613 100644 --- a/include/drat/func/xf.h +++ b/include/drat/func/xf.h @@ -1,23 +1,23 @@ -#ifndef DRAT_FUNC_XF_H -#define DRAT_FUNC_XF_H - -#include - -/** - * Data structure used to collect an xfield's key (an instance of `x_field_t`, - * which contains its type, flags, and the size of its value) and its value - * together. The function `get_xfields_array()`, which returns a pointer - * to an array of instance of this data structure, allows us to more easily - * access xfields without needing to do cumbersome arithmetic to find the xfield - * value in an instance of `xf_blob_t`. - */ -typedef struct { - x_field_t key; - uint8_t value[]; -} xf_pair_t; - -xf_pair_t** get_xf_pairs_array(xf_blob_t* xfields); - -void free_xf_pairs_array(xf_pair_t** xfields_array); - -#endif // DRAT_FUNC_XF_H +#ifndef DRAT_FUNC_XF_H +#define DRAT_FUNC_XF_H + +#include + +/** + * Data structure used to collect an xfield's key (an instance of `x_field_t`, + * which contains its type, flags, and the size of its value) and its value + * together. The function `get_xfields_array()`, which returns a pointer + * to an array of instance of this data structure, allows us to more easily + * access xfields without needing to do cumbersome arithmetic to find the xfield + * value in an instance of `xf_blob_t`. + */ +typedef struct { + x_field_t key; + uint8_t value[]; +} xf_pair_t; + +xf_pair_t** get_xf_pairs_array(xf_blob_t* xfields); + +void free_xf_pairs_array(xf_pair_t** xfields_array); + +#endif // DRAT_FUNC_XF_H diff --git a/include/drat/io.h b/include/drat/io.h index aa64a1d..6bb135b 100644 --- a/include/drat/io.h +++ b/include/drat/io.h @@ -1,19 +1,19 @@ -#ifndef DRAT_IO_H -#define DRAT_IO_H - -#include -#include -#include -#include -#include - -extern char* nx_path; -extern FILE* nx; -extern uint32_t nx_block_size; - -void report_fopen_error(void); - -size_t read_blocks (void* buffer, long start_block, size_t num_blocks); -size_t write_blocks(void* buffer, long start_block, size_t num_blocks); - -#endif // DRAT_IO_H +#ifndef DRAT_IO_H +#define DRAT_IO_H + +#include +#include +#include +#include +#include + +extern char* nx_path; +extern FILE* nx; +extern uint32_t nx_block_size; + +void report_fopen_error(void); + +size_t read_blocks (void* buffer, long start_block, size_t num_blocks); +size_t write_blocks(void* buffer, long start_block, size_t num_blocks); + +#endif // DRAT_IO_H diff --git a/include/drat/print-fs-records.h b/include/drat/print-fs-records.h index d9963ec..c6c2384 100644 --- a/include/drat/print-fs-records.h +++ b/include/drat/print-fs-records.h @@ -1,9 +1,9 @@ -#ifndef DRAT_PRINT_FS_RECORDS_H -#define DRAT_PRINT_FS_RECORDS_H - -#include - -char* get_file_name(j_rec_t** file_record); -void print_fs_records(j_rec_t** fs_records); - -#endif // DRAT_PRINT_FS_RECORDS_H +#ifndef DRAT_PRINT_FS_RECORDS_H +#define DRAT_PRINT_FS_RECORDS_H + +#include + +char* get_file_name(j_rec_t** file_record); +void print_fs_records(j_rec_t** fs_records); + +#endif // DRAT_PRINT_FS_RECORDS_H diff --git a/include/drat/string/README.md b/include/drat/string/README.md index 4a5c102..64b7729 100644 --- a/include/drat/string/README.md +++ b/include/drat/string/README.md @@ -1,4 +1,4 @@ -# `include/drat/string` - -This directory contains functions that take APFS objects as input and produce -human-readable descriptions of the data contained in them. +# `include/drat/string` + +This directory contains functions that take APFS objects as input and produce +human-readable descriptions of the data contained in them. diff --git a/include/drat/string/btree.h b/include/drat/string/btree.h index 711b9b1..2a0e133 100644 --- a/include/drat/string/btree.h +++ b/include/drat/string/btree.h @@ -1,12 +1,12 @@ -#ifndef DRAT_STRING_BTREE_H -#define DRAT_STRING_BTREE_H - -#include - -char* get_btn_flags_string(btree_node_phys_t* btn); -char* get_bt_info_flags_string(btree_info_t* bt_info); - -void print_btree_info(btree_info_t* bt_info); -void print_btree_node_phys(btree_node_phys_t* btn); - -#endif // DRAT_STRING_BTREE_H +#ifndef DRAT_STRING_BTREE_H +#define DRAT_STRING_BTREE_H + +#include + +char* get_btn_flags_string(btree_node_phys_t* btn); +char* get_bt_info_flags_string(btree_info_t* bt_info); + +void print_btree_info(btree_info_t* bt_info); +void print_btree_node_phys(btree_node_phys_t* btn); + +#endif // DRAT_STRING_BTREE_H diff --git a/include/drat/string/common.c b/include/drat/string/common.c index eba4902..513c86d 100644 --- a/include/drat/string/common.c +++ b/include/drat/string/common.c @@ -1,141 +1,141 @@ -#include "common.h" - -#include -#include -#include - -/** - * Get a human-readable representation of the value set in a given enum-field, - * based on an enum-to-string mapping. - * - * mapping: - * An enum-to-string mapping for the enum-field being passed. That is, an - * array of instances of `enum_string_mapping_t`. - * - * mapping_length: The length of the array `mapping`; the number of instances of - * `enum_string_mapping_t` within it. - * - * enum_field: - * The enum-field for which we want to get a string representation. - * - * RETURN VALUE: - * String representation of the value set in the enum-field, as a C-string - * (char*). The caller must free this pointer when it is no longer needed. - * If an error occurs, NULL is returned. - */ -char* get_single_enum_string( - const enum_string_mapping_t* mapping, - size_t mapping_length, - uint64_t enum_field -) { - char* result_string = NULL; - - for (size_t i = 0; i < mapping_length; i++) { - if (enum_field == mapping[i].value) { - if (asprintf(&result_string, "%s", mapping[i].string) == -1) { - fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory for `result_string`.\n", __func__); - return NULL; - } - return result_string; - } - } - - // No role matched; use default string. - if (asprintf(&result_string, "Unknown value (%#"PRIx64")", enum_field) == -1) { - fprintf(stderr, "%s: Couldn't allocate sufficient memory for `result_string` when returning default string.\n", __func__); - return NULL; - } - - return result_string; -} - -/** - * Get a human-readable list of the flags that are set in a given flags-field, - * based on an enum-to-string mapping. - * - * mapping: - * An enum-to-string mapping for the flags-field being passed. That is, an - * array of instances of `enum_string_mapping_t`. - * - * mapping_length: The length of the array `mapping`; the number of instances of - * `enum_string_mapping_t` within it. - * - * flags_field: - * The flags-field for which we want to get a string representation. - * - * single_line: - * Boolean value. If true, generate a comma-delimited string representation - * (containing no newline characters); else, generate a bulleted list - * (using hyphen characters for the bullets, and with each list item being - * terminated with a newline character). - * - * RETURN VALUE: - * String representation of the flags that are set, as a C-string (char*). - * The caller must free this pointer when it is no longer needed. If an - * error occurs, NULL is returned. - */ -char* get_flags_enum_string( - const enum_string_mapping_t* mapping, - size_t mapping_length, - uint64_t flags_field, - bool single_line -) { - // Initialise result buffer as empty string - size_t bufsize = 2048; // This should be enough for anything that call us. - char* result_string = malloc(bufsize); - if (!result_string) { - fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory to initialise buffer.\n", __func__); - return NULL; - } - *result_string = '\0'; - - // Generate list - char* list_item_format_string = single_line ? "%s, " : "- %s\n"; - size_t bytes_to_rewrite = 0; // Used if initial write attempt fails - size_t bytes_written = 0; - for (size_t i = 0; i < mapping_length; i++) { - if (flags_field & mapping[i].value) { - bytes_to_rewrite = snprintf( - result_string + bytes_written, - bufsize - bytes_written, - list_item_format_string, - mapping[i].string - ); - bytes_written += bytes_to_rewrite; - - if (bytes_written > bufsize - 1) { - // Exhausted buffer; allocate sufficient memory and try again. - bufsize = bytes_written + 1; - result_string = realloc(result_string, bufsize); - if (!result_string) { - fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory to hold entire result.\n", __func__); - return NULL; - } - - snprintf( - result_string + (bytes_written - bytes_to_rewrite), - bufsize - (bytes_written - bytes_to_rewrite), - list_item_format_string, - mapping[i].string - ); - } - } - } - - if (bytes_written == 0) { - // No flags are set; use default string. - bytes_written = snprintf(result_string, bufsize, single_line ? "(no flags)" : "- No flags.\n"); - } else { - // At least one flag is set. If we generated a single-line list, - // trim the trailing comma and space characters. - if (single_line) { - bytes_written -= 2; - result_string[bytes_written] = '\0'; - } - } - - // Truncate buffer - result_string = realloc(result_string, bytes_written + 1); - - return result_string; -} +#include "common.h" + +#include +#include +#include + +/** + * Get a human-readable representation of the value set in a given enum-field, + * based on an enum-to-string mapping. + * + * mapping: + * An enum-to-string mapping for the enum-field being passed. That is, an + * array of instances of `enum_string_mapping_t`. + * + * mapping_length: The length of the array `mapping`; the number of instances of + * `enum_string_mapping_t` within it. + * + * enum_field: + * The enum-field for which we want to get a string representation. + * + * RETURN VALUE: + * String representation of the value set in the enum-field, as a C-string + * (char*). The caller must free this pointer when it is no longer needed. + * If an error occurs, NULL is returned. + */ +char* get_single_enum_string( + const enum_string_mapping_t* mapping, + size_t mapping_length, + uint64_t enum_field +) { + char* result_string = NULL; + + for (size_t i = 0; i < mapping_length; i++) { + if (enum_field == mapping[i].value) { + if (asprintf(&result_string, "%s", mapping[i].string) == -1) { + fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory for `result_string`.\n", __func__); + return NULL; + } + return result_string; + } + } + + // No role matched; use default string. + if (asprintf(&result_string, "Unknown value (%#"PRIx64")", enum_field) == -1) { + fprintf(stderr, "%s: Couldn't allocate sufficient memory for `result_string` when returning default string.\n", __func__); + return NULL; + } + + return result_string; +} + +/** + * Get a human-readable list of the flags that are set in a given flags-field, + * based on an enum-to-string mapping. + * + * mapping: + * An enum-to-string mapping for the flags-field being passed. That is, an + * array of instances of `enum_string_mapping_t`. + * + * mapping_length: The length of the array `mapping`; the number of instances of + * `enum_string_mapping_t` within it. + * + * flags_field: + * The flags-field for which we want to get a string representation. + * + * single_line: + * Boolean value. If true, generate a comma-delimited string representation + * (containing no newline characters); else, generate a bulleted list + * (using hyphen characters for the bullets, and with each list item being + * terminated with a newline character). + * + * RETURN VALUE: + * String representation of the flags that are set, as a C-string (char*). + * The caller must free this pointer when it is no longer needed. If an + * error occurs, NULL is returned. + */ +char* get_flags_enum_string( + const enum_string_mapping_t* mapping, + size_t mapping_length, + uint64_t flags_field, + bool single_line +) { + // Initialise result buffer as empty string + size_t bufsize = 2048; // This should be enough for anything that call us. + char* result_string = malloc(bufsize); + if (!result_string) { + fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory to initialise buffer.\n", __func__); + return NULL; + } + *result_string = '\0'; + + // Generate list + char* list_item_format_string = single_line ? "%s, " : "- %s\n"; + size_t bytes_to_rewrite = 0; // Used if initial write attempt fails + size_t bytes_written = 0; + for (size_t i = 0; i < mapping_length; i++) { + if (flags_field & mapping[i].value) { + bytes_to_rewrite = snprintf( + result_string + bytes_written, + bufsize - bytes_written, + list_item_format_string, + mapping[i].string + ); + bytes_written += bytes_to_rewrite; + + if (bytes_written > bufsize - 1) { + // Exhausted buffer; allocate sufficient memory and try again. + bufsize = bytes_written + 1; + result_string = realloc(result_string, bufsize); + if (!result_string) { + fprintf(stderr, "\nERROR: %s: Couldn't allocate sufficient memory to hold entire result.\n", __func__); + return NULL; + } + + snprintf( + result_string + (bytes_written - bytes_to_rewrite), + bufsize - (bytes_written - bytes_to_rewrite), + list_item_format_string, + mapping[i].string + ); + } + } + } + + if (bytes_written == 0) { + // No flags are set; use default string. + bytes_written = snprintf(result_string, bufsize, single_line ? "(no flags)" : "- No flags.\n"); + } else { + // At least one flag is set. If we generated a single-line list, + // trim the trailing comma and space characters. + if (single_line) { + bytes_written -= 2; + result_string[bytes_written] = '\0'; + } + } + + // Truncate buffer + result_string = realloc(result_string, bytes_written + 1); + + return result_string; +} diff --git a/include/drat/string/common.h b/include/drat/string/common.h index 494d5fc..50af652 100644 --- a/include/drat/string/common.h +++ b/include/drat/string/common.h @@ -1,37 +1,37 @@ -#ifndef DRAT_STRING_COMMON_H -#define DRAT_STRING_COMMON_H - -/** - * Collection of common routines and structures used by `get_*_string()` - * functions. - */ - -#include -#include -#include - -/** - * Data structure that encodes an enum member's value along with a string - * describing what that enum member represents. This is used to create an array - * of such strctures for a particular enum type, which thus collectively - * describes all of an enum's members, providing an enum-to-string mapping. - */ -typedef struct enum_string_pair { - uint64_t value; - char* string; -} enum_string_mapping_t; - -char* get_single_enum_string( - const enum_string_mapping_t* mapping, - size_t mapping_length, - uint64_t enum_field -); - -char* get_flags_enum_string( - const enum_string_mapping_t* mapping, - size_t mapping_length, - uint64_t flags_field, - bool single_line -); - -#endif // DRAT_STRING_COMMON_H +#ifndef DRAT_STRING_COMMON_H +#define DRAT_STRING_COMMON_H + +/** + * Collection of common routines and structures used by `get_*_string()` + * functions. + */ + +#include +#include +#include + +/** + * Data structure that encodes an enum member's value along with a string + * describing what that enum member represents. This is used to create an array + * of such strctures for a particular enum type, which thus collectively + * describes all of an enum's members, providing an enum-to-string mapping. + */ +typedef struct enum_string_pair { + uint64_t value; + char* string; +} enum_string_mapping_t; + +char* get_single_enum_string( + const enum_string_mapping_t* mapping, + size_t mapping_length, + uint64_t enum_field +); + +char* get_flags_enum_string( + const enum_string_mapping_t* mapping, + size_t mapping_length, + uint64_t flags_field, + bool single_line +); + +#endif // DRAT_STRING_COMMON_H diff --git a/include/drat/string/dstream.c b/include/drat/string/dstream.c index 4c35626..8f16f3a 100644 --- a/include/drat/string/dstream.c +++ b/include/drat/string/dstream.c @@ -1,20 +1,20 @@ -#include "dstream.h" - -#include -#include - -void print_j_dstream(j_dstream_t* dstream) { - printf( - "Size: %"PRIu64" bytes\n" - "Allocated size: %"PRIu64" bytes\n" - "Default crypto ID: %#"PRIx64"\n" - "Total bytes written: %"PRIu64" bytes\n" - "Total bytes read: %"PRIu64" bytes\n", - - dstream->size, - dstream->alloced_size, - dstream->default_crypto_id, - dstream->total_bytes_written, - dstream->total_bytes_read - ); -} +#include "dstream.h" + +#include +#include + +void print_j_dstream(j_dstream_t* dstream) { + printf( + "Size: %"PRIu64" bytes\n" + "Allocated size: %"PRIu64" bytes\n" + "Default crypto ID: %#"PRIx64"\n" + "Total bytes written: %"PRIu64" bytes\n" + "Total bytes read: %"PRIu64" bytes\n", + + dstream->size, + dstream->alloced_size, + dstream->default_crypto_id, + dstream->total_bytes_written, + dstream->total_bytes_read + ); +} diff --git a/include/drat/string/dstream.h b/include/drat/string/dstream.h index 1a5ceb5..d382be6 100644 --- a/include/drat/string/dstream.h +++ b/include/drat/string/dstream.h @@ -1,8 +1,8 @@ -#ifndef DRAT_STRING_DSTREAM_H -#define DRAT_STRING_DSTREAM_H - -#include - -void print_j_dstream(j_dstream_t* dstream); - -#endif // DRAT_STRING_DSTREAM_H +#ifndef DRAT_STRING_DSTREAM_H +#define DRAT_STRING_DSTREAM_H + +#include + +void print_j_dstream(j_dstream_t* dstream); + +#endif // DRAT_STRING_DSTREAM_H diff --git a/include/drat/string/fs.h b/include/drat/string/fs.h index 1efe948..2ba0e7f 100644 --- a/include/drat/string/fs.h +++ b/include/drat/string/fs.h @@ -1,13 +1,13 @@ -#ifndef DRAT_STRING_FS_H -#define DRAT_STRING_FS_H - -#include - -char* get_apfs_features_string(apfs_superblock_t* apsb); -char* get_apfs_readonly_compatible_features_string(apfs_superblock_t* apsb); -char* get_apfs_incompatible_features_string(apfs_superblock_t* apsb); -char* get_apfs_fs_flags_string(apfs_superblock_t* apsb); -char* get_apfs_role_string(apfs_superblock_t* apsb); -void print_apfs_superblock(apfs_superblock_t* apsb); - -#endif // DRAT_STRING_FS_H +#ifndef DRAT_STRING_FS_H +#define DRAT_STRING_FS_H + +#include + +char* get_apfs_features_string(apfs_superblock_t* apsb); +char* get_apfs_readonly_compatible_features_string(apfs_superblock_t* apsb); +char* get_apfs_incompatible_features_string(apfs_superblock_t* apsb); +char* get_apfs_fs_flags_string(apfs_superblock_t* apsb); +char* get_apfs_role_string(apfs_superblock_t* apsb); +void print_apfs_superblock(apfs_superblock_t* apsb); + +#endif // DRAT_STRING_FS_H diff --git a/include/drat/string/general.c b/include/drat/string/general.c index 3df5bce..2ae856e 100644 --- a/include/drat/string/general.c +++ b/include/drat/string/general.c @@ -1,18 +1,18 @@ -#include "general.h" - -#include -#include -#include - -void print_uuid(uuid_t uuid) { - for (int i = 0; i < 16; i++) { - switch (i) { - case 4: case 6: case 8: case 10: - printf("-"); - // fall through - default: - printf("%02X", uuid[i]); - break; - } - } -} +#include "general.h" + +#include +#include +#include + +void print_uuid(uuid_t uuid) { + for (int i = 0; i < 16; i++) { + switch (i) { + case 4: case 6: case 8: case 10: + printf("-"); + // fall through + default: + printf("%02X", uuid[i]); + break; + } + } +} diff --git a/include/drat/string/general.h b/include/drat/string/general.h index 477e9a9..b3a6388 100644 --- a/include/drat/string/general.h +++ b/include/drat/string/general.h @@ -1,8 +1,8 @@ -#ifndef DRAT_STRING_GENERAL_H -#define DRAT_STRING_GENERAL_H - -#include - -void print_uuid(uuid_t uuid); - -#endif // DRAT_STRING_GENERAL_H +#ifndef DRAT_STRING_GENERAL_H +#define DRAT_STRING_GENERAL_H + +#include + +void print_uuid(uuid_t uuid); + +#endif // DRAT_STRING_GENERAL_H diff --git a/include/drat/string/j.h b/include/drat/string/j.h index 46024f0..b9cffee 100644 --- a/include/drat/string/j.h +++ b/include/drat/string/j.h @@ -1,32 +1,32 @@ -#ifndef DRAT_STRING_J_H -#define DRAT_STRING_J_H - -#include -#include - -#include -#include - -char* j_key_type_to_string(uint8_t j_key_type); -void print_j_key(j_key_t* key); -void print_j_inode_key(j_inode_key_t* key); - -char* get_j_inode_type(j_inode_val_t* val); -char* j_inode_mode_to_string(apfs_mode_t mode); -char* get_j_inode_internal_flags_string(uint64_t internal_flags); -char* get_j_inode_bsd_flags_string(uint32_t bsd_flags); - -char* get_item_name(uint8_t *xfields, uint16_t val_len); -void print_j_inode_info(j_inode_val_t* val, uint16_t val_len); -void print_j_inode_val(j_inode_val_t* val, uint16_t val_len); -void print_j_file_extent_key(j_file_extent_key_t* key); -void print_j_file_extent_val(j_file_extent_val_t* val); -void print_j_drec_hashed_key(j_drec_hashed_key_t* key); - -char* drec_val_to_type_string(j_drec_val_t* val); -char* drec_val_to_short_type_string(j_drec_val_t* val); -void print_j_drec_val(j_drec_val_t* val, uint16_t val_len); - -void print_j_dir_stats_val(j_dir_stats_val_t* val); - -#endif // DRAT_STRING_J_H +#ifndef DRAT_STRING_J_H +#define DRAT_STRING_J_H + +#include +#include + +#include +#include + +char* j_key_type_to_string(uint8_t j_key_type); +void print_j_key(j_key_t* key); +void print_j_inode_key(j_inode_key_t* key); + +char* get_j_inode_type(j_inode_val_t* val); +char* j_inode_mode_to_string(apfs_mode_t mode); +char* get_j_inode_internal_flags_string(uint64_t internal_flags); +char* get_j_inode_bsd_flags_string(uint32_t bsd_flags); + +char* get_item_name(uint8_t *xfields, uint16_t val_len); +void print_j_inode_info(j_inode_val_t* val, uint16_t val_len); +void print_j_inode_val(j_inode_val_t* val, uint16_t val_len); +void print_j_file_extent_key(j_file_extent_key_t* key); +void print_j_file_extent_val(j_file_extent_val_t* val); +void print_j_drec_hashed_key(j_drec_hashed_key_t* key); + +char* drec_val_to_type_string(j_drec_val_t* val); +char* drec_val_to_short_type_string(j_drec_val_t* val); +void print_j_drec_val(j_drec_val_t* val, uint16_t val_len); + +void print_j_dir_stats_val(j_dir_stats_val_t* val); + +#endif // DRAT_STRING_J_H diff --git a/include/drat/string/nx.h b/include/drat/string/nx.h index f98ebc6..2b3af32 100644 --- a/include/drat/string/nx.h +++ b/include/drat/string/nx.h @@ -1,16 +1,16 @@ -#ifndef DRAT_STRING_NX_H -#define DRAT_STRING_NX_H - -#include - -char* get_nx_features_string(nx_superblock_t* nxsb); -char* get_nx_readonly_compatible_features_string(nx_superblock_t* nxsb); -char* get_nx_incompatible_features_string(nx_superblock_t* nxsb); -char* get_nx_flags_string(nx_superblock_t* nxsb); -void print_nx_superblock(nx_superblock_t* nxsb); -void print_checkpoint_mapping(checkpoint_mapping_t* cpm); -char* get_cpm_flags_string(checkpoint_map_phys_t* cpm); -void print_checkpoint_map_phys(checkpoint_map_phys_t* cpm); -void print_checkpoint_map_phys_mappings(checkpoint_map_phys_t* cpm); - -#endif // DRAT_STRING_NX_H +#ifndef DRAT_STRING_NX_H +#define DRAT_STRING_NX_H + +#include + +char* get_nx_features_string(nx_superblock_t* nxsb); +char* get_nx_readonly_compatible_features_string(nx_superblock_t* nxsb); +char* get_nx_incompatible_features_string(nx_superblock_t* nxsb); +char* get_nx_flags_string(nx_superblock_t* nxsb); +void print_nx_superblock(nx_superblock_t* nxsb); +void print_checkpoint_mapping(checkpoint_mapping_t* cpm); +char* get_cpm_flags_string(checkpoint_map_phys_t* cpm); +void print_checkpoint_map_phys(checkpoint_map_phys_t* cpm); +void print_checkpoint_map_phys_mappings(checkpoint_map_phys_t* cpm); + +#endif // DRAT_STRING_NX_H diff --git a/include/drat/string/object.h b/include/drat/string/object.h index d9d4423..1be6e5d 100644 --- a/include/drat/string/object.h +++ b/include/drat/string/object.h @@ -1,12 +1,12 @@ -#ifndef DRAT_STRING_OBJECT_H -#define DRAT_STRING_OBJECT_H - -#include - -char* o_storage_type_to_string(uint32_t o_type); -char* get_o_type_flags_string(uint32_t o_type); -char* get_o_type_string(uint32_t o_type); -char* get_o_subtype_string(uint32_t o_subtype); -void print_obj_phys(obj_phys_t* obj); - -#endif // DRAT_STRING_OBJECT_H +#ifndef DRAT_STRING_OBJECT_H +#define DRAT_STRING_OBJECT_H + +#include + +char* o_storage_type_to_string(uint32_t o_type); +char* get_o_type_flags_string(uint32_t o_type); +char* get_o_type_string(uint32_t o_type); +char* get_o_subtype_string(uint32_t o_subtype); +void print_obj_phys(obj_phys_t* obj); + +#endif // DRAT_STRING_OBJECT_H diff --git a/include/drat/string/omap.h b/include/drat/string/omap.h index c24340d..4719e6a 100644 --- a/include/drat/string/omap.h +++ b/include/drat/string/omap.h @@ -1,11 +1,11 @@ -#ifndef DRAT_STRING_OMAP_H -#define DRAT_STRING_OMAP_H - -#include - -char* get_om_flags_string(omap_phys_t* omap); -void print_omap_phys(omap_phys_t* omap); -void print_omap_key(omap_key_t* omap_key); -void print_omap_val(omap_val_t* omap_val); - -#endif // DRAT_STRING_OMAP_H +#ifndef DRAT_STRING_OMAP_H +#define DRAT_STRING_OMAP_H + +#include + +char* get_om_flags_string(omap_phys_t* omap); +void print_omap_phys(omap_phys_t* omap); +void print_omap_key(omap_key_t* omap_key); +void print_omap_val(omap_val_t* omap_val); + +#endif // DRAT_STRING_OMAP_H diff --git a/include/drat/string/xf.c b/include/drat/string/xf.c index c61dcdb..5f9f5b3 100644 --- a/include/drat/string/xf.c +++ b/include/drat/string/xf.c @@ -1,189 +1,189 @@ -#include "xf.h" - -#include -#include -#include -#include - -#include -#include // oid_t, xid_t -#include // j_dstream_t -#include // j_dir_stats_val_t -#include // uuid_t - -#include - -#include - -#include -#include -#include -#include - -/** - * Get a human-readable string describing the type of a given xfield. - * - * x_type: - * The `x_type` field of an instance of `x_field_t`. - * - * RETURN VALUE: - * A C-string. This pointer needn't be freed by the caller. - */ -char* x_type_to_string(uint8_t x_type) { - switch (x_type) { - case INO_EXT_TYPE_SNAP_XID: return "Snapshot XID"; - case INO_EXT_TYPE_DELTA_TREE_OID: return "Snapshot extent delta tree Virtual OID"; - case INO_EXT_TYPE_DOCUMENT_ID: return "Document ID"; - case INO_EXT_TYPE_NAME: return "Item name"; - case INO_EXT_TYPE_PREV_FSIZE: return "Previous file size"; - case INO_EXT_TYPE_RESERVED_6: return "Reserved field 6"; - case INO_EXT_TYPE_FINDER_INFO: return "Finder info"; - case INO_EXT_TYPE_DSTREAM: return "Data stream"; - case INO_EXT_TYPE_RESERVED_9: return "Reserved field 9"; - case INO_EXT_TYPE_DIR_STATS_KEY: return "Directory statistics"; - case INO_EXT_TYPE_FS_UUID: return "Filesystem auto-mount UUID"; - case INO_EXT_TYPE_RESERVED_12: return "Reserved field 12"; - case INO_EXT_TYPE_SPARSE_BYTES: return "Number of sparse bytes in data stream"; - case INO_EXT_TYPE_RDEV: return "ID of special device"; - case INO_EXT_TYPE_PURGEABLE_FLAGS: return "Flags for purgeable file"; - case INO_EXT_TYPE_ORIG_SYNC_ROOT_ID: return "Inode number of file's original sync-root"; - default: return "(unknown)"; - } -} - -/** - * Get a human-readable string that lists the type flags that are set on a given - * xfield. - * - * x_flags: - * The `x_flags` field of an instance of `x_field_t`. - * - * RETURN VALUE: - * A C-string. The caller must free this pointer when it is no longer needed. - */ -char* get_x_flags_string(uint8_t x_flags) { - enum_string_mapping_t flags[] = { - { XF_DATA_DEPENDENT, "Dependent on file data" }, - { XF_DO_NOT_COPY, "Do not copy" }, - { XF_RESERVED_4, "Reserved flag 3 (0x04)" }, - { XF_CHILDREN_INHERIT, "Inherited by children" }, - { XF_USER_FIELD, "Set in userspace" }, - { XF_SYSTEM_FIELD, "Set by system (in kernel space)" }, - { XF_RESERVED_40, "Reserved flag 7 (0x40)" }, - { XF_RESERVED_80, "Reserved flag 8 (0x80)" }, - }; - - return get_flags_enum_string(flags, ARRAY_SIZE(flags), x_flags, true); -} - -void print_x_field(x_field_t* xfield) { - char* flags_string = get_x_flags_string(xfield->x_flags); - - printf("Type: %s\n", x_type_to_string(xfield->x_type)); - printf("Size: %"PRIu16" bytes\n", xfield->x_size); - printf("Flags: %s\n", flags_string); - - free(flags_string); -} - -void print_xf_pair(xf_pair_t* xf_pair) { - printf("== KEY ==\n"); - print_x_field(xf_pair); - - printf("== VALUE ==\n"); - - // Avoid having to rewrite this expression a bazillion times - void* value = &(xf_pair->value); - - /* - * xfield types whose values are of unknown data type (e.g. reserved types, - * Finder data, purgeable flags) appear together at the bottom of this list. - * - * NOTE: Need to enclose each case here in a block `{}` to prevent the - * following error when compiling on Linux: "a label can only be part of - * a statement and a declaration is not a statement". This practice also - * mitigates the possibility of names being declared multiple times in the - * same variable scope. - */ - switch(xf_pair->key.x_type) { - case INO_EXT_TYPE_SNAP_XID: { - xid_t* snap_xid = value; - printf("Snapshot XID: %#"PRIx64"\n", *snap_xid); - } break; - case INO_EXT_TYPE_DELTA_TREE_OID: { - oid_t* delta_tree_oid = value; - printf("Delta tree Virtual OID: %#"PRIx64"\n", *delta_tree_oid); - } break; - case INO_EXT_TYPE_DOCUMENT_ID: { - uint32_t* document_id = value; - printf("Document ID: %#"PRIx32"\n", *document_id); - } break; - case INO_EXT_TYPE_NAME: { - char* name = value; - printf("Item name: %s\n", name); - } break; - case INO_EXT_TYPE_PREV_FSIZE: { - uint64_t* size = value; - printf("Previous file size: %"PRIu64" bytes\n", *size); - } break; - case INO_EXT_TYPE_DSTREAM: { - j_dstream_t* dstream = value; - print_j_dstream(dstream); - } break; - case INO_EXT_TYPE_DIR_STATS_KEY: { - j_dir_stats_val_t* dir_stats = value; - print_j_dir_stats_val(dir_stats); - } break; - case INO_EXT_TYPE_FS_UUID: { - unsigned char* uuid = value; - printf("Filesystem UUID: "); - print_uuid(uuid); - printf("\n"); - } break; - case INO_EXT_TYPE_SPARSE_BYTES: { - uint64_t* bytes = value; - printf("Number of sparse bytes: %"PRIu64" bytes\n", *bytes); - } break; - case INO_EXT_TYPE_RDEV: { - uint32_t* rdev = value; - printf("Device ID: %#"PRIx32"\n", *rdev); - } break; - case INO_EXT_TYPE_ORIG_SYNC_ROOT_ID: { - uint64_t* inode_number = value; - printf("Inode number: %#"PRIx64"\n", *inode_number); - } break; - - // xfield types whose value is of unknown type - case INO_EXT_TYPE_RESERVED_6: - case INO_EXT_TYPE_RESERVED_9: - case INO_EXT_TYPE_RESERVED_12: - case INO_EXT_TYPE_FINDER_INFO: - case INO_EXT_TYPE_PURGEABLE_FLAGS: - default: { - printf("Value has unknown type. Hexdump:\n"); - // Print hexdump - for (uint16_t i = 0; i < xf_pair->key.x_size; i++) { - if (i % 16 == 0) { - printf("\n"); - } else if (i % 2 == 0) { - printf(" "); - } - printf("%02x", xf_pair->value[i]); - } - printf("\n"); - } break; - } -} - -/** - * Given an array of xfields as returned by a call to `get_xf_pairs_array()`, - * print a human-readable description of the data contained within. - * - * xf_pairs_array: The array to print a description of. - */ -void print_xf_pairs_array(xf_pair_t** xf_pairs_array) { - for (xf_pair_t** cursor = xf_pairs_array; *cursor; cursor++) { - print_xf_pair(*cursor); - printf("\n"); - } -} +#include "xf.h" + +#include +#include +#include +#include + +#include +#include // oid_t, xid_t +#include // j_dstream_t +#include // j_dir_stats_val_t +#include // uuid_t + +#include + +#include + +#include +#include +#include +#include + +/** + * Get a human-readable string describing the type of a given xfield. + * + * x_type: + * The `x_type` field of an instance of `x_field_t`. + * + * RETURN VALUE: + * A C-string. This pointer needn't be freed by the caller. + */ +char* x_type_to_string(uint8_t x_type) { + switch (x_type) { + case INO_EXT_TYPE_SNAP_XID: return "Snapshot XID"; + case INO_EXT_TYPE_DELTA_TREE_OID: return "Snapshot extent delta tree Virtual OID"; + case INO_EXT_TYPE_DOCUMENT_ID: return "Document ID"; + case INO_EXT_TYPE_NAME: return "Item name"; + case INO_EXT_TYPE_PREV_FSIZE: return "Previous file size"; + case INO_EXT_TYPE_RESERVED_6: return "Reserved field 6"; + case INO_EXT_TYPE_FINDER_INFO: return "Finder info"; + case INO_EXT_TYPE_DSTREAM: return "Data stream"; + case INO_EXT_TYPE_RESERVED_9: return "Reserved field 9"; + case INO_EXT_TYPE_DIR_STATS_KEY: return "Directory statistics"; + case INO_EXT_TYPE_FS_UUID: return "Filesystem auto-mount UUID"; + case INO_EXT_TYPE_RESERVED_12: return "Reserved field 12"; + case INO_EXT_TYPE_SPARSE_BYTES: return "Number of sparse bytes in data stream"; + case INO_EXT_TYPE_RDEV: return "ID of special device"; + case INO_EXT_TYPE_PURGEABLE_FLAGS: return "Flags for purgeable file"; + case INO_EXT_TYPE_ORIG_SYNC_ROOT_ID: return "Inode number of file's original sync-root"; + default: return "(unknown)"; + } +} + +/** + * Get a human-readable string that lists the type flags that are set on a given + * xfield. + * + * x_flags: + * The `x_flags` field of an instance of `x_field_t`. + * + * RETURN VALUE: + * A C-string. The caller must free this pointer when it is no longer needed. + */ +char* get_x_flags_string(uint8_t x_flags) { + enum_string_mapping_t flags[] = { + { XF_DATA_DEPENDENT, "Dependent on file data" }, + { XF_DO_NOT_COPY, "Do not copy" }, + { XF_RESERVED_4, "Reserved flag 3 (0x04)" }, + { XF_CHILDREN_INHERIT, "Inherited by children" }, + { XF_USER_FIELD, "Set in userspace" }, + { XF_SYSTEM_FIELD, "Set by system (in kernel space)" }, + { XF_RESERVED_40, "Reserved flag 7 (0x40)" }, + { XF_RESERVED_80, "Reserved flag 8 (0x80)" }, + }; + + return get_flags_enum_string(flags, ARRAY_SIZE(flags), x_flags, true); +} + +void print_x_field(x_field_t* xfield) { + char* flags_string = get_x_flags_string(xfield->x_flags); + + printf("Type: %s\n", x_type_to_string(xfield->x_type)); + printf("Size: %"PRIu16" bytes\n", xfield->x_size); + printf("Flags: %s\n", flags_string); + + free(flags_string); +} + +void print_xf_pair(xf_pair_t* xf_pair) { + printf("== KEY ==\n"); + print_x_field(xf_pair); + + printf("== VALUE ==\n"); + + // Avoid having to rewrite this expression a bazillion times + void* value = &(xf_pair->value); + + /* + * xfield types whose values are of unknown data type (e.g. reserved types, + * Finder data, purgeable flags) appear together at the bottom of this list. + * + * NOTE: Need to enclose each case here in a block `{}` to prevent the + * following error when compiling on Linux: "a label can only be part of + * a statement and a declaration is not a statement". This practice also + * mitigates the possibility of names being declared multiple times in the + * same variable scope. + */ + switch(xf_pair->key.x_type) { + case INO_EXT_TYPE_SNAP_XID: { + xid_t* snap_xid = value; + printf("Snapshot XID: %#"PRIx64"\n", *snap_xid); + } break; + case INO_EXT_TYPE_DELTA_TREE_OID: { + oid_t* delta_tree_oid = value; + printf("Delta tree Virtual OID: %#"PRIx64"\n", *delta_tree_oid); + } break; + case INO_EXT_TYPE_DOCUMENT_ID: { + uint32_t* document_id = value; + printf("Document ID: %#"PRIx32"\n", *document_id); + } break; + case INO_EXT_TYPE_NAME: { + char* name = value; + printf("Item name: %s\n", name); + } break; + case INO_EXT_TYPE_PREV_FSIZE: { + uint64_t* size = value; + printf("Previous file size: %"PRIu64" bytes\n", *size); + } break; + case INO_EXT_TYPE_DSTREAM: { + j_dstream_t* dstream = value; + print_j_dstream(dstream); + } break; + case INO_EXT_TYPE_DIR_STATS_KEY: { + j_dir_stats_val_t* dir_stats = value; + print_j_dir_stats_val(dir_stats); + } break; + case INO_EXT_TYPE_FS_UUID: { + unsigned char* uuid = value; + printf("Filesystem UUID: "); + print_uuid(uuid); + printf("\n"); + } break; + case INO_EXT_TYPE_SPARSE_BYTES: { + uint64_t* bytes = value; + printf("Number of sparse bytes: %"PRIu64" bytes\n", *bytes); + } break; + case INO_EXT_TYPE_RDEV: { + uint32_t* rdev = value; + printf("Device ID: %#"PRIx32"\n", *rdev); + } break; + case INO_EXT_TYPE_ORIG_SYNC_ROOT_ID: { + uint64_t* inode_number = value; + printf("Inode number: %#"PRIx64"\n", *inode_number); + } break; + + // xfield types whose value is of unknown type + case INO_EXT_TYPE_RESERVED_6: + case INO_EXT_TYPE_RESERVED_9: + case INO_EXT_TYPE_RESERVED_12: + case INO_EXT_TYPE_FINDER_INFO: + case INO_EXT_TYPE_PURGEABLE_FLAGS: + default: { + printf("Value has unknown type. Hexdump:\n"); + // Print hexdump + for (uint16_t i = 0; i < xf_pair->key.x_size; i++) { + if (i % 16 == 0) { + printf("\n"); + } else if (i % 2 == 0) { + printf(" "); + } + printf("%02x", xf_pair->value[i]); + } + printf("\n"); + } break; + } +} + +/** + * Given an array of xfields as returned by a call to `get_xf_pairs_array()`, + * print a human-readable description of the data contained within. + * + * xf_pairs_array: The array to print a description of. + */ +void print_xf_pairs_array(xf_pair_t** xf_pairs_array) { + for (xf_pair_t** cursor = xf_pairs_array; *cursor; cursor++) { + print_xf_pair(*cursor); + printf("\n"); + } +} diff --git a/include/drat/string/xf.h b/include/drat/string/xf.h index b8eaf67..187f813 100644 --- a/include/drat/string/xf.h +++ b/include/drat/string/xf.h @@ -1,12 +1,12 @@ -#ifndef DRAT_STRING_XF_H -#define DRAT_STRING_XF_H - -#include - -char* x_type_to_string(uint8_t x_type); -char* get_x_flags_string(uint8_t x_flags); -void print_x_field(x_field_t* xfield); -void print_xf_pair(xf_pair_t* xf_pair); -void print_xf_pairs_array(xf_pair_t** xf_pairs_array); - -#endif // DRAT_STRING_XF_H +#ifndef DRAT_STRING_XF_H +#define DRAT_STRING_XF_H + +#include + +char* x_type_to_string(uint8_t x_type); +char* get_x_flags_string(uint8_t x_flags); +void print_x_field(x_field_t* xfield); +void print_xf_pair(xf_pair_t* xf_pair); +void print_xf_pairs_array(xf_pair_t** xf_pairs_array); + +#endif // DRAT_STRING_XF_H diff --git a/include/drat/time.c b/include/drat/time.c index 83c0138..bf1ba6c 100644 --- a/include/drat/time.c +++ b/include/drat/time.c @@ -1,17 +1,17 @@ -#include "time.h" - -#include - -/** - * Get a human-readable timestamp from an APFS timestamp (Unix timestamp in - * nanoseconds). The return value is a newline-terminated C-string stored in - * the static buffer provided by the `ctime` API, so it must not be freed, and - * this function is not thread-safe. - */ -char* apfs_timestamp_to_string(uint64_t apfs_timestamp) { - // Dividing timestamps by 10^9 to convert APFS timestamps (Unix timestamps - // in nanoseconds) to Unix timestamps (in seconds). - // Trailing '\n' is provided by the result of `ctime()`. - time_t timestamp = apfs_timestamp / 1000000000; - return ctime(×tamp); -} +#include "time.h" + +#include + +/** + * Get a human-readable timestamp from an APFS timestamp (Unix timestamp in + * nanoseconds). The return value is a newline-terminated C-string stored in + * the static buffer provided by the `ctime` API, so it must not be freed, and + * this function is not thread-safe. + */ +char* apfs_timestamp_to_string(uint64_t apfs_timestamp) { + // Dividing timestamps by 10^9 to convert APFS timestamps (Unix timestamps + // in nanoseconds) to Unix timestamps (in seconds). + // Trailing '\n' is provided by the result of `ctime()`. + time_t timestamp = apfs_timestamp / 1000000000; + return ctime(×tamp); +} diff --git a/include/drat/time.h b/include/drat/time.h index 0df9aa6..cbd4d20 100644 --- a/include/drat/time.h +++ b/include/drat/time.h @@ -1,8 +1,8 @@ -#ifndef DRAT_TIME_H -#define DRAT_TIME_H - -#include - -char* apfs_timestamp_to_string(uint64_t apfs_timestamp); - -#endif // DRAT_TIME_H +#ifndef DRAT_TIME_H +#define DRAT_TIME_H + +#include + +char* apfs_timestamp_to_string(uint64_t apfs_timestamp); + +#endif // DRAT_TIME_H diff --git a/include/drat/utilities.c b/include/drat/utilities.c index bfc360f..d6c6dcc 100644 --- a/include/drat/utilities.c +++ b/include/drat/utilities.c @@ -1,133 +1,133 @@ -#include - -const char* nth_strchr(const char* s, char c, int n) { - int c_count; - char* nth_ptr; - for (c_count=1,nth_ptr=strchr(s,c); nth_ptr != NULL && c_count < n && c!=0; c_count++) { - nth_ptr = strchr(nth_ptr+1, c); - } - return nth_ptr; -} - -// /private/var/etc -> /var/etc -> /etc -> null -char* get_remaining_url(char *url, char token) { - const char *position_ptr = nth_strchr(url, token, 2); - int position = (position_ptr == NULL ? -1 : position_ptr - url); - - if (position > 0) { - char *newurl = malloc(strlen(url) - position); - strncpy(newurl, url+position, strlen(url) - position + 1); - return newurl; - } - return NULL; -} - -// /private/var/etc -> private -char* get_first_entry(char *url, char token) { - char *left_position_ptr = strchr(url, token); - if (left_position_ptr == NULL) { - return NULL; - } - - int left_position = left_position_ptr - url; - const char *right_position_ptr = nth_strchr(url, token, 2); - if (right_position_ptr == NULL) { - char *entry = malloc(strlen(url)); - strncpy(entry, url + left_position + 1, strlen(url)); - return entry; - } - int right_position = right_position_ptr - url; - char *entry = malloc(right_position - left_position - 1); - strncpy(entry, url+left_position+1, right_position - left_position - 1); - return entry; -} - -struct fs_browse_info* get_root_fs_tree(apfs_superblock_t* apsb, uint64_t nx_block_size) { - omap_phys_t *fs_omap = malloc(nx_block_size); - if (!fs_omap) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return NULL; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) - { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return NULL; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) - { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return NULL; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return NULL; - } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return NULL; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) - { - fprintf(stderr, "FAILED.\n"); - } - else - { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) - { - fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return NULL; - } - fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t *fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return NULL; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return NULL; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) - { - fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); - return NULL; - } - fprintf(stderr, "OK.\n"); - struct fs_browse_info* return_info = malloc(sizeof(struct fs_browse_info)); - return_info->fs_omap_btree = fs_omap_btree; - return_info->fs_root_btree = fs_root_btree; - free(fs_omap); - return return_info; -} +#include + +const char* nth_strchr(const char* s, char c, int n) { + int c_count; + char* nth_ptr; + for (c_count=1,nth_ptr=strchr(s,c); nth_ptr != NULL && c_count < n && c!=0; c_count++) { + nth_ptr = strchr(nth_ptr+1, c); + } + return nth_ptr; +} + +// /private/var/etc -> /var/etc -> /etc -> null +char* get_remaining_url(char *url, char token) { + const char *position_ptr = nth_strchr(url, token, 2); + int position = (position_ptr == NULL ? -1 : position_ptr - url); + + if (position > 0) { + char *newurl = malloc(strlen(url) - position); + strncpy(newurl, url+position, strlen(url) - position + 1); + return newurl; + } + return NULL; +} + +// /private/var/etc -> private +char* get_first_entry(char *url, char token) { + char *left_position_ptr = strchr(url, token); + if (left_position_ptr == NULL) { + return NULL; + } + + int left_position = left_position_ptr - url; + const char *right_position_ptr = nth_strchr(url, token, 2); + if (right_position_ptr == NULL) { + char *entry = malloc(strlen(url)); + strncpy(entry, url + left_position + 1, strlen(url)); + return entry; + } + int right_position = right_position_ptr - url; + char *entry = malloc(right_position - left_position - 1); + strncpy(entry, url+left_position+1, right_position - left_position - 1); + return entry; +} + +struct fs_browse_info* get_root_fs_tree(apfs_superblock_t* apsb, uint64_t nx_block_size) { + omap_phys_t *fs_omap = malloc(nx_block_size); + if (!fs_omap) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return NULL; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) + { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return NULL; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) + { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return NULL; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t *fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return NULL; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return NULL; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) + { + fprintf(stderr, "FAILED.\n"); + } + else + { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t *fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) + { + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return NULL; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t *fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return NULL; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return NULL; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) + { + fprintf(stderr, "FAILED.\nGoing back to look at the previous checkpoint instead.\n"); + return NULL; + } + fprintf(stderr, "OK.\n"); + struct fs_browse_info* return_info = malloc(sizeof(struct fs_browse_info)); + return_info->fs_omap_btree = fs_omap_btree; + return_info->fs_root_btree = fs_root_btree; + free(fs_omap); + return return_info; +} diff --git a/include/drat/utilities.h b/include/drat/utilities.h index ec3c694..0dc7d35 100644 --- a/include/drat/utilities.h +++ b/include/drat/utilities.h @@ -1,39 +1,39 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct fs_browse_info { - btree_node_phys_t *fs_omap_btree; - btree_node_phys_t *fs_root_btree; -}; - -const char* nth_strchr(const char* s, char c, int n); -char* get_remaining_url(char *url, char token); -char* get_first_entry(char *url, char token); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct fs_browse_info { + btree_node_phys_t *fs_omap_btree; + btree_node_phys_t *fs_root_btree; +}; + +const char* nth_strchr(const char* s, char c, int n); +char* get_remaining_url(char *url, char token); +char* get_first_entry(char *url, char token); struct fs_browse_info* get_root_fs_tree(apfs_superblock_t* apsb, uint64_t nx_block_size); \ No newline at end of file diff --git a/pull.sh b/pull.sh index fd8ef97..66d833b 100755 --- a/pull.sh +++ b/pull.sh @@ -1,51 +1,51 @@ -#!/bin/bash - -if [ $# -ne 2 ] ; then - echo "Incorrect number of arguments" - echo "Usage: $0 " - exit 1 -fi - -echo "Started recovery on $(date "+%Y-%m-%d") at $(date "+%H:%M:%S")" - -pathlist="$1" -rec_dir="$2" - -mkdir -p "$rec_dir" - -while IFS= read -r filepath; do - # Replace consecutive slashes (e.g. `///`) with single slash `/` - backup_path=$(echo "${rec_dir}/${filepath}" | sed -E 's:/+:/:g') - - mkdir -p "$(dirname "$backup_path")" - - # Don't overwrite existing files - if [ -f "$backup_path" ] ; then - echo "ALREADY EXISTS: $filepath" - continue - fi - - echo -n "- $filepath ... " - ./bin/apfs-recover /dev/disk2s2 0 "$filepath" > "$backup_path" 2> /dev/null - if [ $? -eq 0 ] ; then - echo "OK." - else - echo "FAILED." - fi -done < <( - # Remove comments (`#` and subsequent characters on each line), - # then remove leading whitespace, - # then remove trailing whitespace, - # then remove empty lines - cat "$pathlist" \ - | sed -E ' - s/#.*$// - s/^ +// - s/ +$// - s/^ *$//' \ - | grep -v '^ *$' -) - -echo "Finished recovery on $(date "+%Y-%m-%d") at $(date "+%H:%M:%S")" - -echo -e "\nALL DONE!\n" +#!/bin/bash + +if [ $# -ne 2 ] ; then + echo "Incorrect number of arguments" + echo "Usage: $0 " + exit 1 +fi + +echo "Started recovery on $(date "+%Y-%m-%d") at $(date "+%H:%M:%S")" + +pathlist="$1" +rec_dir="$2" + +mkdir -p "$rec_dir" + +while IFS= read -r filepath; do + # Replace consecutive slashes (e.g. `///`) with single slash `/` + backup_path=$(echo "${rec_dir}/${filepath}" | sed -E 's:/+:/:g') + + mkdir -p "$(dirname "$backup_path")" + + # Don't overwrite existing files + if [ -f "$backup_path" ] ; then + echo "ALREADY EXISTS: $filepath" + continue + fi + + echo -n "- $filepath ... " + ./bin/apfs-recover /dev/disk2s2 0 "$filepath" > "$backup_path" 2> /dev/null + if [ $? -eq 0 ] ; then + echo "OK." + else + echo "FAILED." + fi +done < <( + # Remove comments (`#` and subsequent characters on each line), + # then remove leading whitespace, + # then remove trailing whitespace, + # then remove empty lines + cat "$pathlist" \ + | sed -E ' + s/#.*$// + s/^ +// + s/ +$// + s/^ *$//' \ + | grep -v '^ *$' +) + +echo "Finished recovery on $(date "+%Y-%m-%d") at $(date "+%H:%M:%S")" + +echo -e "\nALL DONE!\n" diff --git a/remove-all-in-filelist.sh b/remove-all-in-filelist.sh index dca14a7..fc076c2 100755 --- a/remove-all-in-filelist.sh +++ b/remove-all-in-filelist.sh @@ -1,24 +1,24 @@ -#!/bin/bash - -if [ $# -ne 1 ] ; then - echo "Incorrect arguments; usage: $0 " - exit 1 -fi - -while IFS= read -r filepath; do - rm -fv "/Users/jivan/Desktop/RECOVERY/$filepath" -done < <( - # Remove comments (`#` and subsequent characters on each line), - # then remove leading whitespace, - # then remove trailing whitespace, - # then remove empty lines - cat "$1" \ - | sed -E ' - s/#.*$// - s/^ +// - s/ +$// - s/^ *$//' \ - | grep -v '^ *$' -) - -echo -e "\nALL DONE!\n" +#!/bin/bash + +if [ $# -ne 1 ] ; then + echo "Incorrect arguments; usage: $0 " + exit 1 +fi + +while IFS= read -r filepath; do + rm -fv "/Users/jivan/Desktop/RECOVERY/$filepath" +done < <( + # Remove comments (`#` and subsequent characters on each line), + # then remove leading whitespace, + # then remove trailing whitespace, + # then remove empty lines + cat "$1" \ + | sed -E ' + s/#.*$// + s/^ +// + s/ +$// + s/^ *$//' \ + | grep -v '^ *$' +) + +echo -e "\nALL DONE!\n" diff --git a/src/commands.h b/src/commands.h index 5486170..fe03c76 100644 --- a/src/commands.h +++ b/src/commands.h @@ -1,78 +1,78 @@ -#ifndef DRAT_COMMANDS_H -#define DRAT_COMMANDS_H - -#include -#include - -#include - -typedef int command_function(int argc, char** argv); - -typedef struct { - const char* name; - command_function* function; - const char* description; -} drat_command_t; - -/** - * Function prototypes; function implementations are - * contained within the respective command's source file. - */ -command_function cmd_explore_fs_tree; -command_function cmd_explore_omap_tree; -command_function cmd_inspect; -command_function cmd_list_raw; -command_function cmd_list; -command_function cmd_timeline; -command_function cmd_dumpfiles; -command_function cmd_modify; -command_function cmd_read; -command_function cmd_recover_raw; -command_function cmd_recover; -command_function cmd_resolver; -command_function cmd_search_last_btree_node; -command_function cmd_search; -command_function cmd_version; - -static drat_command_t drat_commands[] = { - { "explore-fs-tree" , cmd_explore_fs_tree , "Explore filesystem B-tree" }, - { "explore-omap-tree" , cmd_explore_omap_tree , "Explore object map B-tree" }, - { "inspect" , cmd_inspect , "Inspect APFS partition" }, - { "list-raw" , cmd_list_raw , "List directory contents or file info based on its filesystem OID" }, - { "list" , cmd_list , "List directory contents or file info based on its filepath" }, - { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, - { "dumpfiles" , cmd_dumpfiles , "Try to recover lost files from a directory" }, - // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, - { "read" , cmd_read , "Read a block and display information about it" }, - { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, - { "recover" , cmd_recover , "Recover a file based on its filepath" }, - { "resolver" , cmd_resolver , "Check if given Virtual OIDs resolve to given Physical OIDs" }, - { "search-last-btree-node" , cmd_search_last_btree_node , "Search the partition for B-tree nodes, reporting the Physical OID of the last one discovered" }, - { "search" , cmd_search , "Search the partition for blocks with certain features/properties" }, - { "version" , cmd_version , "Display Drat's version number along with legal info (copyright, warranty, and license)" }, -}; - -/** - * Given the name of a command `command_name`, return a pointer to the - * corresponding function `command_main` for that command. - * - * RETURN VALUE: - * Return pointer to function `command_main`, where `command_main` has the - * type signature `int command_main(int argc, char** argv)`. - * If `command_name` is `NULL` or an unrecognised command, return `NULL`. - */ -command_function* get_command_function(char* command_name) { - if (!command_name) { - return NULL; - } - - for (size_t i = 0; i < ARRAY_SIZE(drat_commands); i++) { - if (strcmp(drat_commands[i].name, command_name) == 0) { - return drat_commands[i].function; - } - } - - return NULL; -} - -#endif // DRAT_COMMANDS_H +#ifndef DRAT_COMMANDS_H +#define DRAT_COMMANDS_H + +#include +#include + +#include + +typedef int command_function(int argc, char** argv); + +typedef struct { + const char* name; + command_function* function; + const char* description; +} drat_command_t; + +/** + * Function prototypes; function implementations are + * contained within the respective command's source file. + */ +command_function cmd_explore_fs_tree; +command_function cmd_explore_omap_tree; +command_function cmd_inspect; +command_function cmd_list_raw; +command_function cmd_list; +command_function cmd_timeline; +command_function cmd_dumpfiles; +command_function cmd_modify; +command_function cmd_read; +command_function cmd_recover_raw; +command_function cmd_recover; +command_function cmd_resolver; +command_function cmd_search_last_btree_node; +command_function cmd_search; +command_function cmd_version; + +static drat_command_t drat_commands[] = { + { "explore-fs-tree" , cmd_explore_fs_tree , "Explore filesystem B-tree" }, + { "explore-omap-tree" , cmd_explore_omap_tree , "Explore object map B-tree" }, + { "inspect" , cmd_inspect , "Inspect APFS partition" }, + { "list-raw" , cmd_list_raw , "List directory contents or file info based on its filesystem OID" }, + { "list" , cmd_list , "List directory contents or file info based on its filepath" }, + { "timeline" , cmd_timeline , "Parse the whole filesystem recursively and build a MACB timeline" }, + { "dumpfiles" , cmd_dumpfiles , "Try to recover lost files from a directory" }, + // { "modify" , cmd_modify , "Modify structures on disk to resolve problems" }, + { "read" , cmd_read , "Read a block and display information about it" }, + { "recover-raw" , cmd_recover_raw , "Recover a file based on its filesystem OID" }, + { "recover" , cmd_recover , "Recover a file based on its filepath" }, + { "resolver" , cmd_resolver , "Check if given Virtual OIDs resolve to given Physical OIDs" }, + { "search-last-btree-node" , cmd_search_last_btree_node , "Search the partition for B-tree nodes, reporting the Physical OID of the last one discovered" }, + { "search" , cmd_search , "Search the partition for blocks with certain features/properties" }, + { "version" , cmd_version , "Display Drat's version number along with legal info (copyright, warranty, and license)" }, +}; + +/** + * Given the name of a command `command_name`, return a pointer to the + * corresponding function `command_main` for that command. + * + * RETURN VALUE: + * Return pointer to function `command_main`, where `command_main` has the + * type signature `int command_main(int argc, char** argv)`. + * If `command_name` is `NULL` or an unrecognised command, return `NULL`. + */ +command_function* get_command_function(char* command_name) { + if (!command_name) { + return NULL; + } + + for (size_t i = 0; i < ARRAY_SIZE(drat_commands); i++) { + if (strcmp(drat_commands[i].name, command_name) == 0) { + return drat_commands[i].function; + } + } + + return NULL; +} + +#endif // DRAT_COMMANDS_H diff --git a/src/commands/README.md b/src/commands/README.md index 6520a74..a9b20da 100644 --- a/src/commands/README.md +++ b/src/commands/README.md @@ -1,56 +1,56 @@ -This directory contains one `.c` source file for each command which Drat -supports. To add support for new commands, we use a system similar to that of -`git`, where new commands are declared in `src/commands.h` once they are -implemented. To add a new command `my-command`, do the following: - -1. Create a file `src/commands/my-command.c` where your command will be defined. - It should contain at least the following two functions: - - 1. `cmd_my_command()`, whose type signature is the same as that of a C - `main()` function, e.g. - - ```c - int cmd_my_command(int argc, char** argv) { - // function body - } - ``` - - This function will be passed `argv` without the name of the executable, and - `argc` equal to the size of that array, e.g. if the user runs - `drat my-command arg1 arg2`, then in the scope of `cmd_my_command()`: - - - `argv` is equal to the array `{"my-command", "arg1", "arg2"}`; and - - `argc` is equal to `3`. - - 2. `static void print_usage(int argc, char** argv)`, which will output a - description of the command's usage. It should be passed the same `argc` - and `argv` which are passwd to `cmd_my_command()`. If no arguments are - supplied to the command, the usage message should be output on `stdout`, - else it should be output on `stderr` along with any info about what the - user did wrong / why the arguments are invalid. - - See other commands' source files for examples. - -2. Add a corresponding function prototype and command entry to `src/commands.h`, - keeping the lists in alphabetical order, e.g. - - ```c - /** - * Function prototypes; function implementations are - * contained within the respective command's source file. - */ - // ... - command_function cmd_my_command; - // ... - - static drat_command_t commands[] = { - // ... - { "my-command" , cmd_my_command }, - // ... - }; - ``` - -3. Add a short description of your command's purpose to the list in - `print_usage()` in `src/drat.c`. This will be output in the list of commands - shown to the user when they run Drat without specifying a command. Please - keep the list in alphabetical order. +This directory contains one `.c` source file for each command which Drat +supports. To add support for new commands, we use a system similar to that of +`git`, where new commands are declared in `src/commands.h` once they are +implemented. To add a new command `my-command`, do the following: + +1. Create a file `src/commands/my-command.c` where your command will be defined. + It should contain at least the following two functions: + + 1. `cmd_my_command()`, whose type signature is the same as that of a C + `main()` function, e.g. + + ```c + int cmd_my_command(int argc, char** argv) { + // function body + } + ``` + + This function will be passed `argv` without the name of the executable, and + `argc` equal to the size of that array, e.g. if the user runs + `drat my-command arg1 arg2`, then in the scope of `cmd_my_command()`: + + - `argv` is equal to the array `{"my-command", "arg1", "arg2"}`; and + - `argc` is equal to `3`. + + 2. `static void print_usage(int argc, char** argv)`, which will output a + description of the command's usage. It should be passed the same `argc` + and `argv` which are passwd to `cmd_my_command()`. If no arguments are + supplied to the command, the usage message should be output on `stdout`, + else it should be output on `stderr` along with any info about what the + user did wrong / why the arguments are invalid. + + See other commands' source files for examples. + +2. Add a corresponding function prototype and command entry to `src/commands.h`, + keeping the lists in alphabetical order, e.g. + + ```c + /** + * Function prototypes; function implementations are + * contained within the respective command's source file. + */ + // ... + command_function cmd_my_command; + // ... + + static drat_command_t commands[] = { + // ... + { "my-command" , cmd_my_command }, + // ... + }; + ``` + +3. Add a short description of your command's purpose to the list in + `print_usage()` in `src/drat.c`. This will be output in the list of commands + shown to the user when they run Drat without specifying a command. Please + keep the list in alphabetical order. diff --git a/src/commands/dumpfiles.c b/src/commands/dumpfiles.c index f0d7228..ff6a0dd 100644 --- a/src/commands/dumpfiles.c +++ b/src/commands/dumpfiles.c @@ -1,303 +1,303 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char **argv) -{ - fprintf( - argc == 1 ? stdout : stderr, - "Usage: %s \n" - "Example: %s /dev/disk0s2 /private/var/mobile /Users/john/Desktop\n", - argv[0], - argv[0]); -} - -char *gen_directory(char *base_dir, char *fn, int xid) { - char xid_str[10]; - sprintf(xid_str, "%d", xid); - if (base_dir[strlen(base_dir) - 1] != '/') { - const char slash = '/'; - strncat(base_dir, &slash, 1); - } - if (access(base_dir, F_OK) != 0) { - fprintf(stderr, "The chosen output directory does not exist\n"); - exit(1); - } - char* out = malloc(strlen(base_dir) + strlen(xid_str) + strlen(fn) + 3); - // cannot import sys/stat.h to mkdir because of constant redefinition - sprintf(out, "%s_%s_%s", base_dir, xid_str, fn); - return out; -} - -void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, char *path_to_process, char *full_path, char *outdir, xid_t xid) -{ - if (fs_records == NULL || fs_records[0] == NULL) { - return; - } - int res = -1; - char *entry = NULL; - char *rest = NULL; - char *result; - if (path_to_process != NULL) { - entry = get_first_entry(path_to_process, '/'); - rest = get_remaining_url(path_to_process, '/'); - } - for (j_rec_t **fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) // for each directory entry... - { - j_rec_t *fs_rec = *fs_rec_cursor; - j_key_t *hdr = fs_rec->data; - if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) - { - //j_drec_key_t *key = fs_rec->data; - j_drec_hashed_key_t *key = fs_rec->data; - if (path_to_process != NULL) { - res = strcmp((char *)key->name, entry); - } - if (res == 0 || path_to_process == NULL || strcmp(path_to_process, "/") == 0) { - printf("Analysing record: %s\n", (char *)key->name); - // diff between found index and start index - signed int matching_record_index = fs_rec_cursor - fs_records; - // Get the file ID of the matching record's target - j_rec_t *fs_rec = fs_records[matching_record_index]; - j_drec_val_t *val = fs_rec->data + fs_rec->key_len; - - // Get the records for the target - oid_t dir_oid = val->file_id; - char *new_full_path = malloc(strlen(full_path) + strlen((char *)key->name) + 2); - strncpy(new_full_path, full_path, strlen(full_path)); - const char* separator = "/"; - strncpy(new_full_path + strlen(full_path), separator, 1); - strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); - //printf("Entering absolute path: %s\n", new_full_path); - parse_fs_and_write(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, rest, new_full_path, outdir, xid); - } - } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE) { - oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; - j_rec_t **file_record = get_fs_records(fs_omap_btree, fs_root_btree, file_oid, (xid_t)(~0)); - print_fs_records(file_record); - // get the inode value records - j_rec_t* fs_rec = *file_record; - j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; - printf("Inode record type: %s\n", get_j_inode_type(inode)); - char *fn = get_file_name(file_record); - bool is_reg_file = strcmp(get_j_inode_type(inode), "Regular file") == 0; - result = gen_directory(outdir, fn, xid); - if (fn && is_reg_file) { - // Output content from all matching file extents - char* buffer = malloc(nx_block_size); - if (!buffer) { - fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); - return; - } - uint64_t file_size = get_file_size(inode, fs_rec->val_len); - printf("File size: %" PRIu64 "\n", file_size); - if (file_size == 0) { - // Not a file, or file size couldn't be found; skip it in this case. - continue; - } - - // Keep track of how many more bytes we need to output - uint64_t bytes_remaining = file_size; - - bool found_file_extent = false; - // Go through all the fs records - for (j_rec_t** fs_rec_cursor = file_record; *fs_rec_cursor; fs_rec_cursor++) { - j_rec_t* fs_rec = *fs_rec_cursor; - j_key_t* hdr = fs_rec->data; - - // Go through all the extent records - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { - found_file_extent = true; - j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; - - // Output the content from this particular file extent - uint64_t block_addr = val->phys_block_num; - - uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; - for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { - if (read_blocks(buffer, block_addr, 1) != 1) { - fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); - exit(-1); - } - - uint64_t bytes_to_write = nx_block_size; - if (bytes_remaining < nx_block_size) { - bytes_to_write = bytes_remaining; - } - - FILE *fp = fopen(result, "ab"); - if (fwrite(buffer, bytes_to_write, 1, fp) != 1) { - fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); - exit(-1); - } - fclose(fp); - - bytes_remaining -= bytes_to_write; - - if (bytes_remaining == 0) { - break; // Exit extent loop - } - } - } - - if (bytes_remaining == 0) { - break; // Exit fs record loop - } - } - if (!found_file_extent) { - fprintf(stderr, "Could not find any file extents for the specified path.\n"); - } - free_j_rec_array(file_record); - } else { - continue; - } - } - } - free_j_rec_array(fs_records); - free(result); -} - -int cmd_dumpfiles(int argc, char **argv) -{ - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 4) - { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - - nx_path = argv[1]; - - char *path_stack = argv[2]; - char *outdir = argv[3]; - // Open (device special) file corresponding to an APFS container, read-only - fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) - { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - return -errno; - } - fseek(nx, 0, SEEK_END); - long size = ftell(nx); - fseek(nx, 0, SEEK_SET); - printf("Image File size: %ld\n", size); - nx_superblock_t *nxsb = malloc(nx_block_size); - if (!nxsb) - { - fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); - return -1; - } - - if (read_blocks(nxsb, 0x0, 1) != 1) - { - fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); - return -1; - } - - fprintf(stderr, "Validating checksum of block 0x0 ..."); - if (!is_cksum_valid(nxsb)) - { - fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); - } - if (!is_nx_superblock(nxsb)) - { - fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); - } - if (nxsb->nx_magic != NX_MAGIC) - { - fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); - } - printf("OK\n"); - uint64_t nx_block_size = nxsb->nx_block_size; - - for (uint32_t block_index = 0; block_index < size/nx_block_size; block_index++) { - char *apsb = malloc(nx_block_size); - if (!apsb) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return 1; - } - if (read_blocks(apsb, block_index, 1) != 1) - { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIu32 ".\n", block_index); - continue; - } - if (is_cksum_valid(apsb)) - { - if (((apfs_superblock_t *)apsb)->apfs_magic == APFS_MAGIC) - { - printf("Found valid APSB block at addr 0x%" PRIu32 "\n", block_index); - struct fs_browse_info *fs_trees = get_root_fs_tree(apsb, nx_block_size); - if (fs_trees == NULL) { - fprintf(stderr, "Error getting the fs tree\n"); - continue; - } - oid_t fs_oid = 0x2; - // Start with root directory oid - j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); - if (!fs_records) - { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - if (path_stack[strlen(path_stack) - 1] == '/' && strcmp(path_stack, "/") != 0) { - path_stack[strlen(path_stack) - 1] = '\0'; - } - char *path = malloc(strlen(path_stack) + 1); - if (!path) - { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); - return -1; - } - memcpy(path, path_stack, strlen(path_stack) + 1); - - printf("Entering directory /\n"); - char *path_builder = ""; - parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir, ((apfs_superblock_t *)apsb)->apfs_o.o_xid); - free(fs_trees->fs_omap_btree); - free(fs_trees->fs_root_btree); - free(fs_trees); - free(apsb); - } - } - } - fprintf(stderr, "END: All done.\n"); - return 0; -} +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char **argv) +{ + fprintf( + argc == 1 ? stdout : stderr, + "Usage: %s \n" + "Example: %s /dev/disk0s2 /private/var/mobile /Users/john/Desktop\n", + argv[0], + argv[0]); +} + +char *gen_directory(char *base_dir, char *fn, int xid) { + char xid_str[10]; + sprintf(xid_str, "%d", xid); + if (base_dir[strlen(base_dir) - 1] != '/') { + const char slash = '/'; + strncat(base_dir, &slash, 1); + } + if (access(base_dir, F_OK) != 0) { + fprintf(stderr, "The chosen output directory does not exist\n"); + exit(1); + } + char* out = malloc(strlen(base_dir) + strlen(xid_str) + strlen(fn) + 3); + // cannot import sys/stat.h to mkdir because of constant redefinition + sprintf(out, "%s_%s_%s", base_dir, xid_str, fn); + return out; +} + +void parse_fs_and_write(j_rec_t **fs_records, btree_node_phys_t *fs_omap_btree, btree_node_phys_t *fs_root_btree, char *path_to_process, char *full_path, char *outdir, xid_t xid) +{ + if (fs_records == NULL || fs_records[0] == NULL) { + return; + } + int res = -1; + char *entry = NULL; + char *rest = NULL; + char *result; + if (path_to_process != NULL) { + entry = get_first_entry(path_to_process, '/'); + rest = get_remaining_url(path_to_process, '/'); + } + for (j_rec_t **fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) // for each directory entry... + { + j_rec_t *fs_rec = *fs_rec_cursor; + j_key_t *hdr = fs_rec->data; + if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_DIR_REC) + { + //j_drec_key_t *key = fs_rec->data; + j_drec_hashed_key_t *key = fs_rec->data; + if (path_to_process != NULL) { + res = strcmp((char *)key->name, entry); + } + if (res == 0 || path_to_process == NULL || strcmp(path_to_process, "/") == 0) { + printf("Analysing record: %s\n", (char *)key->name); + // diff between found index and start index + signed int matching_record_index = fs_rec_cursor - fs_records; + // Get the file ID of the matching record's target + j_rec_t *fs_rec = fs_records[matching_record_index]; + j_drec_val_t *val = fs_rec->data + fs_rec->key_len; + + // Get the records for the target + oid_t dir_oid = val->file_id; + char *new_full_path = malloc(strlen(full_path) + strlen((char *)key->name) + 2); + strncpy(new_full_path, full_path, strlen(full_path)); + const char* separator = "/"; + strncpy(new_full_path + strlen(full_path), separator, 1); + strncpy(new_full_path + strlen(full_path) + 1, (char *)key->name, strlen((char *)key->name) + 1); + //printf("Entering absolute path: %s\n", new_full_path); + parse_fs_and_write(get_fs_records(fs_omap_btree, fs_root_btree, dir_oid, (xid_t)(~0)), fs_omap_btree, fs_root_btree, rest, new_full_path, outdir, xid); + } + } else if (((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_INODE) { + oid_t file_oid = hdr->obj_id_and_type & OBJ_ID_MASK; + j_rec_t **file_record = get_fs_records(fs_omap_btree, fs_root_btree, file_oid, (xid_t)(~0)); + print_fs_records(file_record); + // get the inode value records + j_rec_t* fs_rec = *file_record; + j_inode_val_t* inode = fs_rec->data + fs_rec->key_len; + printf("Inode record type: %s\n", get_j_inode_type(inode)); + char *fn = get_file_name(file_record); + bool is_reg_file = strcmp(get_j_inode_type(inode), "Regular file") == 0; + result = gen_directory(outdir, fn, xid); + if (fn && is_reg_file) { + // Output content from all matching file extents + char* buffer = malloc(nx_block_size); + if (!buffer) { + fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); + return; + } + uint64_t file_size = get_file_size(inode, fs_rec->val_len); + printf("File size: %" PRIu64 "\n", file_size); + if (file_size == 0) { + // Not a file, or file size couldn't be found; skip it in this case. + continue; + } + + // Keep track of how many more bytes we need to output + uint64_t bytes_remaining = file_size; + + bool found_file_extent = false; + // Go through all the fs records + for (j_rec_t** fs_rec_cursor = file_record; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + + // Go through all the extent records + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { + found_file_extent = true; + j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; + + // Output the content from this particular file extent + uint64_t block_addr = val->phys_block_num; + + uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; + for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { + if (read_blocks(buffer, block_addr, 1) != 1) { + fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); + exit(-1); + } + + uint64_t bytes_to_write = nx_block_size; + if (bytes_remaining < nx_block_size) { + bytes_to_write = bytes_remaining; + } + + FILE *fp = fopen(result, "ab"); + if (fwrite(buffer, bytes_to_write, 1, fp) != 1) { + fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); + exit(-1); + } + fclose(fp); + + bytes_remaining -= bytes_to_write; + + if (bytes_remaining == 0) { + break; // Exit extent loop + } + } + } + + if (bytes_remaining == 0) { + break; // Exit fs record loop + } + } + if (!found_file_extent) { + fprintf(stderr, "Could not find any file extents for the specified path.\n"); + } + free_j_rec_array(file_record); + } else { + continue; + } + } + } + free_j_rec_array(fs_records); + free(result); +} + +int cmd_dumpfiles(int argc, char **argv) +{ + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 4) + { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + + nx_path = argv[1]; + + char *path_stack = argv[2]; + char *outdir = argv[3]; + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) + { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fseek(nx, 0, SEEK_END); + long size = ftell(nx); + fseek(nx, 0, SEEK_SET); + printf("Image File size: %ld\n", size); + nx_superblock_t *nxsb = malloc(nx_block_size); + if (!nxsb) + { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) + { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ..."); + if (!is_cksum_valid(nxsb)) + { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + if (!is_nx_superblock(nxsb)) + { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) + { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + printf("OK\n"); + uint64_t nx_block_size = nxsb->nx_block_size; + + for (uint32_t block_index = 0; block_index < size/nx_block_size; block_index++) { + char *apsb = malloc(nx_block_size); + if (!apsb) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return 1; + } + if (read_blocks(apsb, block_index, 1) != 1) + { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIu32 ".\n", block_index); + continue; + } + if (is_cksum_valid(apsb)) + { + if (((apfs_superblock_t *)apsb)->apfs_magic == APFS_MAGIC) + { + printf("Found valid APSB block at addr 0x%" PRIu32 "\n", block_index); + struct fs_browse_info *fs_trees = get_root_fs_tree(apsb, nx_block_size); + if (fs_trees == NULL) { + fprintf(stderr, "Error getting the fs tree\n"); + continue; + } + oid_t fs_oid = 0x2; + // Start with root directory oid + j_rec_t **fs_records = get_fs_records(fs_trees->fs_omap_btree, fs_trees->fs_root_btree, fs_oid, (xid_t)(~0)); + if (!fs_records) + { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + if (path_stack[strlen(path_stack) - 1] == '/' && strcmp(path_stack, "/") != 0) { + path_stack[strlen(path_stack) - 1] = '\0'; + } + char *path = malloc(strlen(path_stack) + 1); + if (!path) + { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `path`.\n"); + return -1; + } + memcpy(path, path_stack, strlen(path_stack) + 1); + + printf("Entering directory /\n"); + char *path_builder = ""; + parse_fs_and_write(fs_records, fs_trees->fs_omap_btree, fs_trees->fs_root_btree, path, path_builder, outdir, ((apfs_superblock_t *)apsb)->apfs_o.o_xid); + free(fs_trees->fs_omap_btree); + free(fs_trees->fs_root_btree); + free(fs_trees); + free(apsb); + } + } + } + fprintf(stderr, "END: All done.\n"); + return 0; +} diff --git a/src/commands/list-raw.c b/src/commands/list-raw.c index f09cf9d..a07c524 100644 --- a/src/commands/list-raw.c +++ b/src/commands/list-raw.c @@ -1,475 +1,475 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char** argv) { - fprintf( - argc == 1 ? stdout: stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2 0 0xd4a7f\n", - - argv[0], - argv[0] - ); -} - -int cmd_list_raw(int argc, char** argv) { - if (argc == 1) { - print_usage(argc, argv); - return 0; - } - - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 4) { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - - nx_path = argv[1]; - - uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%u", &volume_id); - if (!parse_success) { - fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); - print_usage(argc, argv); - return 1; - } - - oid_t fs_oid; - parse_success = sscanf(argv[3], "0x%" PRIx64 "", &fs_oid); - if (!parse_success) { - parse_success = sscanf(argv[3], "%" PRIu64, &fs_oid); - } - if (!parse_success) { - fprintf(stderr, "%s is not a valid block address.\n", argv[3]); - print_usage(argc, argv); - return 1; - } - - // Open (device special) file corresponding to an APFS container, read-only - fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - return -errno; - } - fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); - - // Using `nx_superblock_t*`, but allocating a whole block of memory. - // This way, we can read the entire block and validate its checksum, - // but still have direct access to the fields in `nx_superblock_t` - // without needing to epxlicitly cast to that datatype. - nx_superblock_t* nxsb = malloc(nx_block_size); - if (!nxsb) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); - return -1; - } - - if (read_blocks(nxsb, 0x0, 1) != 1) { - fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); - return -1; - } - - fprintf(stderr, "Validating checksum of block 0x0 ... "); - if (!is_cksum_valid(nxsb)) { - fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); - } - fprintf(stderr, "OK.\n"); - - if (!is_nx_superblock(nxsb)) { - fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); - } - if (nxsb->nx_magic != NX_MAGIC) { - fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); - } - - fprintf(stderr, "Locating the checkpoint descriptor area:\n"); - - uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); - - char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); - if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); - return -1; - } - - if (nxsb->nx_xp_desc_blocks >> 31) { - fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous - return -1; - } else { - fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - - fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); - if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { - fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); - - uint32_t i_latest_nx = 0; - xid_t xid_latest_nx = 0; - - xid_t max_xid = ~0; // `~0` is the highest possible XID - - for (uint32_t i = 0; i < xp_desc_blocks; i++) { - if (!is_cksum_valid(xp_desc[i])) { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); - continue; - } - - if (is_nx_superblock(xp_desc[i])) { - if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; - } - - if ( - ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid > xid_latest_nx ) - && ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid <= max_xid ) - ) { - i_latest_nx = i; - xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; - } - } else if (!is_checkpoint_map_phys(xp_desc[i])) { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; - } - } - - if (xid_latest_nx == 0) { - fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); - return -1; - } - - // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` - // anymore; replace that data with the latest NXSB. - // This also lets us avoid repeatedly casting to `nx_superblock_t*`. - memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); - fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); - - // Copy the contents of the checkpoint we are currently considering to its - // own array for easy access. The checkpoint descriptor area is a ring - // buffer stored as an array, so doing this also allows us to handle the - // case where the checkpoint we're considering wraps around the ring buffer. - fprintf(stderr, "Loading the corresponding checkpoint ... "); - - // The array `xp` will comprise the blocks in the checkpoint, in order. - char (*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); - if (!xp) { - fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); - return -1; - } - - if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) { - // The simple case: the checkpoint is already contiguous in `xp_desc`. - memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); - } else { - // The case where the checkpoint wraps around from the end of the - // checkpoint descriptor area to the start. - uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; - uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; - memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); - memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); - } - fprintf(stderr, "OK.\n"); - - // We could `free(xp_desc)` at this point, but instead, we retain our copy - // of the checkpoint descriptor area in case any of the Ephemeral objects - // referenced by the current checkpoint are malformed; then, we can - // retrieve an older checkpoint without having to read the checkpoint - // descriptor area again. - - uint32_t xp_obj_len = 0; // This variable will equal the number of - // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { - if (is_checkpoint_map_phys(xp[i])) { - xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; - } - } - fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); - - fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); - char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); - if (!xp_obj) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); - return -1; - } - uint32_t num_read = 0; - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { - if (is_checkpoint_map_phys(xp[i])) { - checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting - for (uint32_t j = 0; j < xp_map->cpm_count; j++) { - if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); - return -1; - } - num_read++; - } - } - } - fprintf(stderr, "OK.\n"); - assert(num_read = xp_obj_len); - - fprintf(stderr, "Validating the Ephemeral objects ... "); - for (uint32_t i = 0; i < xp_obj_len; i++) { - if (!is_cksum_valid(xp_obj[i])) { - fprintf(stderr, "FAILED.\n"); - fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - free(xp); - free(xp_desc); - - fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); - - fprintf(stderr, "Loading the container object map ... "); - omap_phys_t* nx_omap = malloc(nx_block_size); - if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the container object map ... "); - if (!is_cksum_valid(nx_omap)) { - fprintf(stderr, "FAILED.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { - fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return -1; - } - - fprintf(stderr, "Reading the root node of the container object map B-tree ... "); - btree_node_phys_t* nx_omap_btree = malloc(nx_block_size); - if (!nx_omap_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); - return -1; - } - if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the container object map B-tree ... "); - if (!is_cksum_valid(nx_omap_btree)) { - fprintf(stderr, "FAILED.\n"); - } else { - fprintf(stderr, "OK.\n"); - } - - uint32_t num_file_systems = 0; - for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) { - if (nxsb->nx_fs_oid[i] == 0) { - break; - } - num_file_systems++; - } - fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); - for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "Reading the APFS volume superblocks ... "); - char (*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); - if (!apsbs) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return -1; - } - for (uint32_t i = 0; i < num_file_systems; i++) { - omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - if (!fs_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - return -1; - } - if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the APFS volume superblocks ... "); - for (uint32_t i = 0; i < num_file_systems; i++) { - if (!is_cksum_valid(apsbs + i)) { - fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - - if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "\n Volume list\n================\n"); - for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); - } - - if (volume_id >= num_file_systems) { - fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); - return -1; - } - apfs_superblock_t* apsb = apsbs + volume_id; - - fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - - fprintf(stderr, "Reading the volume object map ... "); - omap_phys_t* fs_omap = malloc(nx_block_size); - if (!fs_omap) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return -1; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return -1; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return -1; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t* fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return -1; - } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) { - fprintf(stderr, "FAILED.\n"); - } else { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return -1; - } - fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t* fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return -1; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return -1; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) { - fprintf(stderr, "FAILED.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); - if (!fs_records) { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - - fprintf(stderr, "\nRecords for file-system object %#" PRIx64 "\n", fs_oid); - // `fs_records` now contains the records for the item at the specified path - print_fs_records(fs_records); - - free_j_rec_array(fs_records); - - // TODO: RESUME HERE - - free(fs_omap_btree); - free(fs_omap); - - // Closing statements; de-allocate all memory, close all file descriptors. - free(apsbs); - free(nx_omap_btree); - free(nx_omap); - free(xp_obj); - free(nxsb); - fclose(nx); - fprintf(stderr, "END: All done.\n"); - return 0; -} +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char** argv) { + fprintf( + argc == 1 ? stdout: stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2 0 0xd4a7f\n", + + argv[0], + argv[0] + ); +} + +int cmd_list_raw(int argc, char** argv) { + if (argc == 1) { + print_usage(argc, argv); + return 0; + } + + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 4) { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + + nx_path = argv[1]; + + uint32_t volume_id; + bool parse_success = sscanf(argv[2], "%u", &volume_id); + if (!parse_success) { + fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); + print_usage(argc, argv); + return 1; + } + + oid_t fs_oid; + parse_success = sscanf(argv[3], "0x%" PRIx64 "", &fs_oid); + if (!parse_success) { + parse_success = sscanf(argv[3], "%" PRIu64, &fs_oid); + } + if (!parse_success) { + fprintf(stderr, "%s is not a valid block address.\n", argv[3]); + print_usage(argc, argv); + return 1; + } + + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); + + // Using `nx_superblock_t*`, but allocating a whole block of memory. + // This way, we can read the entire block and validate its checksum, + // but still have direct access to the fields in `nx_superblock_t` + // without needing to epxlicitly cast to that datatype. + nx_superblock_t* nxsb = malloc(nx_block_size); + if (!nxsb) { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ... "); + if (!is_cksum_valid(nxsb)) { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + fprintf(stderr, "OK.\n"); + + if (!is_nx_superblock(nxsb)) { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + + fprintf(stderr, "Locating the checkpoint descriptor area:\n"); + + uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); + + char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); + if (!xp_desc) { + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); + return -1; + } + + if (nxsb->nx_xp_desc_blocks >> 31) { + fprintf(stderr, "- It is not contiguous.\n"); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous + return -1; + } else { + fprintf(stderr, "- It is contiguous.\n"); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + + fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); + if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { + fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); + + uint32_t i_latest_nx = 0; + xid_t xid_latest_nx = 0; + + xid_t max_xid = ~0; // `~0` is the highest possible XID + + for (uint32_t i = 0; i < xp_desc_blocks; i++) { + if (!is_cksum_valid(xp_desc[i])) { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + + if (is_nx_superblock(xp_desc[i])) { + if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + + if ( + ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid > xid_latest_nx ) + && ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid <= max_xid ) + ) { + i_latest_nx = i; + xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; + } + } else if (!is_checkpoint_map_phys(xp_desc[i])) { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + + if (xid_latest_nx == 0) { + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); + return -1; + } + + // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` + // anymore; replace that data with the latest NXSB. + // This also lets us avoid repeatedly casting to `nx_superblock_t*`. + memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); + + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); + + // Copy the contents of the checkpoint we are currently considering to its + // own array for easy access. The checkpoint descriptor area is a ring + // buffer stored as an array, so doing this also allows us to handle the + // case where the checkpoint we're considering wraps around the ring buffer. + fprintf(stderr, "Loading the corresponding checkpoint ... "); + + // The array `xp` will comprise the blocks in the checkpoint, in order. + char (*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); + if (!xp) { + fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); + return -1; + } + + if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) { + // The simple case: the checkpoint is already contiguous in `xp_desc`. + memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); + } else { + // The case where the checkpoint wraps around from the end of the + // checkpoint descriptor area to the start. + uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; + uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; + memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); + memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); + } + fprintf(stderr, "OK.\n"); + + // We could `free(xp_desc)` at this point, but instead, we retain our copy + // of the checkpoint descriptor area in case any of the Ephemeral objects + // referenced by the current checkpoint are malformed; then, we can + // retrieve an older checkpoint without having to read the checkpoint + // descriptor area again. + + uint32_t xp_obj_len = 0; // This variable will equal the number of + // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { + if (is_checkpoint_map_phys(xp[i])) { + xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; + } + } + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + + fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); + char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); + if (!xp_obj) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); + return -1; + } + uint32_t num_read = 0; + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { + if (is_checkpoint_map_phys(xp[i])) { + checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting + for (uint32_t j = 0; j < xp_map->cpm_count; j++) { + if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); + return -1; + } + num_read++; + } + } + } + fprintf(stderr, "OK.\n"); + assert(num_read = xp_obj_len); + + fprintf(stderr, "Validating the Ephemeral objects ... "); + for (uint32_t i = 0; i < xp_obj_len; i++) { + if (!is_cksum_valid(xp_obj[i])) { + fprintf(stderr, "FAILED.\n"); + fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + free(xp); + free(xp_desc); + + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); + + fprintf(stderr, "Loading the container object map ... "); + omap_phys_t* nx_omap = malloc(nx_block_size); + if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the container object map ... "); + if (!is_cksum_valid(nx_omap)) { + fprintf(stderr, "FAILED.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { + fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return -1; + } + + fprintf(stderr, "Reading the root node of the container object map B-tree ... "); + btree_node_phys_t* nx_omap_btree = malloc(nx_block_size); + if (!nx_omap_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); + return -1; + } + if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the container object map B-tree ... "); + if (!is_cksum_valid(nx_omap_btree)) { + fprintf(stderr, "FAILED.\n"); + } else { + fprintf(stderr, "OK.\n"); + } + + uint32_t num_file_systems = 0; + for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) { + if (nxsb->nx_fs_oid[i] == 0) { + break; + } + num_file_systems++; + } + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + for (uint32_t i = 0; i < num_file_systems; i++) { + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Reading the APFS volume superblocks ... "); + char (*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); + if (!apsbs) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return -1; + } + for (uint32_t i = 0; i < num_file_systems; i++) { + omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + if (!fs_entry) { + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + return -1; + } + if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the APFS volume superblocks ... "); + for (uint32_t i = 0; i < num_file_systems; i++) { + if (!is_cksum_valid(apsbs + i)) { + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + + if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "\n Volume list\n================\n"); + for (uint32_t i = 0; i < num_file_systems; i++) { + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); + } + + if (volume_id >= num_file_systems) { + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); + return -1; + } + apfs_superblock_t* apsb = apsbs + volume_id; + + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + + fprintf(stderr, "Reading the volume object map ... "); + omap_phys_t* fs_omap = malloc(nx_block_size); + if (!fs_omap) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return -1; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return -1; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return -1; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t* fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return -1; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) { + fprintf(stderr, "FAILED.\n"); + } else { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) { + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return -1; + } + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t* fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return -1; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return -1; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) { + fprintf(stderr, "FAILED.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); + if (!fs_records) { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + + fprintf(stderr, "\nRecords for file-system object %#" PRIx64 "\n", fs_oid); + // `fs_records` now contains the records for the item at the specified path + print_fs_records(fs_records); + + free_j_rec_array(fs_records); + + // TODO: RESUME HERE + + free(fs_omap_btree); + free(fs_omap); + + // Closing statements; de-allocate all memory, close all file descriptors. + free(apsbs); + free(nx_omap_btree); + free(nx_omap); + free(xp_obj); + free(nxsb); + fclose(nx); + fprintf(stderr, "END: All done.\n"); + return 0; +} diff --git a/src/commands/modify.c b/src/commands/modify.c index 459f6dc..70cc9b2 100644 --- a/src/commands/modify.c +++ b/src/commands/modify.c @@ -1,691 +1,691 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char** argv) { - fprintf( - argc == 1 ? stdout : stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2\n", - - argv[0], - argv[0] - ); -} - -int cmd_modify(int argc, char** argv) { - if (argc == 1) { - print_usage(argc, argv); - return 0; - } - - /** Setup **/ - - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 2) { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - nx_path = argv[1]; - - // Open (device special) file corresponding to an APFS container, read-only - printf("Opening file at `%s` in read-and-write mode ... ", nx_path); - nx = fopen(nx_path, "w+b"); - if (!nx) { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - printf("\n"); - return -errno; - } - printf("OK.\n"); - - /** Copy one block to another block address **/ - if (true) { - long read_from = 0xe1b61; - long write_to = 0xd3793; - - char* block = malloc(nx_block_size); - if (!block) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); - return -1; - } - - if (read_blocks(block, read_from, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); - return -1; - } - - if (write_blocks(block, write_to, 1) != 1) { - fprintf(stderr, "\nABORT: An error occurred when writing to block %#lx.\n", write_to); - return -1; - } - - printf("Successfully copied block %#lx to %#lx.\n", read_from, write_to); - } - - /** Create an Omap B-tree node and write it to disk **/ - if (false) { - size_t NUM_RECORDS = 82; - - /** Create a B-tree node in memory **/ - - btree_node_phys_t* node = calloc(1, nx_block_size); // Using `calloc()` to get zeroed out memory - if (!node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); - return -1; - } - - /** Set node header values **/ - - node->btn_o.o_oid = 0xe4759; - node->btn_o.o_xid = 0x1bca0d; - node->btn_o.o_type = OBJ_PHYSICAL | OBJECT_TYPE_BTREE_NODE; - node->btn_o.o_subtype = OBJECT_TYPE_OMAP; - - /** Set B-trees structure values **/ - - node->btn_flags = BTNODE_LEAF | BTNODE_FIXED_KV_SIZE; - - node->btn_level = 0; - - node->btn_nkeys = NUM_RECORDS; - - node->btn_table_space.off = 0; - node->btn_table_space.len = node->btn_nkeys * sizeof(kvoff_t); - - node->btn_free_space.off = 0; // TODO: this value - node->btn_free_space.len = 0; // TODO: this value - - // Empty free list for keys and values - node->btn_key_free_list.off = 0; - node->btn_key_free_list.len = 0; - node->btn_val_free_list.off = 0; - node->btn_val_free_list.len = 0; - - /** Add records to node **/ - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - - /** Add records to node by copying them from other nodes **/ - - btree_node_phys_t* ref_node = malloc(nx_block_size); - if (!ref_node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `ref_node`.\n"); - return -1; - } - - /** Declare data needed to construct B-tree node **/ - - /** - * Array describing which entries of which existing nodes need to - * be copied to the new node. Each array entry comprises: - * - * - 0: The reference node's block address - * - 1: The index of the first entry to copy - * - 2: The index of the last entry to copy - */ - size_t NUM_NODES = 2; - uint64_t ref_nodes[][3] = { - {0xe4759, 1, 58}, - {0xe1bc1, 0, 23}, - }; - - kvoff_t* toc_entry = toc_start; - omap_key_t* key = key_start; - omap_val_t* val = (omap_val_t*)val_end - 1; - - for (size_t i = 0; i < NUM_NODES; i++) { - if (read_blocks(ref_node, ref_nodes[i][0], 1) != 1) { - fprintf(stderr, "\nABORT: Error reading block %#" PRIx64 ".\n", ref_nodes[i][0]); - return -1; - } - - char* ref_toc_start = (char*)ref_node->btn_data + ref_node->btn_table_space.off; - char* ref_key_start = ref_toc_start + ref_node->btn_table_space.len; - char* ref_val_end = (char*)ref_node + nx_block_size; - - uint32_t NUM_ENTRIES = (ref_nodes[i][2] - ref_nodes[i][1]) + 1; - - kvoff_t* ref_toc_entry = (kvoff_t*)ref_toc_start + ref_nodes[i][1]; - - for (uint32_t j = 0; j < NUM_ENTRIES; j++, ref_toc_entry++, toc_entry++, key++, val--) { - toc_entry->k = (char*)key - key_start; - toc_entry->v = val_end - (char*)val; - - omap_key_t* ref_key = ref_key_start + ref_toc_entry->k; - omap_val_t* ref_val = ref_val_end - ref_toc_entry->v; - - omap_key_t* key = key_start + toc_entry->k; - omap_val_t* val = val_end - toc_entry->v; - - memcpy(key, ref_key, sizeof(omap_key_t)); - memcpy(val, ref_val, sizeof(omap_val_t)); - } - } - assert(toc_entry - (kvoff_t*)toc_start == (long)NUM_RECORDS); - - free(ref_node); - - /** Now we can declare where the node's free space is **/ - - node->btn_free_space.off = (char*)key - key_start; - node->btn_free_space.len = (char*)(val - 1) - (char*)key; - - /** Set checksum **/ - - uint64_t block_checksum = compute_block_cksum(node); - *((uint64_t*)node) = block_checksum; - - /** Print out the contents of the B-tree node that is in memory **/ - - print_btree_node_phys(node); - - /** Write the data to disk **/ - - if (write_blocks(node, 0xd35a3, 1) != 1) { - fprintf(stderr, "\nABORT: Error writing to 0xd35a3.\n"); - return -1; - } else { - printf("\nSuccessfully wrote new node to 0xd35a3.\n"); - } - - /** Cleanup **/ - - free(node); - } - - /** Add an entry to an Omap B-tree leaf node and write the new node to disk **/ - if (false) { - long addr = 0xd350b; - size_t NEW_ENTRY_INDEX = 24; - - /** Create a B-tree node in memory **/ - - btree_node_phys_t* node = malloc(nx_block_size); - if (!node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); - return -1; - } - if (read_blocks(node, addr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", addr); - return -1; - } - - node->btn_nkeys++; - - if ( node->btn_table_space.len < node->btn_nkeys * sizeof(kvoff_t) ) { - fprintf(stderr, "\nABORT: Not enough space in TOC area for the new entry.\n"); - return -1; - } - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - - omap_key_t* key; - omap_val_t* val; - - if ( node->btn_free_space.len >= sizeof(omap_key_t) + sizeof(omap_val_t) ) { - key = (omap_key_t*)( key_start + node->btn_free_space.off ); - val = (omap_val_t*)( (char*)key + node->btn_free_space.len - sizeof(omap_val_t) ); - - node->btn_free_space.off += sizeof(omap_key_t); - node->btn_free_space.len -= sizeof(omap_key_t) + sizeof(omap_val_t); - } else { - fprintf(stderr, "Not enough space in the shared free space area. Checking the key- and value-free lists.\n"); - - /** Check overall free space in each list **/ - - if (node->btn_key_free_list.len < sizeof(omap_key_t) ) { - fprintf(stderr, "\nABORT: Not enough free space in the key free-list.\n"); - return -1; - } - uint16_t key_free_len_remaining_in_search = node->btn_key_free_list.len; - - if (node->btn_val_free_list.len < sizeof(omap_val_t) ) { - fprintf(stderr, "\nABORT: Not enough free space in the value free-list.\n"); - return -1; - } - uint16_t val_free_len_remaining_in_search = node->btn_val_free_list.len; - - /** Now actually look for sufficient free blocks **/ - - nloc_t* key_free_loc = key_start + node->btn_key_free_list.off; - while (key_free_loc->len < sizeof(omap_key_t)) { - if (key_free_len_remaining_in_search <= 0) { - fprintf(stderr, "\nABORT: No sufficiently sized free block referenced by the key free list.\n"); - return -1; - } - key_free_len_remaining_in_search -= key_free_loc->len; - key_free_loc = key_start + key_free_loc->off; - } - key = key_free_loc; - - nloc_t* val_free_loc = val_end - node->btn_val_free_list.off; - while (val_free_loc->len < sizeof(omap_val_t)) { - if (val_free_len_remaining_in_search <= 0) { - fprintf(stderr, "\nABORT: No sufficiently sized free block referenced by the value free list.\n"); - return -1; - } - val_free_len_remaining_in_search -= val_free_loc->len; - val_free_loc = val_end - val_free_loc->off; - } - val = val_free_loc; - } - - /** Add new record to node **/ - - key->ok_oid = 0x277afc; - key->ok_xid = 0x1af045; - - val->ov_size = nx_block_size; - val->ov_paddr = 0xe911f; - - // Shift the TOC entries as needed, then add the new entry - kvoff_t* new_toc_entry = (kvoff_t*)toc_start + NEW_ENTRY_INDEX; - memmove(new_toc_entry + 1, new_toc_entry, (node->btn_nkeys - NEW_ENTRY_INDEX) * sizeof(kvoff_t)); - - new_toc_entry->k = (char*)key - key_start; - new_toc_entry->v = val_end - (char*)val; - - /** Set checksum **/ - - uint64_t block_checksum = compute_block_cksum(node); - *((uint64_t*)node) = block_checksum; - - /** Print out the contents of the B-tree node that is in memory **/ - - print_btree_node_phys(node); - - /** Write the data to disk **/ - - if (write_blocks(node, addr, 1) != 1) { - fprintf(stderr, "\nABORT: Error writing to %#lx.\n", addr); - return -1; - } else { - printf("\nSuccessfully wrote new node to %#lx.\n", addr); - } - - /** Cleanup **/ - - free(node); - } - - /** Create an FS-root B-tree node and write it to disk **/ - if (false) { - /** Declare data needed to construct B-tree node **/ - - /** - * Array containing data for all of the file-system records which need to - * be added to the node. Each entry comprises: - * - * - 0: The record type, as in `obj_id_and_type` - * - 1: The record ID, as in `obj_id_and_type` - * - 2: The block address of the target node - * - 3: The Virtual OID of the target node - */ - size_t NUM_RECORDS = 61; - uint64_t record_data[][4] = { - { 0x8, 0xb46a8, 0xd6cf2, 0xa3bc }, - { 0x9, 0xb4703, 0xd4ba8, 0x77e69 }, - { 0x9, 0xb471d, 0xd0add, 0xa3c0 }, - { 0x4, 0xb4746, 0xe14ec, 0xa3c2 }, - { 0x6, 0xb4784, 0xa9a58, 0xa3c5 }, - { 0x3, 0xb494c, 0xd47c4, 0xa3c6 }, - { 0x3, 0xb4999, 0xb7292, 0xa3db }, - { 0x3, 0xb49a6, 0xac8b2, 0xa3dc }, - { 0x3, 0xb49aa, 0xd668d, 0x12394a }, - { 0x8, 0xb4a57, 0xd60f4, 0xa3e0 }, - { 0x6, 0xb4b0d, 0xd67ec, 0xa3ee }, - { 0x3, 0xb4b1c, 0xd6941, 0xa3ef }, - { 0x3, 0xb4b24, 0xd6a18, 0xa3f0 }, - { 0x3, 0xb4b34, 0xd69fb, 0xa3f1 }, - { 0x3, 0xb4b43, 0xd3d06, 0xa3f2 }, - { 0x8, 0xb4b52, 0xd68b5, 0xa3f3 }, - { 0x3, 0xb4b64, 0xd683c, 0xa3f4 }, - { 0x3, 0xb4b72, 0xd6848, 0xa3f6 }, - { 0x3, 0xb4b83, 0xd6820, 0xa3f7 }, - { 0x9, 0xb4b9f, 0xd6865, 0xa3f9 }, - { 0x9, 0xb4bae, 0xd67ed, 0xa3fa }, - { 0x3, 0xb4bbd, 0xd6ac1, 0xa3fb }, - { 0x9, 0xb4bea, 0xd5b73, 0xa3fd }, - { 0x8, 0xb4c50, 0xf3864, 0xa3ff }, - { 0x4, 0xb4c63, 0xd4393, 0xa403 }, - { 0x3, 0xb4c79, 0xd3a9d, 0xa405 }, - { 0x3, 0xb4c8d, 0xd4392, 0xa406 }, - { 0x3, 0xb4ca1, 0xd4395, 0xa407 }, - { 0x3, 0xb4cb7, 0xd4372, 0xa409 }, - { 0x3, 0xb4cc9, 0xd437d, 0xa40a }, - { 0x3, 0xb4cdc, 0xd4381, 0xa40b }, - { 0x3, 0xb4cf2, 0xd436a, 0xa40c }, - { 0x3, 0xb4d07, 0xd4371, 0xa40d }, - { 0x3, 0xb4d1d, 0xd436b, 0xa410 }, - { 0x3, 0xb4d33, 0xd4383, 0xa411 }, - { 0x3, 0xb4d47, 0xd461c, 0xa412 }, - { 0x3, 0xb4d55, 0xd984c, 0xa413 }, - { 0x6, 0xb4da0, 0xd44c3, 0xa416 }, - { 0x3, 0xb4e58, 0xd1e78, 0xa41e }, - { 0x3, 0xb4ec9, 0xd3adb, 0xb7e3d }, - { 0x4, 0xb4ee9, 0xd0ee5, 0xa428 }, - { 0x4, 0xb4eff, 0xd0251, 0xa42d }, - { 0x4, 0xb4f29, 0xd25f3, 0xa431 }, - { 0x9, 0xb540e, 0xb6907, 0xa495 }, - { 0x8, 0xb547d, 0xa7ccd, 0xa49e }, - { 0x9, 0xb54a9, 0xd4efa, 0xa4a2 }, - { 0x9, 0xb54aa, 0xd760b, 0x1e6456 }, - { 0x9, 0xb54af, 0xd2625, 0x18f420 }, - { 0x9, 0xb54b4, 0xd38af, 0x16951 }, - { 0x9, 0xb54b5, 0xd1de4, 0xd4a9 }, - { 0x9, 0xb54b7, 0xd0536, 0xa51f }, - { 0x3, 0xb54bf, 0xd1002, 0xd4ab }, - { 0x9, 0xb54c1, 0xd0f0d, 0x1f37be }, - { 0x9, 0xb54c3, 0xd3015, 0xa51a }, - { 0x9, 0xb54d2, 0xd171c, 0xa4a3 }, - { 0x3, 0xb54e6, 0xdb96a, 0xa4a4 }, - { 0x9, 0xb54ed, 0xd5ff0, 0xd7bb }, - { 0x9, 0xb54f5, 0xd193f, 0xa4a5 }, - { 0x3, 0xb5509, 0xed80b, 0x151a0 }, - { 0x9, 0xb550a, 0xd1588, 0xa4a6 }, - { 0x9, 0xb550c, 0xd85ff, 0xd784 }, - }; - - /** Create a B-tree node in memory **/ - - btree_node_phys_t* node = calloc(1, nx_block_size); // Using `calloc()` to get zeroed out memory - if (!node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); - return -1; - } - - /** Set node header values **/ - - node->btn_o.o_oid = 0xa450; - node->btn_o.o_xid = 0x1bca0d; - node->btn_o.o_type = OBJ_VIRTUAL | OBJECT_TYPE_BTREE_NODE; - node->btn_o.o_subtype = OBJECT_TYPE_FSTREE; - - /** Set B-trees structure values **/ - - // node->btn_flags -- do nothing - - node->btn_level = 1; - - node->btn_nkeys = NUM_RECORDS; - - node->btn_table_space.off = 0; - node->btn_table_space.len = node->btn_nkeys * sizeof(kvloc_t); - - node->btn_free_space.off = 0; // TODO: this value - node->btn_free_space.len = 0; // TODO: this value - - // Empty free list for keys and values - node->btn_key_free_list.off = 0; - node->btn_key_free_list.len = 0; - node->btn_val_free_list.off = 0; - node->btn_val_free_list.len = 0; - - /** Add records to node **/ - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - - kvloc_t* toc_entry = toc_start; - j_key_t* key = key_start; - oid_t* val = (oid_t*)val_end - 1; - for (size_t i = 0; i < NUM_RECORDS; i++, toc_entry++, key++, val--) { - toc_entry->k.off = (char*)key - key_start; - toc_entry->k.len = sizeof(j_key_t); - toc_entry->v.off = val_end - (char*)val; - toc_entry->v.len = sizeof(oid_t); - - uint64_t obj_type = record_data[i][0]; - uint64_t obj_id = record_data[i][1]; - - key->obj_id_and_type = (obj_type << OBJ_TYPE_SHIFT) | obj_id; - - *val = record_data[i][3]; - } - - /** Now we can delcare where the node's free space is **/ - - node->btn_free_space.off = (char*)key - key_start; - node->btn_free_space.len = (char*)(val - 1) - (char*)key; - - /** Set checksum **/ - - uint64_t block_checksum = compute_block_cksum(node); - *((uint64_t*)node) = block_checksum; - - /** Print out the contents of the B-tree node that is in memory **/ - - print_btree_node_phys(node); - - /** Write the data to disk **/ - - if (write_blocks(node, 0xd33ce, 1) != 1) { - fprintf(stderr, "\nWRITE ERROR!\n"); - } else { - printf("\nWRITE COMPLETED SUCCESSFULLY\n"); - } - - /** Cleanup **/ - - free(node); - } - - /** Modify an FS-root B-tree non-leaf node and write the new version to disk **/ - if (false) { - long read_from = 0xd3aef; - long write_to = 0xd3aef; - - /** Read the B-tree node to modify **/ - btree_node_phys_t* node = malloc(nx_block_size); - if (!node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); - return -1; - } - if (read_blocks(node, read_from, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); - return -1; - } - - /** Modify the `obj_id_and_type` field of given entries **/ - if (false) { - /** Declare data needed to edit node **/ - - /** - * Array containing data for all of the OID mappings which need to - * be edited. Each entry comprises: - * - * - 0: The index of the entry within the node - * - 1: The desired `obj_id_and_type` value - */ - size_t NUM_RECORDS = 1; // The number of records that will be modified; the size of the array `record_data` - uint64_t record_data[][2] = { - { 59, ((uint64_t)0x4 << OBJ_TYPE_SHIFT) | 0xaea315 }, - }; - - /** Edit records as specified in `entry_data` **/ - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - val_end -= sizeof(btree_info_t); - } - - for (size_t i = 0; i < NUM_RECORDS; i++) { - kvloc_t* toc_entry = (kvloc_t*)toc_start + record_data[i][0]; - - j_key_t* key = key_start + toc_entry->k.off; - key->obj_id_and_type = record_data[i][1]; - } - } - - /** Modify the target Virtual OID of given entries **/ - if (false) { - /** Declare data needed to edit node **/ - - /** - * Array containing data for all of the OID mappings which need to - * be edited. Each entry comprises: - * - * - 0: The index of the entry within the node - * - 1: The target Virtual OID - */ - size_t NUM_RECORDS = 1; // The number of records that will be modified; the size of the array `record_data` - uint64_t record_data[][2] = { - { 32, 0x20a49f }, - }; - - /** Edit records as specified in `entry_data` **/ - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - val_end -= sizeof(btree_info_t); - } - - for (size_t i = 0; i < NUM_RECORDS; i++) { - kvloc_t* toc_entry = (kvloc_t*)toc_start + record_data[i][0]; - - oid_t* target_virt_oid = val_end - toc_entry->v.off; - *target_virt_oid = record_data[i][1]; - } - } - - /** Set checksum **/ - - uint64_t block_checksum = compute_block_cksum(node); - *((uint64_t*)node) = block_checksum; - - /** Print out the contents of the B-tree node that is in memory **/ - - print_btree_node_phys(node); - - /** Write the data to disk **/ - - if (write_blocks(node, write_to, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to write to block %#lx.\n", write_to); - return -1; - } - printf("Successfully wrote modified version of block %#lx to %#lx.\n", read_from, write_to); - - /** Cleanup **/ - - free(node); - } - - /** Modify an Omap B-tree node and write the new version to disk **/ - if (false) { - long read_from = 0xe98c5; - long write_to = 0xd3691; - - /** Declare data needed to edit node **/ - - /** - * Array containing data for all of the OID mappings which need to - * be edited. Each entry comprises: - * - * - 0: The index of the entry within the node - * - 1: The target block address - */ - size_t NUM_RECORDS = 6; // The number of records that will be modified; the size of the array `record_data` - uint64_t record_data[][2] = { - { 50, 0xd6e9d }, - { 66, 0xd2a9b }, - { 75, 0xd214b }, - { 76, 0xd2155 }, - { 78, 0xd271b }, - { 81, 0xd0fd1 }, - }; - - /** Read the B-tree node to modify **/ - btree_node_phys_t* node = malloc(nx_block_size); - if (!node) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); - return -1; - } - if (read_blocks(node, read_from, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); - return -1; - } - - /** Edit records as specified in `entry_data` **/ - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - val_end -= sizeof(btree_info_t); - } - - for (size_t i = 0; i < NUM_RECORDS; i++) { - kvoff_t* toc_entry = (kvoff_t*)toc_start + record_data[i][0]; - - omap_val_t* val = val_end - toc_entry->v; - val->ov_paddr = record_data[i][1]; - } - - /** Set checksum **/ - - uint64_t block_checksum = compute_block_cksum(node); - *((uint64_t*)node) = block_checksum; - - /** Print out the contents of the B-tree node that is in memory **/ - - print_btree_node_phys(node); - - /** Write the data to disk **/ - - if (write_blocks(node, write_to, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to write to block %#lx.\n", write_to); - return -1; - } - printf("Successfully wrote modified version of block %#lx to %#lx.\n", read_from, write_to); - - /** Cleanup **/ - - free(node); - } - - fclose(nx); - - return 0; -} +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char** argv) { + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2\n", + + argv[0], + argv[0] + ); +} + +int cmd_modify(int argc, char** argv) { + if (argc == 1) { + print_usage(argc, argv); + return 0; + } + + /** Setup **/ + + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 2) { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + nx_path = argv[1]; + + // Open (device special) file corresponding to an APFS container, read-only + printf("Opening file at `%s` in read-and-write mode ... ", nx_path); + nx = fopen(nx_path, "w+b"); + if (!nx) { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + printf("\n"); + return -errno; + } + printf("OK.\n"); + + /** Copy one block to another block address **/ + if (true) { + long read_from = 0xe1b61; + long write_to = 0xd3793; + + char* block = malloc(nx_block_size); + if (!block) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); + return -1; + } + + if (read_blocks(block, read_from, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); + return -1; + } + + if (write_blocks(block, write_to, 1) != 1) { + fprintf(stderr, "\nABORT: An error occurred when writing to block %#lx.\n", write_to); + return -1; + } + + printf("Successfully copied block %#lx to %#lx.\n", read_from, write_to); + } + + /** Create an Omap B-tree node and write it to disk **/ + if (false) { + size_t NUM_RECORDS = 82; + + /** Create a B-tree node in memory **/ + + btree_node_phys_t* node = calloc(1, nx_block_size); // Using `calloc()` to get zeroed out memory + if (!node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); + return -1; + } + + /** Set node header values **/ + + node->btn_o.o_oid = 0xe4759; + node->btn_o.o_xid = 0x1bca0d; + node->btn_o.o_type = OBJ_PHYSICAL | OBJECT_TYPE_BTREE_NODE; + node->btn_o.o_subtype = OBJECT_TYPE_OMAP; + + /** Set B-trees structure values **/ + + node->btn_flags = BTNODE_LEAF | BTNODE_FIXED_KV_SIZE; + + node->btn_level = 0; + + node->btn_nkeys = NUM_RECORDS; + + node->btn_table_space.off = 0; + node->btn_table_space.len = node->btn_nkeys * sizeof(kvoff_t); + + node->btn_free_space.off = 0; // TODO: this value + node->btn_free_space.len = 0; // TODO: this value + + // Empty free list for keys and values + node->btn_key_free_list.off = 0; + node->btn_key_free_list.len = 0; + node->btn_val_free_list.off = 0; + node->btn_val_free_list.len = 0; + + /** Add records to node **/ + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + + /** Add records to node by copying them from other nodes **/ + + btree_node_phys_t* ref_node = malloc(nx_block_size); + if (!ref_node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `ref_node`.\n"); + return -1; + } + + /** Declare data needed to construct B-tree node **/ + + /** + * Array describing which entries of which existing nodes need to + * be copied to the new node. Each array entry comprises: + * + * - 0: The reference node's block address + * - 1: The index of the first entry to copy + * - 2: The index of the last entry to copy + */ + size_t NUM_NODES = 2; + uint64_t ref_nodes[][3] = { + {0xe4759, 1, 58}, + {0xe1bc1, 0, 23}, + }; + + kvoff_t* toc_entry = toc_start; + omap_key_t* key = key_start; + omap_val_t* val = (omap_val_t*)val_end - 1; + + for (size_t i = 0; i < NUM_NODES; i++) { + if (read_blocks(ref_node, ref_nodes[i][0], 1) != 1) { + fprintf(stderr, "\nABORT: Error reading block %#" PRIx64 ".\n", ref_nodes[i][0]); + return -1; + } + + char* ref_toc_start = (char*)ref_node->btn_data + ref_node->btn_table_space.off; + char* ref_key_start = ref_toc_start + ref_node->btn_table_space.len; + char* ref_val_end = (char*)ref_node + nx_block_size; + + uint32_t NUM_ENTRIES = (ref_nodes[i][2] - ref_nodes[i][1]) + 1; + + kvoff_t* ref_toc_entry = (kvoff_t*)ref_toc_start + ref_nodes[i][1]; + + for (uint32_t j = 0; j < NUM_ENTRIES; j++, ref_toc_entry++, toc_entry++, key++, val--) { + toc_entry->k = (char*)key - key_start; + toc_entry->v = val_end - (char*)val; + + omap_key_t* ref_key = ref_key_start + ref_toc_entry->k; + omap_val_t* ref_val = ref_val_end - ref_toc_entry->v; + + omap_key_t* key = key_start + toc_entry->k; + omap_val_t* val = val_end - toc_entry->v; + + memcpy(key, ref_key, sizeof(omap_key_t)); + memcpy(val, ref_val, sizeof(omap_val_t)); + } + } + assert(toc_entry - (kvoff_t*)toc_start == (long)NUM_RECORDS); + + free(ref_node); + + /** Now we can declare where the node's free space is **/ + + node->btn_free_space.off = (char*)key - key_start; + node->btn_free_space.len = (char*)(val - 1) - (char*)key; + + /** Set checksum **/ + + uint64_t block_checksum = compute_block_cksum(node); + *((uint64_t*)node) = block_checksum; + + /** Print out the contents of the B-tree node that is in memory **/ + + print_btree_node_phys(node); + + /** Write the data to disk **/ + + if (write_blocks(node, 0xd35a3, 1) != 1) { + fprintf(stderr, "\nABORT: Error writing to 0xd35a3.\n"); + return -1; + } else { + printf("\nSuccessfully wrote new node to 0xd35a3.\n"); + } + + /** Cleanup **/ + + free(node); + } + + /** Add an entry to an Omap B-tree leaf node and write the new node to disk **/ + if (false) { + long addr = 0xd350b; + size_t NEW_ENTRY_INDEX = 24; + + /** Create a B-tree node in memory **/ + + btree_node_phys_t* node = malloc(nx_block_size); + if (!node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); + return -1; + } + if (read_blocks(node, addr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", addr); + return -1; + } + + node->btn_nkeys++; + + if ( node->btn_table_space.len < node->btn_nkeys * sizeof(kvoff_t) ) { + fprintf(stderr, "\nABORT: Not enough space in TOC area for the new entry.\n"); + return -1; + } + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + + omap_key_t* key; + omap_val_t* val; + + if ( node->btn_free_space.len >= sizeof(omap_key_t) + sizeof(omap_val_t) ) { + key = (omap_key_t*)( key_start + node->btn_free_space.off ); + val = (omap_val_t*)( (char*)key + node->btn_free_space.len - sizeof(omap_val_t) ); + + node->btn_free_space.off += sizeof(omap_key_t); + node->btn_free_space.len -= sizeof(omap_key_t) + sizeof(omap_val_t); + } else { + fprintf(stderr, "Not enough space in the shared free space area. Checking the key- and value-free lists.\n"); + + /** Check overall free space in each list **/ + + if (node->btn_key_free_list.len < sizeof(omap_key_t) ) { + fprintf(stderr, "\nABORT: Not enough free space in the key free-list.\n"); + return -1; + } + uint16_t key_free_len_remaining_in_search = node->btn_key_free_list.len; + + if (node->btn_val_free_list.len < sizeof(omap_val_t) ) { + fprintf(stderr, "\nABORT: Not enough free space in the value free-list.\n"); + return -1; + } + uint16_t val_free_len_remaining_in_search = node->btn_val_free_list.len; + + /** Now actually look for sufficient free blocks **/ + + nloc_t* key_free_loc = key_start + node->btn_key_free_list.off; + while (key_free_loc->len < sizeof(omap_key_t)) { + if (key_free_len_remaining_in_search <= 0) { + fprintf(stderr, "\nABORT: No sufficiently sized free block referenced by the key free list.\n"); + return -1; + } + key_free_len_remaining_in_search -= key_free_loc->len; + key_free_loc = key_start + key_free_loc->off; + } + key = key_free_loc; + + nloc_t* val_free_loc = val_end - node->btn_val_free_list.off; + while (val_free_loc->len < sizeof(omap_val_t)) { + if (val_free_len_remaining_in_search <= 0) { + fprintf(stderr, "\nABORT: No sufficiently sized free block referenced by the value free list.\n"); + return -1; + } + val_free_len_remaining_in_search -= val_free_loc->len; + val_free_loc = val_end - val_free_loc->off; + } + val = val_free_loc; + } + + /** Add new record to node **/ + + key->ok_oid = 0x277afc; + key->ok_xid = 0x1af045; + + val->ov_size = nx_block_size; + val->ov_paddr = 0xe911f; + + // Shift the TOC entries as needed, then add the new entry + kvoff_t* new_toc_entry = (kvoff_t*)toc_start + NEW_ENTRY_INDEX; + memmove(new_toc_entry + 1, new_toc_entry, (node->btn_nkeys - NEW_ENTRY_INDEX) * sizeof(kvoff_t)); + + new_toc_entry->k = (char*)key - key_start; + new_toc_entry->v = val_end - (char*)val; + + /** Set checksum **/ + + uint64_t block_checksum = compute_block_cksum(node); + *((uint64_t*)node) = block_checksum; + + /** Print out the contents of the B-tree node that is in memory **/ + + print_btree_node_phys(node); + + /** Write the data to disk **/ + + if (write_blocks(node, addr, 1) != 1) { + fprintf(stderr, "\nABORT: Error writing to %#lx.\n", addr); + return -1; + } else { + printf("\nSuccessfully wrote new node to %#lx.\n", addr); + } + + /** Cleanup **/ + + free(node); + } + + /** Create an FS-root B-tree node and write it to disk **/ + if (false) { + /** Declare data needed to construct B-tree node **/ + + /** + * Array containing data for all of the file-system records which need to + * be added to the node. Each entry comprises: + * + * - 0: The record type, as in `obj_id_and_type` + * - 1: The record ID, as in `obj_id_and_type` + * - 2: The block address of the target node + * - 3: The Virtual OID of the target node + */ + size_t NUM_RECORDS = 61; + uint64_t record_data[][4] = { + { 0x8, 0xb46a8, 0xd6cf2, 0xa3bc }, + { 0x9, 0xb4703, 0xd4ba8, 0x77e69 }, + { 0x9, 0xb471d, 0xd0add, 0xa3c0 }, + { 0x4, 0xb4746, 0xe14ec, 0xa3c2 }, + { 0x6, 0xb4784, 0xa9a58, 0xa3c5 }, + { 0x3, 0xb494c, 0xd47c4, 0xa3c6 }, + { 0x3, 0xb4999, 0xb7292, 0xa3db }, + { 0x3, 0xb49a6, 0xac8b2, 0xa3dc }, + { 0x3, 0xb49aa, 0xd668d, 0x12394a }, + { 0x8, 0xb4a57, 0xd60f4, 0xa3e0 }, + { 0x6, 0xb4b0d, 0xd67ec, 0xa3ee }, + { 0x3, 0xb4b1c, 0xd6941, 0xa3ef }, + { 0x3, 0xb4b24, 0xd6a18, 0xa3f0 }, + { 0x3, 0xb4b34, 0xd69fb, 0xa3f1 }, + { 0x3, 0xb4b43, 0xd3d06, 0xa3f2 }, + { 0x8, 0xb4b52, 0xd68b5, 0xa3f3 }, + { 0x3, 0xb4b64, 0xd683c, 0xa3f4 }, + { 0x3, 0xb4b72, 0xd6848, 0xa3f6 }, + { 0x3, 0xb4b83, 0xd6820, 0xa3f7 }, + { 0x9, 0xb4b9f, 0xd6865, 0xa3f9 }, + { 0x9, 0xb4bae, 0xd67ed, 0xa3fa }, + { 0x3, 0xb4bbd, 0xd6ac1, 0xa3fb }, + { 0x9, 0xb4bea, 0xd5b73, 0xa3fd }, + { 0x8, 0xb4c50, 0xf3864, 0xa3ff }, + { 0x4, 0xb4c63, 0xd4393, 0xa403 }, + { 0x3, 0xb4c79, 0xd3a9d, 0xa405 }, + { 0x3, 0xb4c8d, 0xd4392, 0xa406 }, + { 0x3, 0xb4ca1, 0xd4395, 0xa407 }, + { 0x3, 0xb4cb7, 0xd4372, 0xa409 }, + { 0x3, 0xb4cc9, 0xd437d, 0xa40a }, + { 0x3, 0xb4cdc, 0xd4381, 0xa40b }, + { 0x3, 0xb4cf2, 0xd436a, 0xa40c }, + { 0x3, 0xb4d07, 0xd4371, 0xa40d }, + { 0x3, 0xb4d1d, 0xd436b, 0xa410 }, + { 0x3, 0xb4d33, 0xd4383, 0xa411 }, + { 0x3, 0xb4d47, 0xd461c, 0xa412 }, + { 0x3, 0xb4d55, 0xd984c, 0xa413 }, + { 0x6, 0xb4da0, 0xd44c3, 0xa416 }, + { 0x3, 0xb4e58, 0xd1e78, 0xa41e }, + { 0x3, 0xb4ec9, 0xd3adb, 0xb7e3d }, + { 0x4, 0xb4ee9, 0xd0ee5, 0xa428 }, + { 0x4, 0xb4eff, 0xd0251, 0xa42d }, + { 0x4, 0xb4f29, 0xd25f3, 0xa431 }, + { 0x9, 0xb540e, 0xb6907, 0xa495 }, + { 0x8, 0xb547d, 0xa7ccd, 0xa49e }, + { 0x9, 0xb54a9, 0xd4efa, 0xa4a2 }, + { 0x9, 0xb54aa, 0xd760b, 0x1e6456 }, + { 0x9, 0xb54af, 0xd2625, 0x18f420 }, + { 0x9, 0xb54b4, 0xd38af, 0x16951 }, + { 0x9, 0xb54b5, 0xd1de4, 0xd4a9 }, + { 0x9, 0xb54b7, 0xd0536, 0xa51f }, + { 0x3, 0xb54bf, 0xd1002, 0xd4ab }, + { 0x9, 0xb54c1, 0xd0f0d, 0x1f37be }, + { 0x9, 0xb54c3, 0xd3015, 0xa51a }, + { 0x9, 0xb54d2, 0xd171c, 0xa4a3 }, + { 0x3, 0xb54e6, 0xdb96a, 0xa4a4 }, + { 0x9, 0xb54ed, 0xd5ff0, 0xd7bb }, + { 0x9, 0xb54f5, 0xd193f, 0xa4a5 }, + { 0x3, 0xb5509, 0xed80b, 0x151a0 }, + { 0x9, 0xb550a, 0xd1588, 0xa4a6 }, + { 0x9, 0xb550c, 0xd85ff, 0xd784 }, + }; + + /** Create a B-tree node in memory **/ + + btree_node_phys_t* node = calloc(1, nx_block_size); // Using `calloc()` to get zeroed out memory + if (!node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); + return -1; + } + + /** Set node header values **/ + + node->btn_o.o_oid = 0xa450; + node->btn_o.o_xid = 0x1bca0d; + node->btn_o.o_type = OBJ_VIRTUAL | OBJECT_TYPE_BTREE_NODE; + node->btn_o.o_subtype = OBJECT_TYPE_FSTREE; + + /** Set B-trees structure values **/ + + // node->btn_flags -- do nothing + + node->btn_level = 1; + + node->btn_nkeys = NUM_RECORDS; + + node->btn_table_space.off = 0; + node->btn_table_space.len = node->btn_nkeys * sizeof(kvloc_t); + + node->btn_free_space.off = 0; // TODO: this value + node->btn_free_space.len = 0; // TODO: this value + + // Empty free list for keys and values + node->btn_key_free_list.off = 0; + node->btn_key_free_list.len = 0; + node->btn_val_free_list.off = 0; + node->btn_val_free_list.len = 0; + + /** Add records to node **/ + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + + kvloc_t* toc_entry = toc_start; + j_key_t* key = key_start; + oid_t* val = (oid_t*)val_end - 1; + for (size_t i = 0; i < NUM_RECORDS; i++, toc_entry++, key++, val--) { + toc_entry->k.off = (char*)key - key_start; + toc_entry->k.len = sizeof(j_key_t); + toc_entry->v.off = val_end - (char*)val; + toc_entry->v.len = sizeof(oid_t); + + uint64_t obj_type = record_data[i][0]; + uint64_t obj_id = record_data[i][1]; + + key->obj_id_and_type = (obj_type << OBJ_TYPE_SHIFT) | obj_id; + + *val = record_data[i][3]; + } + + /** Now we can delcare where the node's free space is **/ + + node->btn_free_space.off = (char*)key - key_start; + node->btn_free_space.len = (char*)(val - 1) - (char*)key; + + /** Set checksum **/ + + uint64_t block_checksum = compute_block_cksum(node); + *((uint64_t*)node) = block_checksum; + + /** Print out the contents of the B-tree node that is in memory **/ + + print_btree_node_phys(node); + + /** Write the data to disk **/ + + if (write_blocks(node, 0xd33ce, 1) != 1) { + fprintf(stderr, "\nWRITE ERROR!\n"); + } else { + printf("\nWRITE COMPLETED SUCCESSFULLY\n"); + } + + /** Cleanup **/ + + free(node); + } + + /** Modify an FS-root B-tree non-leaf node and write the new version to disk **/ + if (false) { + long read_from = 0xd3aef; + long write_to = 0xd3aef; + + /** Read the B-tree node to modify **/ + btree_node_phys_t* node = malloc(nx_block_size); + if (!node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); + return -1; + } + if (read_blocks(node, read_from, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); + return -1; + } + + /** Modify the `obj_id_and_type` field of given entries **/ + if (false) { + /** Declare data needed to edit node **/ + + /** + * Array containing data for all of the OID mappings which need to + * be edited. Each entry comprises: + * + * - 0: The index of the entry within the node + * - 1: The desired `obj_id_and_type` value + */ + size_t NUM_RECORDS = 1; // The number of records that will be modified; the size of the array `record_data` + uint64_t record_data[][2] = { + { 59, ((uint64_t)0x4 << OBJ_TYPE_SHIFT) | 0xaea315 }, + }; + + /** Edit records as specified in `entry_data` **/ + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + val_end -= sizeof(btree_info_t); + } + + for (size_t i = 0; i < NUM_RECORDS; i++) { + kvloc_t* toc_entry = (kvloc_t*)toc_start + record_data[i][0]; + + j_key_t* key = key_start + toc_entry->k.off; + key->obj_id_and_type = record_data[i][1]; + } + } + + /** Modify the target Virtual OID of given entries **/ + if (false) { + /** Declare data needed to edit node **/ + + /** + * Array containing data for all of the OID mappings which need to + * be edited. Each entry comprises: + * + * - 0: The index of the entry within the node + * - 1: The target Virtual OID + */ + size_t NUM_RECORDS = 1; // The number of records that will be modified; the size of the array `record_data` + uint64_t record_data[][2] = { + { 32, 0x20a49f }, + }; + + /** Edit records as specified in `entry_data` **/ + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + val_end -= sizeof(btree_info_t); + } + + for (size_t i = 0; i < NUM_RECORDS; i++) { + kvloc_t* toc_entry = (kvloc_t*)toc_start + record_data[i][0]; + + oid_t* target_virt_oid = val_end - toc_entry->v.off; + *target_virt_oid = record_data[i][1]; + } + } + + /** Set checksum **/ + + uint64_t block_checksum = compute_block_cksum(node); + *((uint64_t*)node) = block_checksum; + + /** Print out the contents of the B-tree node that is in memory **/ + + print_btree_node_phys(node); + + /** Write the data to disk **/ + + if (write_blocks(node, write_to, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to write to block %#lx.\n", write_to); + return -1; + } + printf("Successfully wrote modified version of block %#lx to %#lx.\n", read_from, write_to); + + /** Cleanup **/ + + free(node); + } + + /** Modify an Omap B-tree node and write the new version to disk **/ + if (false) { + long read_from = 0xe98c5; + long write_to = 0xd3691; + + /** Declare data needed to edit node **/ + + /** + * Array containing data for all of the OID mappings which need to + * be edited. Each entry comprises: + * + * - 0: The index of the entry within the node + * - 1: The target block address + */ + size_t NUM_RECORDS = 6; // The number of records that will be modified; the size of the array `record_data` + uint64_t record_data[][2] = { + { 50, 0xd6e9d }, + { 66, 0xd2a9b }, + { 75, 0xd214b }, + { 76, 0xd2155 }, + { 78, 0xd271b }, + { 81, 0xd0fd1 }, + }; + + /** Read the B-tree node to modify **/ + btree_node_phys_t* node = malloc(nx_block_size); + if (!node) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `node`.\n"); + return -1; + } + if (read_blocks(node, read_from, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block %#lx.\n", read_from); + return -1; + } + + /** Edit records as specified in `entry_data` **/ + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + val_end -= sizeof(btree_info_t); + } + + for (size_t i = 0; i < NUM_RECORDS; i++) { + kvoff_t* toc_entry = (kvoff_t*)toc_start + record_data[i][0]; + + omap_val_t* val = val_end - toc_entry->v; + val->ov_paddr = record_data[i][1]; + } + + /** Set checksum **/ + + uint64_t block_checksum = compute_block_cksum(node); + *((uint64_t*)node) = block_checksum; + + /** Print out the contents of the B-tree node that is in memory **/ + + print_btree_node_phys(node); + + /** Write the data to disk **/ + + if (write_blocks(node, write_to, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to write to block %#lx.\n", write_to); + return -1; + } + printf("Successfully wrote modified version of block %#lx to %#lx.\n", read_from, write_to); + + /** Cleanup **/ + + free(node); + } + + fclose(nx); + + return 0; +} diff --git a/src/commands/recover-raw.c b/src/commands/recover-raw.c index c78567d..4473bd2 100644 --- a/src/commands/recover-raw.c +++ b/src/commands/recover-raw.c @@ -1,511 +1,511 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char** argv) { - fprintf( - argc == 1 ? stdout : stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2 0 0xd4a7f\n", - - argv[0], - argv[0] - ); -} - -int cmd_recover_raw(int argc, char** argv) { - if (argc == 1) { - print_usage(argc, argv); - return 0; - } - - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 4) { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - - nx_path = argv[1]; - - uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%u", &volume_id); - if (!parse_success) { - fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); - print_usage(argc, argv); - return 1; - } - - oid_t fs_oid; - parse_success = sscanf(argv[3], "0x%" PRIx64 "", &fs_oid); - if (!parse_success) { - parse_success = sscanf(argv[3], "%" PRIu64 "", &fs_oid); - } - if (!parse_success) { - fprintf(stderr, "%s is not a valid block address.\n", argv[3]); - print_usage(argc, argv); - return 1; - } - - // Open (device special) file corresponding to an APFS container, read-only - fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - return -errno; - } - fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); - - // Using `nx_superblock_t*`, but allocating a whole block of memory. - // This way, we can read the entire block and validate its checksum, - // but still have direct access to the fields in `nx_superblock_t` - // without needing to epxlicitly cast to that datatype. - nx_superblock_t* nxsb = malloc(nx_block_size); - if (!nxsb) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); - return -1; - } - - if (read_blocks(nxsb, 0x0, 1) != 1) { - fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); - return -1; - } - - fprintf(stderr, "Validating checksum of block 0x0 ... "); - if (!is_cksum_valid(nxsb)) { - fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); - } - fprintf(stderr, "OK.\n"); - - if (!is_nx_superblock(nxsb)) { - fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); - } - if (nxsb->nx_magic != NX_MAGIC) { - fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); - } - - fprintf(stderr, "Locating the checkpoint descriptor area:\n"); - - uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); - - char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); - if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); - return -1; - } - - if (nxsb->nx_xp_desc_blocks >> 31) { - fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous - return -1; - } else { - fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); - - fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); - if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { - fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); - - uint32_t i_latest_nx = 0; - xid_t xid_latest_nx = 0; - - xid_t max_xid = ~0; // `~0` is the highest possible XID - - for (uint32_t i = 0; i < xp_desc_blocks; i++) { - if (!is_cksum_valid(xp_desc[i])) { - fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); - continue; - } - - if (is_nx_superblock(xp_desc[i])) { - if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); - continue; - } - - if ( - ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid > xid_latest_nx ) - && ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid <= max_xid ) - ) { - i_latest_nx = i; - xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; - } - } else if (!is_checkpoint_map_phys(xp_desc[i])) { - fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); - continue; - } - } - - if (xid_latest_nx == 0) { - fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); - return -1; - } - - // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` - // anymore; replace that data with the latest NXSB. - // This also lets us avoid repeatedly casting to `nx_superblock_t*`. - memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - - fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); - fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); - - // Copy the contents of the checkpoint we are currently considering to its - // own array for easy access. The checkpoint descriptor area is a ring - // buffer stored as an array, so doing this also allows us to handle the - // case where the checkpoint we're considering wraps around the ring buffer. - fprintf(stderr, "Loading the corresponding checkpoint ... "); - - // The array `xp` will comprise the blocks in the checkpoint, in order. - char (*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); - if (!xp) { - fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); - return -1; - } - - if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) { - // The simple case: the checkpoint is already contiguous in `xp_desc`. - memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); - } else { - // The case where the checkpoint wraps around from the end of the - // checkpoint descriptor area to the start. - uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; - uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; - memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); - memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); - } - fprintf(stderr, "OK.\n"); - - // We could `free(xp_desc)` at this point, but instead, we retain our copy - // of the checkpoint descriptor area in case any of the Ephemeral objects - // referenced by the current checkpoint are malformed; then, we can - // retrieve an older checkpoint without having to read the checkpoint - // descriptor area again. - - uint32_t xp_obj_len = 0; // This variable will equal the number of - // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { - if (is_checkpoint_map_phys(xp[i])) { - xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; - } - } - fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); - - fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); - char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); - if (!xp_obj) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); - return -1; - } - uint32_t num_read = 0; - for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { - if (is_checkpoint_map_phys(xp[i])) { - checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting - for (uint32_t j = 0; j < xp_map->cpm_count; j++) { - if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); - return -1; - } - num_read++; - } - } - } - fprintf(stderr, "OK.\n"); - assert(num_read = xp_obj_len); - - fprintf(stderr, "Validating the Ephemeral objects ... "); - for (uint32_t i = 0; i < xp_obj_len; i++) { - if (!is_cksum_valid(xp_obj[i])) { - fprintf(stderr, "FAILED.\n"); - fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - free(xp); - free(xp_desc); - - fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); - - fprintf(stderr, "Loading the container object map ... "); - omap_phys_t* nx_omap = malloc(nx_block_size); - if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the container object map ... "); - if (!is_cksum_valid(nx_omap)) { - fprintf(stderr, "FAILED.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { - fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return -1; - } - - fprintf(stderr, "Reading the root node of the container object map B-tree ... "); - btree_node_phys_t* nx_omap_btree = malloc(nx_block_size); - if (!nx_omap_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); - return -1; - } - if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the container object map B-tree ... "); - if (!is_cksum_valid(nx_omap_btree)) { - fprintf(stderr, "FAILED.\n"); - } else { - fprintf(stderr, "OK.\n"); - } - - uint32_t num_file_systems = 0; - for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) { - if (nxsb->nx_fs_oid[i] == 0) { - break; - } - num_file_systems++; - } - fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); - for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "Reading the APFS volume superblocks ... "); - char (*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); - if (!apsbs) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); - return -1; - } - for (uint32_t i = 0; i < num_file_systems; i++) { - omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - if (!fs_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); - return -1; - } - if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the APFS volume superblocks ... "); - for (uint32_t i = 0; i < num_file_systems; i++) { - if (!is_cksum_valid(apsbs + i)) { - fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - - if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); - - // TODO: Handle case where data for a given checkpoint is malformed - fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); - return -1; - } - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "\n Volume list\n================\n"); - for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); - } - - if (volume_id >= num_file_systems) { - fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); - return -1; - } - apfs_superblock_t* apsb = apsbs + volume_id; - - fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - - fprintf(stderr, "Reading the volume object map ... "); - omap_phys_t* fs_omap = malloc(nx_block_size); - if (!fs_omap) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); - return -1; - } - if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the volume object map ... "); - if (!is_cksum_valid(fs_omap)) { - fprintf(stderr, "\nFAILED. The checksum did not validate."); - return -1; - } - fprintf(stderr, "OK.\n"); - - if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { - fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); - return -1; - } - - fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); - btree_node_phys_t* fs_omap_btree = malloc(nx_block_size); - if (!fs_omap_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); - return -1; - } - if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); - return -1; - } - fprintf(stderr, "OK.\n"); - - fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); - if (!is_cksum_valid(fs_omap_btree)) { - fprintf(stderr, "FAILED.\n"); - } else { - fprintf(stderr, "OK.\n"); - } - - fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); - fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); - omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - if (!fs_root_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); - return -1; - } - fprintf(stderr, "corresponding block address is %#" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - - fprintf(stderr, "Reading ... "); - btree_node_phys_t* fs_root_btree = malloc(nx_block_size); - if (!fs_root_btree) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); - return -1; - } - if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#" PRIx64 ".\n", fs_root_entry->val.ov_paddr); - return -1; - } - free(fs_root_entry); // No longer need the block address of the file-system root. - - fprintf(stderr, "validating ... "); - if (!is_cksum_valid(fs_root_btree)) { - fprintf(stderr, "FAILED.\n"); - return -1; - } - fprintf(stderr, "OK.\n"); - - j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); - if (!fs_records) { - fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); - return -1; - } - - fprintf(stderr, "\nRecords for file-system object %#" PRIx64 "\n", fs_oid); - // `fs_records` now contains the records for the item at the specified path - print_fs_records(fs_records); - - // Output content from all matching file extents - char* buffer = malloc(nx_block_size); - if (!buffer) { - fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); - return -1; - } - - bool found_file_extent = false; - for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { - j_rec_t* fs_rec = *fs_rec_cursor; - j_key_t* hdr = fs_rec->data; - if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { - found_file_extent = true; - j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; - - // Output the content from this particular file extent - uint64_t block_addr = val->phys_block_num; - - uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; - for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { - if (read_blocks(buffer, block_addr, 1) != 1) { - fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); - return -1; - } - - if (fwrite(buffer, nx_block_size, 1, stdout) != 1) { - fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); - return -1; - } - } - } - } - if (!found_file_extent) { - fprintf(stderr, "Could not find any file extents for the specified path.\n"); - } - - free_j_rec_array(fs_records); - - // TODO: RESUME HERE - - free(fs_omap_btree); - free(fs_omap); - - // Closing statements; de-allocate all memory, close all file descriptors. - free(apsbs); - free(nx_omap_btree); - free(nx_omap); - free(xp_obj); - free(nxsb); - fclose(nx); - fprintf(stderr, "END: All done.\n"); - return 0; -} +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char** argv) { + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2 0 0xd4a7f\n", + + argv[0], + argv[0] + ); +} + +int cmd_recover_raw(int argc, char** argv) { + if (argc == 1) { + print_usage(argc, argv); + return 0; + } + + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 4) { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + + nx_path = argv[1]; + + uint32_t volume_id; + bool parse_success = sscanf(argv[2], "%u", &volume_id); + if (!parse_success) { + fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); + print_usage(argc, argv); + return 1; + } + + oid_t fs_oid; + parse_success = sscanf(argv[3], "0x%" PRIx64 "", &fs_oid); + if (!parse_success) { + parse_success = sscanf(argv[3], "%" PRIu64 "", &fs_oid); + } + if (!parse_success) { + fprintf(stderr, "%s is not a valid block address.\n", argv[3]); + print_usage(argc, argv); + return 1; + } + + // Open (device special) file corresponding to an APFS container, read-only + fprintf(stderr, "Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + return -errno; + } + fprintf(stderr, "OK.\nSimulating a mount of the APFS container.\n"); + + // Using `nx_superblock_t*`, but allocating a whole block of memory. + // This way, we can read the entire block and validate its checksum, + // but still have direct access to the fields in `nx_superblock_t` + // without needing to epxlicitly cast to that datatype. + nx_superblock_t* nxsb = malloc(nx_block_size); + if (!nxsb) { + fprintf(stderr, "ABORT: Could not allocate sufficient memory to create `nxsb`.\n"); + return -1; + } + + if (read_blocks(nxsb, 0x0, 1) != 1) { + fprintf(stderr, "ABORT: Failed to successfully read block 0x0.\n"); + return -1; + } + + fprintf(stderr, "Validating checksum of block 0x0 ... "); + if (!is_cksum_valid(nxsb)) { + fprintf(stderr, "FAILED.\n!! APFS ERROR !! Checksum of block 0x0 should validate, but it doesn't. Proceeding as if it does.\n"); + } + fprintf(stderr, "OK.\n"); + + if (!is_nx_superblock(nxsb)) { + fprintf(stderr, "\nABORT: Block 0x0 isn't a container superblock.\n\n"); + } + if (nxsb->nx_magic != NX_MAGIC) { + fprintf(stderr, "!! APFS ERROR !! Container superblock at 0x0 doesn't have the correct magic number. Proceeding as if it does.\n"); + } + + fprintf(stderr, "Locating the checkpoint descriptor area:\n"); + + uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); + + char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); + if (!xp_desc) { + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); + return -1; + } + + if (nxsb->nx_xp_desc_blocks >> 31) { + fprintf(stderr, "- It is not contiguous.\n"); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous + return -1; + } else { + fprintf(stderr, "- It is contiguous.\n"); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); + + fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); + if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { + fprintf(stderr, "\nABORT: Failed to read all blocks in the checkpoint descriptor area.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "Locating the most recent well-formed container superblock in the checkpoint descriptor area:\n"); + + uint32_t i_latest_nx = 0; + xid_t xid_latest_nx = 0; + + xid_t max_xid = ~0; // `~0` is the highest possible XID + + for (uint32_t i = 0; i < xp_desc_blocks; i++) { + if (!is_cksum_valid(xp_desc[i])) { + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); + continue; + } + + if (is_nx_superblock(xp_desc[i])) { + if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); + continue; + } + + if ( + ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid > xid_latest_nx ) + && ( ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid <= max_xid ) + ) { + i_latest_nx = i; + xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; + } + } else if (!is_checkpoint_map_phys(xp_desc[i])) { + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + continue; + } + } + + if (xid_latest_nx == 0) { + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); + return -1; + } + + // Don't need a copy of the block 0x0 NXSB which is stored in `nxsb` + // anymore; replace that data with the latest NXSB. + // This also lets us avoid repeatedly casting to `nx_superblock_t*`. + memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); + + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); + + // Copy the contents of the checkpoint we are currently considering to its + // own array for easy access. The checkpoint descriptor area is a ring + // buffer stored as an array, so doing this also allows us to handle the + // case where the checkpoint we're considering wraps around the ring buffer. + fprintf(stderr, "Loading the corresponding checkpoint ... "); + + // The array `xp` will comprise the blocks in the checkpoint, in order. + char (*xp)[nx_block_size] = malloc(nxsb->nx_xp_desc_len * nx_block_size); + if (!xp) { + fprintf(stderr, "\nABORT: Couldn't allocate sufficient memory.\n"); + return -1; + } + + if (nxsb->nx_xp_desc_index + nxsb->nx_xp_desc_len <= xp_desc_blocks) { + // The simple case: the checkpoint is already contiguous in `xp_desc`. + memcpy(xp, xp_desc[nxsb->nx_xp_desc_index], nxsb->nx_xp_desc_len * nx_block_size); + } else { + // The case where the checkpoint wraps around from the end of the + // checkpoint descriptor area to the start. + uint32_t segment_1_len = xp_desc_blocks - nxsb->nx_xp_desc_index; + uint32_t segment_2_len = nxsb->nx_xp_desc_len - segment_1_len; + memcpy(xp, xp_desc + nxsb->nx_xp_desc_index, segment_1_len * nx_block_size); + memcpy(xp + segment_1_len, xp_desc, segment_2_len * nx_block_size); + } + fprintf(stderr, "OK.\n"); + + // We could `free(xp_desc)` at this point, but instead, we retain our copy + // of the checkpoint descriptor area in case any of the Ephemeral objects + // referenced by the current checkpoint are malformed; then, we can + // retrieve an older checkpoint without having to read the checkpoint + // descriptor area again. + + uint32_t xp_obj_len = 0; // This variable will equal the number of + // checkpoint-mappings = no. of Ephemeral objects used by this checkpoint. + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { + if (is_checkpoint_map_phys(xp[i])) { + xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; + } + } + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + + fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); + char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); + if (!xp_obj) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `xp_obj`.\n"); + return -1; + } + uint32_t num_read = 0; + for (uint32_t i = 0; i < nxsb->nx_xp_desc_len; i++) { + if (is_checkpoint_map_phys(xp[i])) { + checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting + for (uint32_t j = 0; j < xp_map->cpm_count; j++) { + if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); + return -1; + } + num_read++; + } + } + } + fprintf(stderr, "OK.\n"); + assert(num_read = xp_obj_len); + + fprintf(stderr, "Validating the Ephemeral objects ... "); + for (uint32_t i = 0; i < xp_obj_len; i++) { + if (!is_cksum_valid(xp_obj[i])) { + fprintf(stderr, "FAILED.\n"); + fprintf(stderr, "An Ephemeral object used by this checkpoint is malformed. Going back to look at the previous checkpoint instead.\n"); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + free(xp); + free(xp_desc); + + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); + + fprintf(stderr, "Loading the container object map ... "); + omap_phys_t* nx_omap = malloc(nx_block_size); + if (read_blocks(nx_omap, nxsb->nx_omap_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap`.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the container object map ... "); + if (!is_cksum_valid(nx_omap)) { + fprintf(stderr, "FAILED.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + if ((nx_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { + fprintf(stderr, "END: The container object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return -1; + } + + fprintf(stderr, "Reading the root node of the container object map B-tree ... "); + btree_node_phys_t* nx_omap_btree = malloc(nx_block_size); + if (!nx_omap_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `nx_omap_btree`.\n"); + return -1; + } + if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the container object map B-tree ... "); + if (!is_cksum_valid(nx_omap_btree)) { + fprintf(stderr, "FAILED.\n"); + } else { + fprintf(stderr, "OK.\n"); + } + + uint32_t num_file_systems = 0; + for (uint32_t i = 0; i < NX_MAX_FILE_SYSTEMS; i++) { + if (nxsb->nx_fs_oid[i] == 0) { + break; + } + num_file_systems++; + } + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + for (uint32_t i = 0; i < num_file_systems; i++) { + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Reading the APFS volume superblocks ... "); + char (*apsbs)[nx_block_size] = malloc(nx_block_size * num_file_systems); + if (!apsbs) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `apsbs`.\n"); + return -1; + } + for (uint32_t i = 0; i < num_file_systems; i++) { + omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + if (!fs_entry) { + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + return -1; + } + if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the APFS volume superblocks ... "); + for (uint32_t i = 0; i < num_file_systems; i++) { + if (!is_cksum_valid(apsbs + i)) { + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + + if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + + // TODO: Handle case where data for a given checkpoint is malformed + fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); + return -1; + } + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "\n Volume list\n================\n"); + for (uint32_t i = 0; i < num_file_systems; i++) { + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); + } + + if (volume_id >= num_file_systems) { + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); + return -1; + } + apfs_superblock_t* apsb = apsbs + volume_id; + + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + + fprintf(stderr, "Reading the volume object map ... "); + omap_phys_t* fs_omap = malloc(nx_block_size); + if (!fs_omap) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap`.\n"); + return -1; + } + if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the volume object map ... "); + if (!is_cksum_valid(fs_omap)) { + fprintf(stderr, "\nFAILED. The checksum did not validate."); + return -1; + } + fprintf(stderr, "OK.\n"); + + if ((fs_omap->om_tree_type & OBJ_STORAGETYPE_MASK) != OBJ_PHYSICAL) { + fprintf(stderr, "END: The volume object map B-tree is not of the Physical storage type, and therefore it cannot be located.\n"); + return -1; + } + + fprintf(stderr, "Reading the root node of the volume object map B-tree ... "); + btree_node_phys_t* fs_omap_btree = malloc(nx_block_size); + if (!fs_omap_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_omap_btree`.\n"); + return -1; + } + if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); + return -1; + } + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Validating the root node of the volume object map B-tree ... "); + if (!is_cksum_valid(fs_omap_btree)) { + fprintf(stderr, "FAILED.\n"); + } else { + fprintf(stderr, "OK.\n"); + } + + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); + omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + if (!fs_root_entry) { + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + return -1; + } + fprintf(stderr, "corresponding block address is %#" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + + fprintf(stderr, "Reading ... "); + btree_node_phys_t* fs_root_btree = malloc(nx_block_size); + if (!fs_root_btree) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_btree`.\n"); + return -1; + } + if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { + fprintf(stderr, "\nABORT: Failed to read block %#" PRIx64 ".\n", fs_root_entry->val.ov_paddr); + return -1; + } + free(fs_root_entry); // No longer need the block address of the file-system root. + + fprintf(stderr, "validating ... "); + if (!is_cksum_valid(fs_root_btree)) { + fprintf(stderr, "FAILED.\n"); + return -1; + } + fprintf(stderr, "OK.\n"); + + j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); + if (!fs_records) { + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); + return -1; + } + + fprintf(stderr, "\nRecords for file-system object %#" PRIx64 "\n", fs_oid); + // `fs_records` now contains the records for the item at the specified path + print_fs_records(fs_records); + + // Output content from all matching file extents + char* buffer = malloc(nx_block_size); + if (!buffer) { + fprintf(stderr, "Could not allocate sufficient memory for `buffer`.\n"); + return -1; + } + + bool found_file_extent = false; + for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { + j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; + if ( ((hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) == APFS_TYPE_FILE_EXTENT ) { + found_file_extent = true; + j_file_extent_val_t* val = fs_rec->data + fs_rec->key_len; + + // Output the content from this particular file extent + uint64_t block_addr = val->phys_block_num; + + uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; + for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { + if (read_blocks(buffer, block_addr, 1) != 1) { + fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); + return -1; + } + + if (fwrite(buffer, nx_block_size, 1, stdout) != 1) { + fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); + return -1; + } + } + } + } + if (!found_file_extent) { + fprintf(stderr, "Could not find any file extents for the specified path.\n"); + } + + free_j_rec_array(fs_records); + + // TODO: RESUME HERE + + free(fs_omap_btree); + free(fs_omap); + + // Closing statements; de-allocate all memory, close all file descriptors. + free(apsbs); + free(nx_omap_btree); + free(nx_omap); + free(xp_obj); + free(nxsb); + fclose(nx); + fprintf(stderr, "END: All done.\n"); + return 0; +} diff --git a/src/commands/search-last-btree-node.c b/src/commands/search-last-btree-node.c index 1c1dd1b..4abf6e8 100644 --- a/src/commands/search-last-btree-node.c +++ b/src/commands/search-last-btree-node.c @@ -1,137 +1,137 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char** argv) { - fprintf( - argc == 1 ? stdout : stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2\n", - - argv[0], - argv[0] - ); -} - -int cmd_search_last_btree_node(int argc, char** argv) { - if (argc == 1) { - print_usage(argc, argv); - return 0; - } - - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 2) { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - nx_path = argv[1]; - - // Open (device special) file corresponding to an APFS container, read-only - printf("Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - printf("\n"); - return -errno; - } - printf("OK.\n"); - - obj_phys_t* block = malloc(nx_block_size); - if (!block) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); - return -1; - } - - printf("Reading block 0x0 to obtain block count ... "); - if (read_blocks(block, 0x0, 1) != 1) { - printf("FAILED.\n"); - return -1; - } - printf("OK.\n"); - - uint64_t num_blocks = ((nx_superblock_t*)block)->nx_block_count; - printf("The specified device has %" PRIu64 " = %#" PRIx64 " blocks. Commencing search:\n\n", num_blocks, num_blocks); - - uint64_t num_matches = 0; - uint64_t first_match_addr = 0; - uint64_t last_match_addr = 0; - - printf("First match: %#" PRIx64 "\n", first_match_addr); - printf("Last match: %#" PRIx64 "\n", last_match_addr); - - for (uint64_t addr = 0xa5e3b; addr < 0x13adf2; addr++) { - printf("\rReading %#" PRIx64 " ...", addr); - - if (read_blocks(block, addr, 1) != 1) { - if (feof(nx)) { - printf("Reached end of file; ending search.\n"); - break; - } - - assert(ferror(nx)); - printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); - continue; - } - - /** Search criteria for dentries of items with certain names **/ - if ( is_cksum_valid(block) - && is_btree_node_phys(block) - ) { - btree_node_phys_t* node = block; - - if (node->btn_flags & BTNODE_LEAF) { - num_matches++; - last_match_addr = addr; - if (first_match_addr == 0) { - first_match_addr = addr; - } - // Move to start of line, - // clear whole line (which reads "Reading 0x..."), - // move up one line, - // clear whole line (which reads "Last match: 0x..."), - // move up one line, - // clear whole line (which reads "First match: 0x...") - printf("\r\033[2K\033[A\033[2K\033[A\033[2K"); - printf("First match: %#" PRIx64 "\n", first_match_addr); - printf("Last match: %#" PRIx64 "\n", last_match_addr); - } - } - } - - printf("\n\nFinished search; found %" PRIu64 " results.\n\n", num_matches); - - return 0; -} +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char** argv) { + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2\n", + + argv[0], + argv[0] + ); +} + +int cmd_search_last_btree_node(int argc, char** argv) { + if (argc == 1) { + print_usage(argc, argv); + return 0; + } + + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 2) { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + nx_path = argv[1]; + + // Open (device special) file corresponding to an APFS container, read-only + printf("Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + printf("\n"); + return -errno; + } + printf("OK.\n"); + + obj_phys_t* block = malloc(nx_block_size); + if (!block) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); + return -1; + } + + printf("Reading block 0x0 to obtain block count ... "); + if (read_blocks(block, 0x0, 1) != 1) { + printf("FAILED.\n"); + return -1; + } + printf("OK.\n"); + + uint64_t num_blocks = ((nx_superblock_t*)block)->nx_block_count; + printf("The specified device has %" PRIu64 " = %#" PRIx64 " blocks. Commencing search:\n\n", num_blocks, num_blocks); + + uint64_t num_matches = 0; + uint64_t first_match_addr = 0; + uint64_t last_match_addr = 0; + + printf("First match: %#" PRIx64 "\n", first_match_addr); + printf("Last match: %#" PRIx64 "\n", last_match_addr); + + for (uint64_t addr = 0xa5e3b; addr < 0x13adf2; addr++) { + printf("\rReading %#" PRIx64 " ...", addr); + + if (read_blocks(block, addr, 1) != 1) { + if (feof(nx)) { + printf("Reached end of file; ending search.\n"); + break; + } + + assert(ferror(nx)); + printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); + continue; + } + + /** Search criteria for dentries of items with certain names **/ + if ( is_cksum_valid(block) + && is_btree_node_phys(block) + ) { + btree_node_phys_t* node = block; + + if (node->btn_flags & BTNODE_LEAF) { + num_matches++; + last_match_addr = addr; + if (first_match_addr == 0) { + first_match_addr = addr; + } + // Move to start of line, + // clear whole line (which reads "Reading 0x..."), + // move up one line, + // clear whole line (which reads "Last match: 0x..."), + // move up one line, + // clear whole line (which reads "First match: 0x...") + printf("\r\033[2K\033[A\033[2K\033[A\033[2K"); + printf("First match: %#" PRIx64 "\n", first_match_addr); + printf("Last match: %#" PRIx64 "\n", last_match_addr); + } + } + } + + printf("\n\nFinished search; found %" PRIu64 " results.\n\n", num_matches); + + return 0; +} diff --git a/src/commands/search.c b/src/commands/search.c index 5b76072..16822f6 100644 --- a/src/commands/search.c +++ b/src/commands/search.c @@ -1,506 +1,506 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * Print usage info for this program. - */ -static void print_usage(int argc, char** argv) { - fprintf( - argc == 1 ? stdout : stderr, - - "Usage: %s \n" - "Example: %s /dev/disk0s2\n", - - argv[0], - argv[0] - ); -} - -int cmd_search(int argc, char** argv) { - if (argc == 1) { - print_usage(argc, argv); - return 0; - } - - setbuf(stdout, NULL); - - // Extrapolate CLI arguments, exit if invalid - if (argc != 2) { - fprintf(stderr, "Incorrect number of arguments.\n"); - print_usage(argc, argv); - return 1; - } - nx_path = argv[1]; - - // Open (device special) file corresponding to an APFS container, read-only - printf("Opening file at `%s` in read-only mode ... ", nx_path); - nx = fopen(nx_path, "rb"); - if (!nx) { - fprintf(stderr, "\nABORT: "); - report_fopen_error(); - printf("\n"); - return -errno; - } - printf("OK.\n"); - - obj_phys_t* block = malloc(nx_block_size); - if (!block) { - fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); - return -1; - } - - printf("Reading block 0x0 to obtain block count ... "); - if (read_blocks(block, 0x0, 1) != 1) { - printf("FAILED.\n"); - return -1; - } - printf("OK.\n"); - - uint64_t num_blocks = ((nx_superblock_t*)block)->nx_block_count; - printf("The specified device has %" PRIu64 " = %#" PRIx64 " blocks. Commencing search:\n\n", num_blocks, num_blocks); - - uint64_t num_matches = 0; - - /** Search for dentries for items with any of these names **/ - size_t NUM_DENTRY_NAMES = 10; - char* dentry_names[] = { - // catch-all entries - ".localized", - - // .ssh entries - "id_rsa", - "id_rsa.pub", - "authorized_keys", - "known_hosts", - - // Desktop entries - "Wallpapers", - - // Documents entries - "Finances", - - // Downloads entries - "Software", - - // Movies entries - "TV", - - // Music entries - "iTunes", - }; - - size_t NUM_FS_OIDS = 9; - uint64_t fs_oids[] = { - 0xb4a57, - 0xb54a9, - 0xb54aa, - 0xb54af, - 0xb54b4, - 0xb54b5, - 0xb54e5, - 0xb54ed, - 0xb550c, - }; - - size_t NUM_FS_OID_RANGES = 14; - uint64_t fs_oid_ranges[][2] = { - {0xb4b1b, 0xb4b1b}, - {0xb4b33, 0xb4b33}, - {0xb4b71, 0xb4b71}, - {0xb4b7f, 0xb4b82}, - {0xb4bbb, 0xb4bbc}, - {0xb4c78, 0xb4c78}, - {0xb4c8c, 0xb4c8c}, - {0xb4cb6, 0xb4cb6}, - {0xb4cdb, 0xb4cdb}, - {0xb4cf1, 0xb4cf1}, - {0xb4d06, 0xb4d06}, - {0xb4d1c, 0xb4d1c}, - {0xb4d32, 0xb4d32}, - {0xb4e2d, 0xb4e57}, - }; - - size_t NUM_BLOCKS = 62; - uint64_t blocks[] = { - 0xd6cf2, 0xd4ba8, 0xd0add, 0xe14ec, 0xa9a58, 0xb6c56, 0xd47c4, 0xb7292, 0xac8b2, - 0xd668d, 0xd60f4, 0xd67ec, 0xd6941, 0xd6a18, 0xd69fb, 0xd3d06, 0xd68b5, 0xd683c, - 0xd6848, 0xd6820, 0xd6865, 0xd67ed, 0xd6ac1, 0xd5b73, 0xf3864, 0xd4393, 0xd3a9d, - 0xd4392, 0xd4395, 0xd4372, 0xd437d, 0xd4381, 0xd436a, 0xd4371, 0xd436b, 0xd4383, - 0xd461c, 0xd984c, 0xd44c3, 0xd1e78, 0xd3adb, 0xd0ee5, 0xd0251, 0xd25f3, 0xb6907, - 0xa7ccd, 0xd4efa, 0xd760b, 0xd2625, 0xd38af, 0xd1de4, 0xd0536, 0xd1002, 0xd0f0d, - 0xd3015, 0xd171c, 0xdb96a, 0xd5ff0, 0xd193f, 0xed80b, 0xd1588, 0xd85ff, - }; - - uint64_t start_addr = 0xa5e3b; - uint64_t end_addr = 0x13adf2; - uint64_t addr_range_size = end_addr - start_addr; - - double addr_index_100 = 0; - - /** Search over all B-tree nodes **/ - if (true) { - for (uint64_t addr = start_addr; addr < end_addr; addr++, addr_index_100 += 100) { - printf("\rReading block %#9" PRIx64 " (%6.2f%%) ... ", addr, addr_index_100/addr_range_size); - - if (read_blocks(block, addr, 1) != 1) { - if (feof(nx)) { - printf("Reached end of file; ending search.\n"); - break; - } - - assert(ferror(nx)); - printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); - continue; - } - - /** Search criteria **/ - - /** Search for Omap leaf nodes that contain mappings for given Virtual OIDs **/ - if (false) { - if ( is_cksum_valid(block) - && is_btree_node_phys_non_root(block) - && is_omap_tree(block) - ) { - btree_node_phys_t* node = block; - - if ( ! (node->btn_flags & BTNODE_FIXED_KV_SIZE) ) { - printf("Omap tree node with non-fixed key and value sizes; skipping this block\n"); - continue; - } - - if ( ! (node->btn_flags & BTNODE_LEAF) ) { - continue; - } - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - - kvoff_t* toc_entry = toc_start; - for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { - omap_key_t* key = key_start + toc_entry->k; - if ( key->ok_oid >= 0x1b16dd && key->ok_oid <= 0x1b3926 ) { - // Found a match; print details, then move on to the next block - num_matches++; - - kvoff_t* first_toc_entry = toc_start; - omap_key_t* first_key = key_start + first_toc_entry->k; - - kvoff_t* last_toc_entry = first_toc_entry + node->btn_nkeys - 1; - omap_key_t* last_key = key_start + last_toc_entry->k; - - printf("\rMATCHED %#8" PRIx64 " || Node XID = %#9" PRIx64 " || from (OID, XID) = (%#9" PRIx64 ", %#9" PRIx64 ") => (%#9" PRIx64 ", %#9" PRIx64 ")\n", - addr, - node->btn_o.o_xid, - first_key->ok_oid, - first_key->ok_xid, - last_key->ok_oid, - last_key->ok_xid - ); - - break; - } - } - } - } - - /** Search for Virtual objects with a given Virtual OID **/ - if (true) { - if (is_cksum_valid(block)) { - if ( (block->o_type & OBJ_STORAGETYPE_MASK) == OBJ_VIRTUAL ) { - switch (block->o_oid) { - case 0x25e8fa: - num_matches++; - printf("\rMATCHED %#8" PRIx64 " || OID = %#9" PRIx64 " || XID = %#9" PRIx64 "\n", addr, block->o_oid, block->o_xid); - break; - - default: - break; - } - } - } - } - - /** Search for FS-Root B-tree leaf nodes containing records for certain FS OIDs **/ - if (true) { - if ( is_cksum_valid(block) - && is_btree_node_phys(block) - && is_fs_tree(block) - ) { - btree_node_phys_t* node = block; - - if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { - // File-system B-tree nodes don't have fixed-size keys and values ... do they? - printf("FIXED_KV_SIZE\n"); - continue; - } - - if ( ! (node->btn_flags & BTNODE_LEAF) ) { - // Not a leaf node; look at next block - printf("NOT A LEAF\n"); - continue; - } - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - val_end -= sizeof(btree_info_t); - } - - kvloc_t* toc_entry = toc_start; - for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { - j_key_t* hdr = key_start + toc_entry->k.off; - uint64_t oid = hdr->obj_id_and_type & OBJ_ID_MASK; - - if ( oid == 0xae9549 - // || (oid >= 0xda06a && oid <= 0xda079) - // || (oid >= 0x3a6386 && oid <= 0x3a6398) - ) { - num_matches++; - - kvloc_t* first_toc_entry = toc_start; - j_key_t* first_hdr = key_start + first_toc_entry->k.off; - uint64_t first_oid = first_hdr->obj_id_and_type & OBJ_ID_MASK; - - kvloc_t* last_toc_entry = first_toc_entry + node->btn_nkeys - 1; - j_key_t* last_hdr = key_start + last_toc_entry->k.off; - uint64_t last_oid = last_hdr->obj_id_and_type & OBJ_ID_MASK; - - printf("\rMATCHED %#8" PRIx64 " || First record OID: %#" PRIx64 " || Last record OID: %#" PRIx64 " || Node XID: %#" PRIx64 "\n", addr, first_oid, last_oid, node->btn_o.o_xid); - - // Found a match; don't need to check other entries in this node - break; - } - } - } - } - - /** Search for dentries of items with certain names/properties **/ - if (true) { - if ( is_cksum_valid(block) - && is_btree_node_phys(block) - && is_fs_tree(block) - ) { - btree_node_phys_t* node = block; - - if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { - printf("FIXED_KV_SIZE\n"); - continue; - } - - if ( ! (node->btn_flags & BTNODE_LEAF) ) { - // Not a leaf node; look at next block - continue; - } - - printf("LEAF NODE // OID = %#" PRIx64 " // XID = %#" PRIx64 " ... ", node->btn_o.o_oid, node->btn_o.o_xid); - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - printf("ALSO A ROOT NODE ..."); - val_end -= sizeof(btree_info_t); - } - - uint32_t num_matches_in_node = 0; - - printf("Inspecting entries contained in this node ...\n"); - kvloc_t* toc_entry = toc_start; - for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { - printf("\r- Entry %u ... ", i); - - j_key_t* hdr = key_start + toc_entry->k.off; - uint8_t record_type = (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT; - switch (record_type) { - case APFS_TYPE_DIR_REC: { - printf("DENTRY ... "); - - j_drec_hashed_key_t* key = hdr; - j_drec_val_t* val = val_end - toc_entry->v.off; - - for (size_t j = 0; j < NUM_DENTRY_NAMES; j++) { - if (strcasecmp((char*)key->name, dentry_names[j]) == 0) { - num_matches_in_node++; - num_matches++; - - printf("MATCHED --- query = %s --- match = %s\n", dentry_names[j], key->name); - - // Found a match; don't need to compare against other strings - break; - } - } - } break; - // case APFS_TYPE_INODE: { - // printf("INDOE ... "); - - // j_inode_key_t* key = hdr; - // j_inode_val_t* val = val_end - toc_entry->v.off; - - // if (val->parent_id == 0x1) { - // num_matches_in_node++; - // num_matches++; - - // printf("MATCHED --- query = INODE 0x1 --- match = ") - // } - // } break; - default: break; - } - } - - if (num_matches_in_node == 0) { - // Move to start of line, - // clear whole line (which reads "- Entry x"), - // move up one line, - // clear whole line (which reads "Reading block [...] Inspecting entries") - printf("\r\033[2K\033[A\033[2K"); - } - } - } - - /** Search for dentries pointing to items with certain file-system object IDs **/ - if (true) { - if ( is_cksum_valid(block) - && is_btree_node_phys(block) - && is_fs_tree(block) - ) { - btree_node_phys_t* node = block; - - if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { - printf("FIXED_KV_SIZE\n"); - continue; - } - - if ( ! (node->btn_flags & BTNODE_LEAF) ) { - // Not a leaf node; look at next block - continue; - } - - char* toc_start = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = toc_start + node->btn_table_space.len; - char* val_end = (char*)node + nx_block_size; - if (node->btn_flags & BTNODE_ROOT) { - val_end -= sizeof(btree_info_t); - } - - uint32_t num_matches_in_node = 0; - - printf("\n"); - - kvloc_t* toc_entry = toc_start; - for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { - printf("\r- Entry %u ... ", i); - - j_key_t* hdr = key_start + toc_entry->k.off; - uint8_t record_type = (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT; - if (record_type != APFS_TYPE_DIR_REC) { - // Not a dentry; look at next entry in this leaf node - continue; - } - - j_drec_hashed_key_t* key = hdr; - j_drec_val_t* val = val_end - toc_entry->v.off; - - /** Check whether FS OID matches one in `fs_oids[]` **/ - // for (size_t j = 0; j < NUM_FS_OIDS; j++) { - // if (val->file_id == fs_oids[j]) { - // num_matches_in_node++; - // num_matches++; - - // printf("MATCHED --- query = %#" PRIx64 " --- match = %s\n", fs_oids[j], key->name); - - // // Found a match; don't need to compare against other FS OIDs - // break; - // } - // } - - /** Check whether FS OID lies within a range in `fs_oid_ranges[]` **/ - for (size_t j = 0; j < NUM_FS_OID_RANGES; j++) { - if ( (val->file_id >= fs_oid_ranges[j][0]) && (val->file_id <= fs_oid_ranges[j][1]) ) { - num_matches_in_node++; - num_matches++; - - printf( "MATCHED --- query = (%#" PRIx64 ", %#" PRIx64 ") --- match = %#" PRIx64 " --- name = %s\n", - fs_oid_ranges[j][0], - fs_oid_ranges[j][1], - val->file_id, - key->name - ); - - // Found a match; don't need to compare against other FS OIDs - break; - } - } - } - - if (num_matches_in_node == 0) { - // Move to start of line, - // clear whole line (which reads "- Entry x"), - // move up one line, - // clear whole line (which reads "Reading block [...] Inspecting entries") - printf("\r\033[2K\033[A\033[2K"); - } - } - } - } - } - - /** Get FS record types of first record in certain blocks on disk **/ - if (false) { - for (size_t block_index = 0; block_index < NUM_BLOCKS; block_index++) { - uint64_t addr = blocks[block_index]; - printf("\rReading block %2lu: %#" PRIx64 " ... ", block_index, addr); - - if (read_blocks(block, addr, 1) != 1) { - if (feof(nx)) { - printf("Reached end of file; ending search.\n"); - break; - } - - assert(ferror(nx)); - printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); - continue; - } - - btree_node_phys_t* node = block; - - kvloc_t* first_toc_entry = (char*)node->btn_data + node->btn_table_space.off; - char* key_start = (char*)first_toc_entry + node->btn_table_space.len; - j_key_t* hdr = key_start + first_toc_entry->k.off; - - // printf("--- first record type = %#" PRIx64 "\n", (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT ); - printf("--- node OID = %#16" PRIx64 "\n", node->btn_o.o_oid); - } - } - - printf("\n\nFinished search; found %" PRIu64 " results.\n\n", num_matches); - - return 0; -} +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Print usage info for this program. + */ +static void print_usage(int argc, char** argv) { + fprintf( + argc == 1 ? stdout : stderr, + + "Usage: %s \n" + "Example: %s /dev/disk0s2\n", + + argv[0], + argv[0] + ); +} + +int cmd_search(int argc, char** argv) { + if (argc == 1) { + print_usage(argc, argv); + return 0; + } + + setbuf(stdout, NULL); + + // Extrapolate CLI arguments, exit if invalid + if (argc != 2) { + fprintf(stderr, "Incorrect number of arguments.\n"); + print_usage(argc, argv); + return 1; + } + nx_path = argv[1]; + + // Open (device special) file corresponding to an APFS container, read-only + printf("Opening file at `%s` in read-only mode ... ", nx_path); + nx = fopen(nx_path, "rb"); + if (!nx) { + fprintf(stderr, "\nABORT: "); + report_fopen_error(); + printf("\n"); + return -errno; + } + printf("OK.\n"); + + obj_phys_t* block = malloc(nx_block_size); + if (!block) { + fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `block`.\n"); + return -1; + } + + printf("Reading block 0x0 to obtain block count ... "); + if (read_blocks(block, 0x0, 1) != 1) { + printf("FAILED.\n"); + return -1; + } + printf("OK.\n"); + + uint64_t num_blocks = ((nx_superblock_t*)block)->nx_block_count; + printf("The specified device has %" PRIu64 " = %#" PRIx64 " blocks. Commencing search:\n\n", num_blocks, num_blocks); + + uint64_t num_matches = 0; + + /** Search for dentries for items with any of these names **/ + size_t NUM_DENTRY_NAMES = 10; + char* dentry_names[] = { + // catch-all entries + ".localized", + + // .ssh entries + "id_rsa", + "id_rsa.pub", + "authorized_keys", + "known_hosts", + + // Desktop entries + "Wallpapers", + + // Documents entries + "Finances", + + // Downloads entries + "Software", + + // Movies entries + "TV", + + // Music entries + "iTunes", + }; + + size_t NUM_FS_OIDS = 9; + uint64_t fs_oids[] = { + 0xb4a57, + 0xb54a9, + 0xb54aa, + 0xb54af, + 0xb54b4, + 0xb54b5, + 0xb54e5, + 0xb54ed, + 0xb550c, + }; + + size_t NUM_FS_OID_RANGES = 14; + uint64_t fs_oid_ranges[][2] = { + {0xb4b1b, 0xb4b1b}, + {0xb4b33, 0xb4b33}, + {0xb4b71, 0xb4b71}, + {0xb4b7f, 0xb4b82}, + {0xb4bbb, 0xb4bbc}, + {0xb4c78, 0xb4c78}, + {0xb4c8c, 0xb4c8c}, + {0xb4cb6, 0xb4cb6}, + {0xb4cdb, 0xb4cdb}, + {0xb4cf1, 0xb4cf1}, + {0xb4d06, 0xb4d06}, + {0xb4d1c, 0xb4d1c}, + {0xb4d32, 0xb4d32}, + {0xb4e2d, 0xb4e57}, + }; + + size_t NUM_BLOCKS = 62; + uint64_t blocks[] = { + 0xd6cf2, 0xd4ba8, 0xd0add, 0xe14ec, 0xa9a58, 0xb6c56, 0xd47c4, 0xb7292, 0xac8b2, + 0xd668d, 0xd60f4, 0xd67ec, 0xd6941, 0xd6a18, 0xd69fb, 0xd3d06, 0xd68b5, 0xd683c, + 0xd6848, 0xd6820, 0xd6865, 0xd67ed, 0xd6ac1, 0xd5b73, 0xf3864, 0xd4393, 0xd3a9d, + 0xd4392, 0xd4395, 0xd4372, 0xd437d, 0xd4381, 0xd436a, 0xd4371, 0xd436b, 0xd4383, + 0xd461c, 0xd984c, 0xd44c3, 0xd1e78, 0xd3adb, 0xd0ee5, 0xd0251, 0xd25f3, 0xb6907, + 0xa7ccd, 0xd4efa, 0xd760b, 0xd2625, 0xd38af, 0xd1de4, 0xd0536, 0xd1002, 0xd0f0d, + 0xd3015, 0xd171c, 0xdb96a, 0xd5ff0, 0xd193f, 0xed80b, 0xd1588, 0xd85ff, + }; + + uint64_t start_addr = 0xa5e3b; + uint64_t end_addr = 0x13adf2; + uint64_t addr_range_size = end_addr - start_addr; + + double addr_index_100 = 0; + + /** Search over all B-tree nodes **/ + if (true) { + for (uint64_t addr = start_addr; addr < end_addr; addr++, addr_index_100 += 100) { + printf("\rReading block %#9" PRIx64 " (%6.2f%%) ... ", addr, addr_index_100/addr_range_size); + + if (read_blocks(block, addr, 1) != 1) { + if (feof(nx)) { + printf("Reached end of file; ending search.\n"); + break; + } + + assert(ferror(nx)); + printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); + continue; + } + + /** Search criteria **/ + + /** Search for Omap leaf nodes that contain mappings for given Virtual OIDs **/ + if (false) { + if ( is_cksum_valid(block) + && is_btree_node_phys_non_root(block) + && is_omap_tree(block) + ) { + btree_node_phys_t* node = block; + + if ( ! (node->btn_flags & BTNODE_FIXED_KV_SIZE) ) { + printf("Omap tree node with non-fixed key and value sizes; skipping this block\n"); + continue; + } + + if ( ! (node->btn_flags & BTNODE_LEAF) ) { + continue; + } + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + + kvoff_t* toc_entry = toc_start; + for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { + omap_key_t* key = key_start + toc_entry->k; + if ( key->ok_oid >= 0x1b16dd && key->ok_oid <= 0x1b3926 ) { + // Found a match; print details, then move on to the next block + num_matches++; + + kvoff_t* first_toc_entry = toc_start; + omap_key_t* first_key = key_start + first_toc_entry->k; + + kvoff_t* last_toc_entry = first_toc_entry + node->btn_nkeys - 1; + omap_key_t* last_key = key_start + last_toc_entry->k; + + printf("\rMATCHED %#8" PRIx64 " || Node XID = %#9" PRIx64 " || from (OID, XID) = (%#9" PRIx64 ", %#9" PRIx64 ") => (%#9" PRIx64 ", %#9" PRIx64 ")\n", + addr, + node->btn_o.o_xid, + first_key->ok_oid, + first_key->ok_xid, + last_key->ok_oid, + last_key->ok_xid + ); + + break; + } + } + } + } + + /** Search for Virtual objects with a given Virtual OID **/ + if (true) { + if (is_cksum_valid(block)) { + if ( (block->o_type & OBJ_STORAGETYPE_MASK) == OBJ_VIRTUAL ) { + switch (block->o_oid) { + case 0x25e8fa: + num_matches++; + printf("\rMATCHED %#8" PRIx64 " || OID = %#9" PRIx64 " || XID = %#9" PRIx64 "\n", addr, block->o_oid, block->o_xid); + break; + + default: + break; + } + } + } + } + + /** Search for FS-Root B-tree leaf nodes containing records for certain FS OIDs **/ + if (true) { + if ( is_cksum_valid(block) + && is_btree_node_phys(block) + && is_fs_tree(block) + ) { + btree_node_phys_t* node = block; + + if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { + // File-system B-tree nodes don't have fixed-size keys and values ... do they? + printf("FIXED_KV_SIZE\n"); + continue; + } + + if ( ! (node->btn_flags & BTNODE_LEAF) ) { + // Not a leaf node; look at next block + printf("NOT A LEAF\n"); + continue; + } + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + val_end -= sizeof(btree_info_t); + } + + kvloc_t* toc_entry = toc_start; + for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { + j_key_t* hdr = key_start + toc_entry->k.off; + uint64_t oid = hdr->obj_id_and_type & OBJ_ID_MASK; + + if ( oid == 0xae9549 + // || (oid >= 0xda06a && oid <= 0xda079) + // || (oid >= 0x3a6386 && oid <= 0x3a6398) + ) { + num_matches++; + + kvloc_t* first_toc_entry = toc_start; + j_key_t* first_hdr = key_start + first_toc_entry->k.off; + uint64_t first_oid = first_hdr->obj_id_and_type & OBJ_ID_MASK; + + kvloc_t* last_toc_entry = first_toc_entry + node->btn_nkeys - 1; + j_key_t* last_hdr = key_start + last_toc_entry->k.off; + uint64_t last_oid = last_hdr->obj_id_and_type & OBJ_ID_MASK; + + printf("\rMATCHED %#8" PRIx64 " || First record OID: %#" PRIx64 " || Last record OID: %#" PRIx64 " || Node XID: %#" PRIx64 "\n", addr, first_oid, last_oid, node->btn_o.o_xid); + + // Found a match; don't need to check other entries in this node + break; + } + } + } + } + + /** Search for dentries of items with certain names/properties **/ + if (true) { + if ( is_cksum_valid(block) + && is_btree_node_phys(block) + && is_fs_tree(block) + ) { + btree_node_phys_t* node = block; + + if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { + printf("FIXED_KV_SIZE\n"); + continue; + } + + if ( ! (node->btn_flags & BTNODE_LEAF) ) { + // Not a leaf node; look at next block + continue; + } + + printf("LEAF NODE // OID = %#" PRIx64 " // XID = %#" PRIx64 " ... ", node->btn_o.o_oid, node->btn_o.o_xid); + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + printf("ALSO A ROOT NODE ..."); + val_end -= sizeof(btree_info_t); + } + + uint32_t num_matches_in_node = 0; + + printf("Inspecting entries contained in this node ...\n"); + kvloc_t* toc_entry = toc_start; + for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { + printf("\r- Entry %u ... ", i); + + j_key_t* hdr = key_start + toc_entry->k.off; + uint8_t record_type = (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT; + switch (record_type) { + case APFS_TYPE_DIR_REC: { + printf("DENTRY ... "); + + j_drec_hashed_key_t* key = hdr; + j_drec_val_t* val = val_end - toc_entry->v.off; + + for (size_t j = 0; j < NUM_DENTRY_NAMES; j++) { + if (strcasecmp((char*)key->name, dentry_names[j]) == 0) { + num_matches_in_node++; + num_matches++; + + printf("MATCHED --- query = %s --- match = %s\n", dentry_names[j], key->name); + + // Found a match; don't need to compare against other strings + break; + } + } + } break; + // case APFS_TYPE_INODE: { + // printf("INDOE ... "); + + // j_inode_key_t* key = hdr; + // j_inode_val_t* val = val_end - toc_entry->v.off; + + // if (val->parent_id == 0x1) { + // num_matches_in_node++; + // num_matches++; + + // printf("MATCHED --- query = INODE 0x1 --- match = ") + // } + // } break; + default: break; + } + } + + if (num_matches_in_node == 0) { + // Move to start of line, + // clear whole line (which reads "- Entry x"), + // move up one line, + // clear whole line (which reads "Reading block [...] Inspecting entries") + printf("\r\033[2K\033[A\033[2K"); + } + } + } + + /** Search for dentries pointing to items with certain file-system object IDs **/ + if (true) { + if ( is_cksum_valid(block) + && is_btree_node_phys(block) + && is_fs_tree(block) + ) { + btree_node_phys_t* node = block; + + if (node->btn_flags & BTNODE_FIXED_KV_SIZE) { + printf("FIXED_KV_SIZE\n"); + continue; + } + + if ( ! (node->btn_flags & BTNODE_LEAF) ) { + // Not a leaf node; look at next block + continue; + } + + char* toc_start = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = toc_start + node->btn_table_space.len; + char* val_end = (char*)node + nx_block_size; + if (node->btn_flags & BTNODE_ROOT) { + val_end -= sizeof(btree_info_t); + } + + uint32_t num_matches_in_node = 0; + + printf("\n"); + + kvloc_t* toc_entry = toc_start; + for (uint32_t i = 0; i < node->btn_nkeys; i++, toc_entry++) { + printf("\r- Entry %u ... ", i); + + j_key_t* hdr = key_start + toc_entry->k.off; + uint8_t record_type = (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT; + if (record_type != APFS_TYPE_DIR_REC) { + // Not a dentry; look at next entry in this leaf node + continue; + } + + j_drec_hashed_key_t* key = hdr; + j_drec_val_t* val = val_end - toc_entry->v.off; + + /** Check whether FS OID matches one in `fs_oids[]` **/ + // for (size_t j = 0; j < NUM_FS_OIDS; j++) { + // if (val->file_id == fs_oids[j]) { + // num_matches_in_node++; + // num_matches++; + + // printf("MATCHED --- query = %#" PRIx64 " --- match = %s\n", fs_oids[j], key->name); + + // // Found a match; don't need to compare against other FS OIDs + // break; + // } + // } + + /** Check whether FS OID lies within a range in `fs_oid_ranges[]` **/ + for (size_t j = 0; j < NUM_FS_OID_RANGES; j++) { + if ( (val->file_id >= fs_oid_ranges[j][0]) && (val->file_id <= fs_oid_ranges[j][1]) ) { + num_matches_in_node++; + num_matches++; + + printf( "MATCHED --- query = (%#" PRIx64 ", %#" PRIx64 ") --- match = %#" PRIx64 " --- name = %s\n", + fs_oid_ranges[j][0], + fs_oid_ranges[j][1], + val->file_id, + key->name + ); + + // Found a match; don't need to compare against other FS OIDs + break; + } + } + } + + if (num_matches_in_node == 0) { + // Move to start of line, + // clear whole line (which reads "- Entry x"), + // move up one line, + // clear whole line (which reads "Reading block [...] Inspecting entries") + printf("\r\033[2K\033[A\033[2K"); + } + } + } + } + } + + /** Get FS record types of first record in certain blocks on disk **/ + if (false) { + for (size_t block_index = 0; block_index < NUM_BLOCKS; block_index++) { + uint64_t addr = blocks[block_index]; + printf("\rReading block %2lu: %#" PRIx64 " ... ", block_index, addr); + + if (read_blocks(block, addr, 1) != 1) { + if (feof(nx)) { + printf("Reached end of file; ending search.\n"); + break; + } + + assert(ferror(nx)); + printf("- An error occurred whilst reading block %#" PRIx64 ".\n", addr); + continue; + } + + btree_node_phys_t* node = block; + + kvloc_t* first_toc_entry = (char*)node->btn_data + node->btn_table_space.off; + char* key_start = (char*)first_toc_entry + node->btn_table_space.len; + j_key_t* hdr = key_start + first_toc_entry->k.off; + + // printf("--- first record type = %#" PRIx64 "\n", (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT ); + printf("--- node OID = %#16" PRIx64 "\n", node->btn_o.o_oid); + } + } + + printf("\n\nFinished search; found %" PRIu64 " results.\n\n", num_matches); + + return 0; +} diff --git a/src/commands/version.c b/src/commands/version.c index 3c701d7..5e0774b 100644 --- a/src/commands/version.c +++ b/src/commands/version.c @@ -1,14 +1,14 @@ -#include - -#include "../legal.h" - -int cmd_version(int argc, char** argv) { - if (argc != 1) { - fprintf(stderr, "The `about` command doesn't take any arguments.\n"); - return 1; - } - - printf(VERSION_AND_COPYRIGHT_STRING "\n" WARRANTY_AND_LICENSE_STRING); - - return 0; -} +#include + +#include "../legal.h" + +int cmd_version(int argc, char** argv) { + if (argc != 1) { + fprintf(stderr, "The `about` command doesn't take any arguments.\n"); + return 1; + } + + printf(VERSION_AND_COPYRIGHT_STRING "\n" WARRANTY_AND_LICENSE_STRING); + + return 0; +} diff --git a/src/drat.c b/src/drat.c index 18bbb5c..d1eaad4 100644 --- a/src/drat.c +++ b/src/drat.c @@ -1,43 +1,43 @@ -#include -#include -#include - -#include "commands.h" -#include "legal.h" - -static void print_usage(bool is_error) { - fprintf( - is_error ? stderr : stdout, - - "Usage: drat \n" - "\n" - "List of commands:\n" - ); - - for (size_t i = 0; i < ARRAY_SIZE(drat_commands); i++) { - fprintf( - is_error ? stderr : stdout, - " %-22s %s\n", - drat_commands[i].name, - drat_commands[i].description - ); - } -} - -int main(int argc, char** argv) { - char* cmd_name = argv[1]; - if (!cmd_name) { - printf(VERSION_AND_COPYRIGHT_STRING "\n"); - print_usage(false); - return 0; - } - - command_function* cmd = get_command_function(cmd_name); - if (!cmd) { - fprintf(stderr, "Unrecognised command `%s`.\n\n", cmd_name); - print_usage(true); - return -1; - } - - return cmd(argc - 1, argv + 1); -} +#include +#include +#include + +#include "commands.h" +#include "legal.h" + +static void print_usage(bool is_error) { + fprintf( + is_error ? stderr : stdout, + + "Usage: drat \n" + "\n" + "List of commands:\n" + ); + + for (size_t i = 0; i < ARRAY_SIZE(drat_commands); i++) { + fprintf( + is_error ? stderr : stdout, + " %-22s %s\n", + drat_commands[i].name, + drat_commands[i].description + ); + } +} + +int main(int argc, char** argv) { + char* cmd_name = argv[1]; + if (!cmd_name) { + printf(VERSION_AND_COPYRIGHT_STRING "\n"); + print_usage(false); + return 0; + } + + command_function* cmd = get_command_function(cmd_name); + if (!cmd) { + fprintf(stderr, "Unrecognised command `%s`.\n\n", cmd_name); + print_usage(true); + return -1; + } + + return cmd(argc - 1, argv + 1); +} diff --git a/src/legal.h b/src/legal.h index 438acf2..ec7178c 100644 --- a/src/legal.h +++ b/src/legal.h @@ -1,16 +1,16 @@ -#ifndef DRAT_LEGAL_H -#define DRAT_LEGAL_H - -#define VERSION_AND_COPYRIGHT_STRING \ - "Drat version 0.1.3\n" \ - "Copyright (C) 2019-2021 Jivan Pal\n" \ - "\n" - -#define WARRANTY_AND_LICENSE_STRING \ - "This program comes with ABSOLUTELY NO WARRANTY; not even the implied warranty\n" \ - "of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This is free software:\n" \ - "you can modify and redistribute it under the terms of the GNU General Public\n" \ - "License, version 3 only, as published by the Free Software Foundation, which\n" \ - "can be found at .\n" \ - +#ifndef DRAT_LEGAL_H +#define DRAT_LEGAL_H + +#define VERSION_AND_COPYRIGHT_STRING \ + "Drat version 0.1.3\n" \ + "Copyright (C) 2019-2021 Jivan Pal\n" \ + "\n" + +#define WARRANTY_AND_LICENSE_STRING \ + "This program comes with ABSOLUTELY NO WARRANTY; not even the implied warranty\n" \ + "of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This is free software:\n" \ + "you can modify and redistribute it under the terms of the GNU General Public\n" \ + "License, version 3 only, as published by the Free Software Foundation, which\n" \ + "can be found at .\n" \ + #endif // DRAT_LEGAL_H \ No newline at end of file From ac919e20e81e0f16fb46e4fbc289caea426b1f98 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Fri, 8 Oct 2021 23:13:07 +0200 Subject: [PATCH 9/9] merge2 --- Makefile | 17 ++--- include/apfs/xf.h | 11 ---- include/drat/func/btree.c | 31 +++++++--- include/drat/func/cksum.c | 6 +- include/drat/io.c | 10 +-- include/drat/print-fs-records.c | 14 ++--- include/drat/string/btree.c | 26 ++++---- include/drat/string/fs.c | 44 ++++++------- include/drat/string/j.c | 49 ++++++++------- include/drat/string/nx.c | 39 ++++++------ include/drat/string/object.c | 14 ++--- include/drat/string/omap.c | 20 +++--- src/commands/explore-fs-tree.c | 57 +++++++---------- src/commands/explore-omap-tree.c | 44 ++++++------- src/commands/inspect.c | 103 +++++++++++++++---------------- src/commands/list.c | 84 ++++++++++--------------- src/commands/read.c | 8 +-- src/commands/recover.c | 84 ++++++++++--------------- src/commands/resolver.c | 73 ++++++++-------------- 19 files changed, 334 insertions(+), 400 deletions(-) diff --git a/Makefile b/Makefile index df20544..2c0774a 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,7 @@ INCDIR=include ### Compiler definition ### CC := gcc - -override CFLAGS += \ +CFLAGS := \ -std=c99 \ -D _GNU_SOURCE \ -Werror \ @@ -21,13 +20,13 @@ override CFLAGS += \ ### Linker definition ### LD := gcc -override LDFLAGS += # Nothing +LDFLAGS := # Nothing ### On macOS, include from Homebrew package `argp-standalone` ifneq ($(OS),Windows_NT) ifeq ($(shell uname -s),Darwin) - override CFLAGS += -I/usr/local/Cellar/argp-standalone/1.3/include/ - override LDFLAGS += -L/usr/local/Cellar/argp-standalone/1.3/lib/ -largp + CFLAGS += -I/usr/local/Cellar/argp-standalone/1.3/include/ + LDFLAGS += -L/usr/local/Cellar/argp-standalone/1.3/lib/ -largp endif endif @@ -83,9 +82,6 @@ binaries: $(BINARIES) clean: rm -rf $(BINARIES) $(OUTDIR) -.PHONY: all -all: headers commands binaries - ## .PHONY: docs @@ -95,3 +91,8 @@ docs: .PHONY: clean-docs clean-docs: (cd docs && make clean) + +## + +.PHONY: all +all: headers commands binaries diff --git a/include/apfs/xf.h b/include/apfs/xf.h index 3ca0c5e..2ae1060 100644 --- a/include/apfs/xf.h +++ b/include/apfs/xf.h @@ -10,18 +10,7 @@ typedef struct { uint16_t xf_num_exts; - - /** - * NOTE: Apple's spec claims that the value of `xf_used_data` is the size in - * bytes of the entirety of `xf_data`, including the instances of `x_field_t` - * that appear at the start of `xf_data`. However, based on real-world - * examples, this doesn't appear to be the case. Instead, `xf_used_data` is - * just the size in bytes of the portion of `xf_data` that contains the - * xfield values, not the instances of `x_field_t`. In practice, this - * doesn't matter, since we never actually use the value of `xf_used_data`. - */ uint16_t xf_used_data; - uint8_t xf_data[]; } xf_blob_t; diff --git a/include/drat/func/btree.c b/include/drat/func/btree.c index ca640c4..b8ef907 100644 --- a/include/drat/func/btree.c +++ b/include/drat/func/btree.c @@ -108,6 +108,10 @@ omap_entry_t* get_btree_phys_omap_entry(btree_node_phys_t* root_node, oid_t oid, // Handle case (c) + #ifdef DEBUG + fprintf(stderr, "\n- get_btree_phys_omap_val: Picked entry %lu\n", toc_entry - (kvoff_t*)toc_start); + #endif + // If this is a leaf node, return the object map entry if (node->btn_flags & BTNODE_LEAF) { // If the object doesn't have the specified OID or its XID exceeds @@ -137,12 +141,12 @@ omap_entry_t* get_btree_phys_omap_entry(btree_node_phys_t* root_node, oid_t oid, paddr_t* child_node_addr = val_end - toc_entry->v; if (read_blocks(node, *child_node_addr, 1) != 1) { - fprintf(stderr, "\nABORT: get_btree_phys_omap_val: Failed to read block %#"PRIx64".\n", *child_node_addr); + fprintf(stderr, "\nABORT: get_btree_phys_omap_val: Failed to read block 0x%" PRIx64 ".\n", *child_node_addr); exit(-1); } if (!is_cksum_valid(node)) { - fprintf(stderr, "\nWARNING: get_btree_phys_omap_val: Checksum of node at block %#"PRIx64" did not validate. Proceeding anyway as if it did.\n", *child_node_addr); + fprintf(stderr, "\nget_btree_phys_omap_val: Checksum of node at block 0x%" PRIx64 " did not validate. Proceeding anyway as if it did.\n", *child_node_addr); } toc_start = (char*)(node->btn_data) + node->btn_table_space.off; @@ -375,19 +379,19 @@ j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_ oid_t* child_node_virt_oid = val_end - toc_entry->v.off; omap_entry_t* child_node_omap_entry = get_btree_phys_omap_entry(vol_omap_root_node, *child_node_virt_oid, max_xid); if (!child_node_omap_entry) { - fprintf(stderr, "\nABORT: get_fs_records: Need to descend to node with Virtual OID %#"PRIx64", but the volume object map lists no objects with this Virtual OID.\n", *child_node_virt_oid); + fprintf(stderr, "\nABORT: get_fs_records: Need to descend to node with Virtual OID 0x%" PRIx64 ", but the volume object map lists no objects with this Virtual OID.\n", *child_node_virt_oid); exit(-1); } if (read_blocks(node, child_node_omap_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: get_fs_records: Failed to read block %#"PRIx64".\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: get_fs_records: Failed to read block 0x%" PRIx64 ".\n", child_node_omap_entry->val.ov_paddr); exit(-1); } // `node` is now the child node we will scan on next loop if (!is_cksum_valid(node)) { - fprintf(stderr, "\nABORT: get_fs_records: Checksum of node at block %#"PRIx64" did not validate.\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: get_fs_records: Checksum of node at block 0x%" PRIx64 " did not validate.\n", child_node_omap_entry->val.ov_paddr); exit(-1); } @@ -417,6 +421,15 @@ j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_ * that next record. */ while (true) { + #ifdef DEBUG + // Print the contents of `desc_path` + fprintf(stderr, "("); + for (i = 0; i < tree_height - 1; i++) { + fprintf(stderr, "%u, ", desc_path[i]); + } + fprintf(stderr, "%u)\n", desc_path[i]); + #endif + // Reset working node and pointers to the root node memcpy(node, vol_fs_root_node, nx_block_size); toc_start = (char*)(node->btn_data) + node->btn_table_space.off; @@ -491,7 +504,7 @@ j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_ records[num_records] = malloc(sizeof(j_rec_t) + toc_entry->k.len + toc_entry->v.len); if (!records[num_records]) { - fprintf(stderr, "\nABORT: get_fs_records: Could not allocate sufficient memory for `records[%zu]`.\n", num_records); + fprintf(stderr, "\nABORT: get_fs_records: Could not allocate sufficient memory for `records[%lu]`.\n", num_records); exit(-1); } @@ -534,19 +547,19 @@ j_rec_t** get_fs_records(btree_node_phys_t* vol_omap_root_node, btree_node_phys_ oid_t* child_node_virt_oid = val_end - toc_entry->v.off; omap_entry_t* child_node_omap_entry = get_btree_phys_omap_entry(vol_omap_root_node, *child_node_virt_oid, max_xid); if (!child_node_omap_entry) { - fprintf(stderr, "\nABORT: get_fs_records: Need to descend to node with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64", but the volume object map lists no such objects.\n", *child_node_virt_oid, max_xid); + fprintf(stderr, "\nABORT: get_fs_records: Need to descend to node with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 ", but the volume object map lists no such objects.\n", *child_node_virt_oid, max_xid); exit(-1); } if (read_blocks(node, child_node_omap_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: get_fs_records: Failed to read block %#"PRIx64".\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: get_fs_records: Failed to read block 0x%" PRIx64 ".\n", child_node_omap_entry->val.ov_paddr); exit(-1); } // `node` is now the child node that we will examine on next loop if (!is_cksum_valid(node)) { - fprintf(stderr, "\nABORT: get_fs_records: Checksum of node at block %#"PRIx64" did not validate.\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: get_fs_records: Checksum of node at block 0x%" PRIx64 " did not validate.\n", child_node_omap_entry->val.ov_paddr); exit(-1); } diff --git a/include/drat/func/cksum.c b/include/drat/func/cksum.c index 929f3eb..5e9a3e2 100644 --- a/include/drat/func/cksum.c +++ b/include/drat/func/cksum.c @@ -24,7 +24,7 @@ * successfully, and non-zero if it fails to do so. */ uint64_t fletcher_cksum(uint32_t* block, bool compute) { - int num_words = nx_block_size / 4; // Using 32-bit (4-byte) words. + int num_words = nx_block_size / 4; // Using 32-bit words. uint32_t modulus = ~0; // all ones; = 2^32 - 1 // These are 32-bit values, but we need at least 33 bits of memory for each @@ -43,7 +43,7 @@ uint64_t fletcher_cksum(uint32_t* block, bool compute) { } /** - * APFS uses a variant of the traditional Fletcher-64 checksum. + * APFS uses a variant of the traditional Flecther-64 checksum. * See: https://blog.cugu.eu/post/apfs/#checksum * * In particular: @@ -93,5 +93,5 @@ bool is_cksum_valid(uint32_t* block) { // return fletcher_cksum(block, 0) == 0; // The following gives the correct result. - return compute_block_cksum(block) == *(uint64_t*)block; + return compute_block_cksum(block) == *(uint64_t*)block; // dereference of cast give } diff --git a/include/drat/io.c b/include/drat/io.c index d514de8..4693d1b 100644 --- a/include/drat/io.c +++ b/include/drat/io.c @@ -61,7 +61,7 @@ void report_fopen_error() { fprintf(stderr, "You tried to open a socket, but your system doesn't support that.\n"); break; case EOVERFLOW: - fprintf(stderr, "The specified file is a regular file whose size exceeds %zu bytes, so it is too large to be handled.\n", sizeof(off_t)); + fprintf(stderr, "The specified file is a regular file whose size exceeds %lu bytes, so it is too large to be handled.\n", sizeof(off_t)); break; default: fprintf(stderr, "Unknown error.\n"); @@ -93,7 +93,7 @@ size_t read_blocks(void* buffer, long start_block, size_t num_blocks) { printf("The specified starting block address, 0x%lx, is invalid, as it lies outside of the file `%s`.\n", start_block, nx_path); break; case EOVERFLOW: - printf("The specified starting block address, 0x%lx, exceeds %zu bits in length, which would result in an overflow.\n", start_block, 8 * sizeof(long)); + printf("The specified starting block address, 0x%lx, exceeds %lu bits in length, which would result in an overflow.\n", start_block, 8 * sizeof(long)); break; case ESPIPE: printf("The data stream associated with the file `%s` is a pipe or FIFO, and thus cannot be seeked through.\n", nx_path); @@ -121,7 +121,7 @@ size_t read_blocks(void* buffer, long start_block, size_t num_blocks) { } assert(feof(nx)); - printf("read_blocks: Reached end-of-file after reading %zu blocks.\n", num_blocks_read); + printf("read_blocks: Reached end-of-file after reading %lu blocks.\n", num_blocks_read); } return num_blocks_read; } @@ -153,7 +153,7 @@ size_t write_blocks(void* buffer, long start_block, size_t num_blocks) { printf("The specified starting block address, 0x%lx, is invalid, as it lies outside of the file `%s`.\n", start_block, nx_path); break; case EOVERFLOW: - printf("The specified starting block address, 0x%lx, exceeds %zu bits in length, which would result in an overflow.\n", start_block, 8 * sizeof(long)); + printf("The specified starting block address, 0x%lx, exceeds %lu bits in length, which would result in an overflow.\n", start_block, 8 * sizeof(long)); break; case ESPIPE: printf("The data stream associated with the file `%s` is a pipe or FIFO, and thus cannot be seeked through.\n", nx_path); @@ -168,7 +168,7 @@ size_t write_blocks(void* buffer, long start_block, size_t num_blocks) { size_t num_blocks_written = fwrite(buffer, nx_block_size, num_blocks, nx); if (num_blocks_written != num_blocks) { // A write error occured - printf("write_blocks: An error occurred after writing %zu blocks.\n", num_blocks_written); + printf("write_blocks: An error occurred after writing %lu blocks.\n", num_blocks_written); } return num_blocks_written; } diff --git a/include/drat/print-fs-records.c b/include/drat/print-fs-records.c index 7d4331f..b4ad403 100644 --- a/include/drat/print-fs-records.c +++ b/include/drat/print-fs-records.c @@ -90,8 +90,8 @@ void print_fs_records(j_rec_t** fs_records) { j_dstream_id_key_t* key = fs_rec->data; j_dstream_id_val_t* val = fs_rec->data + fs_rec->key_len; fprintf(stderr, "DSTREAM ID " - " || file ID = %#8"PRIx64 - " || ref. count = %"PRIu32, + " || file ID = %#8llx" + " || ref. count = %u", key->hdr.obj_id_and_type & OBJ_ID_MASK, val->refcnt @@ -110,10 +110,10 @@ void print_fs_records(j_rec_t** fs_records) { uint64_t extent_length_blocks = extent_length_bytes / nx_block_size; fprintf(stderr, "FILE EXTENT" - " || file ID = %#8"PRIx64 - " || log. addr. = %#10"PRIx64 - " || length = %8"PRIu64" B = %#10"PRIx64" B = %5"PRIu64" blocks = %#7"PRIx64" blocks" - " || phys. block = %#10"PRIx64, + " || file ID = %#8llx" + " || log. addr. = %#10" PRIx64 + " || length = %8" PRIu64 " B = %#10" PRIx64 " B = %5" PRIu64 " blocks = %#7" PRIx64 " blocks" + " || phys. block = %#10" PRIx64, key->hdr.obj_id_and_type & OBJ_ID_MASK, key->logical_addr, @@ -130,7 +130,7 @@ void print_fs_records(j_rec_t** fs_records) { fprintf(stderr, "DIR REC" " || %s" - " || target ID = %#8"PRIx64 + " || target ID = %#8" PRIx64 " || name = %s", drec_val_to_short_type_string(val), diff --git a/include/drat/string/btree.c b/include/drat/string/btree.c index 6896316..d4438fd 100644 --- a/include/drat/string/btree.c +++ b/include/drat/string/btree.c @@ -81,16 +81,16 @@ void print_btree_info(btree_info_t* bt_info) { printf("- Flags:\n%s", flags_string); free(flags_string); - printf("- Node size: %"PRIu32" bytes\n", bt_info->bt_fixed.bt_node_size); - printf("- Key size: %"PRIu32" bytes\n", bt_info->bt_fixed.bt_key_size); - printf("- Value size: %"PRIu32" bytes\n", bt_info->bt_fixed.bt_val_size); + printf("- Node size: %u bytes\n", bt_info->bt_fixed.bt_node_size); + printf("- Key size: %u bytes\n", bt_info->bt_fixed.bt_key_size); + printf("- Value size: %u bytes\n", bt_info->bt_fixed.bt_val_size); printf("\n"); - printf("- Length of longest key: %"PRIu32" bytes\n", bt_info->bt_longest_key); - printf("- Length of longest value: %"PRIu32" bytes\n", bt_info->bt_longest_val); - printf("- Number of keys: %"PRIu64"\n", bt_info->bt_key_count); - printf("- Number of nodes: %"PRIu64"\n", bt_info->bt_node_count); + printf("- Length of longest key: %u bytes\n", bt_info->bt_longest_key); + printf("- Length of longest value: %u bytes\n", bt_info->bt_longest_val); + printf("- Number of keys: %" PRIu64 "\n", bt_info->bt_key_count); + printf("- Number of nodes: %" PRIu64 "\n", bt_info->bt_node_count); } /** @@ -105,16 +105,16 @@ void print_btree_node_phys(btree_node_phys_t* btn) { printf("Flags:\n%s", flags_string); free(flags_string); - printf("Number of child levels: %"PRIu16"\n", btn->btn_level); - printf("Number of keys in this node: %"PRIu32"\n", btn->btn_nkeys); + printf("Number of child levels: %u\n", btn->btn_level); + printf("Number of keys in this node: %u\n", btn->btn_nkeys); printf("Location of table of contents:\n"); - printf("- Offset from start of node data area: %#"PRIx16" = %"PRIu16"\n", btn->btn_table_space.off, btn->btn_table_space.off); - printf("- Length (bytes): %#"PRIx16" = %"PRIu16"\n", btn->btn_table_space.len, btn->btn_table_space.len); + printf("- Offset from start of node data area: 0x%x = %u\n", btn->btn_table_space.off, btn->btn_table_space.off); + printf("- Length (bytes): 0x%x = %u\n", btn->btn_table_space.len, btn->btn_table_space.len); printf("Location of key–value shared free space:\n"); - printf("- Offset from start of keys area: %#"PRIx16" = %"PRIu16"\n", btn->btn_free_space.off, btn->btn_free_space.off); - printf("- Length (bytes): %#"PRIx16" = %"PRIu16"\n", btn->btn_free_space.len, btn->btn_free_space.len); + printf("- Offset from start of keys area: 0x%x = %u\n", btn->btn_free_space.off, btn->btn_free_space.off); + printf("- Length (bytes): 0x%x = %u\n", btn->btn_free_space.len, btn->btn_free_space.len); if (is_btree_node_phys_root(btn)) { printf("\n"); diff --git a/include/drat/string/fs.c b/include/drat/string/fs.c index f55cbdc..1e7622b 100644 --- a/include/drat/string/fs.c +++ b/include/drat/string/fs.c @@ -167,7 +167,7 @@ void print_apfs_superblock(apfs_superblock_t* apsb) { '\0' }; printf("Magic string: %s\n", magic_string); - printf("Index within container volume array: %"PRIu32"\n", apsb->apfs_fs_index); + printf("Index within container volume array: %u\n", apsb->apfs_fs_index); printf("\n"); printf("Volume name: ### %s ###\n", apsb->apfs_volname); @@ -200,17 +200,17 @@ void print_apfs_superblock(apfs_superblock_t* apsb) { printf("Last modification time: %s", apfs_timestamp_to_string(apsb->apfs_last_mod_time)); printf("\n"); - printf("Reserved blocks: %"PRIu64" blocks\n", apsb->apfs_fs_reserve_block_count); - printf("Block quota: %"PRIu64" blocks\n", apsb->apfs_fs_quota_block_count); - printf("Allocated blocks: %"PRIu64" blocks\n", apsb->apfs_fs_alloc_count); + printf("Reserved blocks: %" PRIu64 " blocks\n", apsb->apfs_fs_reserve_block_count); + printf("Block quota: %" PRIu64 " blocks\n", apsb->apfs_fs_quota_block_count); + printf("Allocated blocks: %" PRIu64 " blocks\n", apsb->apfs_fs_alloc_count); printf("\n"); - printf("Volume object map Physical OID: %#"PRIx64"\n", apsb->apfs_omap_oid); + printf("Volume object map Physical OID: 0x%" PRIx64 "\n", apsb->apfs_omap_oid); printf("\n"); printf("Root tree info:\n"); - printf("- OID: %#"PRIx64"\n", apsb->apfs_root_tree_oid); - printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_root_tree_type)); + printf("- OID: 0x%" PRIx64 "\n", apsb->apfs_root_tree_oid); + printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_root_tree_type)); tmp_string = get_o_type_flags_string(apsb->apfs_root_tree_type); printf("- Type flags: %s\n", tmp_string); @@ -223,8 +223,8 @@ void print_apfs_superblock(apfs_superblock_t* apsb) { printf("\n"); printf("Extent-reference tree info:\n"); - printf("- OID: %#"PRIx64"\n", apsb->apfs_extentref_tree_oid); - printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_extentref_tree_type)); + printf("- OID: 0x%" PRIx64 "\n", apsb->apfs_extentref_tree_oid); + printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_extentref_tree_type)); tmp_string = get_o_type_flags_string(apsb->apfs_extentref_tree_type); printf("- Type flags: %s\n", tmp_string); @@ -237,8 +237,8 @@ void print_apfs_superblock(apfs_superblock_t* apsb) { printf("\n"); printf("Snapshot metadata tree info:\n"); - printf("- OID: %#"PRIx64"\n", apsb->apfs_snap_meta_tree_oid); - printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_snap_meta_tree_type)); + printf("- OID: 0x%" PRIx64 "\n", apsb->apfs_snap_meta_tree_oid); + printf("- Storage type: %s\n", o_storage_type_to_string(apsb->apfs_snap_meta_tree_type)); tmp_string = get_o_type_flags_string(apsb->apfs_snap_meta_tree_type); printf("- Type flags: %s\n", tmp_string); @@ -251,24 +251,24 @@ void print_apfs_superblock(apfs_superblock_t* apsb) { printf("\n"); printf("On next mount, revert to:\n"); - printf("- snapshot with this XID: %#"PRIx64"\n", apsb->apfs_revert_to_xid); - printf("- APFS volume superblock with this Physical OID: %#"PRIx64"\n", apsb->apfs_revert_to_sblock_oid); + printf("- snapshot with this XID: 0x%" PRIx64 "\n", apsb->apfs_revert_to_xid); + printf("- APFS volume superblock with this Physical OID: 0x%" PRIx64 "\n", apsb->apfs_revert_to_sblock_oid); printf("\n"); - printf("Next file-system object ID that will be assigned: %#"PRIx64"\n", apsb->apfs_next_obj_id); - printf("Next document ID that will be assigned: %#"PRIx32"\n", apsb->apfs_next_doc_id); + printf("Next file-system object ID that will be assigned: 0x%" PRIx64 "\n", apsb->apfs_next_obj_id); + printf("Next document ID that will be assigned: 0x%x\n", apsb->apfs_next_doc_id); printf("\n"); printf("Number of:\n"); printf("\n"); - printf("- regular files: %"PRIu64"\n", apsb->apfs_num_files); - printf("- directories: %"PRIu64"\n", apsb->apfs_num_directories); - printf("- symbolic links: %"PRIu64"\n", apsb->apfs_num_symlinks); - printf("- other file-system objects: %"PRIu64"\n", apsb->apfs_num_other_fsobjects); + printf("- regular files: %" PRIu64 "\n", apsb->apfs_num_files); + printf("- directories: %" PRIu64 "\n", apsb->apfs_num_directories); + printf("- symbolic links: %" PRIu64 "\n", apsb->apfs_num_symlinks); + printf("- other file-system objects: %" PRIu64 "\n", apsb->apfs_num_other_fsobjects); printf("\n"); - printf("- snapshots: %"PRIu64"\n", apsb->apfs_num_snapshots); - printf("- block allocations ever made: %"PRIu64"\n", apsb->apfs_total_block_alloced); - printf("- block liberations ever made: %"PRIu64"\n", apsb->apfs_total_blocks_freed); + printf("- snapshots: %" PRIu64 "\n", apsb->apfs_num_snapshots); + printf("- block allocations ever made: %" PRIu64 "\n", apsb->apfs_total_block_alloced); + printf("- block liberations ever made: %" PRIu64 "\n", apsb->apfs_total_blocks_freed); printf("\n"); printf("UUID: "); diff --git a/include/drat/string/j.c b/include/drat/string/j.c index dcc11c2..4128b5e 100644 --- a/include/drat/string/j.c +++ b/include/drat/string/j.c @@ -50,7 +50,7 @@ // Helper function for printing objects which have xfields static void print_xf_details(bool has_xfields, xf_blob_t* xfields) { - printf("Number of extended fields: %"PRIu16"\n", has_xfields ? xfields->xf_num_exts : 0); + printf("Number of extended fields: %u\n", has_xfields ? xfields->xf_num_exts : 0); if (has_xfields) { printf("Details of extended fields:\n\n"); xf_pair_t** xf_pairs_array = get_xf_pairs_array(xfields); @@ -104,8 +104,8 @@ char* j_key_type_to_string(uint8_t j_key_type) { } void print_j_key(j_key_t* key) { - printf("Virtual OID: %#"PRIx64"\n", key->obj_id_and_type & OBJ_ID_MASK); - printf("Object type: %s\n", j_key_type_to_string( + printf("Virtual OID: 0x%llx\n", key->obj_id_and_type & OBJ_ID_MASK); + printf("Object type: %s\n", j_key_type_to_string( (key->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT )); } @@ -196,19 +196,21 @@ char *get_j_inode_type(j_inode_val_t* val) { } void print_j_inode_val(j_inode_val_t* val, uint16_t val_len) { - printf("Parent ID: %#"PRIx64"\n", val->parent_id); - printf("Private ID: %#"PRIx64"\n", val->private_id); + printf("Parent ID: 0x%" PRIx64 "\n", val->parent_id); + printf("Private ID: 0x%" PRIx64 "\n", val->private_id); printf("\n"); char* tmp_string = NULL; if (val->internal_flags & INODE_HAS_UNCOMPRESSED_SIZE) { - asprintf(&tmp_string, "%"PRIu64" bytes", val->uncompressed_size); + if (asprintf(&tmp_string, "%" PRIu64 " bytes", val->uncompressed_size) < 0) { + fprintf(stderr, "ABORT: %s:%d: call to asprintf() couldn't allocate sufficient memory", __func__, __LINE__); + exit(-1); + } } else { - asprintf(&tmp_string, "(none)"); - } - if (!tmp_string) { - fprintf(stderr, "ABORT: %s: call to asprintf() couldn't allocate sufficient memory", __func__); - exit(-1); + if (asprintf(&tmp_string, "(none)") < 0) { + fprintf(stderr, "ABORT: %s:%d: call to asprintf() couldn't allocate sufficient memory", __func__, __LINE__); + exit(-1); + } } printf("Uncompressed size: %s\n", tmp_string); free(tmp_string); @@ -218,11 +220,11 @@ void print_j_inode_val(j_inode_val_t* val, uint16_t val_len) { printf("Last access time: %s", apfs_timestamp_to_string(val->change_time)); printf("\n"); - printf("Number of children / hard links: %"PRId32"\n", val->nchildren); + printf("Number of children / hard links: %u\n", val->nchildren); printf("\n"); - printf("Owner UID: %"PRIu32"\n", val->owner); - printf("Group GID: %"PRIu32"\n", val->group); + printf("Owner UID: %u\n", val->owner); + printf("Group GID: %u\n", val->group); printf("\n"); printf("Mode: %s\n", j_inode_mode_to_string(val->mode)); @@ -244,15 +246,15 @@ void print_j_inode_val(j_inode_val_t* val, uint16_t val_len) { void print_j_file_extent_key(j_file_extent_key_t* key) { print_j_key(key); // `key` equals `&(key->hdr)` printf("\n"); - printf("Extent offset within file: %#"PRIx64"\n", key->logical_addr); + printf("Extent offset within file: %#" PRIx64 "\n", key->logical_addr); } void print_j_file_extent_val(j_file_extent_val_t* val) { // TODO: Print flags // TODO: Print crypto ID - printf("Length: %"PRIu64" bytes\n", val->len_and_flags & J_FILE_EXTENT_LEN_MASK); - printf("Start block: %#"PRIx64"\n", val->phys_block_num); + printf("Length (bytes): %llu\n", val->len_and_flags & J_FILE_EXTENT_LEN_MASK); + printf("Start block: %#" PRIx64 "\n", val->phys_block_num); } void print_j_drec_hashed_key(j_drec_hashed_key_t* key) { @@ -263,10 +265,11 @@ void print_j_drec_hashed_key(j_drec_hashed_key_t* key) { uint16_t name_len = key->name_len_and_hash & J_DREC_LEN_MASK; // 22-bit value; next smallest datatype is 32 bits uint32_t name_hash = (key->name_len_and_hash & J_DREC_HASH_MASK) >> J_DREC_HASH_SHIFT; + // TODO: validate the hash? - printf("Dentry name length: %"PRIu16" UTF-8 bytes (including terminating NULL (U+0000) byte)\n", name_len); - printf("Dentry name hash: %#06"PRIx32"\n", name_hash); - printf("Dentry name: ### %s ###\n", key->name); + printf("Dentry name length: %u UTF-8 bytes (including terminating NULL (U+0000) byte)\n", name_len); + printf("Dentry name hash: 0x%06x\n", name_hash); + printf("Dentry name: ### %s ####\n", key->name); } char* drec_val_to_type_string(j_drec_val_t* val) { @@ -301,9 +304,9 @@ char* drec_val_to_short_type_string(j_drec_val_t* val) { } void print_j_drec_val(j_drec_val_t* val, uint16_t val_len) { - printf("Dentry Virtual OID: %#"PRIx64"\n", val->file_id); - printf("Time added: %s", apfs_timestamp_to_string(val->date_added)); - printf("Dentry type: %s\n", drec_val_to_type_string(val)); + printf("Dentry Virtual OID: 0x%" PRIx64 "\n", val->file_id); + printf("Time added: %s", apfs_timestamp_to_string(val->date_added)); + printf("Dentry type: %s\n", drec_val_to_type_string(val)); print_xf_details(val_len != sizeof(j_drec_val_t), val->xfields); } diff --git a/include/drat/string/nx.c b/include/drat/string/nx.c index b1e7980..d626c5f 100644 --- a/include/drat/string/nx.c +++ b/include/drat/string/nx.c @@ -107,7 +107,7 @@ void print_nx_superblock(nx_superblock_t* nxsb) { printf("none (spans 0 blocks)\n"); } else { printf( - "first block %#"PRIx64", spans %"PRIu64" (%#"PRIx64") blocks (last block %#"PRIx64")\n", + "first block %#" PRIx64 ", spans %" PRIu64 " (%#" PRIx64 ") blocks (last block %#" PRIx64 ")\n", nxsb->nx_keylocker.pr_start_paddr, nxsb->nx_keylocker.pr_block_count, nxsb->nx_keylocker.pr_block_count, @@ -120,7 +120,7 @@ void print_nx_superblock(nx_superblock_t* nxsb) { printf("none (spans 0 blocks)\n"); } else { printf( - "first block %#"PRIx64", spans %"PRIu64" (%#"PRIx64") blocks (last block %#"PRIx64")\n", + "first block %#" PRIx64 ", spans %" PRIu64 " (%#" PRIx64 ") blocks (last block %#" PRIx64 ")\n", nxsb->nx_mkb_locker.pr_start_paddr, nxsb->nx_mkb_locker.pr_block_count, nxsb->nx_mkb_locker.pr_block_count, @@ -128,6 +128,7 @@ void print_nx_superblock(nx_superblock_t* nxsb) { ); } + char magic_string[] = { (char)(nxsb->nx_magic), (char)(nxsb->nx_magic >> 8), @@ -139,17 +140,17 @@ void print_nx_superblock(nx_superblock_t* nxsb) { printf( "Latest version of Apple APFS software that mounted this container: " - "%"PRIu64".%"PRIu64".%"PRIu64".%"PRIu64".%"PRIu64"\n", + "%" PRIu64 ".%llu.%llu.%llu.%llu\n", nxsb->nx_newest_mounted_version >> 40, - (nxsb->nx_newest_mounted_version >> 30) & 0x3ff, // mask 0x3ff is lowest 10 bits - (nxsb->nx_newest_mounted_version >> 20) & 0x3ff, - (nxsb->nx_newest_mounted_version >> 10) & 0x3ff, - (nxsb->nx_newest_mounted_version) & 0x3ff + (nxsb->nx_newest_mounted_version >> 30) & ~(~0ULL << 10), + (nxsb->nx_newest_mounted_version >> 20) & ~(~0ULL << 10), + (nxsb->nx_newest_mounted_version >> 10) & ~(~0ULL << 10), + (nxsb->nx_newest_mounted_version) & ~(~0ULL << 10) ); - printf("Block size: %"PRIu32" bytes\n", nxsb->nx_block_size); - printf("Block count: %"PRIu64" (last block %#"PRIx64")\n", + printf("Block size: %u bytes\n", nxsb->nx_block_size); + printf("Block count: %" PRIu64 " (last block %#" PRIx64 ")\n", nxsb->nx_block_count, nxsb->nx_block_count - 1 ); @@ -170,14 +171,14 @@ void print_nx_superblock(nx_superblock_t* nxsb) { print_uuid(nxsb->nx_uuid); printf("\n"); - printf("Next OID: %#"PRIx64"\n", nxsb->nx_next_oid); - printf("Next XID: %#"PRIx64"\n", nxsb->nx_next_xid); + printf("Next OID: 0x%" PRIx64 "\n", nxsb->nx_next_oid); + printf("Next XID: 0x%" PRIx64 "\n", nxsb->nx_next_xid); // TODO: Maybe print `xp_desc` and `xp_data` fields. - printf("Space manager Ephemeral OID: %#"PRIx64"\n", nxsb->nx_spaceman_oid); - printf("Object map Physical OID: %#"PRIx64"\n", nxsb->nx_omap_oid); - printf("Reaper Ephemeral OID: %#"PRIx64"\n", nxsb->nx_reaper_oid); + printf("Space manager Ephemeral OID: 0x%" PRIx64 "\n", nxsb->nx_spaceman_oid); + printf("Object map Physical OID: 0x%" PRIx64 "\n", nxsb->nx_omap_oid); + printf("Reaper Ephemeral OID: 0x%" PRIx64 "\n", nxsb->nx_reaper_oid); char* flags_string = get_nx_flags_string(nxsb); printf("Other flags:\n%s", flags_string); @@ -196,8 +197,8 @@ void print_nx_superblock(nx_superblock_t* nxsb) { * cpm: A pointer to the checkpoint-mapping in question. */ void print_checkpoint_mapping(checkpoint_mapping_t* cpm) { - printf("Ephemeral OID: %#"PRIx64"\n", cpm->cpm_oid); - printf("Logical block address on disk: %#"PRIx64"\n", cpm->cpm_paddr); + printf("Ephemeral OID: 0x%" PRIx64 "\n", cpm->cpm_oid); + printf("Logical block address on disk: 0x%" PRIx64 "\n", cpm->cpm_paddr); char* type_string = get_o_type_string(cpm->cpm_type); printf("Object type: %s\n", type_string); @@ -207,8 +208,8 @@ void print_checkpoint_mapping(checkpoint_mapping_t* cpm) { printf("Object subtype: %s\n", subtype_string); free(subtype_string); - printf("Object size: %"PRIu32" bytes\n", cpm->cpm_size); - printf("Associated volume OID (virtual): %#"PRIx64"\n", cpm->cpm_fs_oid); + printf("Object size: %u bytes\n", cpm->cpm_size); + printf("Associated volume OID (virtual): 0x%" PRIx64 "\n", cpm->cpm_fs_oid); } /** @@ -245,7 +246,7 @@ void print_checkpoint_map_phys(checkpoint_map_phys_t* cpm) { printf("Flags:\n%s", flags_string); free(flags_string); - printf("Number of mappings: %"PRIu32"\n", cpm->cpm_count); + printf("Number of mappings: %u\n", cpm->cpm_count); } /** diff --git a/include/drat/string/object.c b/include/drat/string/object.c index dd4249c..d34d189 100644 --- a/include/drat/string/object.c +++ b/include/drat/string/object.c @@ -164,13 +164,13 @@ void print_obj_phys(obj_phys_t* obj) { char* subtype_string = get_o_subtype_string(obj->o_subtype); // Print the info - printf("Stored checksum: %#016"PRIx64"\n", *(uint64_t*)obj); - printf("OID: %#"PRIx64"\n", obj->o_oid); - printf("XID: %#"PRIx64"\n", obj->o_xid); - printf("Storage type: %s\n", o_storage_type_to_string(obj->o_type)); - printf("Type flags: %s\n", type_flags_string); - printf("Type: %s\n", type_string); - printf("Subtype: %s\n", subtype_string); + printf("Stored checksum: 0x%016" PRIx64 "\n", *(uint64_t*)obj); + printf("OID: 0x%" PRIx64 "\n", obj->o_oid); + printf("XID: 0x%" PRIx64 "\n", obj->o_xid); + printf("Storage type: %s\n", o_storage_type_to_string(obj->o_type)); + printf("Type flags: %s\n", type_flags_string); + printf("Type: %s\n", type_string); + printf("Subtype: %s\n", subtype_string); free(type_flags_string); free(type_string); diff --git a/include/drat/string/omap.c b/include/drat/string/omap.c index a808273..f4a25cc 100644 --- a/include/drat/string/omap.c +++ b/include/drat/string/omap.c @@ -61,7 +61,7 @@ void print_omap_phys(omap_phys_t* omap) { printf("- Storage type: %s\n", o_storage_type_to_string(omap->om_tree_type)); printf("- Type flags: %s\n", type_flags_string); printf("- Type: %s\n", type_string); - printf("- Object ID: %#"PRIx64"\n", omap->om_tree_oid); + printf("- Object ID: 0x%" PRIx64 "\n", omap->om_tree_oid); free(type_flags_string); free(type_string); @@ -71,15 +71,15 @@ void print_omap_phys(omap_phys_t* omap) { printf("- Storage type: %s\n", o_storage_type_to_string(omap->om_snapshot_tree_type)); printf("- Type flags: %s\n", type_flags_string); printf("- Type: %s\n", type_string); - printf("- Object ID: %#"PRIx64"\n", omap->om_snapshot_tree_oid); + printf("- Object ID: 0x%" PRIx64 "\n", omap->om_snapshot_tree_oid); free(type_flags_string); free(type_string); - printf("- Number of snapshots: %"PRIu32" snapshots\n", omap->om_snap_count); - printf("- Latest snapshot XID: %#"PRIx64"\n", omap->om_most_recent_snap); + printf("- Number of snapshots: %u snapshots\n", omap->om_snap_count); + printf("- Latest snapshot XID: 0x%" PRIx64 "\n", omap->om_most_recent_snap); printf("In-progress revert:\n"); - printf("- Minimum XID: %#"PRIx64"\n", omap->om_pending_revert_min); - printf("- Maximum XID: %#"PRIx64"\n", omap->om_pending_revert_max); + printf("- Minimum XID: 0x%" PRIx64 "\n", omap->om_pending_revert_min); + printf("- Maximum XID: 0x%" PRIx64 "\n", omap->om_pending_revert_max); } /** @@ -87,8 +87,8 @@ void print_omap_phys(omap_phys_t* omap) { * map key. */ void print_omap_key(omap_key_t* omap_key) { - printf(" - OID: %#"PRIx64"\n", omap_key->ok_oid); - printf(" - XID: %#"PRIx64"\n", omap_key->ok_xid); + printf(" - OID: 0x%" PRIx64 "\n", omap_key->ok_oid); + printf(" - XID: 0x%" PRIx64 "\n", omap_key->ok_xid); } /** @@ -97,6 +97,6 @@ void print_omap_key(omap_key_t* omap_key) { */ void print_omap_val(omap_val_t* omap_val) { // TODO: Print flags - printf(" - Object size: %"PRIu32" bytes\n", omap_val->ov_size); - printf(" - Object address in container: %#"PRIx64"\n", omap_val->ov_paddr); + printf(" - Object size: %u bytes\n", omap_val->ov_size); + printf(" - Object address in container: 0x%" PRIx64 "\n", omap_val->ov_paddr); } diff --git a/src/commands/explore-fs-tree.c b/src/commands/explore-fs-tree.c index 9cff3d3..e82bd14 100644 --- a/src/commands/explore-fs-tree.c +++ b/src/commands/explore-fs-tree.c @@ -54,9 +54,9 @@ int cmd_explore_fs_tree(int argc, char** argv) { // Capture paddr_t fs_root_addr; - bool parse_success = sscanf(argv[2], "0x%"SCNx64"", &fs_root_addr); + bool parse_success = sscanf(argv[2], "0x%" PRIx64 "", &fs_root_addr); if (!parse_success) { - parse_success = sscanf(argv[2], "%"SCNu64"", &fs_root_addr); + parse_success = sscanf(argv[2], "%" PRIu64, &fs_root_addr); } if (!parse_success) { fprintf(stderr, "%s is not a valid block address.\n", argv[2]); @@ -66,9 +66,9 @@ int cmd_explore_fs_tree(int argc, char** argv) { // Capture paddr_t omap_root_addr; - parse_success = sscanf(argv[3], "0x%"SCNx64"", &omap_root_addr); + parse_success = sscanf(argv[3], "0x%" PRIx64 "", &omap_root_addr); if (!parse_success) { - parse_success = sscanf(argv[3], "%"SCNu64"", &omap_root_addr); + parse_success = sscanf(argv[3], "%" PRIu64, &omap_root_addr); } if (!parse_success) { fprintf(stderr, "%s is not a valid block address.\n", argv[3]); @@ -88,7 +88,7 @@ int cmd_explore_fs_tree(int argc, char** argv) { printf("OK.\n\n"); // Read the specified root nodes - printf("Reading the file-system tree root node (block %#"PRIx64") ... ", fs_root_addr); + printf("Reading the file-system tree root node (block 0x%" PRIx64 ") ... ", fs_root_addr); btree_node_phys_t* fs_root_node = malloc(nx_block_size); if (!fs_root_node) { fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `fs_root_node`.\n"); @@ -108,7 +108,7 @@ int cmd_explore_fs_tree(int argc, char** argv) { } printf("\n"); - printf("Reading the object map root node (block %#"PRIx64") ... ", omap_root_addr); + printf("Reading the object map root node (block 0x%" PRIx64 ") ... ", omap_root_addr); btree_node_phys_t* omap_root_node = malloc(nx_block_size); if (!omap_root_node) { fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `omap_root_node`.\n"); @@ -166,7 +166,7 @@ int cmd_explore_fs_tree(int argc, char** argv) { printf("--------------------------------------------------------------------------------\n"); printf("\n"); - printf("\nNode has %"PRIu32" entries, as follows:\n", node->btn_nkeys); + printf("\nNode has %u entries, as follows:\n", node->btn_nkeys); assert(node->btn_nkeys > 0); kvloc_t* toc_entry = toc_start; @@ -175,11 +175,8 @@ int cmd_explore_fs_tree(int argc, char** argv) { uint8_t type = (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT; printf( - "- %3"PRIu32": %#15"PRIx64" = Virtual OID || %2"PRIu8" = %#1"PRIx8" = Type", - i, - hdr->obj_id_and_type & OBJ_ID_MASK, - type, - type + "- %3u: %#15llx = Virtual OID || %2u = %#1x = Type", + i, hdr->obj_id_and_type & OBJ_ID_MASK, type, type ); if (node->btn_flags & BTNODE_LEAF) { @@ -187,14 +184,11 @@ int cmd_explore_fs_tree(int argc, char** argv) { j_drec_hashed_key_t* key = hdr; j_drec_val_t* val = val_end - toc_entry->v.off; - printf( - " = `dentry` || Dentry Virtual OID = %#16"PRIx64" || Dentry name = %s", - val->file_id, key->name - ); + printf(" = `dentry` || Dentry Virtual OID = %#16" PRIx64 " || Dentry name = %s", val->file_id, key->name); } } else { oid_t* child_node_virt_oid = val_end - toc_entry->v.off; - printf(" || Target child node Virtual OID = %#16"PRIx64"", *child_node_virt_oid); + printf(" || Target child node Virtual OID = %#16" PRIx64 "", *child_node_virt_oid); omap_entry_t* child_node_omap_entry = get_btree_phys_omap_entry(omap_root_node, *child_node_virt_oid, (xid_t)(~0)); if (!child_node_omap_entry) { printf(" || UNRESOLVABLE"); @@ -205,7 +199,7 @@ int cmd_explore_fs_tree(int argc, char** argv) { return -1; } if (read_blocks(child_node, child_node_omap_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block %#" PRIx64 ".\n", child_node_omap_entry->val.ov_paddr); return -1; } @@ -223,11 +217,11 @@ int cmd_explore_fs_tree(int argc, char** argv) { } uint32_t entry_index; - printf("Choose an entry [0-%"PRIu32"]: ", node->btn_nkeys - 1); - scanf("%"SCNu32"", &entry_index); + printf("Choose an entry [0-%u]: ", node->btn_nkeys - 1); + scanf("%u", &entry_index); while (entry_index >= node->btn_nkeys) { - printf("Invalid choice; choose an entry [0-%"PRIu32"]: ", node->btn_nkeys - 1); - scanf("%"SCNu32"", &entry_index); + printf("Invalid choice; choose an entry [0-%u]: ", node->btn_nkeys - 1); + scanf("%u", &entry_index); } toc_entry = (kvloc_t*)toc_start + entry_index; @@ -254,9 +248,9 @@ int cmd_explore_fs_tree(int argc, char** argv) { ); j_key_t* hdr = fs_rec->data; - printf("Key size: %"PRIu16" bytes\n", fs_rec->key_len); - printf("Value size: %"PRIu16" bytes\n", fs_rec->val_len); - printf("ID and type field: %#016"PRIx64"\n", hdr->obj_id_and_type); + printf("Key size: %u bytes\n", fs_rec->key_len); + printf("Value size: %u bytes\n", fs_rec->val_len); + printf("ID and type field: 0x%016" PRIx64 "\n", hdr->obj_id_and_type); printf("\n"); switch ( (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT ) { @@ -333,22 +327,17 @@ int cmd_explore_fs_tree(int argc, char** argv) { // Else, read the corresponding child node into `node` and loop oid_t* child_node_virt_oid = val_end - toc_entry->v.off; - printf("Child node has Virtual OID %#"PRIx64".\n", *child_node_virt_oid); + printf("Child node has Virtual OID 0x%" PRIx64 ".\n", *child_node_virt_oid); omap_entry_t* child_node_omap_entry = get_btree_phys_omap_entry(omap_root_node, *child_node_virt_oid, fs_root_node->btn_o.o_xid); if (!child_node_omap_entry) { - printf( - "Need to descend to node with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64"," - " but the object map lists no objects with this Virtual OID.\n", - *child_node_virt_oid, - fs_root_node->btn_o.o_xid - ); + printf("Need to descend to node with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 ", but the object map lists no objects with this Virtual OID.\n", *child_node_virt_oid, fs_root_node->btn_o.o_xid); return 0; } - printf("The object map resolved this Virtual OID to block address %#"PRIx64". Reading ... ", child_node_omap_entry->val.ov_paddr); + printf("The object map resolved this Virtual OID to block address %#" PRIx64 ". Reading ... ", child_node_omap_entry->val.ov_paddr); if (read_blocks(node, child_node_omap_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", child_node_omap_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", child_node_omap_entry->val.ov_paddr); return -1; } diff --git a/src/commands/explore-omap-tree.c b/src/commands/explore-omap-tree.c index e8db7d7..9116a6d 100644 --- a/src/commands/explore-omap-tree.c +++ b/src/commands/explore-omap-tree.c @@ -49,9 +49,9 @@ int cmd_explore_omap_tree(int argc, char** argv) { char* nx_path = argv[1]; paddr_t root_node_block_addr; - bool parse_success = sscanf(argv[2], "0x%"SCNx64"", &root_node_block_addr); + bool parse_success = sscanf(argv[2], "0x%" PRIx64 "", &root_node_block_addr); if (!parse_success) { - parse_success = sscanf(argv[2], "%"SCNu64"", &root_node_block_addr); + parse_success = sscanf(argv[2], "%" PRIu64, &root_node_block_addr); } if (!parse_success) { fprintf(stderr, "%s is not a valid block address.\n", argv[2]); @@ -71,7 +71,7 @@ int cmd_explore_omap_tree(int argc, char** argv) { printf("OK.\n\n"); // Read the specified root node - printf("Reading block %#"PRIx64" ... ", root_node_block_addr); + printf("Reading block 0x%" PRIx64 " ... ", root_node_block_addr); btree_node_phys_t* root_node = malloc(nx_block_size); if (!root_node) { fprintf(stderr, "\nABORT: Could not allocate sufficient memory for `root_node`.\n"); @@ -123,7 +123,7 @@ int cmd_explore_omap_tree(int argc, char** argv) { return 0; } - printf("\nNode has %"PRIu32" entries, as follows:\n", node->btn_nkeys); + printf("\nNode has %u entries, as follows:\n", node->btn_nkeys); kvoff_t* toc_entry = toc_start; // Print mapped block's details if explroing a leaf node if (node->btn_flags & BTNODE_LEAF) { @@ -136,24 +136,24 @@ int cmd_explore_omap_tree(int argc, char** argv) { omap_key_t* key = key_start + toc_entry->k; omap_val_t* val = val_end - toc_entry->v; if (read_blocks(block, val->ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: read_blocks: Error reading block %#"PRIx64".\n", val->ov_paddr); + fprintf(stderr, "\nABORT: read_blocks: Error reading block %#" PRIx64 ".\n", val->ov_paddr); return -1; } printf( - "- %3"PRIu32":" - " OID = %#9"PRIx64"" - " || XID = %#9"PRIx64"" - " || Target block = %#9"PRIx64"" - " || Target's actual OID = %#9"PRIx64" (%s)" - " || Target's actual XID = %#9"PRIx64" (%s)\n", + "- %3u:" + " OID = %#9" PRIx64 "" + " || XID = %#9" PRIx64 "" + " || Target block = %#9" PRIx64 "" + " || Target's actual OID = %#9" PRIx64 " (%s)" + " || Target's actual XID = %#9" PRIx64 " (%s)\n", i, key->ok_oid, key->ok_xid, val->ov_paddr, - block->o_oid, block->o_oid == key->ok_oid ? "YES " : " NO", - block->o_xid, block->o_xid == key->ok_xid ? "YES " : " NO" + block->o_oid, (block->o_oid == key->ok_oid ? "YES " : " NO"), + block->o_xid, (block->o_xid == key->ok_xid ? "YES " : " NO") ); } free(block); @@ -163,9 +163,9 @@ int cmd_explore_omap_tree(int argc, char** argv) { omap_val_t* val = val_end - toc_entry->v; printf( - "- %3"PRIu32":" - " OID = %#9"PRIx64"" - " || XID = %#9"PRIx64"" + "- %3u:" + " OID = %#9" PRIx64 + " || XID = %#9" PRIx64 " || Target child node = %#9"PRIx64"\n", i, @@ -177,11 +177,11 @@ int cmd_explore_omap_tree(int argc, char** argv) { } uint32_t entry_index; - printf("Choose an entry [0-%"PRIu32"]: ", node->btn_nkeys - 1); - scanf("%"SCNu32"", &entry_index); + printf("Choose an entry [0-%u]: ", node->btn_nkeys - 1); + scanf("%u", &entry_index); while (entry_index >= node->btn_nkeys) { - printf("Invalid choice; choose an entry [0-%"PRIu32"]: ", node->btn_nkeys - 1); - scanf("%"SCNu32"", &entry_index); + printf("Invalid choice; choose an entry [0-%u]: ", node->btn_nkeys - 1); + scanf("%u", &entry_index); } toc_entry = (kvoff_t*)toc_start + entry_index; @@ -201,9 +201,9 @@ int cmd_explore_omap_tree(int argc, char** argv) { // Else, read the corresponding child node into `node` and loop paddr_t* child_node_addr = val_end - toc_entry->v; - printf("Child node resides at address %#"PRIx64". Reading ... ", *child_node_addr); + printf("Child node resides at address 0x%" PRIx64 ". Reading ... ", *child_node_addr); if (read_blocks(node, *child_node_addr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", *child_node_addr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", *child_node_addr); return -1; } diff --git a/src/commands/inspect.c b/src/commands/inspect.c index e2b498b..b32f4bf 100644 --- a/src/commands/inspect.c +++ b/src/commands/inspect.c @@ -66,7 +66,7 @@ int cmd_inspect(int argc, char** argv) { } printf("OK.\nSimulating a mount of the APFS container.\n"); - printf("Reading container superblock at address 0x0, assuming default block size of %"PRIu32" bytes ... ", nx_block_size); + printf("Reading container superblock at address 0x0, assuming default block size of %d bytes ... ", NX_DEFAULT_BLOCK_SIZE); nx_superblock_t* nxsb = malloc(nx_block_size); if (!nxsb) { fprintf(stderr, "ABORT: Could not allocate sufficient memory for `nxsb`.\n"); @@ -79,7 +79,7 @@ int cmd_inspect(int argc, char** argv) { if (nx_block_size != nxsb->nx_block_size) { nx_block_size = nxsb->nx_block_size; - printf("Actual block size stated in container superblock is %"PRIu32" bytes; re-reading block 0x0 using new block size ... ", nx_block_size); + printf("Actual block size stated in container superblock is %d bytes; re-reading block 0x0 using new block size ... ", nx_block_size); nxsb = realloc(nxsb, nx_block_size); if (!nxsb) { fprintf(stderr, "ABORT: Could not allocate sufficient memory for `nxsb`.\n"); @@ -114,22 +114,22 @@ int cmd_inspect(int argc, char** argv) { printf("Locating the checkpoint descriptor area:\n"); uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - printf("- Its length is %"PRIu32" blocks.\n", xp_desc_blocks); + printf("- Its length is %u blocks.\n", xp_desc_blocks); char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); return -1; } if (nxsb->nx_xp_desc_blocks >> 31) { printf("- It is not contiguous.\n"); - printf("- The Physical OID of the B-tree representing it is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + printf("- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); printf("END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous return 0; } else { printf("- It is contiguous.\n"); - printf("- The address of its first block is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + printf("- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); printf("Loading the checkpoint descriptor area into memory ... "); if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { @@ -148,13 +148,13 @@ int cmd_inspect(int argc, char** argv) { for (uint32_t i = 0; i < xp_desc_blocks; i++) { if (!is_cksum_valid(xp_desc[i])) { - printf("- !! APFS WARNING !! Block at index %"PRIu32" within this area failed checksum validation. Skipping it.\n", i); + printf("- !! APFS WARNING !! Block at index %u within this area failed checksum validation. Skipping it.\n", i); continue; } if (is_nx_superblock(xp_desc[i])) { if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - printf("- !! APFS WARNING !! Container superblock at index %"PRIu32" within this area is malformed; incorrect magic number. Skipping it.\n", i); + printf("- !! APFS WARNING !! Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); continue; } @@ -166,13 +166,13 @@ int cmd_inspect(int argc, char** argv) { xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; } } else if (!is_checkpoint_map_phys(xp_desc[i])) { - printf("- !! APFS ERROR !! Block at index %"PRIu32" within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + printf("- !! APFS ERROR !! Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); continue; } } if (xid_latest_nx == 0) { - printf("No container superblock with an XID that doesn't exceed %#"PRIx64" exists in the checkpoint descriptor area.\n", max_xid); + printf("No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); return 0; } @@ -181,19 +181,13 @@ int cmd_inspect(int argc, char** argv) { // This also lets us avoid repeatedly casting to `nx_superblock_t*`. memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - printf("- It lies at index %"PRIu32" within the checkpoint descriptor area.\n", i_latest_nx); + printf("- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); printf("\nDetails of this container superblock:\n"); printf("--------------------------------------------------------------------------------\n"); print_nx_superblock(nxsb); printf("--------------------------------------------------------------------------------\n"); - - printf( - "- The corresponding checkpoint starts at index %"PRIu32" within the" - " checkpoint descriptor area, and spans %"PRIu32" blocks.\n\n", - nxsb->nx_xp_desc_index, - nxsb->nx_xp_desc_len - ); + printf("- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); // Copy the contents of the checkpoint we are currently considering to its // own array for easy access. The checkpoint descriptor area is a ring @@ -249,7 +243,7 @@ int cmd_inspect(int argc, char** argv) { xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; } } - printf("- There are %"PRIu32" checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + printf("- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); printf("Reading the Ephemeral objects used by this checkpoint ... "); char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); @@ -263,7 +257,7 @@ int cmd_inspect(int argc, char** argv) { checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting for (uint32_t j = 0; j < xp_map->cpm_count; j++) { if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", xp_map->cpm_map[j].cpm_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); return -1; } num_read++; @@ -297,7 +291,7 @@ int cmd_inspect(int argc, char** argv) { } printf("\n"); - printf("The container superblock states that the container object map has Physical OID %#"PRIx64".\n", nxsb->nx_omap_oid); + printf("The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); printf("Loading the container object map ... "); omap_phys_t* nx_omap = malloc(nx_block_size); @@ -336,7 +330,7 @@ int cmd_inspect(int argc, char** argv) { return -1; } if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", nx_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); return -1; } printf("OK.\n"); @@ -361,9 +355,9 @@ int cmd_inspect(int argc, char** argv) { } num_file_systems++; } - printf("The container superblock lists %"PRIu32" APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + printf("The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); for (uint32_t i = 0; i < num_file_systems; i++) { - printf("- %#"PRIx64"\n", nxsb->nx_fs_oid[i]); + printf("- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); } printf("\n"); @@ -376,11 +370,11 @@ int cmd_inspect(int argc, char** argv) { for (uint32_t i = 0; i < num_file_systems; i++) { omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); if (!fs_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64" exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); return -1; } if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); return -1; } } @@ -389,7 +383,7 @@ int cmd_inspect(int argc, char** argv) { printf("Validating the APFS volume superblocks ... "); for (uint32_t i = 0; i < num_file_systems; i++) { if (!is_cksum_valid(apsbs + i)) { - printf("FAILED.\n- The checksum of the APFS volume with OID %#"PRIx64" did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + printf("FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed printf("END: Handling of this case has not yet been implemented.\n"); @@ -397,7 +391,7 @@ int cmd_inspect(int argc, char** argv) { } if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - printf("FAILED.\n- The magic string of the APFS volume with OID %#"PRIx64" did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); + printf("FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed printf("END: Handling of this case has not yet been implemented.\n"); @@ -420,10 +414,10 @@ int cmd_inspect(int argc, char** argv) { printf("\n"); for (uint32_t i = 0; i < num_file_systems; i++) { apfs_superblock_t* apsb = apsbs + i; - printf("Simulating a mount of volume %"PRIu32" (%s).\n", i, apsb->apfs_volname); + printf("Simulating a mount of volume %u (%s).\n", i, apsb->apfs_volname); printf("\n"); - printf("The volume object map has Physical OID %#"PRIx64".\n", apsb->apfs_omap_oid); + printf("The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); printf("Reading the volume object map ... "); omap_phys_t* fs_omap = malloc(nx_block_size); @@ -432,7 +426,7 @@ int cmd_inspect(int argc, char** argv) { return -1; } if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); return -1; } printf("OK.\n"); @@ -465,7 +459,7 @@ int cmd_inspect(int argc, char** argv) { return -1; } if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); return -1; } printf("OK.\n"); @@ -483,14 +477,14 @@ int cmd_inspect(int argc, char** argv) { printf("--------------------------------------------------------------------------------\n"); printf("\n"); - printf("The file-system tree root for this volume has Virtual OID %#"PRIx64".\n", apsb->apfs_root_tree_oid); + printf("The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); printf("Looking up this Virtual OID in the volume object map ... "); omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); if (!fs_root_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64" exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); return -1; } - printf("corresponding block address is %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + printf("corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); printf("Reading ... "); btree_node_phys_t* fs_root_btree = malloc(nx_block_size); @@ -499,7 +493,7 @@ int cmd_inspect(int argc, char** argv) { return -1; } if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); return -1; } free(fs_root_entry); // No longer need the block address of the file-system root. @@ -532,11 +526,14 @@ int cmd_inspect(int argc, char** argv) { for (size_t j = 0; j < NUM_QUERIES; j++) { oid_t fs_oid = fs_oid_queries[j]; - printf("\nResults for FSOID %#"PRIx64":\n", fs_oid); + // printf("Search results for file-system records with Virtual OID 0x%" PRIx64 ":\n", fs_oid); + // printf("--------------------------------------------------------------------------------\n"); + + printf("\n%#" PRIx64 ":\n", fs_oid); j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, nxsb->nx_o.o_xid); if (!fs_records) { - printf("No records found with OID %#"PRIx64".\n", fs_oid); + printf("No records found with OID 0x%" PRIx64 ".\n", fs_oid); return -1; } @@ -545,14 +542,12 @@ int cmd_inspect(int argc, char** argv) { for (j_rec_t** fs_rec_cursor = fs_records; *fs_rec_cursor; fs_rec_cursor++) { num_records++; j_rec_t* fs_rec = *fs_rec_cursor; + j_key_t* hdr = fs_rec->data; - - // TODO: Debugging code - // printf("Key size: %"PRIu16" bytes\n", fs_rec->key_len); - // printf("Value size: %"PRIu16" bytes\n", fs_rec->val_len); - // printf("ID and type field: %#016"PRIx64"\n", hdr->obj_id_and_type); + // printf("Key size: %u bytes\n", fs_rec->key_len); + // printf("Value size: %u bytes\n", fs_rec->val_len); + // printf("ID and type field: 0x%016" PRIx64 "\n", hdr->obj_id_and_type); // printf("\n"); - printf("- "); switch ( (hdr->obj_id_and_type & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT ) { @@ -591,8 +586,8 @@ int cmd_inspect(int argc, char** argv) { j_dstream_id_key_t* key = fs_rec->data; j_dstream_id_val_t* val = fs_rec->data + fs_rec->key_len; printf("DSTREAM ID " - " || file ID = %#8"PRIx64"" - " || ref. count = %"PRIu32"", + " || file ID = %#8llx" + " || ref. count = %u", key->hdr.obj_id_and_type & OBJ_ID_MASK, val->refcnt @@ -611,10 +606,10 @@ int cmd_inspect(int argc, char** argv) { uint64_t extent_length_blocks = extent_length_bytes / nx_block_size; printf( "FILE EXTENT" - " || file ID = %#8"PRIx64"" - " || log. addr. = %#10"PRIx64"" - " || length = %8"PRIu64" B = %#10"PRIx64" B = %5"PRIu64" blocks = %#7"PRIx64" blocks" - " || phys. block = %#10"PRIx64"", + " || file ID = %#8llx" + " || log. addr. = %#10" PRIx64 "" + " || length = %8" PRIu64 " B = %#10" PRIx64 " B = %5" PRIu64 " blocks = %#7" PRIx64 " blocks" + " || phys. block = %#10" PRIx64 "", key->hdr.obj_id_and_type & OBJ_ID_MASK, key->logical_addr, @@ -627,7 +622,7 @@ int cmd_inspect(int argc, char** argv) { j_drec_hashed_key_t* key = fs_rec->data; j_drec_val_t* val = fs_rec->data + fs_rec->key_len; printf("DIR REC" - " || target ID = %#8"PRIx64"" + " || target ID = %#8" PRIx64 "" " || name = %s", val->file_id, @@ -654,11 +649,11 @@ int cmd_inspect(int argc, char** argv) { } break; case APFS_TYPE_INVALID: printf("INVALID"); - // fprintf(stderr, "- A record with OID %#"PRIx64" has an invalid type.\n", fs_oid); + // fprintf(stderr, "- A record with OID 0x%" PRIx64 " has an invalid type.\n", fs_oid); break; default: printf("(unknown)"); - fprintf(stderr, "- A record with OID %#"PRIx64" has an unknown type.\n", fs_oid); + fprintf(stderr, "- A record with OID 0x%" PRIx64 " has an unknown type.\n", fs_oid); break; } @@ -666,7 +661,7 @@ int cmd_inspect(int argc, char** argv) { printf("\n"); } - // printf("- Found %zu records with FSOID %#"PRIx64".\n", num_records, fs_oid); + // printf("- Found %lu records with Virtual OID 0x%" PRIx64 ".\n", num_records, fs_oid); free_j_rec_array(fs_records); } diff --git a/src/commands/list.c b/src/commands/list.c index 04a8f6b..015f274 100644 --- a/src/commands/list.c +++ b/src/commands/list.c @@ -61,7 +61,7 @@ int cmd_list(int argc, char** argv) { nx_path = argv[1]; uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%"SCNu32"", &volume_id); + bool parse_success = sscanf(argv[2], "%u", &volume_id); if (!parse_success) { fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); print_usage(argc, argv); @@ -111,22 +111,22 @@ int cmd_list(int argc, char** argv) { fprintf(stderr, "Locating the checkpoint descriptor area:\n"); uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); return -1; } if (nxsb->nx_xp_desc_blocks >> 31) { fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous return 0; } else { fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { @@ -145,13 +145,13 @@ int cmd_list(int argc, char** argv) { for (uint32_t i = 0; i < xp_desc_blocks; i++) { if (!is_cksum_valid(xp_desc[i])) { - fprintf(stderr, "- Block at index %"PRIu32" within this area failed checksum validation. Skipping it.\n", i); + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); continue; } if (is_nx_superblock(xp_desc[i])) { if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - fprintf(stderr, "- Container superblock at index %"PRIu32" within this area is malformed; incorrect magic number. Skipping it.\n", i); + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); continue; } @@ -163,13 +163,13 @@ int cmd_list(int argc, char** argv) { xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; } } else if (!is_checkpoint_map_phys(xp_desc[i])) { - fprintf(stderr, "- Block at index %"PRIu32" within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); continue; } } if (xid_latest_nx == 0) { - fprintf(stderr, "No container superblock with an XID that doesn't exceed %#"PRIx64" exists in the checkpoint descriptor area.\n", max_xid); + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); return 0; } @@ -178,14 +178,8 @@ int cmd_list(int argc, char** argv) { // This also lets us avoid repeatedly casting to `nx_superblock_t*`. memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - fprintf(stderr, "- It lies at index %"PRIu32" within the checkpoint descriptor area.\n", i_latest_nx); - fprintf( - stderr, - "- The corresponding checkpoint starts at index %"PRIu32" within the" - " checkpoint descriptor area, and spans %"PRIu32" blocks.\n\n", - nxsb->nx_xp_desc_index, - nxsb->nx_xp_desc_len - ); + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); // Copy the contents of the checkpoint we are currently considering to its // own array for easy access. The checkpoint descriptor area is a ring @@ -226,7 +220,7 @@ int cmd_list(int argc, char** argv) { xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; } } - fprintf(stderr, "- There are %"PRIu32" checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); @@ -240,7 +234,7 @@ int cmd_list(int argc, char** argv) { checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting for (uint32_t j = 0; j < xp_map->cpm_count; j++) { if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", xp_map->cpm_map[j].cpm_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); return -1; } num_read++; @@ -266,7 +260,7 @@ int cmd_list(int argc, char** argv) { free(xp); free(xp_desc); - fprintf(stderr, "The container superblock states that the container object map has Physical OID %#"PRIx64".\n", nxsb->nx_omap_oid); + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); fprintf(stderr, "Loading the container object map ... "); omap_phys_t* nx_omap = malloc(nx_block_size); @@ -295,7 +289,7 @@ int cmd_list(int argc, char** argv) { return -1; } if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", nx_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -314,9 +308,9 @@ int cmd_list(int argc, char** argv) { } num_file_systems++; } - fprintf(stderr, "The container superblock lists %"PRIu32" APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "- %#"PRIx64"\n", nxsb->nx_fs_oid[i]); + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); } fprintf(stderr, "\n"); @@ -329,11 +323,11 @@ int cmd_list(int argc, char** argv) { for (uint32_t i = 0; i < num_file_systems; i++) { omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); if (!fs_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64" exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); return -1; } if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); return -1; } } @@ -342,12 +336,7 @@ int cmd_list(int argc, char** argv) { fprintf(stderr, "Validating the APFS volume superblocks ... "); for (uint32_t i = 0; i < num_file_systems; i++) { if (!is_cksum_valid(apsbs + i)) { - fprintf( - stderr, - "FAILED.\n- The checksum of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); @@ -355,12 +344,7 @@ int cmd_list(int argc, char** argv) { } if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - fprintf( - stderr, - "FAILED.\n- The magic string of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); @@ -371,16 +355,16 @@ int cmd_list(int argc, char** argv) { fprintf(stderr, "\n Volume list\n================\n"); for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "%2"PRIu32": %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); } if (volume_id >= num_file_systems) { - fprintf(stderr, "The specified volume ID (%"PRIu32") does not exist in the list above. Exiting.\n", volume_id); + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); return 0; } apfs_superblock_t* apsb = apsbs + volume_id; - fprintf(stderr, "The volume object map has Physical OID %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); fprintf(stderr, "Reading the volume object map ... "); omap_phys_t* fs_omap = malloc(nx_block_size); @@ -389,7 +373,7 @@ int cmd_list(int argc, char** argv) { return -1; } if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -413,7 +397,7 @@ int cmd_list(int argc, char** argv) { return -1; } if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -429,16 +413,10 @@ int cmd_list(int argc, char** argv) { fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); if (!fs_root_entry) { - fprintf( - stderr, - "\nABORT: No objects with Virtual OID %#"PRIx64" and" - " maximum XID %#"PRIx64" exist in `fs_omap_btree`.\n", - apsb->apfs_root_tree_oid, - apsb->apfs_o.o_xid - ); + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); return -1; } - fprintf(stderr, "corresponding block address is %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); fprintf(stderr, "Reading ... "); btree_node_phys_t* fs_root_btree = malloc(nx_block_size); @@ -447,7 +425,7 @@ int cmd_list(int argc, char** argv) { return -1; } if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); return -1; } free(fs_root_entry); // No longer need the block address of the file-system root. @@ -463,7 +441,7 @@ int cmd_list(int argc, char** argv) { j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); if (!fs_records) { - fprintf(stderr, "No records found with OID %#"PRIx64".\n", fs_oid); + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); return -1; } @@ -510,7 +488,7 @@ int cmd_list(int argc, char** argv) { fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); } - fprintf(stderr, "\nRecords for file-system object %#"PRIx64" -- `%s` --\n", fs_oid, path_stack); + fprintf(stderr, "\nRecords for file-system object %#" PRIx64 " -- `%s` --\n", fs_oid, path_stack); // `fs_records` now contains the records for the item at the specified path print_fs_records(fs_records); diff --git a/src/commands/read.c b/src/commands/read.c index 004946d..6981b71 100644 --- a/src/commands/read.c +++ b/src/commands/read.c @@ -44,9 +44,9 @@ int cmd_read(int argc, char** argv) { } char* nx_path = argv[1]; paddr_t nx_block_addr = 0x0; - bool parse_success = sscanf(argv[2], "0x%"SCNx64"", &nx_block_addr); + bool parse_success = sscanf(argv[2], "0x%" PRIx64 "", &nx_block_addr); if (!parse_success) { - parse_success = sscanf(argv[2], "%"SCNu64"", &nx_block_addr); + parse_success = sscanf(argv[2], "%" PRIu64 "", &nx_block_addr); } if (!parse_success) { printf("%s is not a valid block address.\n", argv[2]); @@ -65,7 +65,7 @@ int cmd_read(int argc, char** argv) { } printf("OK.\n\n"); - printf("Reading block %#"PRIx64" ... ", nx_block_addr); + printf("Reading block 0x%" PRIx64 " ... ", nx_block_addr); obj_phys_t* block = malloc(nx_block_size); if (read_blocks(block, nx_block_addr, 1) == 0) { printf("\nEND: Block index %s does not exist in `%s`.\n", argv[2], nx_path); @@ -79,7 +79,7 @@ int cmd_read(int argc, char** argv) { } printf("\n"); - printf("Details of block %#"PRIx64":\n", nx_block_addr); + printf("Details of block 0x%" PRIx64 ":\n", nx_block_addr); printf("--------------------------------------------------------------------------------\n"); switch (block->o_type & OBJECT_TYPE_MASK) { case OBJECT_TYPE_NX_SUPERBLOCK: diff --git a/src/commands/recover.c b/src/commands/recover.c index 5c56378..846c8ba 100644 --- a/src/commands/recover.c +++ b/src/commands/recover.c @@ -62,7 +62,7 @@ int cmd_recover(int argc, char** argv) { nx_path = argv[1]; uint32_t volume_id; - bool parse_success = sscanf(argv[2], "%"SCNu32"", &volume_id); + bool parse_success = sscanf(argv[2], "%u", &volume_id); if (!parse_success) { fprintf(stderr, "%s is not a valid volume ID.\n", argv[2]); print_usage(argc, argv); @@ -112,22 +112,22 @@ int cmd_recover(int argc, char** argv) { fprintf(stderr, "Locating the checkpoint descriptor area:\n"); uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - fprintf(stderr, "- Its length is %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "- Its length is %u blocks.\n", xp_desc_blocks); char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); return -1; } if (nxsb->nx_xp_desc_blocks >> 31) { fprintf(stderr, "- It is not contiguous.\n"); - fprintf(stderr, "- The Physical OID of the B-tree representing it is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); fprintf(stderr, "END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous return -1; } else { fprintf(stderr, "- It is contiguous.\n"); - fprintf(stderr, "- The address of its first block is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + fprintf(stderr, "- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); fprintf(stderr, "Loading the checkpoint descriptor area into memory ... "); if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { @@ -146,13 +146,13 @@ int cmd_recover(int argc, char** argv) { for (uint32_t i = 0; i < xp_desc_blocks; i++) { if (!is_cksum_valid(xp_desc[i])) { - fprintf(stderr, "- Block at index %"PRIu32" within this area failed checksum validation. Skipping it.\n", i); + fprintf(stderr, "- Block at index %u within this area failed checksum validation. Skipping it.\n", i); continue; } if (is_nx_superblock(xp_desc[i])) { if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - fprintf(stderr, "- Container superblock at index %"PRIu32" within this area is malformed; incorrect magic number. Skipping it.\n", i); + fprintf(stderr, "- Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); continue; } @@ -164,13 +164,13 @@ int cmd_recover(int argc, char** argv) { xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; } } else if (!is_checkpoint_map_phys(xp_desc[i])) { - fprintf(stderr, "- Block at index %"PRIu32" within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + fprintf(stderr, "- Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); continue; } } if (xid_latest_nx == 0) { - fprintf(stderr, "No container superblock with an XID that doesn't exceed %#"PRIx64" exists in the checkpoint descriptor area.\n", max_xid); + fprintf(stderr, "No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); return -1; } @@ -179,14 +179,8 @@ int cmd_recover(int argc, char** argv) { // This also lets us avoid repeatedly casting to `nx_superblock_t*`. memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - fprintf(stderr, "- It lies at index %"PRIu32" within the checkpoint descriptor area.\n", i_latest_nx); - fprintf( - stderr, - "- The corresponding checkpoint starts at index %"PRIu32" within" - " the checkpoint descriptor area, and spans %"PRIu32" blocks.\n\n", - nxsb->nx_xp_desc_index, - nxsb->nx_xp_desc_len - ); + fprintf(stderr, "- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); + fprintf(stderr, "- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); // Copy the contents of the checkpoint we are currently considering to its // own array for easy access. The checkpoint descriptor area is a ring @@ -227,7 +221,7 @@ int cmd_recover(int argc, char** argv) { xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; } } - fprintf(stderr, "- There are %"PRIu32" checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + fprintf(stderr, "- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); fprintf(stderr, "Reading the Ephemeral objects used by this checkpoint ... "); char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); @@ -241,7 +235,7 @@ int cmd_recover(int argc, char** argv) { checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting for (uint32_t j = 0; j < xp_map->cpm_count; j++) { if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", xp_map->cpm_map[j].cpm_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); return -1; } num_read++; @@ -267,7 +261,7 @@ int cmd_recover(int argc, char** argv) { free(xp); free(xp_desc); - fprintf(stderr, "The container superblock states that the container object map has Physical OID %#"PRIx64".\n", nxsb->nx_omap_oid); + fprintf(stderr, "The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); fprintf(stderr, "Loading the container object map ... "); omap_phys_t* nx_omap = malloc(nx_block_size); @@ -296,7 +290,7 @@ int cmd_recover(int argc, char** argv) { return -1; } if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", nx_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -315,9 +309,9 @@ int cmd_recover(int argc, char** argv) { } num_file_systems++; } - fprintf(stderr, "The container superblock lists %"PRIu32" APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + fprintf(stderr, "The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "- %#"PRIx64"\n", nxsb->nx_fs_oid[i]); + fprintf(stderr, "- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); } fprintf(stderr, "\n"); @@ -330,11 +324,11 @@ int cmd_recover(int argc, char** argv) { for (uint32_t i = 0; i < num_file_systems; i++) { omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); if (!fs_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64" exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); return -1; } if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block %#" PRIx64 ".\n", fs_entry->val.ov_paddr); return -1; } } @@ -343,12 +337,7 @@ int cmd_recover(int argc, char** argv) { fprintf(stderr, "Validating the APFS volume superblocks ... "); for (uint32_t i = 0; i < num_file_systems; i++) { if (!is_cksum_valid(apsbs + i)) { - fprintf( - stderr, - "FAILED.\n- The checksum of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + fprintf(stderr, "FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); @@ -356,12 +345,7 @@ int cmd_recover(int argc, char** argv) { } if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - fprintf( - stderr, - "FAILED.\n- The magic string of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + fprintf(stderr, "FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed fprintf(stderr, "END: Handling of this case has not yet been implemented.\n"); @@ -372,16 +356,16 @@ int cmd_recover(int argc, char** argv) { fprintf(stderr, "\n Volume list\n================\n"); for (uint32_t i = 0; i < num_file_systems; i++) { - fprintf(stderr, "%2"PRIu32": %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); + fprintf(stderr, "%2u: %s\n", i, ((apfs_superblock_t*)(apsbs + i))->apfs_volname); } if (volume_id >= num_file_systems) { - fprintf(stderr, "The specified volume ID (%"PRIu32") does not exist in the list above. Exiting.\n", volume_id); + fprintf(stderr, "The specified volume ID (%u) does not exist in the list above. Exiting.\n", volume_id); return -1; } apfs_superblock_t* apsb = apsbs + volume_id; - fprintf(stderr, "The volume object map has Physical OID %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); fprintf(stderr, "Reading the volume object map ... "); omap_phys_t* fs_omap = malloc(nx_block_size); @@ -390,7 +374,7 @@ int cmd_recover(int argc, char** argv) { return -1; } if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -414,7 +398,7 @@ int cmd_recover(int argc, char** argv) { return -1; } if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); return -1; } fprintf(stderr, "OK.\n"); @@ -426,14 +410,14 @@ int cmd_recover(int argc, char** argv) { fprintf(stderr, "OK.\n"); } - fprintf(stderr, "The file-system tree root for this volume has Virtual OID %#"PRIx64".\n", apsb->apfs_root_tree_oid); + fprintf(stderr, "The file-system tree root for this volume has Virtual OID 0x%" PRIx64 ".\n", apsb->apfs_root_tree_oid); fprintf(stderr, "Looking up this Virtual OID in the volume object map ... "); omap_entry_t* fs_root_entry = get_btree_phys_omap_entry(fs_omap_btree, apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); if (!fs_root_entry) { - fprintf(stderr, "\nABORT: No objects with Virtual OID %#"PRIx64" and maximum XID %#"PRIx64" exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); + fprintf(stderr, "\nABORT: No objects with Virtual OID %#" PRIx64 " and maximum XID %#" PRIx64 " exist in `fs_omap_btree`.\n", apsb->apfs_root_tree_oid, apsb->apfs_o.o_xid); return -1; } - fprintf(stderr, "corresponding block address is %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + fprintf(stderr, "corresponding block address is 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); fprintf(stderr, "Reading ... "); btree_node_phys_t* fs_root_btree = malloc(nx_block_size); @@ -442,7 +426,7 @@ int cmd_recover(int argc, char** argv) { return -1; } if (read_blocks(fs_root_btree, fs_root_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_root_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_root_entry->val.ov_paddr); return -1; } free(fs_root_entry); // No longer need the block address of the file-system root. @@ -458,7 +442,7 @@ int cmd_recover(int argc, char** argv) { j_rec_t** fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); if (!fs_records) { - fprintf(stderr, "No records found with OID %#"PRIx64".\n", fs_oid); + fprintf(stderr, "No records found with OID 0x%" PRIx64 ".\n", fs_oid); return -1; } @@ -505,7 +489,7 @@ int cmd_recover(int argc, char** argv) { fs_records = get_fs_records(fs_omap_btree, fs_root_btree, fs_oid, (xid_t)(~0) ); } - fprintf(stderr, "\nRecords for file-system object %#"PRIx64" -- `%s` --\n", fs_oid, path_stack); + fprintf(stderr, "\nRecords for file-system object %#" PRIx64 " -- `%s` --\n", fs_oid, path_stack); // `fs_records` now contains the records for the item at the specified path print_fs_records(fs_records); @@ -552,7 +536,7 @@ int cmd_recover(int argc, char** argv) { uint64_t extent_len_blocks = (val->len_and_flags & J_FILE_EXTENT_LEN_MASK) / nx_block_size; for (uint64_t i = 0; i < extent_len_blocks; i++, block_addr++) { if (read_blocks(buffer, block_addr, 1) != 1) { - fprintf(stderr, "\n\nEncountered an error reading block %#"PRIx64" (block %"PRIu64" of %"PRIu64"). Exiting.\n\n", block_addr, i+1, extent_len_blocks); + fprintf(stderr, "\n\nEncountered an error reading block %#" PRIx64 " (block %" PRIu64 " of %" PRIu64 "). Exiting.\n\n", block_addr, i+1, extent_len_blocks); return -1; } @@ -561,7 +545,7 @@ int cmd_recover(int argc, char** argv) { bytes_to_write = bytes_remaining; } if (fwrite(buffer, bytes_to_write, 1, stdout) != 1) { - fprintf(stderr, "\n\nEncountered an error writing block %"PRIu64" of %"PRIu64" to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); + fprintf(stderr, "\n\nEncountered an error writing block %" PRIu64 " of %" PRIu64 " to `stdout`. Exiting.\n\n", i+1, extent_len_blocks); return -1; } bytes_remaining -= bytes_to_write; diff --git a/src/commands/resolver.c b/src/commands/resolver.c index 34986c1..f198348 100644 --- a/src/commands/resolver.c +++ b/src/commands/resolver.c @@ -107,22 +107,22 @@ int cmd_resolver(int argc, char** argv) { printf("Locating the checkpoint descriptor area:\n"); uint32_t xp_desc_blocks = nxsb->nx_xp_desc_blocks & ~(1 << 31); - printf("- Its length is %"PRIu32" blocks.\n", xp_desc_blocks); + printf("- Its length is %u blocks.\n", xp_desc_blocks); char (*xp_desc)[nx_block_size] = malloc(xp_desc_blocks * nx_block_size); if (!xp_desc) { - fprintf(stderr, "ABORT: Could not allocate sufficient memory for %"PRIu32" blocks.\n", xp_desc_blocks); + fprintf(stderr, "ABORT: Could not allocate sufficient memory for %u blocks.\n", xp_desc_blocks); return -1; } if (nxsb->nx_xp_desc_blocks >> 31) { printf("- It is not contiguous.\n"); - printf("- The Physical OID of the B-tree representing it is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + printf("- The Physical OID of the B-tree representing it is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); printf("END: The ability to handle this case has not yet been implemented.\n\n"); // TODO: implement case when xp_desc area is not contiguous return 0; } else { printf("- It is contiguous.\n"); - printf("- The address of its first block is %#"PRIx64".\n", nxsb->nx_xp_desc_base); + printf("- The address of its first block is 0x%" PRIx64 ".\n", nxsb->nx_xp_desc_base); printf("Loading the checkpoint descriptor area into memory ... "); if (read_blocks(xp_desc, nxsb->nx_xp_desc_base, xp_desc_blocks) != xp_desc_blocks) { @@ -141,13 +141,13 @@ int cmd_resolver(int argc, char** argv) { for (uint32_t i = 0; i < xp_desc_blocks; i++) { if (!is_cksum_valid(xp_desc[i])) { - printf("- !! APFS WARNING !! Block at index %"PRIu32" within this area failed checksum validation. Skipping it.\n", i); + printf("- !! APFS WARNING !! Block at index %u within this area failed checksum validation. Skipping it.\n", i); continue; } if (is_nx_superblock(xp_desc[i])) { if ( ((nx_superblock_t*)xp_desc[i])->nx_magic != NX_MAGIC ) { - printf("- !! APFS WARNING !! Container superblock at index %"PRIu32" within this area is malformed; incorrect magic number. Skipping it.\n", i); + printf("- !! APFS WARNING !! Container superblock at index %u within this area is malformed; incorrect magic number. Skipping it.\n", i); continue; } @@ -159,13 +159,13 @@ int cmd_resolver(int argc, char** argv) { xid_latest_nx = ((nx_superblock_t*)xp_desc[i])->nx_o.o_xid; } } else if (!is_checkpoint_map_phys(xp_desc[i])) { - printf("- !! APFS ERROR !! Block at index %"PRIu32" within this area is not a container superblock or checkpoint map. Skipping it.\n", i); + printf("- !! APFS ERROR !! Block at index %u within this area is not a container superblock or checkpoint map. Skipping it.\n", i); continue; } } if (xid_latest_nx == 0) { - printf("No container superblock with an XID that doesn't exceed %#"PRIx64" exists in the checkpoint descriptor area.\n", max_xid); + printf("No container superblock with an XID that doesn't exceed 0x%" PRIx64 " exists in the checkpoint descriptor area.\n", max_xid); return 0; } @@ -174,18 +174,13 @@ int cmd_resolver(int argc, char** argv) { // This also lets us avoid repeatedly casting to `nx_superblock_t*`. memcpy(nxsb, xp_desc[i_latest_nx], sizeof(nx_superblock_t)); - printf("- It lies at index %"PRIu32" within the checkpoint descriptor area.\n", i_latest_nx); + printf("- It lies at index %u within the checkpoint descriptor area.\n", i_latest_nx); printf("\nDetails of this container superblock:\n"); printf("--------------------------------------------------------------------------------\n"); print_nx_superblock(nxsb); printf("--------------------------------------------------------------------------------\n"); - printf( - "- The corresponding checkpoint starts at index %"PRIu32" within the" - " checkpoint descriptor area, and spans %"PRIu32" blocks.\n\n", - nxsb->nx_xp_desc_index, - nxsb->nx_xp_desc_len - ); + printf("- The corresponding checkpoint starts at index %u within the checkpoint descriptor area, and spans %u blocks.\n\n", nxsb->nx_xp_desc_index, nxsb->nx_xp_desc_len); // Copy the contents of the checkpoint we are currently considering to its // own array for easy access. The checkpoint descriptor area is a ring @@ -241,7 +236,7 @@ int cmd_resolver(int argc, char** argv) { xp_obj_len += ((checkpoint_map_phys_t*)xp[i])->cpm_count; } } - printf("- There are %"PRIu32" checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); + printf("- There are %u checkpoint-mappings in this checkpoint.\n\n", xp_obj_len); printf("Reading the Ephemeral objects used by this checkpoint ... "); char (*xp_obj)[nx_block_size] = malloc(xp_obj_len * nx_block_size); @@ -255,7 +250,7 @@ int cmd_resolver(int argc, char** argv) { checkpoint_map_phys_t* xp_map = xp[i]; // Avoid lots of casting for (uint32_t j = 0; j < xp_map->cpm_count; j++) { if (read_blocks(xp_obj[num_read], xp_map->cpm_map[j].cpm_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", xp_map->cpm_map[j].cpm_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", xp_map->cpm_map[j].cpm_paddr); return -1; } num_read++; @@ -289,7 +284,7 @@ int cmd_resolver(int argc, char** argv) { } printf("\n"); - printf("The container superblock states that the container object map has Physical OID %#"PRIx64".\n", nxsb->nx_omap_oid); + printf("The container superblock states that the container object map has Physical OID 0x%" PRIx64 ".\n", nxsb->nx_omap_oid); printf("Loading the container object map ... "); omap_phys_t* nx_omap = malloc(nx_block_size); @@ -328,7 +323,7 @@ int cmd_resolver(int argc, char** argv) { return -1; } if (read_blocks(nx_omap_btree, nx_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", nx_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", nx_omap->om_tree_oid); return -1; } printf("OK.\n"); @@ -353,9 +348,9 @@ int cmd_resolver(int argc, char** argv) { } num_file_systems++; } - printf("The container superblock lists %"PRIu32" APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); + printf("The container superblock lists %u APFS volumes, whose superblocks have the following Virtual OIDs:\n", num_file_systems); for (uint32_t i = 0; i < num_file_systems; i++) { - printf("- %#"PRIx64"\n", nxsb->nx_fs_oid[i]); + printf("- 0x%" PRIx64 "\n", nxsb->nx_fs_oid[i]); } printf("\n"); @@ -368,17 +363,11 @@ int cmd_resolver(int argc, char** argv) { for (uint32_t i = 0; i < num_file_systems; i++) { omap_entry_t* fs_entry = get_btree_phys_omap_entry(nx_omap_btree, nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); if (!fs_entry) { - fprintf( - stderr, - "\nABORT: No objects with Virtual OID %#"PRIx64" and" - " maximum XID %#"PRIx64" exist in `nx_omap_btree`.\n", - nxsb->nx_fs_oid[i], - nxsb->nx_o.o_xid - ); + fprintf(stderr, "\nABORT: No objects with Virtual OID 0x%" PRIx64 " and maximum XID 0x%" PRIx64 " exist in `nx_omap_btree`.\n", nxsb->nx_fs_oid[i], nxsb->nx_o.o_xid); return -1; } if (read_blocks(apsbs + i, fs_entry->val.ov_paddr, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_entry->val.ov_paddr); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_entry->val.ov_paddr); return -1; } } @@ -387,11 +376,7 @@ int cmd_resolver(int argc, char** argv) { printf("Validating the APFS volume superblocks ... "); for (uint32_t i = 0; i < num_file_systems; i++) { if (!is_cksum_valid(apsbs + i)) { - printf( - "FAILED.\n- The checksum of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + printf("FAILED.\n- The checksum of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed printf("END: Handling of this case has not yet been implemented.\n"); @@ -399,11 +384,7 @@ int cmd_resolver(int argc, char** argv) { } if ( ((apfs_superblock_t*)(apsbs + i))->apfs_magic != APFS_MAGIC ) { - printf( - "FAILED.\n- The magic string of the APFS volume with OID %#"PRIx64" did not validate." - "\n- Going back to look at the previous checkpoint instead.\n", - nxsb->nx_fs_oid[i] - ); + printf("FAILED.\n- The magic string of the APFS volume with OID 0x%" PRIx64 " did not validate.\n- Going back to look at the previous checkpoint instead.\n", nxsb->nx_fs_oid[i]); // TODO: Handle case where data for a given checkpoint is malformed printf("END: Handling of this case has not yet been implemented.\n"); @@ -428,10 +409,10 @@ int cmd_resolver(int argc, char** argv) { uint32_t i = 0; apfs_superblock_t* apsb = apsbs + i; - printf("Simulating a mount of volume %"PRIu32" (%s).\n", i, apsb->apfs_volname); + printf("Simulating a mount of volume %u (%s).\n", i, apsb->apfs_volname); printf("\n"); - printf("The volume object map has Physical OID %#"PRIx64".\n", apsb->apfs_omap_oid); + printf("The volume object map has Physical OID 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); printf("Reading the volume object map ... "); omap_phys_t* fs_omap = malloc(nx_block_size); @@ -440,7 +421,7 @@ int cmd_resolver(int argc, char** argv) { return -1; } if (read_blocks(fs_omap, apsb->apfs_omap_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", apsb->apfs_omap_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", apsb->apfs_omap_oid); return -1; } printf("OK.\n"); @@ -473,7 +454,7 @@ int cmd_resolver(int argc, char** argv) { return -1; } if (read_blocks(fs_omap_btree, fs_omap->om_tree_oid, 1) != 1) { - fprintf(stderr, "\nABORT: Failed to read block %#"PRIx64".\n", fs_omap->om_tree_oid); + fprintf(stderr, "\nABORT: Failed to read block 0x%" PRIx64 ".\n", fs_omap->om_tree_oid); return -1; } printf("OK.\n"); @@ -559,18 +540,18 @@ int cmd_resolver(int argc, char** argv) { printf("\n\nHELLO, IS IT ME YOU'RE LOOKING FOR?\n\n"); for (size_t j = 0; j < NUM_RECORDS; j++) { - printf("%2zu -- ", j); + printf("%2lu -- ", j); omap_entry_t* omap_entry = get_btree_phys_omap_entry(fs_omap_btree, record_data[j][3], (xid_t)(~0) ); if (!omap_entry) { - printf("Could not resolve %#"PRIx64" --- no such entry in omap tree\n", record_data[j][3]); + printf("Could not resolve %#" PRIx64 " --- no such entry in omap tree\n", record_data[j][3]); continue; } if ( (uint64_t)(omap_entry->val.ov_paddr) == record_data[j][2] ) { printf("OK.\n"); } else { - printf( "Failed to resolve %#"PRIx64" to %#"PRIx64" --- it resolved to %#"PRIx64" instead\n", + printf( "Failed to resolve %#" PRIx64 " to %#" PRIx64 " --- it resolved to %#" PRIx64 " instead\n", record_data[j][3], record_data[j][2], omap_entry->val.ov_paddr