xref: /onnv-gate/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.c (revision 9663:ace9a2ac3683)
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2000, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
4 
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.
9 
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.
14 
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/>.
17 */
18 
19 /*
20    Author : Guillaume Knispel <k_guillaume@libertysurf.fr>
21    Report bug to <bug-parted@gnu.org>
22 */
23 
24 #include <config.h>
25 
26 #include <parted/parted.h>
27 #include <parted/endian.h>
28 #include <parted/debug.h>
29 #include <stdint.h>
30 
31 #if ENABLE_NLS
32 #  include <libintl.h>
33 #  define _(String) dgettext (PACKAGE, String)
34 #else
35 #  define _(String) (String)
36 #endif /* ENABLE_NLS */
37 
38 #include "hfs.h"
39 #include "probe.h"
40 
41 uint8_t* hfs_block = NULL;
42 uint8_t* hfsp_block = NULL;
43 unsigned hfs_block_count;
44 unsigned hfsp_block_count;
45 
46 #define HFS_BLOCK_SIZES       ((int[2]){512, 0})
47 #define HFSP_BLOCK_SIZES       ((int[2]){512, 0})
48 #define HFSX_BLOCK_SIZES       ((int[2]){512, 0})
49 
50 #ifndef DISCOVER_ONLY
51 #include "file.h"
52 #include "reloc.h"
53 #include "advfs.h"
54 
55 static PedFileSystemType hfs_type;
56 static PedFileSystemType hfsplus_type;
57 
58 
59 /* ----- HFS ----- */
60 
61 /* This is a very unundoable operation */
62 /* Maybe I shouldn't touch the alternate MDB ? */
63 /* Anyway clobber is call before other fs creation */
64 /* So this is a non-issue */
65 static int
hfs_clobber(PedGeometry * geom)66 hfs_clobber (PedGeometry* geom)
67 {
68 	uint8_t	buf[PED_SECTOR_SIZE_DEFAULT];
69 
70 	memset (buf, 0, PED_SECTOR_SIZE_DEFAULT);
71 
72 	/* destroy boot blocks, mdb, alternate mdb ... */
73 	return	(!!ped_geometry_write (geom, buf, 0, 1)) &
74 		(!!ped_geometry_write (geom, buf, 1, 1)) &
75 		(!!ped_geometry_write (geom, buf, 2, 1)) &
76 		(!!ped_geometry_write (geom, buf, geom->length - 2, 1)) &
77 		(!!ped_geometry_write (geom, buf, geom->length - 1, 1)) &
78 		(!!ped_geometry_sync  (geom));
79 }
80 
81 static PedFileSystem*
hfs_open(PedGeometry * geom)82 hfs_open (PedGeometry* geom)
83 {
84 	uint8_t			buf[PED_SECTOR_SIZE_DEFAULT];
85 	PedFileSystem*		fs;
86 	HfsMasterDirectoryBlock* mdb;
87 	HfsPrivateFSData* 	priv_data;
88 
89 	if (!hfsc_can_use_geom (geom))
90 		return NULL;
91 
92 	/* Read MDB */
93 	if (!ped_geometry_read (geom, buf, 2, 1))
94 		return NULL;
95 
96 	/* Allocate memory */
97 	fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
98 	if (!fs) goto ho;
99 	mdb = (HfsMasterDirectoryBlock*)
100 		ped_malloc (sizeof (HfsMasterDirectoryBlock));
101 	if (!mdb) goto ho_fs;
102 	priv_data = (HfsPrivateFSData*)
103 		ped_malloc (sizeof (HfsPrivateFSData));
104 	if (!priv_data) goto ho_mdb;
105 
106 	memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock));
107 
108 	/* init structures */
109 	priv_data->mdb = mdb;
110 	priv_data->bad_blocks_loaded = 0;
111 	priv_data->bad_blocks_xtent_nb = 0;
112 	priv_data->bad_blocks_xtent_list = NULL;
113 	priv_data->extent_file =
114 	    hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
115 			   mdb->extents_file_rec,
116 			   PED_CPU_TO_BE32 (mdb->extents_file_size)
117 			   / PED_SECTOR_SIZE_DEFAULT);
118 	if (!priv_data->extent_file) goto ho_pd;
119 	priv_data->catalog_file =
120 	    hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
121 			   mdb->catalog_file_rec,
122 			   PED_CPU_TO_BE32 (mdb->catalog_file_size)
123 			   / PED_SECTOR_SIZE_DEFAULT);
124 	if (!priv_data->catalog_file) goto ho_ce;
125 	/* Read allocation blocks */
126 	if (!ped_geometry_read(geom, priv_data->alloc_map,
127 			       PED_BE16_TO_CPU (mdb->volume_bitmap_block),
128 			       ( PED_BE16_TO_CPU (mdb->total_blocks)
129 			         + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
130 			       / (PED_SECTOR_SIZE_DEFAULT * 8) ) )
131 		goto ho_cf;
132 
133 	fs->type = &hfs_type;
134 	fs->geom = ped_geometry_duplicate (geom);
135 	if (!fs->geom) goto ho_cf;
136 	fs->type_specific = (void*) priv_data;
137 	fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes)
138 			>> HFS_UNMOUNTED ) & 1;
139 
140 	return fs;
141 
142 /*--- clean error handling ---*/
143 ho_cf:	hfs_file_close(priv_data->catalog_file);
144 ho_ce:	hfs_file_close(priv_data->extent_file);
145 ho_pd:	ped_free(priv_data);
146 ho_mdb: ped_free(mdb);
147 ho_fs:	ped_free(fs);
148 ho:	return NULL;
149 }
150 
151 static int
hfs_close(PedFileSystem * fs)152 hfs_close (PedFileSystem *fs)
153 {
154 	HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific;
155 
156 	hfs_file_close (priv_data->extent_file);
157 	hfs_file_close (priv_data->catalog_file);
158 	if (priv_data->bad_blocks_loaded)
159 		hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list);
160 	ped_free (priv_data->mdb);
161 	ped_free (priv_data);
162 	ped_geometry_destroy (fs->geom);
163 	ped_free (fs);
164 
165 	return 1;
166 }
167 
168 static PedConstraint*
hfs_get_resize_constraint(const PedFileSystem * fs)169 hfs_get_resize_constraint (const PedFileSystem *fs)
170 {
171 	PedDevice*	dev = fs->geom->dev;
172 	PedAlignment	start_align;
173 	PedGeometry	start_sector;
174 	PedGeometry	full_dev;
175 	PedSector	min_size;
176 
177 	if (!ped_alignment_init (&start_align, fs->geom->start, 0))
178 		return NULL;
179 	if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
180 		return NULL;
181 	if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
182 		return NULL;
183 	/* 2 = last two sectors (alternate MDB and unused sector) */
184 	min_size = hfs_get_empty_end(fs) + 2;
185 	if (min_size == 2) return NULL;
186 
187 	return ped_constraint_new (&start_align, ped_alignment_any,
188 				   &start_sector, &full_dev, min_size,
189 				   fs->geom->length);
190 }
191 
192 static int
hfs_resize(PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)193 hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
194 {
195 	uint8_t			buf[PED_SECTOR_SIZE_DEFAULT];
196 	unsigned int		nblock, nfree;
197 	unsigned int		block, to_free;
198 	HfsPrivateFSData* 	priv_data;
199 	HfsMasterDirectoryBlock* mdb;
200 	int			resize = 1;
201 	unsigned int		hfs_sect_block;
202 	PedSector		hgee;
203 
204 	/* check preconditions */
205 	PED_ASSERT (fs != NULL, return 0);
206 	PED_ASSERT (fs->geom != NULL, return 0);
207 	PED_ASSERT (geom != NULL, return 0);
208 #ifdef DEBUG
209         PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
210 #else
211         if ((hgee = hfs_get_empty_end(fs)) == 0)
212                 return 0;
213 #endif
214 
215 	PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
216 
217 	if (ped_geometry_test_equal(fs->geom, geom))
218 		return 1;
219 
220 	priv_data = (HfsPrivateFSData*) fs->type_specific;
221 	mdb = priv_data->mdb;
222 	hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size)
223 			 / PED_SECTOR_SIZE_DEFAULT;
224 
225 	if (fs->geom->start != geom->start
226 	    || geom->length > fs->geom->length
227 	    || geom->length < hgee + 2) {
228 		ped_exception_throw (
229 			PED_EXCEPTION_NO_FEATURE,
230 			PED_EXCEPTION_CANCEL,
231 			_("Sorry, HFS cannot be resized that way yet."));
232 		return 0;
233 	}
234 
235 	/* Flush caches */
236 	if (!ped_geometry_sync(fs->geom))
237 		return 0;
238 
239 	/* Clear the unmounted bit */
240 	mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED ));
241 	if (!ped_geometry_read (fs->geom, buf, 2, 1))
242 		return 0;
243 	memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock));
244 	if (   !ped_geometry_write (fs->geom, buf, 2, 1)
245 	    || !ped_geometry_sync  (fs->geom))
246 		return 0;
247 
248 	ped_timer_reset (timer);
249 	ped_timer_set_state_name(timer, _("shrinking"));
250 	ped_timer_update(timer, 0.0);
251 	/* relocate data */
252 	to_free = ( fs->geom->length - geom->length
253 		    + hfs_sect_block - 1 )
254 		  / hfs_sect_block ;
255 	block = hfs_find_start_pack (fs, to_free);
256 	if (!hfs_pack_free_space_from_block (fs, block,	timer, to_free)) {
257 		resize = 0;
258 		ped_exception_throw (
259 			PED_EXCEPTION_ERROR,
260 			PED_EXCEPTION_CANCEL,
261 			_("Data relocation has failed."));
262 		goto write_MDB;
263 	}
264 
265 	/* Calculate new block number and other MDB field */
266 	nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) )
267 		 / hfs_sect_block;
268 	nfree = PED_BE16_TO_CPU (mdb->free_blocks)
269 		- ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock );
270 
271 	/* Check that all block after future end are really free */
272 	for (block = nblock;
273 	     block < PED_BE16_TO_CPU (mdb->total_blocks);
274 	     block++) {
275 		if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
276 			resize = 0;
277 			ped_exception_throw (
278 				PED_EXCEPTION_ERROR,
279 				PED_EXCEPTION_CANCEL,
280 				_("Data relocation left some data in the end "
281 				  "of the volume."));
282 			goto write_MDB;
283 		}
284 	}
285 
286 	/* Mark out of volume blocks as used
287 	(broken implementations compatibility) */
288 	for ( block = nblock; block < (1 << 16); ++block)
289 		SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
290 
291 	/* save the allocation map
292 	I do not write until start of allocation blocks
293 	but only until pre-resize end of bitmap blocks
294 	because the specifications do _not_ assert that everything
295 	until allocation blocks is boot, mdb and alloc */
296 	ped_geometry_write(fs->geom, priv_data->alloc_map,
297 		PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
298 		( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
299 		  + PED_SECTOR_SIZE_DEFAULT * 8 - 1)
300 		/ (PED_SECTOR_SIZE_DEFAULT * 8));
301 
302 	/* Update geometry */
303 	if (resize) {
304 		/* update in fs structure */
305 		if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock)
306 			mdb->next_allocation = PED_CPU_TO_BE16 (0);
307 		mdb->total_blocks = PED_CPU_TO_BE16 (nblock);
308 		mdb->free_blocks = PED_CPU_TO_BE16 (nfree);
309 		/* update parted structure */
310 		fs->geom->length = geom->length;
311 		fs->geom->end = fs->geom->start + geom->length - 1;
312 	}
313 
314 	/* Set the unmounted bit */
315 	mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED );
316 
317 	/* Effective write */
318     write_MDB:
319 	ped_timer_set_state_name(timer,_("writing HFS Master Directory Block"));
320 
321 	if (!hfs_update_mdb(fs)) {
322 		ped_geometry_sync(geom);
323 		return 0;
324 	}
325 
326 	if (!ped_geometry_sync(geom))
327 		return 0;
328 
329 	ped_timer_update(timer, 1.0);
330 
331 	return (resize);
332 }
333 
334 /* ----- HFS+ ----- */
335 
336 #include "file_plus.h"
337 #include "advfs_plus.h"
338 #include "reloc_plus.h"
339 #include "journal.h"
340 
341 static int
hfsplus_clobber(PedGeometry * geom)342 hfsplus_clobber (PedGeometry* geom)
343 {
344 	unsigned int i = 1;
345 	uint8_t				buf[PED_SECTOR_SIZE_DEFAULT];
346 	HfsMasterDirectoryBlock		*mdb;
347 
348 	mdb = (HfsMasterDirectoryBlock *) buf;
349 
350 	if (!ped_geometry_read (geom, buf, 2, 1))
351 		return 0;
352 
353 	if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) {
354 		/* embedded hfs+ */
355 		PedGeometry	*embedded;
356 
357 		i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT;
358 		embedded = ped_geometry_new (
359 		    geom->dev,
360 		    (PedSector) geom->start
361 		     + PED_BE16_TO_CPU (mdb->start_block)
362 		     + (PedSector) PED_BE16_TO_CPU (
363 			mdb->old_new.embedded.location.start_block ) * i,
364 		    (PedSector) PED_BE16_TO_CPU (
365 			mdb->old_new.embedded.location.block_count ) * i );
366 		if (!embedded) i = 0;
367 		else {
368 			i = hfs_clobber (embedded);
369 			ped_geometry_destroy (embedded);
370 		}
371 	}
372 
373 	/* non-embedded or envelop destroy as hfs */
374 	return ( hfs_clobber (geom) && i );
375 }
376 
377 static int
hfsplus_close(PedFileSystem * fs)378 hfsplus_close (PedFileSystem *fs)
379 {
380 	HfsPPrivateFSData* 	priv_data = (HfsPPrivateFSData*)
381 						fs->type_specific;
382 
383 	if (priv_data->bad_blocks_loaded)
384 		hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
385 	ped_free(priv_data->alloc_map);
386 	ped_free(priv_data->dirty_alloc_map);
387 	hfsplus_file_close (priv_data->allocation_file);
388 	hfsplus_file_close (priv_data->attributes_file);
389 	hfsplus_file_close (priv_data->catalog_file);
390 	hfsplus_file_close (priv_data->extents_file);
391 	if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
392 	if (priv_data->wrapper) hfs_close(priv_data->wrapper);
393 	ped_geometry_destroy (fs->geom);
394 	ped_free(priv_data->vh);
395 	ped_free(priv_data);
396 	ped_free(fs);
397 
398 	return 1;
399 }
400 
401 static PedFileSystem*
hfsplus_open(PedGeometry * geom)402 hfsplus_open (PedGeometry* geom)
403 {
404 	uint8_t			buf[PED_SECTOR_SIZE_DEFAULT];
405 	PedFileSystem*		fs;
406 	HfsPVolumeHeader*	vh;
407 	HfsPPrivateFSData* 	priv_data;
408 	PedGeometry*		wrapper_geom;
409 	unsigned int		map_sectors;
410 
411 	if (!hfsc_can_use_geom (geom))
412 		return NULL;
413 
414 	fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
415 	if (!fs) goto hpo;
416 	vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader));
417 	if (!vh) goto hpo_fs;
418 	priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData));
419 	if (!priv_data) goto hpo_vh;
420 
421 	fs->geom = ped_geometry_duplicate (geom);
422 	if (!fs->geom) goto hpo_pd;
423 	fs->type_specific = (void*) priv_data;
424 
425 	if ((wrapper_geom = hfs_and_wrapper_probe (geom))) {
426 		HfsPrivateFSData* 	hfs_priv_data;
427 		PedSector		abs_sect, length;
428 		unsigned int		bs;
429 
430 		ped_geometry_destroy (wrapper_geom);
431 		priv_data->wrapper = hfs_open(geom);
432 		if (!priv_data->wrapper) goto hpo_gm;
433 		hfs_priv_data = (HfsPrivateFSData*)
434 			priv_data->wrapper->type_specific;
435 		bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
436 		     / PED_SECTOR_SIZE_DEFAULT;
437 		abs_sect = (PedSector) geom->start
438 			   + (PedSector) PED_BE16_TO_CPU (
439 					    hfs_priv_data->mdb->start_block)
440 			   + (PedSector) PED_BE16_TO_CPU (
441 					    hfs_priv_data->mdb->old_new
442 					    .embedded.location.start_block )
443 			                 * bs;
444 		length = (PedSector) PED_BE16_TO_CPU (
445 					    hfs_priv_data->mdb->old_new
446 					    .embedded.location.block_count)
447 				     * bs;
448 		priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect,
449 							 length);
450 		if (!priv_data->plus_geom) goto hpo_wr;
451 		priv_data->free_geom = 1;
452 	} else {
453 		priv_data->wrapper = NULL;
454 		priv_data->plus_geom = fs->geom;
455 		priv_data->free_geom = 0;
456 	}
457 
458 	if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg;
459 	memcpy (vh, buf, sizeof (HfsPVolumeHeader));
460 	priv_data->vh = vh;
461 
462 	if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE)
463 	    && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) {
464 		ped_exception_throw (
465 			PED_EXCEPTION_BUG,
466 			PED_EXCEPTION_CANCEL,
467 			_("No valid HFS[+X] signature has been found while "
468 			  "opening."));
469 		goto hpo_pg;
470 	}
471 
472 	if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE)
473 	    && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) {
474 		if (ped_exception_throw (
475 			PED_EXCEPTION_NO_FEATURE,
476 			PED_EXCEPTION_IGNORE_CANCEL,
477 			_("Version %d of HFS+ isn't supported."),
478 			PED_BE16_TO_CPU(vh->version))
479 				!= PED_EXCEPTION_IGNORE)
480 			goto hpo_pg;
481 	}
482 
483 	if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE)
484 	    && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) {
485 		if (ped_exception_throw (
486 			PED_EXCEPTION_NO_FEATURE,
487 			PED_EXCEPTION_IGNORE_CANCEL,
488 			_("Version %d of HFSX isn't supported."),
489 			PED_BE16_TO_CPU(vh->version))
490 				!= PED_EXCEPTION_IGNORE)
491 			goto hpo_pg;
492 	}
493 
494 	priv_data->jib_start_block = 0;
495 	priv_data->jl_start_block = 0;
496 	if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) {
497 		if (!hfsj_replay_journal(fs))
498 			goto hpo_pg;
499 	}
500 
501 	priv_data->bad_blocks_loaded = 0;
502 	priv_data->bad_blocks_xtent_nb = 0;
503 	priv_data->bad_blocks_xtent_list = NULL;
504 	priv_data->extents_file =
505 		hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
506 				   vh->extents_file.extents,
507 				   PED_BE64_TO_CPU (
508 					vh->extents_file.logical_size )
509 				   / PED_SECTOR_SIZE_DEFAULT);
510 	if (!priv_data->extents_file) goto hpo_pg;
511 	priv_data->catalog_file =
512 		hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
513 				   vh->catalog_file.extents,
514 				   PED_BE64_TO_CPU (
515 					vh->catalog_file.logical_size )
516 				   / PED_SECTOR_SIZE_DEFAULT);
517 	if (!priv_data->catalog_file) goto hpo_ce;
518 	priv_data->attributes_file =
519 		hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID),
520 				   vh->attributes_file.extents,
521 				   PED_BE64_TO_CPU (
522 					vh->attributes_file.logical_size)
523 				   / PED_SECTOR_SIZE_DEFAULT);
524 	if (!priv_data->attributes_file) goto hpo_cc;
525 
526 	map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks)
527 	                + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
528 		      / (PED_SECTOR_SIZE_DEFAULT * 8);
529 	priv_data->dirty_alloc_map = (uint8_t*)
530 		ped_malloc ((map_sectors + 7) / 8);
531 	if (!priv_data->dirty_alloc_map) goto hpo_cl;
532 	memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8);
533 	priv_data->alloc_map = (uint8_t*)
534 		ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT);
535 	if (!priv_data->alloc_map) goto hpo_dm;
536 
537 	priv_data->allocation_file =
538 		hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID),
539 				   vh->allocation_file.extents,
540 				   PED_BE64_TO_CPU (
541 					vh->allocation_file.logical_size)
542 				   / PED_SECTOR_SIZE_DEFAULT);
543 	if (!priv_data->allocation_file) goto hpo_am;
544 	if (!hfsplus_file_read (priv_data->allocation_file,
545 				priv_data->alloc_map, 0, map_sectors)) {
546 		hfsplus_close(fs);
547 		return NULL;
548 	}
549 
550 	fs->type = &hfsplus_type;
551 	fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1)
552 	      && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1);
553 
554 	return fs;
555 
556 /*--- clean error handling ---*/
557 hpo_am: ped_free(priv_data->alloc_map);
558 hpo_dm: ped_free(priv_data->dirty_alloc_map);
559 hpo_cl: hfsplus_file_close (priv_data->attributes_file);
560 hpo_cc:	hfsplus_file_close (priv_data->catalog_file);
561 hpo_ce:	hfsplus_file_close (priv_data->extents_file);
562 hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
563 hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper);
564 hpo_gm: ped_geometry_destroy (fs->geom);
565 hpo_pd: ped_free(priv_data);
566 hpo_vh: ped_free(vh);
567 hpo_fs: ped_free(fs);
568 hpo:	return NULL;
569 }
570 
571 static PedConstraint*
hfsplus_get_resize_constraint(const PedFileSystem * fs)572 hfsplus_get_resize_constraint (const PedFileSystem *fs)
573 {
574 	PedDevice*	dev = fs->geom->dev;
575 	PedAlignment	start_align;
576 	PedGeometry	start_sector;
577 	PedGeometry	full_dev;
578 	PedSector	min_size;
579 
580 	if (!ped_alignment_init (&start_align, fs->geom->start, 0))
581 		return NULL;
582 	if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
583 		return NULL;
584 	if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
585 		return NULL;
586 
587 	min_size = hfsplus_get_min_size (fs);
588 	if (!min_size) return NULL;
589 
590 	return ped_constraint_new (&start_align, ped_alignment_any,
591 				   &start_sector, &full_dev, min_size,
592 				   fs->geom->length);
593 }
594 
595 static int
hfsplus_volume_resize(PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)596 hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
597 {
598 	uint8_t			buf[PED_SECTOR_SIZE_DEFAULT];
599 	unsigned int		nblock, nfree, mblock;
600 	unsigned int		block, to_free, old_blocks;
601 	HfsPPrivateFSData* 	priv_data = (HfsPPrivateFSData*)
602 						fs->type_specific;
603 	HfsPVolumeHeader* 	vh = priv_data->vh;
604 	int			resize = 1;
605 	unsigned int		hfsp_sect_block =
606 				    ( PED_BE32_TO_CPU (vh->block_size)
607 				      / PED_SECTOR_SIZE_DEFAULT );
608 	unsigned int		map_sectors;
609 
610 	old_blocks = PED_BE32_TO_CPU (vh->total_blocks);
611 
612 	/* Flush caches */
613 	if (!ped_geometry_sync(priv_data->plus_geom))
614 		return 0;
615 
616 	/* Clear the unmounted bit */
617 	/* and set the implementation code (Apple Creator Code) */
618 	vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED ));
619 	vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk);
620 	if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1))
621 		return 0;
622 	memcpy (buf, vh, sizeof (HfsPVolumeHeader));
623 	if (   !ped_geometry_write (priv_data->plus_geom, buf, 2, 1)
624 	    || !ped_geometry_sync (priv_data->plus_geom))
625 		return 0;
626 
627 	ped_timer_reset (timer);
628 	ped_timer_set_state_name(timer, _("shrinking"));
629 	ped_timer_update(timer, 0.0);
630 	/* relocate data */
631 	to_free = ( priv_data->plus_geom->length
632 	          - geom->length + hfsp_sect_block
633 		  - 1 ) / hfsp_sect_block;
634 	block = hfsplus_find_start_pack (fs, to_free);
635 	if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) {
636 		resize = 0;
637 		ped_exception_throw (
638 			PED_EXCEPTION_ERROR,
639 			PED_EXCEPTION_CANCEL,
640 			_("Data relocation has failed."));
641 		goto write_VH;
642 	}
643 
644 	/* Calculate new block number and other VH field */
645 	/* nblock must be rounded _down_ */
646 	nblock = geom->length / hfsp_sect_block;
647 	nfree = PED_BE32_TO_CPU (vh->free_blocks)
648 		- (old_blocks - nblock);
649 	/* free block readjustement is only needed when incorrect nblock
650 	   was used by my previous implementation, so detect the case */
651 	if (priv_data->plus_geom->length < old_blocks
652 					   * ( PED_BE32_TO_CPU (vh->block_size)
653 					       / PED_SECTOR_SIZE_DEFAULT) ) {
654 		if (priv_data->plus_geom->length % hfsp_sect_block == 1)
655 			nfree++;
656 	}
657 
658 	/* Check that all block after future end are really free */
659 	mblock = ( priv_data->plus_geom->length - 2 )
660 		 / hfsp_sect_block;
661 	if (mblock > old_blocks - 1)
662 		mblock = old_blocks - 1;
663 	for ( block = nblock;
664 	      block < mblock;
665 	      block++ ) {
666 		if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
667 			resize = 0;
668 			ped_exception_throw (
669 				PED_EXCEPTION_ERROR,
670 				PED_EXCEPTION_CANCEL,
671 				_("Data relocation left some data at the end "
672 				  "of the volume."));
673 			goto write_VH;
674 		}
675 	}
676 
677 	/* Mark out of volume blocks as used */
678 	map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
679 	                / (PED_SECTOR_SIZE_DEFAULT * 8) )
680 		      * (PED_SECTOR_SIZE_DEFAULT * 8);
681 	for ( block = nblock; block < map_sectors; ++block)
682 		SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
683 
684 	/* Update geometry */
685 	if (resize) {
686 		/* update in fs structure */
687 		if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock)
688 			vh->next_allocation = PED_CPU_TO_BE32 (0);
689 		vh->total_blocks = PED_CPU_TO_BE32 (nblock);
690 		vh->free_blocks = PED_CPU_TO_BE32 (nfree);
691 		/* update parted structure */
692 		priv_data->plus_geom->length = geom->length;
693 		priv_data->plus_geom->end = priv_data->plus_geom->start
694 					    + geom->length - 1;
695 	}
696 
697 	/* Effective write */
698     write_VH:
699     	/* lasts two sectors are allocated by the alternate VH
700 	   and a reserved sector, and last block is always reserved */
701 	block = (priv_data->plus_geom->length - 1) / hfsp_sect_block;
702 	if (block < PED_BE32_TO_CPU (vh->total_blocks))
703 		SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
704 	block = (priv_data->plus_geom->length - 2) / hfsp_sect_block;
705 	if (block < PED_BE32_TO_CPU (vh->total_blocks))
706 		SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
707 	SET_BLOC_OCCUPATION(priv_data->alloc_map,
708 			    PED_BE32_TO_CPU (vh->total_blocks) - 1);
709 
710 	/* Write the _old_ area to set out of volume blocks as used */
711 	map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
712 	              / (PED_SECTOR_SIZE_DEFAULT * 8);
713 	if (!hfsplus_file_write (priv_data->allocation_file,
714 				 priv_data->alloc_map, 0, map_sectors)) {
715 		resize = 0;
716 		ped_exception_throw (
717 			PED_EXCEPTION_ERROR,
718 			PED_EXCEPTION_CANCEL,
719 			_("Error while writing the allocation file."));
720 	} else {
721 	/* Write remaining part of allocation bitmap */
722 	/* This is necessary to handle pre patch-11 and third party */
723 	/* implementations */
724 		memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT);
725 		for (block = map_sectors;
726 		     block < priv_data->allocation_file->sect_nb;
727 		     ++block) {
728 			if (!hfsplus_file_write_sector (
729 					priv_data->allocation_file,
730 					buf, block)) {
731 				ped_exception_throw (
732 					PED_EXCEPTION_WARNING,
733 					PED_EXCEPTION_IGNORE,
734 					_("Error while writing the "
735 					  "compatibility part of the "
736 					  "allocation file."));
737 				break;
738 			}
739 		}
740 	}
741 	ped_geometry_sync (priv_data->plus_geom);
742 
743 	if (resize) {
744 		/* Set the unmounted bit and clear the inconsistent bit */
745 		vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED );
746 		vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT );
747 	}
748 
749 	ped_timer_set_state_name(timer, _("writing HFS+ Volume Header"));
750 	if (!hfsplus_update_vh(fs)) {
751 		ped_geometry_sync(priv_data->plus_geom);
752 		return 0;
753 	}
754 
755 	if (!ped_geometry_sync(priv_data->plus_geom))
756 		return 0;
757 
758 	ped_timer_update(timer, 1.0);
759 
760 	return (resize);
761 }
762 
763 /* Update the HFS wrapper mdb and bad blocks file to reflect
764    the new geometry of the embedded HFS+ volume */
765 static int
hfsplus_wrapper_update(PedFileSystem * fs)766 hfsplus_wrapper_update (PedFileSystem* fs)
767 {
768 	uint8_t			node[PED_SECTOR_SIZE_DEFAULT];
769 	HfsCPrivateLeafRec	ref;
770 	HfsExtentKey		key;
771 	HfsNodeDescriptor*	node_desc = (HfsNodeDescriptor*) node;
772 	HfsExtentKey*		ret_key;
773 	HfsExtDescriptor*	ret_data;
774 	unsigned int		i;
775 	HfsPPrivateFSData* 	priv_data = (HfsPPrivateFSData*)
776 						fs->type_specific;
777 	HfsPrivateFSData* 	hfs_priv_data = (HfsPrivateFSData*)
778 					    priv_data->wrapper->type_specific;
779 	unsigned int		hfs_sect_block =
780 			PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
781 			/ PED_SECTOR_SIZE_DEFAULT ;
782 	PedSector		hfsplus_sect = (PedSector)
783 			PED_BE32_TO_CPU (priv_data->vh->total_blocks)
784 			* ( PED_BE32_TO_CPU (priv_data->vh->block_size)
785 			    / PED_SECTOR_SIZE_DEFAULT );
786 	unsigned int		hfs_blocks_embedded =
787 				    (hfsplus_sect + hfs_sect_block - 1)
788 				    / hfs_sect_block;
789 	unsigned int		hfs_blocks_embedded_old;
790 
791 	/* update HFS wrapper MDB */
792 	hfs_blocks_embedded_old = PED_BE16_TO_CPU (
793 					hfs_priv_data->mdb->old_new
794 					.embedded.location.block_count );
795 	hfs_priv_data->mdb->old_new.embedded.location.block_count =
796 		PED_CPU_TO_BE16 (hfs_blocks_embedded);
797 	/* maybe macOS will boot with this */
798 	/* update : yes it does \o/ :) */
799 	hfs_priv_data->mdb->free_blocks =
800 	    PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks)
801 	                    + hfs_blocks_embedded_old
802 			    - hfs_blocks_embedded );
803 
804 	if (!hfs_update_mdb(priv_data->wrapper))
805 		return 0;
806 
807 	/* force reload bad block list */
808 	if (hfs_priv_data->bad_blocks_loaded) {
809 		hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list);
810 		hfs_priv_data->bad_blocks_xtent_list = NULL;
811 		hfs_priv_data->bad_blocks_xtent_nb = 0;
812 		hfs_priv_data->bad_blocks_loaded = 0;
813 	}
814 
815 	/* clean HFS wrapper allocation map */
816 	for (i = PED_BE16_TO_CPU (
817 			hfs_priv_data->mdb->old_new.embedded
818 			.location.start_block )
819 		 + hfs_blocks_embedded;
820 	     i < PED_BE16_TO_CPU (
821 	    		hfs_priv_data->mdb->old_new.embedded
822 			.location.start_block )
823 		 + hfs_blocks_embedded_old;
824 	     i++ ) {
825 		CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i);
826 	}
827 	/* and save it */
828 	if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map,
829 				 PED_BE16_TO_CPU (
830 				      hfs_priv_data->mdb->volume_bitmap_block ),
831 				 ( PED_BE16_TO_CPU (
832 				        hfs_priv_data->mdb->total_blocks )
833 				   + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
834 				 / (PED_SECTOR_SIZE_DEFAULT * 8)))
835 		return 0;
836 	if (!ped_geometry_sync (fs->geom))
837 		return 0;
838 
839 	/* search and update the bad blocks file */
840 	key.key_length = sizeof(key) - 1;
841 	key.type = HFS_DATA_FORK;
842 	key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
843 	key.start = 0;
844 	if (!hfs_btree_search (hfs_priv_data->extent_file,
845 			       (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) {
846 		ped_exception_throw (
847 			PED_EXCEPTION_ERROR,
848 			PED_EXCEPTION_CANCEL,
849 			_("An error occurred while looking for the mandatory "
850 			  "bad blocks file."));
851 		return 0;
852 	}
853 	if (!hfs_file_read_sector (hfs_priv_data->extent_file, node,
854 				   ref.node_number))
855 		return 0;
856 	ret_key = (HfsExtentKey*) (node + ref.record_pos);
857 	ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
858 					 + sizeof (HfsExtentKey) );
859 
860 	while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) {
861 		for (i = 0; i < HFS_EXT_NB; i++) {
862 			if ( ret_data[i].start_block
863 			     == hfs_priv_data->mdb->old_new
864 			        .embedded.location.start_block) {
865 				ret_data[i].block_count =
866 				    hfs_priv_data->mdb->old_new
867 				    .embedded.location.block_count;
868 				/* found ! : update */
869 				if (!hfs_file_write_sector (
870 					  hfs_priv_data->extent_file,
871 					  node, ref.node_number)
872 				    || !ped_geometry_sync(fs->geom))
873 					return 0;
874 				return 1;
875 			}
876 		}
877 
878 		if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) {
879 			ref.record_number++;
880 		} else {
881 			ref.node_number = PED_BE32_TO_CPU (node_desc->next);
882 			if (!ref.node_number
883 			    || !hfs_file_read_sector(hfs_priv_data->extent_file,
884 						     node, ref.node_number))
885 				goto bb_not_found;
886 			ref.record_number = 1;
887 		}
888 
889 		ref.record_pos =
890 			PED_BE16_TO_CPU (*((uint16_t *)
891 				(node + (PED_SECTOR_SIZE_DEFAULT
892 				         - 2*ref.record_number))));
893 		ret_key = (HfsExtentKey*) (node + ref.record_pos);
894 		ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
895 						 + sizeof (HfsExtentKey) );
896 	}
897 
898 bb_not_found:
899 	/* not found : not a valid hfs+ wrapper : failure */
900 	ped_exception_throw (
901 		PED_EXCEPTION_ERROR,
902 		PED_EXCEPTION_CANCEL,
903 		_("It seems there is an error in the HFS wrapper: the bad "
904 		  "blocks file doesn't contain the embedded HFS+ volume."));
905 	return 0;
906 }
907 
908 static int
hfsplus_resize(PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)909 hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
910 {
911 	HfsPPrivateFSData* 	priv_data;
912 	PedTimer*		timer_plus;
913 	PedGeometry*		embedded_geom;
914 	PedSector		hgms;
915 
916 	/* check preconditions */
917 	PED_ASSERT (fs != NULL, return 0);
918 	PED_ASSERT (fs->geom != NULL, return 0);
919 	PED_ASSERT (geom != NULL, return 0);
920 	PED_ASSERT (fs->geom->dev == geom->dev, return 0);
921 #ifdef DEBUG
922         PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0, return 0);
923 #else
924         if ((hgms = hfsplus_get_min_size (fs)) == 0)
925                 return 0;
926 #endif
927 
928 	if (ped_geometry_test_equal(fs->geom, geom))
929 		return 1;
930 
931 	priv_data = (HfsPPrivateFSData*) fs->type_specific;
932 
933 	if (fs->geom->start != geom->start
934 	    || geom->length > fs->geom->length
935 	    || geom->length < hgms) {
936 		ped_exception_throw (
937 			PED_EXCEPTION_NO_FEATURE,
938 			PED_EXCEPTION_CANCEL,
939 			_("Sorry, HFS+ cannot be resized that way yet."));
940 		return 0;
941 	}
942 
943 	if (priv_data->wrapper) {
944 		PedSector		red, hgee;
945 		HfsPrivateFSData* 	hfs_priv_data = (HfsPrivateFSData*)
946 					    priv_data->wrapper->type_specific;
947 		unsigned int		hfs_sect_block =
948 			    PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
949 			    / PED_SECTOR_SIZE_DEFAULT;
950 
951 		/* There is a wrapper so we must calculate the new geometry
952 		   of the embedded HFS+ volume */
953 		red = ( (fs->geom->length - geom->length + hfs_sect_block - 1)
954 			/ hfs_sect_block ) * hfs_sect_block;
955 		/* Can't we shrink the hfs+ volume by the desired size ? */
956 		hgee = hfsplus_get_empty_end (fs);
957 		if (!hgee) return 0;
958 		if (red > priv_data->plus_geom->length - hgee) {
959 			/* No, shrink hfs+ by the greatest possible value */
960 			hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block)
961 			       * hfs_sect_block;
962 			red = priv_data->plus_geom->length - hgee;
963 		}
964 		embedded_geom = ped_geometry_new (geom->dev,
965 						  priv_data->plus_geom->start,
966 						  priv_data->plus_geom->length
967 						  - red);
968 
969 		/* There is a wrapper so the resize process is a two stages
970 		   process (embedded resizing then wrapper resizing) :
971 		   we create a sub timer */
972 		ped_timer_reset (timer);
973 		ped_timer_set_state_name (timer,
974 					  _("shrinking embedded HFS+ volume"));
975 		ped_timer_update(timer, 0.0);
976 		timer_plus = ped_timer_new_nested (timer, 0.98);
977 	} else {
978 		/* No wrapper : the desired geometry is the desired
979 		   HFS+ volume geometry */
980 		embedded_geom = geom;
981 		timer_plus = timer;
982 	}
983 
984 	/* Resize the HFS+ volume */
985 	if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) {
986 		if (timer_plus != timer) ped_timer_destroy_nested (timer_plus);
987 		ped_exception_throw (
988 			PED_EXCEPTION_ERROR,
989 			PED_EXCEPTION_CANCEL,
990 			_("Resizing the HFS+ volume has failed."));
991 		return 0;
992 	}
993 
994 	if (priv_data->wrapper) {
995 		ped_geometry_destroy (embedded_geom);
996 		ped_timer_destroy_nested (timer_plus);
997 		ped_timer_set_state_name(timer, _("shrinking HFS wrapper"));
998 		timer_plus = ped_timer_new_nested (timer, 0.02);
999 		/* There's a wrapper : second stage = resizing it */
1000 		if (!hfsplus_wrapper_update (fs)
1001 		    || !hfs_resize (priv_data->wrapper, geom, timer_plus)) {
1002 			ped_timer_destroy_nested (timer_plus);
1003 			ped_exception_throw (
1004 				PED_EXCEPTION_ERROR,
1005 				PED_EXCEPTION_CANCEL,
1006 				_("Updating the HFS wrapper has failed."));
1007 			return 0;
1008 		}
1009 		ped_timer_destroy_nested (timer_plus);
1010 	}
1011 	ped_timer_update(timer, 1.0);
1012 
1013 	return 1;
1014 }
1015 
1016 #ifdef HFS_EXTRACT_FS
1017 /* The following is for debugging purpose only, NOT for packaging */
1018 
1019 #include <stdio.h>
1020 
1021 uint8_t* extract_buffer = NULL;
1022 
1023 static int
hfs_extract_file(const char * filename,HfsPrivateFile * hfs_file)1024 hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file)
1025 {
1026 	FILE*		fout;
1027 	PedSector	sect;
1028 
1029 	fout = fopen(filename, "w");
1030 	if (!fout) return 0;
1031 
1032 	for (sect = 0; sect < hfs_file->sect_nb; ++sect) {
1033 		if (!hfs_file_read_sector(hfs_file, extract_buffer, sect))
1034 			goto err_close;
1035 		if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1036 			goto err_close;
1037 	}
1038 
1039 	return (fclose(fout) == 0 ? 1 : 0);
1040 
1041 err_close:
1042 	fclose(fout);
1043 	return 0;
1044 }
1045 
1046 static int
hfs_extract_bitmap(const char * filename,PedFileSystem * fs)1047 hfs_extract_bitmap(const char* filename, PedFileSystem* fs)
1048 {
1049 	HfsPrivateFSData*		priv_data = (HfsPrivateFSData*)
1050 						fs->type_specific;
1051 	HfsMasterDirectoryBlock*	mdb = priv_data->mdb;
1052 	unsigned int 	count;
1053 	FILE*		fout;
1054 	PedSector	sect;
1055 
1056 	fout = fopen(filename, "w");
1057 	if (!fout) return 0;
1058 
1059 	for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block);
1060 	     sect < PED_BE16_TO_CPU(mdb->start_block);
1061 	     sect += count) {
1062 		uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block);
1063 		count = (st_block-sect) < BLOCK_MAX_BUFF ?
1064 			(st_block-sect) : BLOCK_MAX_BUFF;
1065 		if (!ped_geometry_read(fs->geom, extract_buffer, sect, count))
1066 			goto err_close;
1067 		if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT,
1068 			     1, fout))
1069 			goto err_close;
1070 	}
1071 
1072 	return (fclose(fout) == 0 ? 1 : 0);
1073 
1074 err_close:
1075 	fclose(fout);
1076 	return 0;
1077 }
1078 
1079 static int
hfs_extract_mdb(const char * filename,PedFileSystem * fs)1080 hfs_extract_mdb (const char* filename, PedFileSystem* fs)
1081 {
1082 	FILE*		fout;
1083 
1084 	fout = fopen(filename, "w");
1085 	if (!fout) return 0;
1086 
1087 	if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1))
1088 		goto err_close;
1089 	if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1090 		goto err_close;
1091 
1092 	return (fclose(fout) == 0 ? 1 : 0);
1093 
1094 err_close:
1095 	fclose(fout);
1096 	return 0;
1097 }
1098 
1099 static int
hfs_extract(PedFileSystem * fs,PedTimer * timer)1100 hfs_extract (PedFileSystem* fs, PedTimer* timer)
1101 {
1102 	HfsPrivateFSData*	priv_data = (HfsPrivateFSData*)
1103 						fs->type_specific;
1104 
1105 	ped_exception_throw (
1106 		PED_EXCEPTION_INFORMATION,
1107 		PED_EXCEPTION_OK,
1108 		_("This is not a real %s check.  This is going to extract "
1109 		  "special low level files for debugging purposes."),
1110 		"HFS");
1111 
1112 	extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
1113 	if (!extract_buffer) return 0;
1114 
1115 	hfs_extract_mdb(HFS_MDB_FILENAME, fs);
1116 	hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file);
1117 	hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file);
1118 	hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs);
1119 
1120 	ped_free(extract_buffer); extract_buffer = NULL;
1121 	return 0; /* nothing has been fixed by us ! */
1122 }
1123 
1124 static int
hfsplus_extract_file(const char * filename,HfsPPrivateFile * hfsp_file)1125 hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file)
1126 {
1127 	FILE*		fout;
1128 	unsigned int	cp_sect;
1129 	PedSector	rem_sect;
1130 
1131 	fout = fopen(filename, "w");
1132 	if (!fout) return 0;
1133 
1134 	for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) {
1135 		cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF;
1136 		if (!hfsplus_file_read(hfsp_file, extract_buffer,
1137 				       hfsp_file->sect_nb - rem_sect, cp_sect))
1138 			goto err_close;
1139 		if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT,
1140 			     1, fout))
1141 			goto err_close;
1142 	}
1143 
1144 	return (fclose(fout) == 0 ? 1 : 0);
1145 
1146 err_close:
1147 	fclose(fout);
1148 	return 0;
1149 }
1150 
1151 static int
hfsplus_extract_vh(const char * filename,PedFileSystem * fs)1152 hfsplus_extract_vh (const char* filename, PedFileSystem* fs)
1153 {
1154 	HfsPPrivateFSData* 	priv_data = (HfsPPrivateFSData*)
1155 						fs->type_specific;
1156 	FILE*		fout;
1157 	PedGeometry*	geom = priv_data->plus_geom;
1158 
1159 
1160 	fout = fopen(filename, "w");
1161 	if (!fout) return 0;
1162 
1163 	if (!ped_geometry_read(geom, extract_buffer, 2, 1))
1164 		goto err_close;
1165 	if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1166 		goto err_close;
1167 
1168 	return (fclose(fout) == 0 ? 1 : 0);
1169 
1170 err_close:
1171 	fclose(fout);
1172 	return 0;
1173 }
1174 
1175 /* TODO : use the timer to report what is happening */
1176 /* TODO : use exceptions to report errors */
1177 static int
hfsplus_extract(PedFileSystem * fs,PedTimer * timer)1178 hfsplus_extract (PedFileSystem* fs, PedTimer* timer)
1179 {
1180 	HfsPPrivateFSData* 	priv_data = (HfsPPrivateFSData*)
1181 						fs->type_specific;
1182 	HfsPVolumeHeader*	vh = priv_data->vh;
1183 	HfsPPrivateFile*	startup_file;
1184 
1185 	if (priv_data->wrapper) {
1186 		/* TODO : create nested timer */
1187 		hfs_extract (priv_data->wrapper, timer);
1188 	}
1189 
1190 	ped_exception_throw (
1191 		PED_EXCEPTION_INFORMATION,
1192 		PED_EXCEPTION_OK,
1193 		_("This is not a real %s check.  This is going to extract "
1194 		  "special low level files for debugging purposes."),
1195 		"HFS+");
1196 
1197 	extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
1198 	if (!extract_buffer) return 0;
1199 
1200 	hfsplus_extract_vh(HFSP_VH_FILENAME, fs);
1201 	hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file);
1202 	hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file);
1203 	hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file);
1204 	hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file);
1205 
1206 	startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID),
1207 					vh->startup_file.extents,
1208 					PED_BE64_TO_CPU (
1209 					   vh->startup_file.logical_size)
1210 					/ PED_SECTOR_SIZE_DEFAULT);
1211 	if (startup_file) {
1212 		hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file);
1213 		hfsplus_file_close(startup_file); startup_file = NULL;
1214 	}
1215 
1216 	ped_free(extract_buffer); extract_buffer = NULL;
1217 	return 0; /* nothing has been fixed by us ! */
1218 }
1219 #endif /* HFS_EXTRACT_FS */
1220 
1221 #endif /* !DISCOVER_ONLY */
1222 
1223 static PedFileSystemOps hfs_ops = {
1224 	.probe =		hfs_probe,
1225 #ifndef DISCOVER_ONLY
1226 	.clobber =	hfs_clobber,
1227 	.open =		hfs_open,
1228 	.create =		NULL,
1229 	.close =		hfs_close,
1230 #ifndef HFS_EXTRACT_FS
1231 	.check =		NULL,
1232 #else
1233 	.check =		hfs_extract,
1234 #endif
1235 	.copy =		NULL,
1236 	.resize =		hfs_resize,
1237 	.get_create_constraint =	NULL,
1238 	.get_resize_constraint =	hfs_get_resize_constraint,
1239 	.get_copy_constraint =	NULL,
1240 #else /* DISCOVER_ONLY */
1241 	.clobber =	NULL,
1242 	.open =		NULL,
1243 	.create =		NULL,
1244 	.close =		NULL,
1245 	.check =		NULL,
1246 	.copy =		NULL,
1247 	.resize =		NULL,
1248 	.get_create_constraint =	NULL,
1249 	.get_resize_constraint =	NULL,
1250 	.get_copy_constraint =	NULL,
1251 #endif /* DISCOVER_ONLY */
1252 };
1253 
1254 static PedFileSystemOps hfsplus_ops = {
1255 	.probe =		hfsplus_probe,
1256 #ifndef DISCOVER_ONLY
1257 	.clobber =	hfsplus_clobber,
1258 	.open =		hfsplus_open,
1259 	.create =		NULL,
1260 	.close =		hfsplus_close,
1261 #ifndef HFS_EXTRACT_FS
1262 	.check =		NULL,
1263 #else
1264 	.check =		hfsplus_extract,
1265 #endif
1266 	.copy =		NULL,
1267 	.resize =		hfsplus_resize,
1268 	.get_create_constraint =	NULL,
1269 	.get_resize_constraint =	hfsplus_get_resize_constraint,
1270 	.get_copy_constraint =	NULL,
1271 #else /* DISCOVER_ONLY */
1272 	.clobber =	NULL,
1273 	.open =		NULL,
1274 	.create =		NULL,
1275 	.close =		NULL,
1276 	.check =		NULL,
1277 	.copy =		NULL,
1278 	.resize =		NULL,
1279 	.get_create_constraint =	NULL,
1280 	.get_resize_constraint =	NULL,
1281 	.get_copy_constraint =	NULL,
1282 #endif /* DISCOVER_ONLY */
1283 };
1284 
1285 static PedFileSystemOps hfsx_ops = {
1286 	.probe =		hfsx_probe,
1287 #ifndef DISCOVER_ONLY
1288 	.clobber =	hfs_clobber, /* NOT hfsplus_clobber !
1289 					HFSX can't be embedded */
1290 	.open =		hfsplus_open,
1291 	.create =		NULL,
1292 	.close =		hfsplus_close,
1293 #ifndef HFS_EXTRACT_FS
1294 	.check =		NULL,
1295 #else
1296 	.check =		hfsplus_extract,
1297 #endif
1298 	.copy =		NULL,
1299 	.resize =		hfsplus_resize,
1300 	.get_create_constraint =	NULL,
1301 	.get_resize_constraint =	hfsplus_get_resize_constraint,
1302 	.get_copy_constraint =	NULL,
1303 #else /* DISCOVER_ONLY */
1304 	.clobber =	NULL,
1305 	.open =		NULL,
1306 	.create =		NULL,
1307 	.close =		NULL,
1308 	.check =		NULL,
1309 	.copy =		NULL,
1310 	.resize =		NULL,
1311 	.get_create_constraint =	NULL,
1312 	.get_resize_constraint =	NULL,
1313 	.get_copy_constraint =	NULL,
1314 #endif /* DISCOVER_ONLY */
1315 };
1316 
1317 
1318 static PedFileSystemType hfs_type = {
1319 	.next =	NULL,
1320 	.ops =	&hfs_ops,
1321 	.name =	"hfs",
1322 	.block_sizes = HFS_BLOCK_SIZES
1323 };
1324 
1325 static PedFileSystemType hfsplus_type = {
1326 	.next =	NULL,
1327 	.ops =	&hfsplus_ops,
1328 	.name =	"hfs+",
1329 	.block_sizes = HFSP_BLOCK_SIZES
1330 };
1331 
1332 static PedFileSystemType hfsx_type = {
1333 	.next =	NULL,
1334 	.ops =	&hfsx_ops,
1335 	.name =	"hfsx",
1336 	.block_sizes = HFSX_BLOCK_SIZES
1337 };
1338 
1339 void
ped_file_system_hfs_init()1340 ped_file_system_hfs_init ()
1341 {
1342 	ped_file_system_type_register (&hfs_type);
1343 	ped_file_system_type_register (&hfsplus_type);
1344 	ped_file_system_type_register (&hfsx_type);
1345 }
1346 
1347 void
ped_file_system_hfs_done()1348 ped_file_system_hfs_done ()
1349 {
1350 	ped_file_system_type_unregister (&hfs_type);
1351 	ped_file_system_type_unregister (&hfsplus_type);
1352 	ped_file_system_type_unregister (&hfsx_type);
1353 }
1354