2 libparted - a library for manipulating disk partitions
3 Copyright (C) 2004-2005, 2007, 2009-2012 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <parted/parted.h>
24 #include <parted/endian.h>
25 #include <parted/debug.h>
30 # define _(String) dgettext (PACKAGE, String)
32 # define _(String) (String)
33 #endif /* ENABLE_NLS */
40 /* - if a < b, 0 if a == b, + if a > b */
41 /* Comparaison is done in the following order : */
42 /* CNID, then fork type, then start block */
43 /* Note that HFS implementation in linux has a bug */
44 /* in this function */
46 hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b)
48 HfsExtentKey* key1 = (HfsExtentKey*) a;
49 HfsExtentKey* key2 = (HfsExtentKey*) b;
51 /* do NOT use a substraction, because */
52 /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */
53 /* would return -2, despite the fact */
54 /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */
55 if (key1->file_ID != key2->file_ID)
56 return PED_BE32_TO_CPU(key1->file_ID) <
57 PED_BE32_TO_CPU(key2->file_ID) ?
60 if (key1->type != key2->type)
61 return (int)(key1->type - key2->type);
63 if (key1->start == key2->start)
65 /* the whole thing wont work with 16 bits ints */
67 return (int)( PED_BE16_TO_CPU(key1->start) -
68 PED_BE16_TO_CPU(key2->start) );
71 /* do a B-Tree lookup */
72 /* read the first record immediatly inferior or egal to the given key */
73 /* return 0 on error */
74 /* record_out _must_ be large enough to receive record_size bytes */
75 /* WARNING : the compare function called only handle Extents BTree */
76 /* so modify this function if you want to do lookup in */
77 /* other BTrees has well */
79 hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
80 void *record_out, unsigned int record_size,
81 HfsCPrivateLeafRec* record_ref)
83 uint8_t node[PED_SECTOR_SIZE_DEFAULT];
84 HfsHeaderRecord* header;
85 HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
86 HfsPrivateGenericKey* record_key = NULL;
87 unsigned int node_number, record_number;
90 /* Read the header node */
91 if (!hfs_file_read_sector(b_tree_file, node, 0))
93 header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
94 (node+(PED_SECTOR_SIZE_DEFAULT-2))))));
96 /* Get the node number of the root */
97 node_number = PED_BE32_TO_CPU(header->root_node);
101 /* Read the root node */
102 if (!hfs_file_read_sector(b_tree_file, node, node_number))
105 /* Follow the white rabbit */
107 record_number = PED_BE16_TO_CPU (desc->rec_nb);
108 for (i = record_number; i; i--) {
109 record_key = (HfsPrivateGenericKey*)
110 (node + PED_BE16_TO_CPU(*((uint16_t *)
111 (node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
112 /* check for obvious error in FS */
113 if (((uint8_t*)record_key - node < HFS_FIRST_REC)
114 || ((uint8_t*)record_key - node
115 >= PED_SECTOR_SIZE_DEFAULT
116 - 2 * (signed)(record_number+1))) {
117 ped_exception_throw (
119 PED_EXCEPTION_CANCEL,
120 _("The file system contains errors."));
123 if (hfs_extent_key_cmp(record_key, key) <= 0)
127 if (desc->type == HFS_IDX_NODE) {
130 skip = (1 + record_key->key_length + 1) & ~1;
131 node_number = PED_BE32_TO_CPU (*((uint32_t *)
132 (((uint8_t *) record_key) + skip)));
133 if (!hfs_file_read_sector(b_tree_file, node,
140 /* copy the result if needed */
142 memcpy (record_out, record_key, record_size);
144 /* send record reference if needed */
146 record_ref->node_size = 1; /* in sectors */
147 record_ref->node_number = node_number;
148 record_ref->record_pos = (uint8_t*)record_key - node;
149 record_ref->record_number = i;
156 /* free the bad blocks linked list */
158 hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first)
160 HfsPrivateLinkExtent* next;
169 /* This function reads bad blocks extents in the extents file
170 and store it in f.s. specific data of fs */
172 hfs_read_bad_blocks (const PedFileSystem *fs)
174 HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
177 if (priv_data->bad_blocks_loaded)
181 uint8_t record[sizeof (HfsExtentKey)
182 + sizeof (HfsExtDataRec)];
184 HfsExtentKey* ret_key = (HfsExtentKey*) record;
185 HfsExtDescriptor* ret_cache = (HfsExtDescriptor*)
186 (record + sizeof (HfsExtentKey));
187 unsigned int block, last_start, first_pass = 1;
189 search.key_length = sizeof (HfsExtentKey) - 1;
190 search.type = HFS_DATA_FORK;
191 search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
193 last_start = -1; block = 0;
197 search.start = PED_CPU_TO_BE16 (block);
198 if (!hfs_btree_search (priv_data->extent_file,
199 (HfsPrivateGenericKey*) &search,
200 record, sizeof (record), NULL)
201 || ret_key->file_ID != search.file_ID
202 || ret_key->type != search.type) {
208 if (PED_BE16_TO_CPU (ret_key->start) == last_start)
211 last_start = PED_BE16_TO_CPU (ret_key->start);
212 for (i = 0; i < HFS_EXT_NB; i++) {
213 if (ret_cache[i].block_count) {
214 HfsPrivateLinkExtent* new_xt =
215 (HfsPrivateLinkExtent*) ped_malloc (
216 sizeof (HfsPrivateLinkExtent));
219 new_xt->next = priv_data->bad_blocks_xtent_list;
220 memcpy(&(new_xt->extent), ret_cache+i,
221 sizeof (HfsExtDescriptor));
222 priv_data->bad_blocks_xtent_list = new_xt;
223 priv_data->bad_blocks_xtent_nb++;
224 block += PED_BE16_TO_CPU (
225 ret_cache[i].block_count);
231 priv_data->bad_blocks_loaded = 1;
234 errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
235 priv_data->bad_blocks_xtent_list=NULL;
236 priv_data->bad_blocks_xtent_nb=0;
240 /* This function check if fblock is a bad block */
242 hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
244 HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
246 HfsPrivateLinkExtent* walk;
248 for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
249 /* Won't compile without the strange cast ! gcc bug ? */
250 /* or maybe C subtilties... */
251 if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) &&
252 (fblock < (unsigned int) (PED_BE16_TO_CPU (
253 walk->extent.start_block)
255 walk->extent.block_count))))
262 /* This function returns the first sector of the last free block of an
263 HFS volume we can get after a hfs_pack_free_space_from_block call */
264 /* On error this function returns 0 */
266 hfs_get_empty_end (const PedFileSystem *fs)
268 HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
270 HfsMasterDirectoryBlock* mdb = priv_data->mdb;
271 unsigned int block, last_bad, end_free_blocks;
273 /* find the next block to the last bad block of the volume */
274 if (!hfs_read_bad_blocks (fs))
277 HfsPrivateLinkExtent* l;
279 for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
280 if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block)
281 + PED_BE16_TO_CPU (l->extent.block_count) > last_bad)
282 last_bad = PED_BE16_TO_CPU (l->extent.start_block)
283 + PED_BE16_TO_CPU (l->extent.block_count);
286 /* Count the free blocks from last_bad to the end of the volume */
288 for (block = last_bad;
289 block < PED_BE16_TO_CPU (mdb->total_blocks);
291 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
295 /* Calculate the block that will by the first free at the
297 block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks;
299 return (PedSector) PED_BE16_TO_CPU (mdb->start_block)
300 + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size)
301 / PED_SECTOR_SIZE_DEFAULT);
304 /* return the block which should be used to pack data to have at
305 least free fblock blocks at the end of the volume */
307 hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
309 HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
313 for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1;
316 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
320 while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
322 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
328 #endif /* !DISCOVER_ONLY */