1 |
/* |
2 |
* libhfs - library for reading and writing Macintosh HFS volumes |
3 |
* |
4 |
* Code to acces the basic volume information of a HFS+ volume. |
5 |
* |
6 |
* Copyright (C) 2000 Klaus Halfmann <klaus.halfmann@feri.de> |
7 |
* Original work by 1996-1998 Robert Leslie <rob@mars.org> |
8 |
* other work 2000 from Brad Boyer (flar@pants.nu) |
9 |
* Additional work in 2004 by Stefan Weyergraf (stefan@weyergraf.de) for use in PearPC |
10 |
* |
11 |
* This program is free software; you can redistribute it and/or modify |
12 |
* it under the terms of the GNU General Public License as published by |
13 |
* the Free Software Foundation; either version 2 of the License, or |
14 |
* (at your option) any later version. |
15 |
* |
16 |
* This program is distributed in the hope that it will be useful, |
17 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 |
* GNU General Public License for more details. |
20 |
* |
21 |
* You should have received a copy of the GNU General Public License |
22 |
* along with this program; if not, write to the Free Software |
23 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
24 |
* |
25 |
*/ |
26 |
|
27 |
# ifdef HAVE_CONFIG_H |
28 |
# include "config.h" |
29 |
# endif |
30 |
|
31 |
# include <stdlib.h> |
32 |
# include <stdio.h> |
33 |
# include <string.h> |
34 |
# include <time.h> |
35 |
# include <errno.h> |
36 |
|
37 |
# include "libhfsp.h" |
38 |
# include "volume.h" |
39 |
# include "record.h" |
40 |
# include "btree.h" |
41 |
# include "blockiter.h" |
42 |
# include "os.h" |
43 |
# include "swab.h" |
44 |
# include "hfstime.h" |
45 |
# include "partitions.h" |
46 |
|
47 |
/* |
48 |
* A replacement of the ffs C library function which does not exist |
49 |
* in all C libraries. |
50 |
* |
51 |
* ffs finds the first set bit in a four-byte integer |
52 |
*/ |
53 |
static unsigned long |
54 |
my_ffs( unsigned long i ) |
55 |
{ |
56 |
register unsigned long j; |
57 |
|
58 |
if( i == 0 ) return( 0 ); |
59 |
|
60 |
for( j = 1; j <= ( sizeof( unsigned long )*8 ); j++ ) |
61 |
{ |
62 |
if( (i>>(j-1))&1 ) return( j ); |
63 |
} |
64 |
|
65 |
return( 0 ); |
66 |
} |
67 |
|
68 |
#define ffs(_a_) my_ffs(_a_) |
69 |
|
70 |
/* Fill a given buffer with the given block in volume. |
71 |
*/ |
72 |
int volume_readinbuf(volume * vol,void* buf, long block) |
73 |
{ |
74 |
UInt16 blksize_bits; |
75 |
ASSERT( block < vol->maxblocks); |
76 |
|
77 |
blksize_bits = vol->blksize_bits; |
78 |
// printf("Reading from %lx\n", block << blksize_bits); |
79 |
if (hfsp_os_seek(&vol->fd, block, blksize_bits) == block) |
80 |
if (1 == hfsp_os_read(&vol->fd, buf, 1, blksize_bits)) |
81 |
return 0; |
82 |
return -1; |
83 |
} |
84 |
|
85 |
/* Write buffer to given block on medium. |
86 |
*/ |
87 |
int volume_writetobuf(volume * vol,void* buf, long block) |
88 |
{ |
89 |
UInt16 blksize_bits; |
90 |
ASSERT( block < vol->maxblocks); |
91 |
|
92 |
blksize_bits = vol->blksize_bits; |
93 |
// printf("Writing to %lx\n", block << blksize_bits); |
94 |
if (hfsp_os_seek(&vol->fd, block, blksize_bits) == block) |
95 |
if (1 == hfsp_os_write(&vol->fd, buf, 1, blksize_bits)) |
96 |
return 0; |
97 |
return -1; |
98 |
} |
99 |
|
100 |
/* read multiple blocks of a fork into given memory. |
101 |
* |
102 |
* block realtive index in fork to start with |
103 |
* count number of blocks to read |
104 |
* forktype usually HFSP_EXTENT_DATA or HFSP_EXTENT_RSRC |
105 |
* fileId id (needed) in case extents must be fetched |
106 |
* |
107 |
* returns given pinter or NULL on failure. |
108 |
*/ |
109 |
void* volume_readfromfork(volume* vol, void* buf, |
110 |
hfsp_fork_raw* f, UInt32 block, |
111 |
UInt32 count, UInt8 forktype, UInt32 fileId) |
112 |
{ |
113 |
blockiter iter; |
114 |
char* cbuf = buf; |
115 |
|
116 |
blockiter_init(&iter, vol, f, forktype, fileId); |
117 |
if (blockiter_skip(&iter, block)) |
118 |
return NULL; |
119 |
while (count > 0) |
120 |
{ |
121 |
--count; |
122 |
if (volume_readinbuf(vol, cbuf, blockiter_curr(&iter))) |
123 |
return NULL; |
124 |
cbuf += vol->blksize; |
125 |
if (count > 0 && blockiter_next(&iter)) |
126 |
return NULL; |
127 |
} |
128 |
return buf; |
129 |
} |
130 |
|
131 |
/* write multiple blocks of a fork buf to medium. |
132 |
* The caller is responsible for allocating a suffient |
133 |
* large fork and eventually needed extends records for now. |
134 |
* |
135 |
* block realtive index in fork to start with |
136 |
* count number of blocks to write |
137 |
* forktype usually HFSP_EXTENT_DATA or HFSP_EXTENT_RSRC |
138 |
* fileId id (needed) in case extents must be written |
139 |
* |
140 |
* returns value != 0 on error. |
141 |
*/ |
142 |
int volume_writetofork(volume* vol, void* buf, |
143 |
hfsp_fork_raw* f, UInt32 block, |
144 |
UInt32 count, UInt8 forktype, UInt32 fileId) |
145 |
{ |
146 |
blockiter iter; |
147 |
char* cbuf = buf; |
148 |
|
149 |
blockiter_init(&iter, vol, f, forktype, fileId); |
150 |
if (blockiter_skip(&iter, block)) |
151 |
return -1; |
152 |
while (count > 0) |
153 |
{ |
154 |
--count; |
155 |
if (volume_writetobuf(vol, cbuf, blockiter_curr(&iter))) |
156 |
return -1; |
157 |
cbuf += vol->blksize; |
158 |
if (count > 0 && blockiter_next(&iter)) |
159 |
return -1; |
160 |
} |
161 |
return 0; |
162 |
} |
163 |
|
164 |
/* Check in Allocation file if given block is allocated. |
165 |
*/ |
166 |
int volume_allocated(volume* vol, UInt32 block) |
167 |
{ |
168 |
int bit = block & 0x07; |
169 |
int mask,index; |
170 |
char* bits; |
171 |
char buf[vol->blksize]; |
172 |
|
173 |
// if (block >= vol->maxblocks) |
174 |
// HFSP_ERROR(-1, "Allocation block out of range."); |
175 |
block >>= 3; |
176 |
mask = (1 << vol->blksize_bits) -1; /* Usually 0x0FFF */ |
177 |
index = block & mask; |
178 |
block >>= vol->blksize_bits; // block in allocation file |
179 |
bits = (char*) volume_readfromfork(vol, buf, &vol->vol.alloc_file, |
180 |
block, 1, HFSP_EXTENT_DATA, HFSP_ALLOC_CNID); |
181 |
if (!bits) |
182 |
HFSP_ERROR(-1, "Allocation block not found !?"); |
183 |
|
184 |
return (bits[index] & (0x80 >> bit)); /* Bit one is 0x80 ! */ |
185 |
return 0; |
186 |
fail: |
187 |
return -1; |
188 |
} |
189 |
|
190 |
/* Mark in Allocation file a given block as allocated. |
191 |
* |
192 |
* ToDo: optimize for adjacent blocks ... |
193 |
* use cache directly |
194 |
*/ |
195 |
int volume_allocate(volume* vol, UInt32 block) |
196 |
{ |
197 |
int bit = block & 0x07; |
198 |
int mask,index; |
199 |
char* bits; |
200 |
char buf[vol->blksize]; |
201 |
int shift = 0x80 >> bit; /* Bit one is 0x80 */ |
202 |
|
203 |
// if (block >= vol->maxblocks) |
204 |
// HFSP_ERROR(-1, "Allocation block out of range."); |
205 |
block >>= 3; |
206 |
mask = (1 << vol->blksize_bits) -1; /* Usually 0x0FFF */ |
207 |
index = block & mask; |
208 |
block >>= vol->blksize_bits; // block in allocation file |
209 |
bits = (char*) volume_readfromfork(vol, buf, &vol->vol.alloc_file, |
210 |
block, 1, HFSP_EXTENT_DATA, HFSP_ALLOC_CNID); |
211 |
if (!bits) |
212 |
HFSP_ERROR(-1, "Allocation block not found !?"); |
213 |
|
214 |
if (bits[index] & shift) |
215 |
HFSP_ERROR(-1, "volume_allocate: Block already allocated"); |
216 |
bits[index] |= shift; |
217 |
return volume_writetofork(vol, buf, &vol->vol.alloc_file, |
218 |
block, 1, HFSP_EXTENT_DATA, HFSP_ALLOC_CNID); |
219 |
fail: |
220 |
return -1; |
221 |
} |
222 |
|
223 |
/* Mark in Allocation file a given block as freee. |
224 |
* |
225 |
* ToDo: optimize for adjacent blocks ... |
226 |
* use cache directly |
227 |
*/ |
228 |
int volume_deallocate(volume* vol, UInt32 block) |
229 |
{ |
230 |
int bit = block & 0x07; |
231 |
int mask,index; |
232 |
char* bits; |
233 |
char buf[vol->blksize]; |
234 |
int shift = 0x80 >> bit; /* Bit one is 0x80 */ |
235 |
|
236 |
// if (block >= vol->maxblocks) |
237 |
// HFSP_ERROR(-1, "Allocation block out of range."); |
238 |
block >>= 3; |
239 |
mask = (1 << vol->blksize_bits) -1; /* Usually 0x0FFF */ |
240 |
index = block & mask; |
241 |
block >>= vol->blksize_bits; // block in allocation file |
242 |
bits = (char*) volume_readfromfork(vol, buf, &vol->vol.alloc_file, |
243 |
block, 1, HFSP_EXTENT_DATA, HFSP_ALLOC_CNID); |
244 |
if (!bits) |
245 |
HFSP_ERROR(-1, "Allocation block not found !?"); |
246 |
|
247 |
if (!(bits[index] & shift)) |
248 |
HFSP_ERROR(-1, "volume_allocate: Block already free"); |
249 |
bits[index] &= ~shift; |
250 |
return volume_writetofork(vol, buf, &vol->vol.alloc_file, |
251 |
block, 1, HFSP_EXTENT_DATA, HFSP_ALLOC_CNID); |
252 |
fail: |
253 |
return -1; |
254 |
} |
255 |
|
256 |
/* Initialize a raw hfsp_extent_rec. |
257 |
*/ |
258 |
static void volume_initextent(hfsp_extent_rec er) |
259 |
{ |
260 |
memset(er, 0, 8 * sizeof(hfsp_extent)); |
261 |
/* |
262 |
int i; |
263 |
hfsp_extent* e; |
264 |
for (i=0; i < 8; i++) |
265 |
{ |
266 |
e = &er[i]; |
267 |
e->start_block = 0; |
268 |
e->block_count = 0; |
269 |
} |
270 |
*/ |
271 |
} |
272 |
|
273 |
/** Initalize an (empty !) fork, you may later request additional space |
274 |
*/ |
275 |
|
276 |
void volume_initfork(volume* vol, hfsp_fork_raw* f, UInt16 fork_type) |
277 |
{ |
278 |
f->total_size = 0; |
279 |
if (fork_type == HFSP_EXTENT_DATA) |
280 |
f->clump_size = vol->vol.data_clump_sz; |
281 |
else |
282 |
f->clump_size = vol->vol.rsrc_clump_sz; |
283 |
f->total_blocks = 0; |
284 |
volume_initextent(f->extents); |
285 |
} |
286 |
|
287 |
/* Read a raw hfsp_extent_rec from memory. |
288 |
* |
289 |
* return pointer right after the structure. |
290 |
*/ |
291 |
char* volume_readextent(char *p, hfsp_extent_rec er) |
292 |
{ |
293 |
int i; |
294 |
hfsp_extent* e; |
295 |
for (i=0; i < 8; i++) |
296 |
{ |
297 |
e = &er[i]; |
298 |
e->start_block = bswabU32_inc(&p); |
299 |
e->block_count = bswabU32_inc(&p); |
300 |
} |
301 |
return p; |
302 |
} |
303 |
|
304 |
/* Write a raw hfsp_extent_rec to memory. |
305 |
* |
306 |
* return pointer right after the structure. |
307 |
*/ |
308 |
char* volume_writeextent(char *p, hfsp_extent_rec er) |
309 |
{ |
310 |
int i; |
311 |
hfsp_extent* e; |
312 |
for (i=0; i < 8; i++) |
313 |
{ |
314 |
e = &er[i]; |
315 |
bstoreU32_inc(&p, e->start_block ); |
316 |
bstoreU32_inc(&p, e->block_count ); |
317 |
} |
318 |
return p; |
319 |
} |
320 |
|
321 |
/* Read a raw hfsp_fork from memory. |
322 |
* |
323 |
* return pointer right after the structure. |
324 |
*/ |
325 |
char* volume_readfork(char *p, hfsp_fork_raw* f) |
326 |
{ |
327 |
f->total_size = bswabU64_inc(&p); |
328 |
f->clump_size = bswabU32_inc(&p); |
329 |
f->total_blocks = bswabU32_inc(&p); |
330 |
return volume_readextent(p, f->extents); |
331 |
} |
332 |
|
333 |
/* Write a raw hfsp_fork to memory. |
334 |
* |
335 |
* return pointer right after the structure. |
336 |
*/ |
337 |
char* volume_writefork(char *p, hfsp_fork_raw* f) |
338 |
{ |
339 |
bstoreU64_inc(&p, f->total_size ); |
340 |
bstoreU32_inc(&p, f->clump_size ); |
341 |
bstoreU32_inc(&p, f->total_blocks); |
342 |
return volume_writeextent(p, f->extents); |
343 |
} |
344 |
|
345 |
/* Read the volume from the given buffer and swap the bytes. |
346 |
*/ |
347 |
static int volume_readbuf(hfsp_vh* vh, char* p) |
348 |
{ |
349 |
if ( (vh->signature = bswabU16_inc(&p)) != HFSP_VOLHEAD_SIG) |
350 |
HFSP_ERROR(-1, "This is not a HFS+ volume"); |
351 |
vh->version = bswabU16_inc(&p); |
352 |
vh->attributes = bswabU32_inc(&p); |
353 |
vh->last_mount_vers = bswabU32_inc(&p); |
354 |
vh->reserved = bswabU32_inc(&p); |
355 |
vh->create_date = bswabU32_inc(&p); |
356 |
vh->modify_date = bswabU32_inc(&p); |
357 |
vh->backup_date = bswabU32_inc(&p); |
358 |
vh->checked_date = bswabU32_inc(&p); |
359 |
vh->file_count = bswabU32_inc(&p); |
360 |
vh->folder_count = bswabU32_inc(&p); |
361 |
vh->blocksize = bswabU32_inc(&p); |
362 |
vh->total_blocks = bswabU32_inc(&p); |
363 |
vh->free_blocks = bswabU32_inc(&p); |
364 |
vh->next_alloc = bswabU32_inc(&p); |
365 |
vh->rsrc_clump_sz = bswabU32_inc(&p); |
366 |
vh->data_clump_sz = bswabU32_inc(&p); |
367 |
vh->next_cnid = bswabU32_inc(&p); |
368 |
vh->write_count = bswabU32_inc(&p); |
369 |
vh->encodings_bmp = bswabU64_inc(&p); |
370 |
memcpy(vh->finder_info, p, 32); |
371 |
p += 32; // finderinfo is not used by now |
372 |
p = volume_readfork(p, &vh->alloc_file ); |
373 |
p = volume_readfork(p, &vh->ext_file ); |
374 |
p = volume_readfork(p, &vh->cat_file ); |
375 |
p = volume_readfork(p, &vh->attr_file ); |
376 |
p = volume_readfork(p, &vh->start_file ); |
377 |
return 0; |
378 |
fail: |
379 |
return -1; |
380 |
} |
381 |
|
382 |
/* Write the volume to the given buffer and swap the bytes. |
383 |
*/ |
384 |
static int volume_writebuf(hfsp_vh* vh, char* p) |
385 |
{ |
386 |
bstoreU16_inc(&p, vh->signature ); |
387 |
bstoreU16_inc(&p, vh->version ); |
388 |
bstoreU32_inc(&p, vh->attributes ); |
389 |
bstoreU32_inc(&p, vh->last_mount_vers); |
390 |
bstoreU32_inc(&p, vh->reserved ); |
391 |
bstoreU32_inc(&p, vh->create_date ); |
392 |
bstoreU32_inc(&p, vh->modify_date ); |
393 |
bstoreU32_inc(&p, vh->backup_date ); |
394 |
bstoreU32_inc(&p, vh->checked_date ); |
395 |
bstoreU32_inc(&p, vh->file_count ); |
396 |
bstoreU32_inc(&p, vh->folder_count ); |
397 |
bstoreU32_inc(&p, vh->blocksize ); |
398 |
bstoreU32_inc(&p, vh->total_blocks ); |
399 |
bstoreU32_inc(&p, vh->free_blocks ); |
400 |
bstoreU32_inc(&p, vh->next_alloc ); |
401 |
bstoreU32_inc(&p, vh->rsrc_clump_sz ); |
402 |
bstoreU32_inc(&p, vh->data_clump_sz ); |
403 |
bstoreU32_inc(&p, vh->next_cnid ); |
404 |
bstoreU32_inc(&p, vh->write_count ); |
405 |
bstoreU64_inc(&p, vh->encodings_bmp ); |
406 |
memcpy(p, vh->finder_info, 32); |
407 |
p += 32; // finderinfo is not used by now |
408 |
p = volume_writefork(p, &vh->alloc_file ); |
409 |
p = volume_writefork(p, &vh->ext_file ); |
410 |
p = volume_writefork(p, &vh->cat_file ); |
411 |
p = volume_writefork(p, &vh->attr_file ); |
412 |
p = volume_writefork(p, &vh->start_file ); |
413 |
return 0; |
414 |
} |
415 |
|
416 |
/* Read the volume from the given block */ |
417 |
static int volume_read(volume * vol, hfsp_vh* vh, UInt32 block) |
418 |
{ |
419 |
char buf[vol->blksize]; |
420 |
if (volume_readinbuf(vol, buf, block)) |
421 |
return -1; |
422 |
return volume_readbuf(vh, buf); |
423 |
} |
424 |
|
425 |
/* Find out whether the volume is wrapped and unwrap it eventually */ |
426 |
static int volume_read_wrapper(volume * vol, hfsp_vh* vh) |
427 |
{ |
428 |
UInt16 signature; |
429 |
char buf[vol->blksize]; |
430 |
char *p = buf; |
431 |
if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here |
432 |
return -1; |
433 |
|
434 |
signature = bswabU16_inc(&p); |
435 |
if (signature == HFS_VOLHEAD_SIG) /* Wrapper */ |
436 |
{ |
437 |
UInt32 drAlBlkSiz; /* size (in bytes) of allocation blocks */ |
438 |
UInt32 sect_per_block; /* how may block build an hfs sector */ |
439 |
UInt16 drAlBlSt; /* first allocation block in volume */ |
440 |
|
441 |
UInt16 embeds, embedl; /* Start/lenght of embedded area in blocks */ |
442 |
|
443 |
p += 0x12; /* skip unneeded HFS vol fields */ |
444 |
drAlBlkSiz = bswabU32_inc(&p); /* offset 0x14 */ |
445 |
p += 0x4; /* skip unneeded HFS vol fields */ |
446 |
drAlBlSt = bswabU16_inc(&p); /* offset 0x1C */ |
447 |
|
448 |
p += 0x5E; /* skip unneeded HFS vol fields */ |
449 |
signature = bswabU16_inc(&p); /* offset 0x7C, drEmbedSigWord */ |
450 |
if (signature != HFSP_VOLHEAD_SIG) |
451 |
HFSP_ERROR(-1, "This looks like a normal HFS volume"); |
452 |
embeds = bswabU16_inc(&p); |
453 |
embedl = bswabU16_inc(&p); |
454 |
sect_per_block = (drAlBlkSiz / HFSP_BLOCKSZ); |
455 |
// end is absolute (not relative to HFS+ start) |
456 |
vol->maxblocks = embedl * sect_per_block; |
457 |
hfsp_set_offset(&vol->fd, hfsp_get_offset(&vol->fd) |
458 |
+ (((APPLEUInt64) (drAlBlSt + embeds * sect_per_block)) << HFS_BLOCKSZ_BITS)); |
459 |
/* Now we can try to read the embedded HFS+ volume header */ |
460 |
return volume_read(vol,vh,2); |
461 |
} |
462 |
else if (signature == HFSP_VOLHEAD_SIG) /* Native HFS+ volume */ |
463 |
{ |
464 |
p = buf; // Restore to begin of block |
465 |
return volume_readbuf(vh, p); |
466 |
} else |
467 |
HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found"); |
468 |
|
469 |
fail: |
470 |
return -1; |
471 |
} |
472 |
|
473 |
/** Mark the volume as modified by setting its modfied date */ |
474 |
void volume_modified(volume* vol) |
475 |
{ |
476 |
time_t now; |
477 |
hfsp_vh* head; |
478 |
|
479 |
gmtime(&now); |
480 |
head = &vol->vol; |
481 |
head->modify_date = now + HFSPTIMEDIFF; |
482 |
} |
483 |
|
484 |
/** Mark this volume as used by Linux by modifying the header */ |
485 |
void volume_linux_mark(volume* vol) |
486 |
{ |
487 |
hfsp_vh* head = &vol->vol; |
488 |
|
489 |
// *** Debugging *** |
490 |
vol ->flags |= HFSP_BACKUP_DIRTY; |
491 |
|
492 |
// MacOS does not like that :( |
493 |
// head->version = HPUTILS_VERS; |
494 |
head->last_mount_vers = HPLS_SIGNATURE; |
495 |
// For now I always mark the volume as inconsistent ... |
496 |
head->attributes |= HFSPLUS_VOL_INCNSTNT; |
497 |
volume_modified(vol); |
498 |
} |
499 |
|
500 |
#include <stdio.h> |
501 |
/* Open the device, read and verify the volume header |
502 |
(and its backup) */ |
503 |
int volume_open(volume* vol, const void *devicehandle, int partition, int rw) |
504 |
{ |
505 |
hfsp_vh backup; /* backup volume found at second to last block */ |
506 |
int shift; |
507 |
int blksize_bits; |
508 |
|
509 |
vol->blksize_bits = HFSP_BLOCKSZ_BITS; |
510 |
vol->flags = 0; |
511 |
vol->blksize = HFSP_BLOCKSZ; |
512 |
vol->maxblocks = 3; |
513 |
/* this should be enough until we find the volume descriptor */ |
514 |
vol->extents = NULL; /* Thanks to Jeremias Sauceda */ |
515 |
|
516 |
btree_reset(&vol->catalog); |
517 |
|
518 |
if (hfsp_os_open(&vol->fd, devicehandle, rw)) |
519 |
return -1; |
520 |
|
521 |
hfsp_set_offset(&vol->fd, 0); |
522 |
|
523 |
/* set the offset to the first block of the given partition */ |
524 |
if (partition != 0) { |
525 |
partition_map map; |
526 |
int block; |
527 |
if( partition_getPartitionMap( &map, vol->fd)== -1) |
528 |
HFSP_ERROR(-1, "No Apple partition map found"); |
529 |
block = partition_getStartBlock( &map, "Apple_HFS", partition); |
530 |
if (block == 0) |
531 |
HFSP_ERROR(-1, "No valid Apple_HFS partition found"); |
532 |
hfsp_set_offset(&vol->fd, ((APPLEUInt64)block) << HFSP_BLOCKSZ_BITS); |
533 |
} |
534 |
|
535 |
vol->flags |= rw & HFSP_MODE_RDWR; |
536 |
|
537 |
if (volume_read_wrapper(vol, &vol->vol)) |
538 |
return -1; |
539 |
|
540 |
if (volume_read(vol, &backup, vol->maxblocks - 2)) |
541 |
/*return -1*/; /* ignore lacking backup */ |
542 |
// fprintf(stderr, "HFS+ has no backup volume header. skipping...\n"); |
543 |
|
544 |
/* Now switch blksize from HFSP_BLOCKSZ (512) to value given in header |
545 |
and adjust depend values accordingly, after that a block always |
546 |
means a HFS+ allocation size */ |
547 |
|
548 |
/* Usually blocksize is 4096 */ |
549 |
blksize_bits = ffs(vol->vol.blocksize) -1; |
550 |
shift = blksize_bits - vol->blksize_bits; |
551 |
vol -> blksize = vol->vol.blocksize; |
552 |
vol -> blksize_bits = blksize_bits; |
553 |
vol -> maxblocks = vol->vol.total_blocks; /* cant calculate via shift ? */ |
554 |
|
555 |
if (vol->flags & HFSP_MODE_RDWR) |
556 |
{ |
557 |
char buf[HFSP_BLOCKSZ]; |
558 |
void *p = buf; |
559 |
|
560 |
volume_linux_mark(vol); |
561 |
|
562 |
// write back (dirty) volume header |
563 |
if (volume_writebuf(&vol->vol, p)) |
564 |
return -1; // evil, but will never happen |
565 |
|
566 |
volume_writetobuf(vol, buf, 2); // This is always block 2 |
567 |
} |
568 |
|
569 |
if (btree_init_cat(&vol->catalog, vol, &vol->vol.cat_file)) |
570 |
return -1; |
571 |
|
572 |
return 0; |
573 |
fail: |
574 |
return -1; |
575 |
} |
576 |
|
577 |
/* Write back all data eventually cached and close the device */ |
578 |
int volume_close(volume* vol) |
579 |
{ |
580 |
btree_close(&vol->catalog); |
581 |
if (vol->extents) |
582 |
{ |
583 |
btree_close(vol->extents); |
584 |
FREE(vol->extents); |
585 |
} |
586 |
if (vol->flags & HFSP_MODE_RDWR) // volume was opened for writing, |
587 |
{ |
588 |
/* Switch back to HFSP_BLOCKSZ (512) */ |
589 |
int shift = vol->blksize_bits - HFSP_BLOCKSZ_BITS; |
590 |
char buf[HFSP_BLOCKSZ]; |
591 |
void *p = buf; |
592 |
|
593 |
hfsp_vh* head = &vol->vol; |
594 |
|
595 |
// Clear inconsistent flag |
596 |
head->attributes &= ~HFSPLUS_VOL_INCNSTNT; |
597 |
// set Unmounted flag |
598 |
head->attributes |= HFSPLUS_VOL_UNMNT; |
599 |
|
600 |
vol->blksize_bits = HFSP_BLOCKSZ_BITS; |
601 |
|
602 |
vol -> maxblocks <<= shift; /* cant calculate via shift ? */ |
603 |
|
604 |
if (volume_writebuf(&vol->vol, p)) |
605 |
return -1; // evil, but will never happen |
606 |
|
607 |
volume_writetobuf(vol, buf, 2); // This is always block 2 |
608 |
|
609 |
if (vol->flags & HFSP_BACKUP_DIRTY) // need to write backup block, too |
610 |
{ |
611 |
// !!! Need to check this with larger volumes, too !!! |
612 |
volume_writetobuf(vol, buf, vol->maxblocks-2); |
613 |
} |
614 |
} |
615 |
return hfsp_os_close(&vol->fd); |
616 |
} |
617 |
|
618 |
/* internal fucntion used to create the extents btree, |
619 |
is called by inline function when needed */ |
620 |
void volume_create_extents_tree(volume* vol) |
621 |
{ |
622 |
btree* result = (btree*) ALLOC(btree*, sizeof(btree)); |
623 |
if (!result) |
624 |
HFSP_ERROR(ENOMEM, "No memory for extents btree"); |
625 |
if (!btree_init_extent(result, vol, &vol->vol.ext_file)) |
626 |
{ |
627 |
vol->extents = result; |
628 |
return; |
629 |
} |
630 |
fail: |
631 |
vol->extents = NULL; |
632 |
} |
633 |
|
634 |
/* return new Id for files/folder and check for overflow. |
635 |
* |
636 |
* retun 0 on error . |
637 |
*/ |
638 |
UInt32 volume_get_nextid(volume* vol) |
639 |
{ |
640 |
UInt32 result = vol->vol.next_cnid; |
641 |
if (result < HFSP_MIN_CNID) // oops possible wrap around overflow |
642 |
{ |
643 |
hfsp_error = "Maximum number of node IDs exhausted, sorry"; |
644 |
return 0; |
645 |
} |
646 |
vol->vol.next_cnid = 1 + result; |
647 |
return result; |
648 |
} |