1 /*
2 * hfsutils - tools for reading and writing Macintosh HFS volumes
3 * Copyright (C) 1996, 1997 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 # include <string.h>
21 # include <stdlib.h>
22 # include <errno.h>
23 # include <unistd.h>
24 # include <fcntl.h>
25
26 # include "internal.h"
27 # include "data.h"
28 # include "block.h"
29 # include "low.h"
30 # include "file.h"
31
32 /*
33 * NAME: low->lockvol()
34 * DESCRIPTION: prevent destructive simultaneous access
35 */
l_lockvol(hfsvol * vol)36 int l_lockvol(hfsvol *vol)
37 {
38 # ifndef NODEVLOCKS
39
40 struct flock lock;
41
42 lock.l_type = (vol->flags & HFS_READONLY) ? F_RDLCK : F_WRLCK;
43 lock.l_start = 0;
44 lock.l_whence = SEEK_SET;
45 lock.l_len = 0;
46
47 if (fcntl(vol->fd, F_SETLK, &lock) < 0)
48 {
49 ERROR(errno, "unable to obtain lock for device");
50 return -1;
51 }
52
53 # endif
54
55 return 0;
56 }
57
58 /*
59 * NAME: low->readblock0()
60 * DESCRIPTION: read the first sector and get bearings
61 */
l_readblock0(hfsvol * vol)62 int l_readblock0(hfsvol *vol)
63 {
64 block b;
65 unsigned char *ptr = b;
66 Block0 rec;
67
68 if (b_readlb(vol, 0, &b) < 0)
69 return -1;
70
71 d_fetchw(&ptr, &rec.sbSig);
72 d_fetchw(&ptr, &rec.sbBlkSize);
73 d_fetchl(&ptr, &rec.sbBlkCount);
74 d_fetchw(&ptr, &rec.sbDevType);
75 d_fetchw(&ptr, &rec.sbDevId);
76 d_fetchl(&ptr, &rec.sbData);
77 d_fetchw(&ptr, &rec.sbDrvrCount);
78 d_fetchl(&ptr, &rec.ddBlock);
79 d_fetchw(&ptr, &rec.ddSize);
80 d_fetchw(&ptr, &rec.ddType);
81
82 switch (rec.sbSig)
83 {
84 case 0x4552: /* block device with a partition table */
85 {
86 if (rec.sbBlkSize != HFS_BLOCKSZ)
87 {
88 ERROR(EINVAL, "unsupported block size");
89 return -1;
90 }
91
92 vol->vlen = rec.sbBlkCount;
93
94 if (l_readpm(vol) < 0)
95 return -1;
96 }
97 break;
98
99 case 0x4c4b: /* bootable floppy */
100 vol->pnum = 0;
101 break;
102
103 default: /* non-bootable floppy or something else */
104
105 /* some miscreant media may also be partitioned;
106 we attempt to read a partition map, but ignore any failure */
107
108 if (l_readpm(vol) < 0)
109 vol->pnum = 0;
110 }
111
112 return 0;
113 }
114
115 /*
116 * NAME: low->readpm()
117 * DESCRIPTION: read the partition map and locate an HFS volume
118 */
l_readpm(hfsvol * vol)119 int l_readpm(hfsvol *vol)
120 {
121 block b;
122 unsigned char *ptr;
123 Partition map;
124 unsigned long bnum;
125 int pnum;
126
127 bnum = 1;
128 pnum = vol->pnum;
129
130 while (1)
131 {
132 if (b_readlb(vol, bnum, &b) < 0)
133 return -1;
134
135 ptr = b;
136
137 d_fetchw(&ptr, &map.pmSig);
138 d_fetchw(&ptr, &map.pmSigPad);
139 d_fetchl(&ptr, &map.pmMapBlkCnt);
140 d_fetchl(&ptr, &map.pmPyPartStart);
141 d_fetchl(&ptr, &map.pmPartBlkCnt);
142
143 memcpy(map.pmPartName, ptr, 32);
144 map.pmPartName[32] = 0;
145 ptr += 32;
146
147 memcpy(map.pmParType, ptr, 32);
148 map.pmParType[32] = 0;
149 ptr += 32;
150
151 d_fetchl(&ptr, &map.pmLgDataStart);
152 d_fetchl(&ptr, &map.pmDataCnt);
153 d_fetchl(&ptr, &map.pmPartStatus);
154 d_fetchl(&ptr, &map.pmLgBootStart);
155 d_fetchl(&ptr, &map.pmBootSize);
156 d_fetchl(&ptr, &map.pmBootAddr);
157 d_fetchl(&ptr, &map.pmBootAddr2);
158 d_fetchl(&ptr, &map.pmBootEntry);
159 d_fetchl(&ptr, &map.pmBootEntry2);
160 d_fetchl(&ptr, &map.pmBootCksum);
161
162 memcpy(map.pmProcessor, ptr, 16);
163 map.pmProcessor[16] = 0;
164 ptr += 16;
165
166 if (map.pmSig == 0x5453)
167 {
168 /* old partition map sig */
169
170 ERROR(EINVAL, "unsupported partition map signature");
171 return -1;
172 }
173
174 if (map.pmSig != 0x504d)
175 {
176 ERROR(EINVAL, "bad partition map");
177 return -1;
178 }
179
180 if (strcmp((char *) map.pmParType, "Apple_HFS") == 0 && --pnum == 0)
181 {
182 if (map.pmLgDataStart != 0)
183 {
184 ERROR(EINVAL, "unsupported start of partition logical data");
185 return -1;
186 }
187
188 vol->vstart = map.pmPyPartStart;
189 vol->vlen = map.pmPartBlkCnt;
190
191 return 0;
192 }
193
194 if (bnum >= map.pmMapBlkCnt)
195 {
196 ERROR(EINVAL, "can't find HFS partition");
197 return -1;
198 }
199
200 ++bnum;
201 }
202 }
203
204 /*
205 * NAME: low->readmdb()
206 * DESCRIPTION: read the master directory block into memory
207 */
l_readmdb(hfsvol * vol)208 int l_readmdb(hfsvol *vol)
209 {
210 block b;
211 unsigned char *ptr = b;
212 MDB *mdb = &vol->mdb;
213 hfsfile *ext = &vol->ext.f;
214 hfsfile *cat = &vol->cat.f;
215 int i;
216
217 if (b_readlb(vol, 2, &b) < 0)
218 return -1;
219
220 d_fetchw(&ptr, &mdb->drSigWord);
221 d_fetchl(&ptr, &mdb->drCrDate);
222 d_fetchl(&ptr, &mdb->drLsMod);
223 d_fetchw(&ptr, &mdb->drAtrb);
224 d_fetchw(&ptr, (short *) &mdb->drNmFls);
225 d_fetchw(&ptr, (short *) &mdb->drVBMSt);
226 d_fetchw(&ptr, (short *) &mdb->drAllocPtr);
227 d_fetchw(&ptr, (short *) &mdb->drNmAlBlks);
228 d_fetchl(&ptr, (long *) &mdb->drAlBlkSiz);
229 d_fetchl(&ptr, (long *) &mdb->drClpSiz);
230 d_fetchw(&ptr, (short *) &mdb->drAlBlSt);
231 d_fetchl(&ptr, &mdb->drNxtCNID);
232 d_fetchw(&ptr, (short *) &mdb->drFreeBks);
233
234 d_fetchs(&ptr, mdb->drVN, sizeof(mdb->drVN));
235
236 if (ptr - b != 64)
237 abort();
238
239 d_fetchl(&ptr, &mdb->drVolBkUp);
240 d_fetchw(&ptr, &mdb->drVSeqNum);
241 d_fetchl(&ptr, (long *) &mdb->drWrCnt);
242 d_fetchl(&ptr, (long *) &mdb->drXTClpSiz);
243 d_fetchl(&ptr, (long *) &mdb->drCTClpSiz);
244 d_fetchw(&ptr, (short *) &mdb->drNmRtDirs);
245 d_fetchl(&ptr, (long *) &mdb->drFilCnt);
246 d_fetchl(&ptr, (long *) &mdb->drDirCnt);
247
248 for (i = 0; i < 8; ++i)
249 d_fetchl(&ptr, &mdb->drFndrInfo[i]);
250
251 if (ptr - b != 124)
252 abort();
253
254 d_fetchw(&ptr, (short *) &mdb->drVCSize);
255 d_fetchw(&ptr, (short *) &mdb->drVBMCSize);
256 d_fetchw(&ptr, (short *) &mdb->drCtlCSize);
257
258 d_fetchl(&ptr, (long *) &mdb->drXTFlSize);
259
260 for (i = 0; i < 3; ++i)
261 {
262 d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrStABN);
263 d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrNumABlks);
264 }
265
266 if (ptr - b != 146)
267 abort();
268
269 d_fetchl(&ptr, (long *) &mdb->drCTFlSize);
270
271 for (i = 0; i < 3; ++i)
272 {
273 d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrStABN);
274 d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrNumABlks);
275 }
276
277 if (ptr - b != 162)
278 abort();
279
280 vol->lpa = mdb->drAlBlkSiz / HFS_BLOCKSZ;
281
282 /* extents pseudo-file structs */
283
284 ext->vol = vol;
285 ext->parid = 0;
286 strcpy(ext->name, "extents overflow");
287
288 ext->cat.cdrType = cdrFilRec;
289 /* ext->cat.cdrResrv2 */
290 ext->cat.u.fil.filFlags = 0;
291 ext->cat.u.fil.filTyp = 0;
292 /* ext->cat.u.fil.filUsrWds */
293 ext->cat.u.fil.filFlNum = HFS_CNID_EXT;
294 ext->cat.u.fil.filStBlk = mdb->drXTExtRec[0].xdrStABN;
295 ext->cat.u.fil.filLgLen = mdb->drXTFlSize;
296 ext->cat.u.fil.filPyLen = mdb->drXTFlSize;
297 ext->cat.u.fil.filRStBlk = 0;
298 ext->cat.u.fil.filRLgLen = 0;
299 ext->cat.u.fil.filRPyLen = 0;
300 ext->cat.u.fil.filCrDat = mdb->drCrDate;
301 ext->cat.u.fil.filMdDat = mdb->drLsMod;
302 ext->cat.u.fil.filBkDat = 0;
303 /* ext->cat.u.fil.filFndrInfo */
304 ext->cat.u.fil.filClpSize = 0;
305
306 memcpy(ext->cat.u.fil.filExtRec, mdb->drXTExtRec, sizeof(ExtDataRec));
307 for (i = 0; i < 3; ++i)
308 {
309 ext->cat.u.fil.filRExtRec[i].xdrStABN = 0;
310 ext->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
311 }
312 f_selectfork(ext, 0);
313
314 ext->clump = mdb->drXTClpSiz;
315 ext->flags = 0;
316
317 ext->prev = ext->next = 0;
318
319 /* catalog pseudo-file structs */
320
321 cat->vol = vol;
322 cat->parid = 0;
323 strcpy(cat->name, "catalog");
324
325 cat->cat.cdrType = cdrFilRec;
326 /* cat->cat.cdrResrv2 */
327 cat->cat.u.fil.filFlags = 0;
328 cat->cat.u.fil.filTyp = 0;
329 /* cat->cat.u.fil.filUsrWds */
330 cat->cat.u.fil.filFlNum = HFS_CNID_CAT;
331 cat->cat.u.fil.filStBlk = mdb->drCTExtRec[0].xdrStABN;
332 cat->cat.u.fil.filLgLen = mdb->drCTFlSize;
333 cat->cat.u.fil.filPyLen = mdb->drCTFlSize;
334 cat->cat.u.fil.filRStBlk = 0;
335 cat->cat.u.fil.filRLgLen = 0;
336 cat->cat.u.fil.filRPyLen = 0;
337 cat->cat.u.fil.filCrDat = mdb->drCrDate;
338 cat->cat.u.fil.filMdDat = mdb->drLsMod;
339 cat->cat.u.fil.filBkDat = 0;
340 /* cat->cat.u.fil.filFndrInfo */
341 cat->cat.u.fil.filClpSize = 0;
342
343 memcpy(cat->cat.u.fil.filExtRec, mdb->drCTExtRec, sizeof(ExtDataRec));
344 for (i = 0; i < 3; ++i)
345 {
346 cat->cat.u.fil.filRExtRec[i].xdrStABN = 0;
347 cat->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
348 }
349 f_selectfork(cat, 0);
350
351 cat->clump = mdb->drCTClpSiz;
352 cat->flags = 0;
353
354 cat->prev = cat->next = 0;
355
356 return 0;
357 }
358
359 /*
360 * NAME: low->writemdb()
361 * DESCRIPTION: write the master directory block to disk
362 */
l_writemdb(hfsvol * vol)363 int l_writemdb(hfsvol *vol)
364 {
365 block b;
366 unsigned char *ptr = b;
367 MDB *mdb = &vol->mdb;
368 hfsfile *ext = &vol->ext.f;
369 hfsfile *cat = &vol->cat.f;
370 int i;
371
372 memset(&b, 0, sizeof(b));
373
374 mdb->drXTFlSize = ext->cat.u.fil.filPyLen;
375 mdb->drXTClpSiz = ext->clump;
376 memcpy(mdb->drXTExtRec, ext->cat.u.fil.filExtRec, sizeof(ExtDataRec));
377
378 mdb->drCTFlSize = cat->cat.u.fil.filPyLen;
379 mdb->drCTClpSiz = cat->clump;
380 memcpy(mdb->drCTExtRec, cat->cat.u.fil.filExtRec, sizeof(ExtDataRec));
381
382 d_storew(&ptr, mdb->drSigWord);
383 d_storel(&ptr, mdb->drCrDate);
384 d_storel(&ptr, mdb->drLsMod);
385 d_storew(&ptr, mdb->drAtrb);
386 d_storew(&ptr, mdb->drNmFls);
387 d_storew(&ptr, mdb->drVBMSt);
388 d_storew(&ptr, mdb->drAllocPtr);
389 d_storew(&ptr, mdb->drNmAlBlks);
390 d_storel(&ptr, mdb->drAlBlkSiz);
391 d_storel(&ptr, mdb->drClpSiz);
392 d_storew(&ptr, mdb->drAlBlSt);
393 d_storel(&ptr, mdb->drNxtCNID);
394 d_storew(&ptr, mdb->drFreeBks);
395 d_stores(&ptr, mdb->drVN, sizeof(mdb->drVN));
396
397 if (ptr - b != 64)
398 abort();
399
400 d_storel(&ptr, mdb->drVolBkUp);
401 d_storew(&ptr, mdb->drVSeqNum);
402 d_storel(&ptr, mdb->drWrCnt);
403 d_storel(&ptr, mdb->drXTClpSiz);
404 d_storel(&ptr, mdb->drCTClpSiz);
405 d_storew(&ptr, mdb->drNmRtDirs);
406 d_storel(&ptr, mdb->drFilCnt);
407 d_storel(&ptr, mdb->drDirCnt);
408
409 for (i = 0; i < 8; ++i)
410 d_storel(&ptr, mdb->drFndrInfo[i]);
411
412 if (ptr - b != 124)
413 abort();
414
415 d_storew(&ptr, mdb->drVCSize);
416 d_storew(&ptr, mdb->drVBMCSize);
417 d_storew(&ptr, mdb->drCtlCSize);
418 d_storel(&ptr, mdb->drXTFlSize);
419
420 for (i = 0; i < 3; ++i)
421 {
422 d_storew(&ptr, mdb->drXTExtRec[i].xdrStABN);
423 d_storew(&ptr, mdb->drXTExtRec[i].xdrNumABlks);
424 }
425
426 if (ptr - b != 146)
427 abort();
428
429 d_storel(&ptr, mdb->drCTFlSize);
430
431 for (i = 0; i < 3; ++i)
432 {
433 d_storew(&ptr, mdb->drCTExtRec[i].xdrStABN);
434 d_storew(&ptr, mdb->drCTExtRec[i].xdrNumABlks);
435 }
436
437 if (ptr - b != 162)
438 abort();
439
440 if (b_writelb(vol, 2, &b) < 0)
441 return -1;
442 if (vol->flags & HFS_UPDATE_ALTMDB)
443 {
444 #ifdef APPLE_HYB
445 /* "write" alternative MDB to memory copy */
446 memcpy(vol->hce->hfs_alt_mdb, &b, sizeof(b));
447 #else
448 if (b_writelb(vol, vol->vlen - 2, &b) < 0)
449 return -1;
450 #endif /* APPLE_HYB */
451 }
452 vol->flags &= ~(HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB);
453
454 return 0;
455 }
456
457 /*
458 * NAME: low->readvbm()
459 * DESCRIPTION: read the volume bit map into memory
460 */
l_readvbm(hfsvol * vol)461 int l_readvbm(hfsvol *vol)
462 {
463 int vbmst = vol->mdb.drVBMSt;
464 int vbmsz = (vol->mdb.drNmAlBlks + 4095) / 4096;
465 block *bp;
466
467 if (vol->mdb.drAlBlSt - vbmst < vbmsz)
468 {
469 ERROR(EIO, "volume bitmap collides with volume data");
470 return -1;
471 }
472
473 bp = ALLOC(block, vbmsz);
474 if (bp == 0)
475 {
476 ERROR(ENOMEM, 0);
477 return -1;
478 }
479
480 vol->vbm = bp;
481
482 while (vbmsz--)
483 {
484 if (b_readlb(vol, vbmst++, bp++) < 0)
485 {
486 FREE(vol->vbm);
487 vol->vbm = 0;
488
489 return -1;
490 }
491 }
492
493 return 0;
494 }
495
496 /*
497 * NAME: low->writevbm()
498 * DESCRIPTION: write the volume bit map to disk
499 */
l_writevbm(hfsvol * vol)500 int l_writevbm(hfsvol *vol)
501 {
502 int vbmst = vol->mdb.drVBMSt;
503 int vbmsz = (vol->mdb.drNmAlBlks + 4095) / 4096;
504 block *bp = vol->vbm;
505
506 while (vbmsz--)
507 {
508 if (b_writelb(vol, vbmst++, bp++) < 0)
509 return -1;
510 }
511
512 vol->flags &= ~HFS_UPDATE_VBM;
513
514 return 0;
515 }
516