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  */
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  */
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  */
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  */
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  */
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  */
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  */
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