From f61654d82821af0fee358ae9cdfad71a805521bb Mon Sep 17 00:00:00 2001 From: FraSharp Date: Fri, 27 Feb 2026 22:14:58 +0100 Subject: [PATCH 1/2] add macOS libfuse3 support This extends the FUSE mount code to work on macOS with libfuse3, which uses different struct types (fuse_darwin_attr instead of stat) and callback signatures. The changes keep existing support for Linux libfuse3 and older libfuse/libosxfuse intact by using conditional compilation. --- fsapfstools/mount_fuse.c | 162 ++++++++++++++++++++++++++++++++++++++- fsapfstools/mount_fuse.h | 130 +++++++++++++++++++++++++++---- 2 files changed, 274 insertions(+), 18 deletions(-) diff --git a/fsapfstools/mount_fuse.c b/fsapfstools/mount_fuse.c index eb8135ae..b084d7d6 100644 --- a/fsapfstools/mount_fuse.c +++ b/fsapfstools/mount_fuse.c @@ -44,6 +44,12 @@ extern mount_handle_t *fsapfsmount_mount_handle; +#if defined( HAVE_LIBFUSE3 ) +#include +#elif defined( HAVE_LIBFUSE ) || defined( HAVE_LIBOSXFUSE ) +#include +#endif + #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBFUSE3 ) || defined( HAVE_LIBOSXFUSE ) #if !defined( ENODATA ) @@ -58,6 +64,17 @@ extern mount_handle_t *fsapfsmount_mount_handle; * The time values are a signed 64-bit POSIX date and time value in number of nanoseconds * Returns 1 if successful or -1 on error */ +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) +int mount_fuse_set_stat_info( + struct fuse_darwin_attr *stat_info, + size64_t size, + uint16_t file_mode, + int64_t access_time, + int64_t inode_change_time, + int64_t modification_time, + libcerror_error_t **error ) +{ +#elif defined( HAVE_LIBFUSE3 ) int mount_fuse_set_stat_info( struct stat *stat_info, size64_t size, @@ -67,6 +84,17 @@ int mount_fuse_set_stat_info( int64_t modification_time, libcerror_error_t **error ) { +#else +int mount_fuse_set_stat_info( + struct stat *stat_info, + size64_t size, + uint16_t file_mode, + int64_t access_time, + int64_t inode_change_time, + int64_t modification_time, + libcerror_error_t **error ) +{ +#endif static char *function = "mount_fuse_set_stat_info"; if( stat_info == NULL ) @@ -95,6 +123,32 @@ int mount_fuse_set_stat_info( return( -1 ); } +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) + stat_info->size = (off_t) size; + stat_info->mode = file_mode; + + if( ( file_mode & 0x4000 ) != 0 ) + { + stat_info->nlink = 2; + } + else + { + stat_info->nlink = 1; + } +#if defined( HAVE_GETEUID ) + stat_info->uid = geteuid(); +#endif +#if defined( HAVE_GETEGID ) + stat_info->gid = getegid(); +#endif + + stat_info->atimespec.tv_sec = access_time / 1000000000; + stat_info->atimespec.tv_nsec = access_time % 1000000000; + stat_info->ctimespec.tv_sec = inode_change_time / 1000000000; + stat_info->ctimespec.tv_nsec = inode_change_time % 1000000000; + stat_info->mtimespec.tv_sec = modification_time / 1000000000; + stat_info->mtimespec.tv_nsec = modification_time % 1000000000; +#else stat_info->st_size = (off_t) size; stat_info->st_mode = file_mode; @@ -121,6 +175,7 @@ int mount_fuse_set_stat_info( stat_info->st_atime_nsec = access_time % 1000000000; stat_info->st_ctime_nsec = inode_change_time % 1000000000; stat_info->st_mtime_nsec = modification_time % 1000000000; +#endif #endif return( 1 ); } @@ -128,6 +183,25 @@ int mount_fuse_set_stat_info( /* Fills a directory entry * Returns 1 if successful or -1 on error */ +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) +int mount_fuse_filldir( + void *buffer, + fuse_darwin_fill_dir_t filler, + const char *name, + struct fuse_darwin_attr *stat_info, + mount_file_entry_t *file_entry, + libcerror_error_t **error ) +{ +#elif defined( HAVE_LIBFUSE3 ) +int mount_fuse_filldir( + void *buffer, + fuse_fill_dir_t filler, + const char *name, + struct stat *stat_info, + mount_file_entry_t *file_entry, + libcerror_error_t **error ) +{ +#else int mount_fuse_filldir( void *buffer, fuse_fill_dir_t filler, @@ -136,6 +210,7 @@ int mount_fuse_filldir( mount_file_entry_t *file_entry, libcerror_error_t **error ) { +#endif static char *function = "mount_fuse_filldir"; size64_t file_size = 0; uint64_t access_time = 0; @@ -227,10 +302,17 @@ int mount_fuse_filldir( return( -1 ); } } +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) + if( memory_set( + stat_info, + 0, + sizeof( struct fuse_darwin_attr ) ) == NULL ) +#else if( memory_set( stat_info, 0, sizeof( struct stat ) ) == NULL ) +#endif { libcerror_error_set( error, @@ -578,11 +660,27 @@ int mount_fuse_release( /* Retrieves the value data of an extended attribute * Returns 0 if successful or a negative errno value otherwise */ +#if defined( HAVE_LIBFUSE3 ) +int mount_fuse_getxattr( + const char *path, + const char *name, + char *value, + size_t size, + uint32_t flags ) +#elif defined( __APPLE__ ) +int mount_fuse_getxattr( + const char *path, + const char *name, + char *value, + size_t size, + uint32_t flags ) +#else int mount_fuse_getxattr( const char *path, const char *name, char *value, size_t size ) +#endif { libcerror_error_t *error = NULL; libfsapfs_extended_attribute_t *extended_attribute = NULL; @@ -819,10 +917,28 @@ int mount_fuse_getxattr( /* Lists the names of extended attributes * Returns 0 if successful or a negative errno value otherwise */ +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) int mount_fuse_listxattr( const char *path, char *list, size_t size ) +#elif defined( HAVE_LIBFUSE3 ) +int mount_fuse_listxattr( + const char *path, + char *list, + size_t size, + uint32_t flags ) +#elif defined( __APPLE__ ) && defined( HAVE_LIBOSXFUSE ) +int mount_fuse_listxattr( + const char *path, + char *list, + size_t size ) +#else +int mount_fuse_listxattr( + const char *path, + char *list, + size_t size ) +#endif { libcerror_error_t *error = NULL; libfsapfs_extended_attribute_t *extended_attribute = NULL; @@ -1152,7 +1268,15 @@ int mount_fuse_opendir( /* Reads a directory * Returns 0 if successful or a negative errno value otherwise */ -#if defined( HAVE_LIBFUSE3 ) +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) +int mount_fuse_readdir( + const char *path, + void *buffer, + fuse_darwin_fill_dir_t filler, + off_t offset FSAPFSTOOLS_ATTRIBUTE_UNUSED, + struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED, + enum fuse_readdir_flags flags FSAPFSTOOLS_ATTRIBUTE_UNUSED ) +#elif defined( HAVE_LIBFUSE3 ) int mount_fuse_readdir( const char *path, void *buffer, @@ -1160,6 +1284,13 @@ int mount_fuse_readdir( off_t offset FSAPFSTOOLS_ATTRIBUTE_UNUSED, struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED, enum fuse_readdir_flags flags FSAPFSTOOLS_ATTRIBUTE_UNUSED ) +#elif defined( __APPLE__ ) && defined( HAVE_LIBOSXFUSE ) +int mount_fuse_readdir( + const char *path, + void *buffer, + fuse_fill_dir_t filler, + off_t offset FSAPFSTOOLS_ATTRIBUTE_UNUSED, + struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED ) #else int mount_fuse_readdir( const char *path, @@ -1169,7 +1300,11 @@ int mount_fuse_readdir( struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED ) #endif { - struct stat *stat_info = NULL; +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) + struct fuse_darwin_attr *stat_info = NULL; +#else + struct stat *stat_info = NULL; +#endif libcerror_error_t *error = NULL; mount_file_entry_t *parent_file_entry = NULL; mount_file_entry_t *sub_file_entry = NULL; @@ -1234,8 +1369,13 @@ int mount_fuse_readdir( goto on_error; } +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) + stat_info = memory_allocate_structure( + struct fuse_darwin_attr ); +#else stat_info = memory_allocate_structure( struct stat ); +#endif if( stat_info == NULL ) { @@ -1555,11 +1695,20 @@ int mount_fuse_releasedir( /* Retrieves the file stat info * Returns 0 if successful or a negative errno value otherwise */ -#if defined( HAVE_LIBFUSE3 ) +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) +int mount_fuse_getattr( + const char *path, + struct fuse_darwin_attr *stat_info, + struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED ) +#elif defined( HAVE_LIBFUSE3 ) int mount_fuse_getattr( const char *path, struct stat *stat_info, struct fuse_file_info *file_info FSAPFSTOOLS_ATTRIBUTE_UNUSED ) +#elif defined( __APPLE__ ) && defined( HAVE_LIBOSXFUSE ) +int mount_fuse_getattr( + const char *path, + struct stat *stat_info ) #else int mount_fuse_getattr( const char *path, @@ -1615,10 +1764,17 @@ int mount_fuse_getattr( goto on_error; } +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) + if( memory_set( + stat_info, + 0, + sizeof( struct fuse_darwin_attr ) ) == NULL ) +#else if( memory_set( stat_info, 0, sizeof( struct stat ) ) == NULL ) +#endif { libcerror_error_set( &error, diff --git a/fsapfstools/mount_fuse.h b/fsapfstools/mount_fuse.h index 77903145..06bf99b0 100644 --- a/fsapfstools/mount_fuse.h +++ b/fsapfstools/mount_fuse.h @@ -60,6 +60,43 @@ extern "C" { #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBFUSE3 ) || defined( HAVE_LIBOSXFUSE ) +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) +int mount_fuse_set_stat_info( + struct fuse_darwin_attr *stat_info, + size64_t size, + uint16_t file_mode, + int64_t access_time, + int64_t inode_change_time, + int64_t modification_time, + libcerror_error_t **error ); + +int mount_fuse_filldir( + void *buffer, + fuse_darwin_fill_dir_t filler, + const char *name, + struct fuse_darwin_attr *stat_info, + mount_file_entry_t *file_entry, + libcerror_error_t **error ); + +#elif defined( HAVE_LIBFUSE3 ) +int mount_fuse_set_stat_info( + struct stat *stat_info, + size64_t size, + uint16_t file_mode, + int64_t access_time, + int64_t inode_change_time, + int64_t modification_time, + libcerror_error_t **error ); + +int mount_fuse_filldir( + void *buffer, + fuse_fill_dir_t filler, + const char *name, + struct stat *stat_info, + mount_file_entry_t *file_entry, + libcerror_error_t **error ); + +#else int mount_fuse_set_stat_info( struct stat *stat_info, size64_t size, @@ -76,6 +113,7 @@ int mount_fuse_filldir( struct stat *stat_info, mount_file_entry_t *file_entry, libcerror_error_t **error ); +#endif int mount_fuse_open( const char *path, @@ -92,22 +130,46 @@ int mount_fuse_release( const char *path, struct fuse_file_info *file_info ); +#if defined( HAVE_LIBFUSE3 ) && defined( __APPLE__ ) int mount_fuse_getxattr( const char *path, const char *name, char *value, - size_t size ); + size_t size, + uint32_t flags ); int mount_fuse_listxattr( const char *path, char *list, size_t size ); -int mount_fuse_opendir( +int mount_fuse_readdir( + const char *path, + void *buffer, + fuse_darwin_fill_dir_t filler, + off_t offset, + struct fuse_file_info *file_info, + enum fuse_readdir_flags flags ); + +int mount_fuse_getattr( const char *path, + struct fuse_darwin_attr *stat_info, struct fuse_file_info *file_info ); -#if defined( HAVE_LIBFUSE3 ) +#elif defined( HAVE_LIBFUSE3 ) +int mount_fuse_getxattr( + const char *path, + const char *name, + char *value, + size_t size, + uint32_t flags ); + +int mount_fuse_listxattr( + const char *path, + char *list, + size_t size, + uint32_t flags ); + int mount_fuse_readdir( const char *path, void *buffer, @@ -115,37 +177,75 @@ int mount_fuse_readdir( off_t offset, struct fuse_file_info *file_info, enum fuse_readdir_flags flags ); + +int mount_fuse_getattr( + const char *path, + struct stat *stat_info, + struct fuse_file_info *file_info ); + + #elif defined( __APPLE__ ) && defined( HAVE_LIBOSXFUSE ) + int mount_fuse_getxattr( + const char *path, + const char *name, + char *value, + size_t size, + uint32_t flags ); + + int mount_fuse_listxattr( + const char *path, + char *list, + size_t size ); + + int mount_fuse_readdir( + const char *path, + void *buffer, + fuse_fill_dir_t filler, + off_t offset, + struct fuse_file_info *file_info ); + + int mount_fuse_getattr( + const char *path, + struct stat *stat_info ); + #else +int mount_fuse_getxattr( + const char *path, + const char *name, + char *value, + size_t size ); + +int mount_fuse_listxattr( + const char *path, + char *list, + size_t size ); + int mount_fuse_readdir( const char *path, void *buffer, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *file_info ); -#endif - -int mount_fuse_releasedir( - const char *path, - struct fuse_file_info *file_info ); -#if defined( HAVE_LIBFUSE3 ) -int mount_fuse_getattr( - const char *path, - struct stat *stat_info, - struct fuse_file_info *file_info ); -#else int mount_fuse_getattr( const char *path, struct stat *stat_info ); #endif +int mount_fuse_opendir( + const char *path, + struct fuse_file_info *file_info ); + +int mount_fuse_releasedir( + const char *path, + struct fuse_file_info *file_info ); + int mount_fuse_readlink( const char *path, char *buffer, size_t size ); void mount_fuse_destroy( - void *private_data ); + void *private_data ); #endif /* defined( HAVE_LIBFUSE ) || defined( HAVE_LIBFUSE3 ) || defined( HAVE_LIBOSXFUSE ) */ From ed8652c9e6a659381a5a40b216335074ea1a0023 Mon Sep 17 00:00:00 2001 From: FraSharp Date: Sat, 28 Feb 2026 02:00:28 +0100 Subject: [PATCH 2/2] Add tolerant mode support Implement tolerant mode that allows the filesystem to continue operating when encountering certain errors. When enabled, the mount_fuse tool will skip problematic directory entries instead of failing the entire operation. This adds: - libfsapfs_volume_set_tolerant_mode() function - Tolerant mode flag in libfsapfs_volume_t - Propagation to file system B-tree - Error handling in mount_fuse_readdir() to skip errors in tolerant mode --- fsapfstools/fsapfsmount.c | 23 +- fsapfstools/mount_file_system.h | 4 + fsapfstools/mount_fuse.c | 40 +- fsapfstools/mount_handle.c | 11 + fsapfstools/mount_handle.h | 4 + libfsapfs/libfsapfs_file_system_btree.c | 624 ++++++++++++++++-------- libfsapfs/libfsapfs_file_system_btree.h | 4 + libfsapfs/libfsapfs_volume.c | 67 +++ libfsapfs/libfsapfs_volume.h | 12 +- manuals/fsapfsmount.1 | 3 + 10 files changed, 577 insertions(+), 215 deletions(-) diff --git a/fsapfstools/fsapfsmount.c b/fsapfstools/fsapfsmount.c index 6cbce16a..92c5cd6b 100644 --- a/fsapfstools/fsapfsmount.c +++ b/fsapfstools/fsapfsmount.c @@ -68,7 +68,7 @@ void usage_fprint( fprintf( stream, "Usage: fsapfsmount [ -f file_system_index ] [ -o offset ] [ -p password ]\n" " [ -r recovery_password ] [ -X extended_options ]\n" - " [ -hvV ] container mount_point\n\n" ); + " [ -hRvV ] container mount_point\n\n" ); fprintf( stream, "\tcontainer: an Apple File System (APFS) container\n\n" ); fprintf( stream, "\tmount_point: the directory to serve as mount point\n\n" ); @@ -78,6 +78,7 @@ void usage_fprint( fprintf( stream, "\t-o: specify the container offset in bytes\n" ); fprintf( stream, "\t-p: specify the password/passphrase\n" ); fprintf( stream, "\t-r: specify the recovery password/passphrase\n" ); + fprintf( stream, "\t-R: enable recovery mode (tolerant for corrupted file system)\n" ); fprintf( stream, "\t-v: verbose output to stderr, while fsapfsmount will remain running in the\n" "\t foreground\n" ); fprintf( stream, "\t-V: print version\n" ); @@ -148,6 +149,7 @@ int main( int argc, char * const argv[] ) system_integer_t option = 0; int result = 0; int verbose = 0; + int option_tolerant_mode = 0; #if defined( HAVE_LIBFUSE ) || defined( HAVE_LIBFUSE3 ) || defined( HAVE_LIBOSXFUSE ) struct fuse_operations fsapfsmount_fuse_operations; @@ -202,7 +204,7 @@ int main( int argc, char * const argv[] ) while( ( option = fsapfstools_getopt( argc, argv, - _SYSTEM_STRING( "f:ho:p:r:vVX:" ) ) ) != (system_integer_t) -1 ) + _SYSTEM_STRING( "f:ho:p:r:RvVX:" ) ) ) != (system_integer_t) -1 ) { switch( option ) { @@ -239,15 +241,19 @@ int main( int argc, char * const argv[] ) break; - case (system_integer_t) 'r': - option_recovery_password = optarg; + case (system_integer_t) 'r': + option_recovery_password = optarg; - break; + break; + + case (system_integer_t) 'R': + option_tolerant_mode = 1; + break; - case (system_integer_t) 'v': - verbose = 1; + case (system_integer_t) 'v': + verbose = 1; - break; + break; case (system_integer_t) 'V': fsapfstools_output_copyright_fprint( @@ -305,6 +311,7 @@ int main( int argc, char * const argv[] ) goto on_error; } + fsapfsmount_mount_handle->tolerant_mode = option_tolerant_mode; if( option_file_system_index != NULL ) { if( mount_handle_set_file_system_index( diff --git a/fsapfstools/mount_file_system.h b/fsapfstools/mount_file_system.h index 36a30b5e..b0c53d9a 100644 --- a/fsapfstools/mount_file_system.h +++ b/fsapfstools/mount_file_system.h @@ -43,6 +43,10 @@ struct mount_file_system /* The volume */ libfsapfs_volume_t *fsapfs_volume; + + /* Tolerant mode (skip corrupt entries) + */ + uint8_t tolerant_mode; }; int mount_file_system_initialize( diff --git a/fsapfstools/mount_fuse.c b/fsapfstools/mount_fuse.c index b084d7d6..61492ba5 100644 --- a/fsapfstools/mount_fuse.c +++ b/fsapfstools/mount_fuse.c @@ -1481,23 +1481,35 @@ int mount_fuse_readdir( sub_file_entry_index < number_of_sub_file_entries; sub_file_entry_index++ ) { - if( mount_file_entry_get_sub_file_entry_by_index( - (mount_file_entry_t *) file_info->fh, - sub_file_entry_index, - &sub_file_entry, - &error ) != 1 ) + int get_sub_result = mount_file_entry_get_sub_file_entry_by_index( + (mount_file_entry_t *) file_info->fh, + sub_file_entry_index, + &sub_file_entry, + &error ); + + if( get_sub_result != 1 ) { - libcerror_error_set( - &error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve sub file entry: %d.", - function, - sub_file_entry_index ); + mount_file_entry_t *parent = (mount_file_entry_t *) file_info->fh; + int tolerant = (parent && parent->file_system) ? parent->file_system->tolerant_mode : 0; - result = -EIO; + if( tolerant != 0 ) + { + libcerror_error_free( &error ); + continue; + } + else + { + libcerror_error_set( + &error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve sub file entry: %d.", + function, + sub_file_entry_index ); - goto on_error; + result = -EIO; + goto on_error; + } } if( mount_file_entry_get_name_size( sub_file_entry, diff --git a/fsapfstools/mount_handle.c b/fsapfstools/mount_handle.c index 762a0769..204e6849 100644 --- a/fsapfstools/mount_handle.c +++ b/fsapfstools/mount_handle.c @@ -30,6 +30,7 @@ #include "fsapfstools_libcerror.h" #include "fsapfstools_libcpath.h" #include "fsapfstools_libfsapfs.h" +#include "fsapfstools_libcnotify.h" #include "mount_file_entry.h" #include "mount_file_system.h" #include "mount_handle.h" @@ -733,6 +734,16 @@ int mount_handle_open( goto on_error; } + /* Propagate tolerant mode to the file system and volume */ + mount_handle->file_system->tolerant_mode = mount_handle->tolerant_mode; + if( libfsapfs_volume_set_tolerant_mode( + fsapfs_volume, + mount_handle->tolerant_mode ? 1 : 0, + NULL ) != 1 ) + { + libcnotify_printf( + "Warning: failed to set tolerant mode on volume.\n" ); + } mount_handle->file_io_handle = file_io_handle; return( 1 ); diff --git a/fsapfstools/mount_handle.h b/fsapfstools/mount_handle.h index cb931cd1..afb3529a 100644 --- a/fsapfstools/mount_handle.h +++ b/fsapfstools/mount_handle.h @@ -83,6 +83,10 @@ struct mount_handle /* The notification output stream */ FILE *notify_stream; + + /* Tolerant mode (skip corrupt entries) + */ + uint8_t tolerant_mode; }; int mount_handle_system_string_copy_from_64_bit_in_decimal( diff --git a/libfsapfs/libfsapfs_file_system_btree.c b/libfsapfs/libfsapfs_file_system_btree.c index f6b666a4..9efae1e4 100644 --- a/libfsapfs/libfsapfs_file_system_btree.c +++ b/libfsapfs/libfsapfs_file_system_btree.c @@ -888,44 +888,92 @@ int libfsapfs_file_system_btree_get_sub_node( goto on_error; } - if( ( node->object_type != 0x00000003UL ) - && ( node->object_type != 0x10000003UL ) ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, - "%s: invalid object type: 0x%08" PRIx32 ".", - function, - node->object_type ); - - goto on_error; - } - if( node->object_subtype != 0x0000000eUL ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, - "%s: invalid object subtype: 0x%08" PRIx32 ".", - function, - node->object_subtype ); - - goto on_error; - } - if( ( ( node->node_header->flags & 0x0001 ) != 0 ) - || ( ( node->node_header->flags & 0x0004 ) != 0 ) ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, - "%s: unsupported flags: 0x%04" PRIx16 ".", - function, - node->node_header->flags ); - - goto on_error; - } + if( ( node->object_type != 0x00000003UL ) + && ( node->object_type != 0x10000003UL ) ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + libcnotify_printf( + "%s: warning: skipping corrupted B-tree node block: %" PRIu64 " (invalid object type: 0x%08" PRIx32 ").\n", + function, + sub_node_block_number, + node->object_type ); + + libfsapfs_btree_node_free( + &node, + error ); + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: invalid object type: 0x%08" PRIx32 ".", + function, + node->object_type ); + + goto on_error; + } + } + if( node->object_subtype != 0x0000000eUL ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + libcnotify_printf( + "%s: warning: skipping corrupted B-tree node block: %" PRIu64 " (invalid object subtype: 0x%08" PRIx32 ").\n", + function, + sub_node_block_number, + node->object_subtype ); + + libfsapfs_btree_node_free( + &node, + error ); + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: invalid object subtype: 0x%08" PRIx32 ".", + function, + node->object_subtype ); + + goto on_error; + } + } + if( ( ( node->node_header->flags & 0x0001 ) != 0 ) + || ( ( node->node_header->flags & 0x0004 ) != 0 ) ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + libcnotify_printf( + "%s: warning: skipping corrupted B-tree node block: %" PRIu64 " (unsupported flags: 0x%04" PRIx16 ").\n", + function, + sub_node_block_number, + node->node_header->flags ); + + libfsapfs_btree_node_free( + &node, + error ); + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unsupported flags: 0x%04" PRIx16 ".", + function, + node->node_header->flags ); + + goto on_error; + } + } if( libfcache_cache_set_value_by_identifier( file_system_btree->node_cache, 0, @@ -1376,24 +1424,45 @@ int libfsapfs_file_system_btree_get_entry_by_identifier( } node = NULL; - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - return( -1 ); - } - recursion_depth++; + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + return( -1 ); + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + return( -1 ); + } + } + recursion_depth++; } while( is_leaf_node == 0 ); @@ -1967,23 +2036,44 @@ int libfsapfs_file_system_btree_get_directory_record_from_branch_node_by_utf8_na goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -2624,23 +2714,44 @@ int libfsapfs_file_system_btree_get_directory_record_from_branch_node_by_utf16_n goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -3186,23 +3297,45 @@ int libfsapfs_file_system_btree_get_directory_entries_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – skip this entry */ + sub_node = NULL; + continue; + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -3276,23 +3409,44 @@ int libfsapfs_file_system_btree_get_directory_entries_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -4004,23 +4158,45 @@ int libfsapfs_file_system_btree_get_attributes_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – skip this entry */ + sub_node = NULL; + continue; + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -4094,23 +4270,44 @@ int libfsapfs_file_system_btree_get_attributes_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -4836,23 +5033,45 @@ int libfsapfs_file_system_btree_get_file_extents_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – skip this entry */ + sub_node = NULL; + continue; + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); @@ -4928,23 +5147,44 @@ int libfsapfs_file_system_btree_get_file_extents_from_branch_node( goto on_error; } - if( libfsapfs_file_system_btree_get_sub_node( - file_system_btree, - file_io_handle, - sub_node_block_number, - &sub_node, - error ) != 1 ) - { - libcerror_error_set( - error, - LIBCERROR_ERROR_DOMAIN_RUNTIME, - LIBCERROR_RUNTIME_ERROR_GET_FAILED, - "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", - function, - sub_node_block_number ); - - goto on_error; - } + int get_sub_result = libfsapfs_file_system_btree_get_sub_node( + file_system_btree, + file_io_handle, + sub_node_block_number, + &sub_node, + error ); + + if( get_sub_result == -1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_GET_FAILED, + "%s: unable to retrieve B-tree sub node from block: %" PRIu64 ".", + function, + sub_node_block_number ); + + goto on_error; + } + else if( get_sub_result == 0 ) + { + if( file_system_btree->tolerant_mode != 0 ) + { + /* Corrupted sub-node – treat as not found */ + return( 0 ); + } + else + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: unable to retrieve B-tree sub node (corrupted).", + function ); + + goto on_error; + } + } is_leaf_node = libfsapfs_btree_node_is_leaf_node( sub_node, error ); diff --git a/libfsapfs/libfsapfs_file_system_btree.h b/libfsapfs/libfsapfs_file_system_btree.h index 21129ec9..dddefe52 100644 --- a/libfsapfs/libfsapfs_file_system_btree.h +++ b/libfsapfs/libfsapfs_file_system_btree.h @@ -76,6 +76,10 @@ struct libfsapfs_file_system_btree /* Flag to indicate case folding should be used */ uint8_t use_case_folding; + + /* Tolerant mode flag (skip corrupt nodes) + */ + uint8_t tolerant_mode; }; int libfsapfs_file_system_btree_initialize( diff --git a/libfsapfs/libfsapfs_volume.c b/libfsapfs/libfsapfs_volume.c index a62bc02d..fe88e1c7 100644 --- a/libfsapfs/libfsapfs_volume.c +++ b/libfsapfs/libfsapfs_volume.c @@ -144,6 +144,7 @@ int libfsapfs_volume_initialize( internal_volume->file_io_handle = file_io_handle; internal_volume->container_key_bag = container_key_bag; internal_volume->is_locked = 1; + internal_volume->tolerant_mode = 0; #if defined( HAVE_LIBFSAPFS_MULTI_THREAD_SUPPORT ) if( libcthreads_read_write_lock_initialize( @@ -2617,6 +2618,70 @@ int libfsapfs_volume_set_utf16_recovery_password( return( -1 ); } +/* Sets the tolerant mode + * Returns 1 if successful or -1 on error + */ +int libfsapfs_volume_set_tolerant_mode( + libfsapfs_volume_t *volume, + uint8_t tolerant_mode, + libcerror_error_t **error ) +{ + libfsapfs_internal_volume_t *internal_volume = NULL; + static char *function = "libfsapfs_volume_set_tolerant_mode"; + + if( volume == NULL ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_ARGUMENTS, + LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, + "%s: invalid volume.", + function ); + + return( -1 ); + } + internal_volume = (libfsapfs_internal_volume_t *) volume; + +#if defined( HAVE_LIBFSAPFS_MULTI_THREAD_SUPPORT ) + if( libcthreads_read_write_lock_grab_for_write( + internal_volume->read_write_lock, + error ) != 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_SET_FAILED, + "%s: unable to grab read/write lock for writing.", + function ); + + return( -1 ); + } +#endif + internal_volume->tolerant_mode = ( tolerant_mode ) ? 1 : 0; + + /* Propagate to file system B-tree if already created */ + if( internal_volume->file_system_btree != NULL ) + { + internal_volume->file_system_btree->tolerant_mode = internal_volume->tolerant_mode; + } +#if defined( HAVE_LIBFSAPFS_MULTI_THREAD_SUPPORT ) + if( libcthreads_read_write_lock_release_for_write( + internal_volume->read_write_lock, + error ) != 1 ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_SET_FAILED, + "%s: unable to release read/write lock for writing.", + function ); + + return( -1 ); + } +#endif + return( 1 ); +} + /* Retrieves the root directory file entry * Returns 1 if successful, or 0 if not available or -1 on error */ @@ -2911,6 +2976,8 @@ int libfsapfs_internal_volume_get_file_system( goto on_error; } + /* Propagate tolerant mode to the newly created B-tree */ + file_system_btree->tolerant_mode = internal_volume->tolerant_mode; if( libfsapfs_file_system_initialize( &( internal_volume->file_system ), internal_volume->io_handle, diff --git a/libfsapfs/libfsapfs_volume.h b/libfsapfs/libfsapfs_volume.h index 8880f14a..db292951 100644 --- a/libfsapfs/libfsapfs_volume.h +++ b/libfsapfs/libfsapfs_volume.h @@ -128,10 +128,14 @@ struct libfsapfs_internal_volume */ uint8_t *recovery_password; - /* The recovery password size + /* The recovery password size */ size_t recovery_password_size; + /* Tolerant mode flag: 0 = strict, 1 = skip corrupt B-tree nodes + */ + uint8_t tolerant_mode; + #if defined( HAVE_LIBFSAPFS_MULTI_THREAD_SUPPORT ) /* The read/write lock */ @@ -250,6 +254,12 @@ int libfsapfs_volume_set_utf16_recovery_password( size_t utf16_string_length, libcerror_error_t **error ); +LIBFSAPFS_EXTERN \ +int libfsapfs_volume_set_tolerant_mode( + libfsapfs_volume_t *volume, + uint8_t tolerant_mode, + libcerror_error_t **error ); + LIBFSAPFS_EXTERN \ int libfsapfs_volume_get_root_directory( libfsapfs_volume_t *volume, diff --git a/manuals/fsapfsmount.1 b/manuals/fsapfsmount.1 index 0b03b667..9d35b7b7 100644 --- a/manuals/fsapfsmount.1 +++ b/manuals/fsapfsmount.1 @@ -10,6 +10,7 @@ .Op Fl o Ar offset .Op Fl p Ar password .Op Fl r Ar password +.Op Fl R .Op Fl hvV .Ar source .Sh DESCRIPTION @@ -38,6 +39,8 @@ specify the container offset in bytes specify the password .It Fl r Ar password specify the recovery password +.It Fl R +enable recovery mode (tolerant for corrupted file system) .It Fl v verbose output to stderr .It Fl V