| /* |
| * libhfs - library for reading and writing Macintosh HFS volumes |
| * |
| * Code to acces the basic volume information of a HFS+ volume. |
| * |
| * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de> |
| * Original work by 1996-1998 Robert Leslie <rob@mars.org> |
| * other work 2000 from Brad Boyer (flar@pants.nu) |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| * MA 02110-1301, USA. |
| * |
| * $Id: volume.c,v 1.21 2000/10/25 05:43:04 hasi Exp $ |
| */ |
| |
| #include "config.h" |
| #include "libhfsp.h" |
| #include "volume.h" |
| #include "record.h" |
| #include "btree.h" |
| #include "blockiter.h" |
| #include "os.h" |
| #include "swab.h" |
| #include "hfstime.h" |
| |
| /* Fill a given buffer with the given block in volume. |
| */ |
| int |
| volume_readinbuf(volume * vol,void* buf, long block) |
| { |
| UInt16 blksize_bits; |
| ASSERT( block < vol->maxblocks); |
| |
| blksize_bits = vol->blksize_bits; |
| block += vol->startblock; |
| if( os_seek(vol->os_fd, block, blksize_bits) == block) |
| if( 1 == os_read(vol->os_fd, buf, 1, blksize_bits)) |
| return 0; |
| return -1; |
| } |
| |
| /* read multiple blocks into given memory. |
| * |
| * returns given pinter or NULL on failure. |
| */ |
| void* |
| volume_readfromfork(volume* vol, void* buf, |
| hfsp_fork_raw* f, UInt32 block, |
| UInt32 count, UInt8 forktype, UInt32 fileId) |
| { |
| blockiter iter; |
| char *cbuf = buf; |
| |
| blockiter_init(&iter, vol, f, forktype, fileId); |
| if( blockiter_skip(&iter, block)) |
| return NULL; |
| |
| while( count > 0) { |
| --count; |
| if( volume_readinbuf(vol, cbuf, blockiter_curr(&iter))) |
| return NULL; |
| cbuf += vol->blksize; |
| if( count > 0 && blockiter_next(&iter)) |
| return NULL; |
| } |
| return buf; |
| } |
| |
| |
| /* Read a raw hfsp_extent_rec from memory. |
| * |
| * return pointer right after the structure. |
| */ |
| void* |
| volume_readextent(void *p, hfsp_extent_rec er) |
| { |
| int i; |
| hfsp_extent *e; |
| |
| for( i=0; i < 8; i++) { |
| e = &er[i]; |
| e->start_block = bswabU32_inc(p); |
| e->block_count = bswabU32_inc(p); |
| } |
| return p; |
| } |
| |
| /* Read a raw hfsp_fork from memory. |
| * |
| * return pointer right after the structure. |
| */ |
| void* |
| volume_readfork(void *p, hfsp_fork_raw* f) |
| { |
| f->total_size = bswabU64_inc(p); |
| f->clump_size = bswabU32_inc(p); |
| f->total_blocks = bswabU32_inc(p); |
| |
| return volume_readextent(p, f->extents); |
| } |
| |
| /* Read the volume from the given buffer and swap the bytes. |
| * |
| * ToDo: add more consitency checks. |
| */ |
| static int |
| volume_readbuf(hfsp_vh* vh, char * p) |
| { |
| if( (vh->signature = bswabU16_inc(p)) != HFSP_VOLHEAD_SIG) |
| HFSP_ERROR(-1, "This is not a HFS+ volume"); |
| |
| vh->version = bswabU16_inc(p); |
| vh->attributes = bswabU32_inc(p); |
| vh->last_mount_vers = bswabU32_inc(p); |
| vh->reserved = bswabU32_inc(p); |
| vh->create_date = bswabU32_inc(p); |
| vh->modify_date = bswabU32_inc(p); |
| vh->backup_date = bswabU32_inc(p); |
| vh->checked_date = bswabU32_inc(p); |
| vh->file_count = bswabU32_inc(p); |
| vh->folder_count = bswabU32_inc(p); |
| vh->blocksize = bswabU32_inc(p); |
| vh->total_blocks = bswabU32_inc(p); |
| vh->free_blocks = bswabU32_inc(p); |
| vh->next_alloc = bswabU32_inc(p); |
| vh->rsrc_clump_sz = bswabU32_inc(p); |
| vh->data_clump_sz = bswabU32_inc(p); |
| vh->next_cnid = bswabU32_inc(p); |
| vh->write_count = bswabU32_inc(p); |
| vh->encodings_bmp = bswabU64_inc(p); |
| memcpy(vh->finder_info, p, 32); |
| p += 32; // So finderinfo must be swapped later, *** |
| p = volume_readfork(p, &vh->alloc_file ); |
| p = volume_readfork(p, &vh->ext_file ); |
| p = volume_readfork(p, &vh->cat_file ); |
| p = volume_readfork(p, &vh->attr_file ); |
| p = volume_readfork(p, &vh->start_file ); |
| return 0; |
| fail: |
| return -1; |
| } |
| |
| /* Read the volume from the given block */ |
| static int |
| volume_read(volume * vol, hfsp_vh* vh, UInt32 block) |
| { |
| char buf[vol->blksize]; |
| |
| if( volume_readinbuf(vol, buf, block)) |
| return -1; |
| return volume_readbuf(vh, buf); |
| } |
| |
| /* Find out wether the volume is wrapped and unwrap it eventually */ |
| static int |
| volume_read_wrapper(volume * vol, hfsp_vh* vh) |
| { |
| UInt16 signature; |
| char buf[vol->blksize]; |
| char *p = buf; |
| |
| if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here |
| return -1; |
| |
| signature = bswabU16_inc(p); |
| if( signature == HFS_VOLHEAD_SIG) { /* Wrapper */ |
| UInt32 drAlBlkSiz; /* size (in bytes) of allocation blocks */ |
| UInt32 sect_per_block; /* how may block build an hfs sector */ |
| UInt16 drAlBlSt; /* first allocation block in volume */ |
| UInt16 embeds, embedl; /* Start/lenght of embedded area in blocks */ |
| |
| p += 0x12; /* skip unneded HFS vol fields */ |
| drAlBlkSiz = bswabU32_inc(p); /* offset 0x14 */ |
| p += 0x4; /* skip unneded HFS vol fields */ |
| drAlBlSt = bswabU16_inc(p); /* offset 0x1C */ |
| |
| p += 0x5E; /* skip unneded HFS vol fields */ |
| signature = bswabU16_inc(p); /* offset 0x7C, drEmbedSigWord */ |
| if( signature != HFSP_VOLHEAD_SIG) |
| HFSP_ERROR(-1, "This looks like a normal HFS volume"); |
| embeds = bswabU16_inc(p); |
| embedl = bswabU16_inc(p); |
| sect_per_block = (drAlBlkSiz / HFSP_BLOCKSZ); |
| // end is absolute (not relative to HFS+ start) |
| vol->maxblocks = embedl * sect_per_block; |
| vol->startblock = drAlBlSt + embeds * sect_per_block; |
| /* Now we can try to read the embedded HFS+ volume header */ |
| return volume_read(vol,vh,2); |
| } |
| else if( signature == HFSP_VOLHEAD_SIG) { /* Native HFS+ volume */ |
| p = buf; // Restore to begin of block |
| return volume_readbuf(vh, p); |
| } else |
| HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found"); |
| fail: |
| return -1; |
| } |
| |
| |
| /* Open the device, read and verify the volume header |
| (and its backup) */ |
| int |
| volume_open( volume* vol, int os_fd ) |
| { |
| hfsp_vh backup; /* backup volume found at second to last block */ |
| long sect_per_block; |
| int shift; |
| |
| vol->blksize_bits = HFSP_BLOCKSZ_BITS; |
| vol->blksize = HFSP_BLOCKSZ; |
| vol->startblock = 0; |
| vol->maxblocks = 3; |
| /* this should be enough until we find the volume descriptor */ |
| vol->extents = NULL; /* Thanks to Jeremias Sauceda */ |
| |
| btree_reset(&vol->catalog); |
| vol->os_fd = os_fd; |
| |
| // vol->maxblocks = os_seek(vol->os_fd, -1, HFSP_BLOCKSZ_BITS); |
| // This wont work for /dev/... but we do not really need it |
| |
| if( volume_read_wrapper(vol, &vol->vol)) |
| return -1; |
| if( volume_read(vol, &backup, vol->maxblocks - 2)) |
| return -1; |
| |
| /* Now switch blksize from HFSP_BLOCKSZ (512) to value given in header |
| and adjust depend values accordingly, after that a block always |
| means a HFS+ allocation size */ |
| |
| /* Usually 4096 / 512 == 8 */ |
| sect_per_block = vol->vol.blocksize / HFSP_BLOCKSZ; |
| shift = 0; |
| if( sect_per_block > 1) { |
| shift = 1; |
| while( sect_per_block > 2) { |
| sect_per_block >>=1; |
| shift++; |
| } /* shift = 3 */ |
| } |
| vol -> blksize_bits += shift; |
| vol -> blksize = 1 << vol->blksize_bits; |
| vol -> startblock >>= shift; |
| vol -> maxblocks = vol->vol.total_blocks; /* cant calculate via shift ? */ |
| |
| if( btree_init_cat(&vol->catalog, vol, &vol->vol.cat_file)) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* Write back all data eventually cached and close the device */ |
| int |
| volume_close(volume* vol) |
| { |
| btree_close(&vol->catalog); |
| if( vol->extents) { |
| btree_close(vol->extents); |
| FREE(vol->extents); |
| } |
| return 0; |
| } |
| |
| /* internal fucntion used to create the extents btree, |
| is called by inline function when needed */ |
| void |
| volume_create_extents_tree(volume* vol) |
| { |
| btree* result = (btree*) ALLOC(btree*, sizeof(btree)); |
| if( !result) |
| HFSP_ERROR(ENOMEM, "No memory for extents btree"); |
| if( !btree_init_extent(result, vol, &vol->vol.ext_file)) { |
| vol->extents = result; |
| return; |
| } |
| fail: |
| vol->extents = NULL; |
| } |