diff options
Diffstat (limited to 'libparted/fs/hfs')
-rw-r--r-- | libparted/fs/hfs/DOC | 92 | ||||
-rw-r--r-- | libparted/fs/hfs/HISTORY | 115 | ||||
-rw-r--r-- | libparted/fs/hfs/TODO | 27 | ||||
-rw-r--r-- | libparted/fs/hfs/hfs.c | 92 | ||||
-rw-r--r-- | libparted/fs/hfs/hfs.h | 648 | ||||
-rw-r--r-- | libparted/fs/hfs/probe.c | 238 | ||||
-rw-r--r-- | libparted/fs/hfs/probe.h | 44 |
7 files changed, 1256 insertions, 0 deletions
diff --git a/libparted/fs/hfs/DOC b/libparted/fs/hfs/DOC new file mode 100644 index 0000000..9ee8b4d --- /dev/null +++ b/libparted/fs/hfs/DOC @@ -0,0 +1,92 @@ +WARNING : Both HFS and HFS+ implementations of Linux 2.4 are buggy, at +least when used before or after this implementation. Some workarounds +are used in this implementation, but there can still be incompatibilities. +Try Linux 2.6 if you want to play with HFS(+) resizing (though some bugs +might also be there in 2.6, there is of course no warranty) + +--- + + Technical doc about Apple HFS and HFS+ file systems is available at : + * For HFS, section "Data Organization on Volumes", + "Chapter 2 - File Manager" + of the book "Inside Macintosh: Files" + http://developer.apple.com/documentation/mac/Files/Files-99.html + * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format" + http://developer.apple.com/technotes/tn/tn1150.html + + Some useful HFS precisions concerning alignement, bit ordering, and + order of fields for extent key comparaisons are only in the HFS+ TN + + These Apple Creator Codes are reserved for us : + Shnk traP GP16 GnuP PH+x Xpnd Resz GP17 GP18 GP19 GP20 + +--- + +* Cache design * + +Versions before HFS Patch 15 were very slow when data relocation was needed, +because every extent to relocate involved scanning the whole file system, +looking for a reference to its physical position on the volume (this was a +dummy algorithm, I know :) + +HFS Patch 16 introduced a cache that allows to efficiently retrieve the place +of the reference in the file system given the physical position of an extent. +The cache is designed for : - efficiency + - scaling + - simplicity + - avoiding memory allocation while resizing + +This cache involves quite big worst case memory consumption, but without it +the time needed to complete the operation in the worst case would be huge +anyway (maybe several years...) so this isn't really an issue. The cache size +is nearly proportional to the number of files you have, or if you have very few +files, to the size of your volume, so worst cases situations occure when you +fill a drive with millions of < 4 ko files :p For this very special usage you +will just need a very special amount of RAM (on typical systems about +(FS size) / 256 )... On a more "normal" volume it's about +(# of files) * 20 bytes. With very few files it's about (FS Size) / 1024 / 256. + +At the beginning of the resize process, the cache is filed by scanning the FS. +The position of each extent is cut into 2 parts : high order is used as +an index into a table of pointer to a linked list which contains : +- the next ptr (sizeof struct *) +- the extent start (4 bytes) +- the extent size (4 bytes) +- number of BTree block or 0 if in prim (4 bytes) +- offset of the extent start reference + from the block beginning (2 bytes) +- sectors by BTree block, or + 1 for VH/MDB (1 byte) +- FS special file / primary structure + where the extent reference is stored (1 byte) + (3 bits for the extent index, 5 for + the actual ref) + + 0 : dont exists --- reserved + 1 : mdb / vh : catalog --- + 2 : mdb / vh : extent --- + 3 : vh : attributes X+- + 4 : vh : allocation X+- + 5 : vh : startup X+- + 6 : catalog --- + 7 : attributes X+- + 8 : extent (nothing to update) --- + 9 : extent (update catalog) --- + 10 : extent (update extent !?!) --- should not exist + 11 : extent (update attributes) X+- + 12 : extent (update allocation) X+- + 13 : extent (update startup) X+- reserved + 14 : vh : journal info block X+J + 15 : jib: journal X+J + 16 - 31 : --- + +mdb : Master Directory Block +vh : Volume Header +X+ : HFSX or HFS+ only +J : Journaled only + +Large amount of memory is allocated at once (first enough memory to fit +every files if there isn't any fragmentation +6.25%, then this value / 4, +if this wasn't enough). On a typical FS, the first allocation should be enough. + +--- diff --git a/libparted/fs/hfs/HISTORY b/libparted/fs/hfs/HISTORY new file mode 100644 index 0000000..5e138a6 --- /dev/null +++ b/libparted/fs/hfs/HISTORY @@ -0,0 +1,115 @@ +## modifications dd-mm-yyyy +---------------------- PATCH FOR PARTED 1.6.5 ---------------------------- + 1 initial revision 07-04-2003 + 2 one pass resizing, removal of debug info 08-04-2003 + 3 safe abort if resize failed, code cleanups, timer, 10-04-2003 + source file split, won't resize if not unmounted, + only relocate data if needed, minimize disk operations + 4 memory leaks removal, code cleanups, resize hfs+ code, 17-04-2003 + more checks, minor hfs resize bugfix, probe code + returns real geometry + 5 hfs+ resize bugfixes : 19-04-2003 + * fragmented fs could be corrupted + * VH wasn't written on error during alloc map writing + * attributes file could be corrupted + 6 Use PedSector to be able to use 2To+ HD 23-04-2003 + Minor probe bugfix, Cleanups, HFS+ Timer tuning, + 7 80 columns indentation 23-04-2003 + 8 Bugfix free blocks calculation in wrapper + (makes Mac OS boot !) 28-04-2003 +---------------------- PATCH FOR PARTED 1.6.6 ---------------------------- + 9 Don't destroy the file being worked on in case of + interruption of Parted 28-10-2003 +---------------------- PATCH FOR PARTED 1.6.10 --------------------------- +10 Regression tests, URL correction, In effect_move_extent : + corrected memory leak & corrected a bug in precondition checks + Added error messages, Check ped_alloc results + Use macro for test / set / clear in the allocation bitmap + Light probe correction, Check return value of get_empty_end + Moved dynamic memory allocation out of effect_move_extent + Check HFS+ version, Set implementation creator code + Check journal absence, Corrected a bug in HFS+ block number + calculation 24-04-2004 +--------------------- PATCH FOR PARTED 1.6.11 ---------------------------- +11-Some pointer dereference moved after non nul assertion + -Error messages for HFS(+) file IO + -Memory leak correction in hfs(plus)_read_bad_blocks + -Mark out of volume blocks as used + (improve compatibility with broken HFS+ Linux + implementation) + WARNING : this fix is not 100% tn1150 compatible : + "The allocation file may be larger than the minimum + number of bits required for the given volume size. + Any unused bits in the bitmap must be set to _zero_." + Anyway neither is the Linux implementation, nor was my + previous implementations + Maybe I should ask Apple to change the specifications + -HISTORY, DOC and TODO files 29-04-2004 +12 Corrected a bug in hfsplus_volume_resize : size of alloc + bitmap could be miscalculated 29-04-2004 +--------------------- PATCH FOR PARTED 1.6.12 ---------------------------- +13-Finally partial rewrite of *_search_move_* + Easier to maintain and prepare for extent search and + relocation algorithm changes for better ones. + -"An extent has not been relocated!" message now only when + relocation requested + -Slightly better and simpler relocation algorithm + -Update of Makefile.in and Makefile.am in fs_hfs + -Sign correction for some 8bits HFS integers + -Added the option --enable-hfs-extract-fs in 'configure' + -Added every ped_geometry_sync where needed + -Bugfix : "A root node does not need to exist + (if the tree is empty)." + - now handled correctly in btree_search + -Bugfix : failure wasn't detected in some cases + during 2 pass relocation (*_search_move_*) + -Bugfix : The extent key comparaison was done in a wrong order + and a pad field was used during the comparaison + -Bugfix : in hfs_file_find_sector and hfsplus_file_find_sector + the absolute position of a file sector could be + miscalculated in case of fragmentation, resulting + in potential data corruption, or various errors + -Bugfix : The end of the HFS bitmap compatibility block was + miscalculated ( (1<<16)/8 instead of (1<<16) ) + in hfs_resize + 07-09-2004 +--------------------- PATCH FOR PARTED 1.6.14 ---------------------------- +14 Port of Patch 13 for Parted 1.6.14 (update timestamps) + 08-09-2004 +--------------------- PATCH FOR PARTED 1.6.15 ---------------------------- +15-hfsplus_open : added a warning message if the "attributes" + special file exists + -hfsplus_open : added a test to check if the "allocation" + special file has been correctly opened + -optimisation of hfs+ block access : don't recalculate + the address of each sector, and avoid checking the cache if + obviously not useful + ( hfsplus_file_read && hfsplus_file_write + && hfsplus_file_find_extent && hfs_file_find_sector) + -cut the "hfs.c" file in several parts + -Bugfix: in hfsplus_do_move_primary, hfs_effect_move_extent + was called instead of hfsplus_effect_move_extent !!! + This could not produce data corruption, because of a welcome + ASSERT in *_effect_move_extent that would detect the bug :) + -Bugfix: in hfs_effect_move_extent, do + PED_ASSERT(*ptr_to_fblock <= *ptr_fblock, return -1); + instead of + PED_ASSERT(*ptr_to_fblock < *ptr_fblock, return -1); + and added that assertion to hfsplus_effect_move_extent + -Bugfix: bugs introduced in rewrite of hfsplus_file_read + && hfsplus_file_write : last sector was incorrectly detected + as out of file. + -Cache the extent references (speed improvement ?) + 23-09-2004 +16-Bugfix: in hfsplus_do_move (reloc_plus.c), case CR_BTREE_EXT_ATTR + incorrectly updated the cached part of priv_data->catalog_file + instead of priv_data->attributes_file + -Bugfix: in hfs_read_bad_blocks && hfsplus_read_bad_blocks, + now generate an error if file_ID or type mismatch after the + first pass + Also check return value of ped_malloc + -Bugfix: in hfsplus_btree_search, check return value of ped_malloc + 29-09-2004 +---------------- INTEGRATION IN PARTED 1.6.22 (cvs) ---------------------- +Futur changes will be described in ../../ChangeLog + 02-02-2005 diff --git a/libparted/fs/hfs/TODO b/libparted/fs/hfs/TODO new file mode 100644 index 0000000..6e408e3 --- /dev/null +++ b/libparted/fs/hfs/TODO @@ -0,0 +1,27 @@ +--- TODO --- + + * Continue to write regressions tests and try on 2.6 kernel -- (high) + * Fix timer progression calculation, according to the new + caching code -- (high) + * write doc, website, ... -- (high) + * Check block allocation in linux 2.4 and remove + compatibility code if possible -- (high) + + * In hfs(plus)_btree_search , use a static variable to detect + illegal recursion and abort in that case. (find the right + number of recursion before reporting bug) -- easy -- (medium) + * Finish the HFS Extractor -- (medium) + (add mdb & vh extraction, and maybe journal) + + * Write code to allow enlarging and moving HFS[+x] -- (low) + * Use a bitmap to internaly store the bad blocks -- (low) + * Less bitmap saves ? -- (low) + * Continue to change the relocation algorithm + for a better one :) -- (low) + +--- NOT todo --- + + * debug HFS(+) Linux implementation (block allocation for HFS+, + hard and sym links for HFS+, filename length for HFS, ...) -- (dont) + /// Linux 2.6 contains HFS(+) implementations with less bugs + /// Linux 2.4 should not be used anymore to access HFS(+) diff --git a/libparted/fs/hfs/hfs.c b/libparted/fs/hfs/hfs.c new file mode 100644 index 0000000..3684646 --- /dev/null +++ b/libparted/fs/hfs/hfs.c @@ -0,0 +1,92 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2003-2005, 2007, 2009-2014, 2019-2023 Free Software + Foundation, Inc. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + Author : Guillaume Knispel <k_guillaume@libertysurf.fr> + Report bug to <bug-parted@gnu.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "probe.h" + +uint8_t* hfs_block = NULL; +uint8_t* hfsp_block = NULL; +unsigned hfs_block_count; +unsigned hfsp_block_count; + +static PedFileSystemOps hfs_ops = { + probe: hfs_probe, +}; + +static PedFileSystemOps hfsplus_ops = { + probe: hfsplus_probe, +}; + +static PedFileSystemOps hfsx_ops = { + probe: hfsx_probe, +}; + + +static PedFileSystemType hfs_type = { + next: NULL, + ops: &hfs_ops, + name: "hfs", +}; + +static PedFileSystemType hfsplus_type = { + next: NULL, + ops: &hfsplus_ops, + name: "hfs+", +}; + +static PedFileSystemType hfsx_type = { + next: NULL, + ops: &hfsx_ops, + name: "hfsx", +}; + +void +ped_file_system_hfs_init () +{ + ped_file_system_type_register (&hfs_type); + ped_file_system_type_register (&hfsplus_type); + ped_file_system_type_register (&hfsx_type); +} + +void +ped_file_system_hfs_done () +{ + ped_file_system_type_unregister (&hfs_type); + ped_file_system_type_unregister (&hfsplus_type); + ped_file_system_type_unregister (&hfsx_type); +} diff --git a/libparted/fs/hfs/hfs.h b/libparted/fs/hfs/hfs.h new file mode 100644 index 0000000..5b9138c --- /dev/null +++ b/libparted/fs/hfs/hfs.h @@ -0,0 +1,648 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003-2005, 2007, 2009-2014, 2019-2023 Free Software + Foundation, Inc. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _HFS_H +#define _HFS_H + +/* WARNING : bn is used 2 times in theses macro */ +/* so _never_ use side effect operators when using them */ +#define TST_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) & (1<<(7-((bn)&7)))) +#define SET_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) |= (1<<(7-((bn)&7)))) +#define CLR_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7)))) + +/* Maximum number of blocks for the copy buffers */ +#define BLOCK_MAX_BUFF 256 +/* Maximum size of the copy buffers, in bytes */ +#define BYTES_MAX_BUFF 8388608 + +/* Apple Creator Codes follow */ +#define HFSP_IMPL_Shnk 0x53686e6b /* in use */ +#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */ +#define HFSP_IMPL_Resz 0x5265737a /* reserved */ +#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */ +#define HFSP_IMPL_traP 0x74726150 /* reserved */ +#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */ + +#define HFS_SIGNATURE 0x4244 /* 'BD' */ +#define HFSP_SIGNATURE 0x482B /* 'H+' */ +#define HFSX_SIGNATURE 0x4858 /* 'HX' */ + +#define HFSP_VERSION 4 +#define HFSX_VERSION 5 + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 +#define HFSP_REUSE_CNID 12 +#define HFSP_JOURNALED 13 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFSJ_JOURN_IN_FS 0x00 +#define HFSJ_JOURN_OTHER_DEV 0x01 +#define HFSJ_JOURN_NEED_INIT 0x02 + +#define HFSJ_HEADER_MAGIC 0x4a4e4c78 +#define HFSJ_ENDIAN_MAGIC 0x12345678 + +#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */ +#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */ + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +/* Define the filenames used by the FS extractor */ +#ifdef HFS_EXTRACT_FS + +#define HFS_MDB_FILENAME "mdb.hfs" +#define HFS_CATALOG_FILENAME "catalog.hfs" +#define HFS_EXTENTS_FILENAME "extents.hfs" +#define HFS_BITMAP_FILENAME "bitmap.hfs" + +#define HFSP_VH_FILENAME "vh.hfsplus" +#define HFSP_CATALOG_FILENAME "catalog.hfsplus" +#define HFSP_EXTENTS_FILENAME "extents.hfsplus" +#define HFSP_BITMAP_FILENAME "bitmap.hfsplus" +#define HFSP_ATTRIB_FILENAME "attributes.hfsplus" +#define HFSP_STARTUP_FILENAME "startup.hfsplus" + +#endif /* HFS_EXTRACT_FS */ + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t reserved; + uint32_t parent_ID; + uint8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* documented since 2004 in tn1150 */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; + uint32_t group_ID; + uint32_t permissions; + uint32_t special_devices; +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t journal_info_block; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree. */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t key_compare_type; /* hfsx => 0xCF = case folding */ + /* 0xBC = binary compare */ + /* otherwise, reserved */ + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + +/* ----------- Journal data structures ----------- */ + +/* Info block : stored in a block # defined in the VH */ +struct __attribute__ ((packed)) _HfsJJournalInfoBlock { + uint32_t flags; + uint32_t device_signature[8]; + uint64_t offset; + uint64_t size; + uint32_t reserved[32]; +}; +typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock; + +struct __attribute__ ((packed)) _HfsJJournalHeader { + uint32_t magic; + uint32_t endian; + uint64_t start; + uint64_t end; + uint64_t size; + uint32_t blhdr_size; + uint32_t checksum; + uint32_t jhdr_size; +}; +typedef struct _HfsJJournalHeader HfsJJournalHeader; + +struct __attribute__ ((packed)) _HfsJBlockInfo { + uint64_t bnum; /* sector number */ + uint32_t bsize; /* size in bytes */ + uint32_t next; +}; +typedef struct _HfsJBlockInfo HfsJBlockInfo; + +struct __attribute__ ((packed)) _HfsJBlockListHeader { + uint16_t max_blocks; /* reserved */ + uint16_t num_blocks; + uint32_t bytes_used; + uint32_t checksum; + uint32_t pad; + HfsJBlockInfo binfo[]; +}; +typedef struct _HfsJBlockListHeader HfsJBlockListHeader; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + HfsPrivateLinkExtent* bad_blocks_xtent_list; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + uint8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +struct _HfsPPrivateExtent { + PedSector start_sector; + PedSector sector_count; +}; +typedef struct _HfsPPrivateExtent HfsPPrivateExtent; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ file system specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + uint8_t* alloc_map; + uint8_t* dirty_alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + HfsPPrivateFile* allocation_file; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; + uint32_t jib_start_block; + uint32_t jl_start_block; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; + char free_geom; /* 1 = plus_geom must be freed */ +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + +extern uint8_t* hfs_block; +extern uint8_t* hfsp_block; +extern unsigned hfs_block_count; +extern unsigned hfsp_block_count; + +#endif /* _HFS_H */ diff --git a/libparted/fs/hfs/probe.c b/libparted/fs/hfs/probe.c new file mode 100644 index 0000000..d02ca28 --- /dev/null +++ b/libparted/fs/hfs/probe.c @@ -0,0 +1,238 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2014, 2019-2023 Free Software + Foundation, Inc. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "probe.h" + +int +hfsc_can_use_geom (PedGeometry* geom) +{ + PedDevice* dev; + + dev = geom->dev; + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't use HFS file systems on disks " + "with a sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + return 1; +} + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + PED_ASSERT (geom != NULL); + PED_ASSERT (hfsc_can_use_geom (geom)); + + const int sectors = ((3 * 512) + geom->dev->sector_size - 1) / + geom->dev->sector_size; + char * buf = alloca (sectors * geom->dev->sector_size); + + mdb = (HfsMasterDirectoryBlock *)(buf+1024); + + /* is 5 an intelligent value ? */ + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 0, sectors)) + || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / geom->dev->sector_size))); + max = search + (PED_BE32_TO_CPU (mdb->block_size) / geom->dev->sector_size); + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} + +PedGeometry* +hfsplus_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_ret = hfs_and_wrapper_probe(geom))) { + /* HFS+ is embedded in an HFS volume ? */ + HfsMasterDirectoryBlock *mdb; + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (mdb->old_new.embedded.signature + != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } else { + /* This is a standalone HFS+ volume ? */ + PedSector search, max; + HfsPVolumeHeader *vh; + vh = (HfsPVolumeHeader *) buf; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) + return NULL; + + /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */ + /* But previous versions of my implementation used to */ + /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */ + /* (blocks-1)*sz-1 has to be scanned last, because */ + /* it can belong to a regular file */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) + 2; + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE)) + return geom_ret; + } + search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 1; + if ((search < 0) + || !ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1) + || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } +} + +PedGeometry* +hfs_probe (PedGeometry* geom) +{ + PedGeometry* geom_base; + PedGeometry* geom_plus = NULL; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_base = hfs_and_wrapper_probe(geom)) + && (!(geom_plus = hfsplus_probe(geom_base)))) + return geom_base; + else { + if (geom_base) ped_geometry_destroy (geom_base); + if (geom_plus) ped_geometry_destroy (geom_plus); + return NULL; + } +} + +PedGeometry* +hfsx_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector search, max; + HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE))) + return NULL; + + /* unlike the hfs+ code, which should be kept compatible + with my old previous implementations, we only care here + about legal alternate VH positions, like TN1150 describes them */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ); + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} diff --git a/libparted/fs/hfs/probe.h b/libparted/fs/hfs/probe.h new file mode 100644 index 0000000..29ce880 --- /dev/null +++ b/libparted/fs/hfs/probe.h @@ -0,0 +1,44 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2014, 2019-2023 Free Software + Foundation, Inc. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _PROBE_H +#define _PROBE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsc_can_use_geom (PedGeometry* geom); + +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom); + +PedGeometry* +hfsplus_probe (PedGeometry* geom); + +PedGeometry* +hfs_probe (PedGeometry* geom); + +PedGeometry* +hfsx_probe (PedGeometry* geom); + +#endif /* _PROBE_H */ |