/[pearpc]/src/io/prom/fs/hfsplus/volume.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /src/io/prom/fs/hfsplus/volume.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Wed Sep 5 17:11:21 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 18824 byte(s)
import upstream CVS
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 }

  ViewVC Help
Powered by ViewVC 1.1.26