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 <khalfmann@libra.de>
7  * Original work by 1996-1998 Robert Leslie <rob@mars.org>
8  * other work 2000 from Brad Boyer (flar@pants.nu)
9  *
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.
14  *
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.
19  *
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,
23  * MA 02110-1301, USA.
24  *
25  * $Id: volume.c,v 1.21 2000/10/25 05:43:04 hasi Exp $
26  */
27 
28 #include "config.h"
29 #include "libhfsp.h"
30 #include "volume.h"
31 #include "record.h"
32 #include "btree.h"
33 #include "blockiter.h"
34 #include "os.h"
35 #include "swab.h"
36 #include "hfstime.h"
37 
38 
39 /* Fill a given buffer with the given block in volume.
40  */
41 int
volume_readinbuf(volume * vol,void * buf,long block)42 volume_readinbuf(volume * vol,void* buf, long block)
43 {
44 	UInt16 blksize_bits;
45 	ASSERT( block < vol->maxblocks);
46 
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))
51 			return 0;
52 	return -1;
53 }
54 
55 /* read multiple blocks into given memory.
56  *
57  * returns given pinter or NULL on failure.
58  */
59 void*
volume_readfromfork(volume * vol,void * buf,hfsp_fork_raw * f,UInt32 block,UInt32 count,UInt8 forktype,UInt32 fileId)60 volume_readfromfork(volume* vol, void* buf,
61 		hfsp_fork_raw* f, UInt32 block,
62 		UInt32 count, UInt8 forktype, UInt32 fileId)
63 {
64 	blockiter iter;
65 	char *cbuf = buf;
66 
67 	blockiter_init(&iter, vol, f, forktype, fileId);
68 	if( blockiter_skip(&iter, block))
69 		return NULL;
70 
71 	while( count > 0) {
72 		--count;
73 		if( volume_readinbuf(vol, cbuf, blockiter_curr(&iter)))
74 			return NULL;
75 		cbuf += vol->blksize;
76 		if( count > 0 && blockiter_next(&iter))
77 			return NULL;
78 	}
79 	return buf;
80 }
81 
82 
83 /* Read a raw hfsp_extent_rec from memory.
84  *
85  * return pointer right after the structure.
86  */
87 void*
volume_readextent(void * p,hfsp_extent_rec er)88 volume_readextent(void *p, hfsp_extent_rec er)
89 {
90 	int 		i;
91 	hfsp_extent	*e;
92 
93 	for( i=0; i < 8; i++) {
94 		e = &er[i];
95 		e->start_block = bswabU32_inc(p);
96 		e->block_count = bswabU32_inc(p);
97 	}
98 	return p;
99 }
100 
101 /* Read a raw hfsp_fork from memory.
102  *
103  * return pointer right after the structure.
104  */
105 void*
volume_readfork(void * p,hfsp_fork_raw * f)106 volume_readfork(void *p, hfsp_fork_raw* f)
107 {
108 	f->total_size   = bswabU64_inc(p);
109 	f->clump_size   = bswabU32_inc(p);
110 	f->total_blocks = bswabU32_inc(p);
111 
112 	return volume_readextent(p, f->extents);
113 }
114 
115 /* Read the volume from the given buffer and swap the bytes.
116  *
117  * ToDo: add more consitency checks.
118  */
119 static int
volume_readbuf(hfsp_vh * vh,char * p)120 volume_readbuf(hfsp_vh* vh, char * p)
121 {
122 	if(  (vh->signature = bswabU16_inc(p)) != HFSP_VOLHEAD_SIG)
123 		HFSP_ERROR(-1, "This is not a HFS+ volume");
124 
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 );
151 	return 0;
152   fail:
153 	return -1;
154 }
155 
156 /* Read the volume from the given block */
157 static int
volume_read(volume * vol,hfsp_vh * vh,UInt32 block)158 volume_read(volume * vol, hfsp_vh* vh, UInt32 block)
159 {
160 	char buf[vol->blksize];
161 
162 	if( volume_readinbuf(vol, buf, block))
163 		return -1;
164         return volume_readbuf(vh, buf);
165 }
166 
167 /* Find out wether the volume is wrapped and unwrap it eventually */
168 static int
volume_read_wrapper(volume * vol,hfsp_vh * vh)169 volume_read_wrapper(volume * vol, hfsp_vh* vh)
170 {
171 	UInt16  signature;
172 	char	buf[vol->blksize];
173         char    *p = buf;
174 	int	ret;
175 	UInt64	vol_size;
176 
177 	if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here
178 		return -1;
179 
180 	signature = bswabU16_inc(p);
181 	if( signature == HFS_VOLHEAD_SIG) {		/* Wrapper */
182 		UInt32  drAlBlkSiz;			/* size (in bytes) of allocation blocks */
183 		UInt32	sect_per_block;			/* how may block build an hfs sector */
184 		UInt16  drAlBlSt;			/* first allocation block in volume */
185 		UInt16	embeds, embedl;			/* Start/lenght of embedded area in blocks */
186 
187 		p += 0x12;			/* skip unneded HFS vol fields */
188 		drAlBlkSiz = bswabU32_inc(p);		/* offset 0x14 */
189 		p += 0x4;			/* skip unneded HFS vol fields */
190 		drAlBlSt = bswabU16_inc(p);		/* offset 0x1C */
191 
192 		p += 0x5E;			/* skip unneded HFS vol fields */
193 		signature = bswabU16_inc(p);		/* offset 0x7C, drEmbedSigWord */
194 		if( signature != HFSP_VOLHEAD_SIG)
195 			HFSP_ERROR(-1, "This looks like a normal HFS volume");
196 		embeds = bswabU16_inc(p);
197 		embedl = bswabU16_inc(p);
198 		sect_per_block =  (drAlBlkSiz / HFSP_BLOCKSZ);
199 		// end is absolute (not relative to HFS+ start)
200 		vol->maxblocks = embedl * sect_per_block;
201 		vol->startblock = drAlBlSt + embeds * sect_per_block;
202 		/* Now we can try to read the embedded HFS+ volume header */
203 		return volume_read(vol,vh,2);
204 	}
205 	else if( signature == HFSP_VOLHEAD_SIG) { /* Native HFS+ volume */
206 		p = buf; // Restore to begin of block
207                 ret = volume_readbuf(vh, p);
208 		if( !ret ) {
209 		    /* When reading the initial partition we must use 512 byte blocks */
210 		    vol_size = (uint64_t)vh->blocksize * vh->total_blocks;
211 		    vol->maxblocks = vol_size / HFSP_BLOCKSZ;
212 		}
213 
214 		return ret;
215 	} else
216 		 HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found");
217 fail:
218 	return -1;
219 }
220 
221 
222 /* Open the device, read and verify the volume header
223    (and its backup) */
224 int
volume_open(volume * vol,int os_fd)225 volume_open( volume* vol, int os_fd )
226 {
227 	hfsp_vh backup;	/* backup volume found at second to last block */
228 	long	sect_per_block;
229 	int	shift;
230 
231 	vol->blksize_bits	= HFSP_BLOCKSZ_BITS;
232 	vol->blksize		= HFSP_BLOCKSZ;
233 	vol->startblock		= 0;
234 	vol->maxblocks		= 3;
235 		/* this should be enough until we find the volume descriptor */
236 	vol->extents		= NULL; /* Thanks to Jeremias Sauceda */
237 
238 	btree_reset(&vol->catalog);
239 	vol->os_fd = os_fd;
240 
241 	// vol->maxblocks = os_seek(vol->os_fd, -1, HFSP_BLOCKSZ_BITS);
242 	// This wont work for /dev/... but we do not really need it
243 
244 	if( volume_read_wrapper(vol, &vol->vol))
245 		return -1;
246 	if( volume_read(vol, &backup, vol->maxblocks - 2))
247 		return -1;
248 
249 	/* Now switch blksize from HFSP_BLOCKSZ (512) to value given in header
250 	   and adjust depend values accordingly, after that a block always
251 	   means a HFS+ allocation size */
252 
253 	/* Usually 4096 / 512  == 8 */
254 	sect_per_block = vol->vol.blocksize / HFSP_BLOCKSZ;
255 	shift = 0;
256 	if( sect_per_block > 1) {
257 		shift = 1;
258 		while( sect_per_block > 2) {
259 			sect_per_block >>=1;
260 			shift++;
261 		}		/* shift = 3 */
262 	}
263 	vol -> blksize_bits += shift;
264 	vol -> blksize = 1 << vol->blksize_bits;
265 	vol -> startblock >>= shift;
266 	vol -> maxblocks = vol->vol.total_blocks;	/* cant calculate via shift ? */
267 
268 	if( btree_init_cat(&vol->catalog, vol, &vol->vol.cat_file))
269 		return -1;
270 
271 	return 0;
272 }
273 
274 /* Write back all data eventually cached and close the device */
275 int
volume_close(volume * vol)276 volume_close(volume* vol)
277 {
278 	btree_close(&vol->catalog);
279 	if( vol->extents) {
280 		btree_close(vol->extents);
281 		FREE(vol->extents);
282 	}
283 	return 0;
284 }
285 
286 /* internal fucntion used to create the extents btree,
287    is called by inline function when needed */
288 void
volume_create_extents_tree(volume * vol)289 volume_create_extents_tree(volume* vol)
290 {
291 	btree* result = (btree*) ALLOC(btree*, sizeof(btree));
292 	if( !result)
293 		HFSP_ERROR(ENOMEM, "No memory for extents btree");
294 	if( !btree_init_extent(result, vol, &vol->vol.ext_file)) {
295 		vol->extents = result;
296 		return;
297 	}
298   fail:
299 	vol->extents = NULL;
300 }
301 
302 /* Determine whether the volume is a HFS-plus volume */
303 int
volume_probe(int fd,long long offset)304 volume_probe(int fd, long long offset)
305 {
306 	UInt16 *vol;
307 	int ret = 0;
308 
309 	vol = (UInt16 *)malloc(2 * 1 << HFSP_BLOCKSZ_BITS);
310 	os_seek_offset( fd, 2 * (1 << HFSP_BLOCKSZ_BITS) + offset );
311 	os_read(fd, vol, 2, HFSP_BLOCKSZ_BITS);
312 
313 	if (__be16_to_cpu(vol[0]) == HFS_VOLHEAD_SIG &&
314 		__be16_to_cpu(vol[0x3e]) == HFSP_VOLHEAD_SIG) {
315 		ret = -1;
316 	} else if (__be16_to_cpu(vol[0]) == HFSP_VOLHEAD_SIG) {
317 		ret = -1;
318 	}
319 
320 	free(vol);
321 	return ret;
322 }
323 
324