Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfsplus / hfsp_volume.c
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
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*
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*
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*
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
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
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
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
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
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
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
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