2 * libhfs - library for reading and writing Macintosh HFS volumes
4 * Code to acces the basic volume information of a HFS+ volume.
6 * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de>
7 * Original work by 1996-1998 Robert Leslie <rob@mars.org>
8 * other work 2000 from Brad Boyer (flar@pants.nu)
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
25 * $Id: volume.c,v 1.21 2000/10/25 05:43:04 hasi Exp $
33 #include "blockiter.h"
39 /* Fill a given buffer with the given block in volume.
42 volume_readinbuf(volume
* vol
,void* buf
, long block
)
45 ASSERT( block
< vol
->maxblocks
);
47 blksize_bits
= vol
->blksize_bits
;
48 block
+= vol
->startblock
;
49 if( os_seek(vol
->os_fd
, block
, blksize_bits
) == block
)
50 if( 1 == os_read(vol
->os_fd
, buf
, 1, blksize_bits
))
55 /* read multiple blocks into given memory.
57 * returns given pinter or NULL on failure.
60 volume_readfromfork(volume
* vol
, void* buf
,
61 hfsp_fork_raw
* f
, UInt32 block
,
62 UInt32 count
, UInt8 forktype
, UInt32 fileId
)
67 blockiter_init(&iter
, vol
, f
, forktype
, fileId
);
68 if( blockiter_skip(&iter
, block
))
73 if( volume_readinbuf(vol
, cbuf
, blockiter_curr(&iter
)))
76 if( count
> 0 && blockiter_next(&iter
))
83 /* Read a raw hfsp_extent_rec from memory.
85 * return pointer right after the structure.
88 volume_readextent(void *p
, hfsp_extent_rec er
)
93 for( i
=0; i
< 8; i
++) {
95 e
->start_block
= bswabU32_inc(p
);
96 e
->block_count
= bswabU32_inc(p
);
101 /* Read a raw hfsp_fork from memory.
103 * return pointer right after the structure.
106 volume_readfork(void *p
, hfsp_fork_raw
* f
)
108 f
->total_size
= bswabU64_inc(p
);
109 f
->clump_size
= bswabU32_inc(p
);
110 f
->total_blocks
= bswabU32_inc(p
);
112 return volume_readextent(p
, f
->extents
);
115 /* Read the volume from the given buffer and swap the bytes.
117 * ToDo: add more consitency checks.
120 volume_readbuf(hfsp_vh
* vh
, char * p
)
122 if( (vh
->signature
= bswabU16_inc(p
)) != HFSP_VOLHEAD_SIG
)
123 HFSP_ERROR(-1, "This is not a HFS+ volume");
125 vh
->version
= bswabU16_inc(p
);
126 vh
->attributes
= bswabU32_inc(p
);
127 vh
->last_mount_vers
= bswabU32_inc(p
);
128 vh
->reserved
= bswabU32_inc(p
);
129 vh
->create_date
= bswabU32_inc(p
);
130 vh
->modify_date
= bswabU32_inc(p
);
131 vh
->backup_date
= bswabU32_inc(p
);
132 vh
->checked_date
= bswabU32_inc(p
);
133 vh
->file_count
= bswabU32_inc(p
);
134 vh
->folder_count
= bswabU32_inc(p
);
135 vh
->blocksize
= bswabU32_inc(p
);
136 vh
->total_blocks
= bswabU32_inc(p
);
137 vh
->free_blocks
= bswabU32_inc(p
);
138 vh
->next_alloc
= bswabU32_inc(p
);
139 vh
->rsrc_clump_sz
= bswabU32_inc(p
);
140 vh
->data_clump_sz
= bswabU32_inc(p
);
141 vh
->next_cnid
= bswabU32_inc(p
);
142 vh
->write_count
= bswabU32_inc(p
);
143 vh
->encodings_bmp
= bswabU64_inc(p
);
144 memcpy(vh
->finder_info
, p
, 32);
145 p
+= 32; // So finderinfo must be swapped later, ***
146 p
= volume_readfork(p
, &vh
->alloc_file
);
147 p
= volume_readfork(p
, &vh
->ext_file
);
148 p
= volume_readfork(p
, &vh
->cat_file
);
149 p
= volume_readfork(p
, &vh
->attr_file
);
150 volume_readfork(p
, &vh
->start_file
);
156 /* Read the volume from the given block */
158 volume_read(volume
* vol
, hfsp_vh
* vh
, UInt32 block
)
160 char buf
[vol
->blksize
];
162 if( volume_readinbuf(vol
, buf
, block
))
164 return volume_readbuf(vh
, buf
);
167 /* Find out wether the volume is wrapped and unwrap it eventually */
169 volume_read_wrapper(volume
* vol
, hfsp_vh
* vh
)
172 char buf
[vol
->blksize
];
175 if( volume_readinbuf(vol
, buf
, 2) ) // Wrapper or volume header starts here
178 signature
= bswabU16_inc(p
);
179 if( signature
== HFS_VOLHEAD_SIG
) { /* Wrapper */
180 UInt32 drAlBlkSiz
; /* size (in bytes) of allocation blocks */
181 UInt32 sect_per_block
; /* how may block build an hfs sector */
182 UInt16 drAlBlSt
; /* first allocation block in volume */
183 UInt16 embeds
, embedl
; /* Start/lenght of embedded area in blocks */
185 p
+= 0x12; /* skip unneded HFS vol fields */
186 drAlBlkSiz
= bswabU32_inc(p
); /* offset 0x14 */
187 p
+= 0x4; /* skip unneded HFS vol fields */
188 drAlBlSt
= bswabU16_inc(p
); /* offset 0x1C */
190 p
+= 0x5E; /* skip unneded HFS vol fields */
191 signature
= bswabU16_inc(p
); /* offset 0x7C, drEmbedSigWord */
192 if( signature
!= HFSP_VOLHEAD_SIG
)
193 HFSP_ERROR(-1, "This looks like a normal HFS volume");
194 embeds
= bswabU16_inc(p
);
195 embedl
= bswabU16_inc(p
);
196 sect_per_block
= (drAlBlkSiz
/ HFSP_BLOCKSZ
);
197 // end is absolute (not relative to HFS+ start)
198 vol
->maxblocks
= embedl
* sect_per_block
;
199 vol
->startblock
= drAlBlSt
+ embeds
* sect_per_block
;
200 /* Now we can try to read the embedded HFS+ volume header */
201 return volume_read(vol
,vh
,2);
203 else if( signature
== HFSP_VOLHEAD_SIG
) { /* Native HFS+ volume */
204 p
= buf
; // Restore to begin of block
205 return volume_readbuf(vh
, p
);
207 HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found");
213 /* Open the device, read and verify the volume header
216 volume_open( volume
* vol
, int os_fd
)
218 hfsp_vh backup
; /* backup volume found at second to last block */
222 vol
->blksize_bits
= HFSP_BLOCKSZ_BITS
;
223 vol
->blksize
= HFSP_BLOCKSZ
;
226 /* this should be enough until we find the volume descriptor */
227 vol
->extents
= NULL
; /* Thanks to Jeremias Sauceda */
229 btree_reset(&vol
->catalog
);
232 // vol->maxblocks = os_seek(vol->os_fd, -1, HFSP_BLOCKSZ_BITS);
233 // This wont work for /dev/... but we do not really need it
235 if( volume_read_wrapper(vol
, &vol
->vol
))
237 if( volume_read(vol
, &backup
, vol
->maxblocks
- 2))
240 /* Now switch blksize from HFSP_BLOCKSZ (512) to value given in header
241 and adjust depend values accordingly, after that a block always
242 means a HFS+ allocation size */
244 /* Usually 4096 / 512 == 8 */
245 sect_per_block
= vol
->vol
.blocksize
/ HFSP_BLOCKSZ
;
247 if( sect_per_block
> 1) {
249 while( sect_per_block
> 2) {
254 vol
-> blksize_bits
+= shift
;
255 vol
-> blksize
= 1 << vol
->blksize_bits
;
256 vol
-> startblock
>>= shift
;
257 vol
-> maxblocks
= vol
->vol
.total_blocks
; /* cant calculate via shift ? */
259 if( btree_init_cat(&vol
->catalog
, vol
, &vol
->vol
.cat_file
))
265 /* Write back all data eventually cached and close the device */
267 volume_close(volume
* vol
)
269 btree_close(&vol
->catalog
);
271 btree_close(vol
->extents
);
277 /* internal fucntion used to create the extents btree,
278 is called by inline function when needed */
280 volume_create_extents_tree(volume
* vol
)
282 btree
* result
= (btree
*) ALLOC(btree
*, sizeof(btree
));
284 HFSP_ERROR(ENOMEM
, "No memory for extents btree");
285 if( !btree_init_extent(result
, vol
, &vol
->vol
.ext_file
)) {
286 vol
->extents
= result
;
293 /* Determine whether the volume is a HFS-plus volume */
295 volume_probe(int fd
, long long offset
)
300 vol
= (UInt16
*)malloc(2 * 1 << HFSP_BLOCKSZ_BITS
);
301 os_seek_offset( fd
, 2 * (1 << HFSP_BLOCKSZ_BITS
) + offset
);
302 os_read(fd
, vol
, 2, HFSP_BLOCKSZ_BITS
);
304 if (__be16_to_cpu(vol
[0]) == HFS_VOLHEAD_SIG
&&
305 __be16_to_cpu(vol
[0x7c]) == HFSP_VOLHEAD_SIG
) {
307 } else if (__be16_to_cpu(vol
[0]) == HFSP_VOLHEAD_SIG
) {