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 /* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */
21 
22 # include <stdlib.h>
23 # include <unistd.h>
24 # include <fcntl.h>
25 # include <errno.h>
26 # include <string.h>
27 # include <time.h>
28 # include <ctype.h>
29 # include <sys/stat.h>
30 
31 # include "internal.h"
32 # include "data.h"
33 # include "block.h"
34 # include "low.h"
35 # include "file.h"
36 # include "btree.h"
37 # include "node.h"
38 # include "record.h"
39 # include "volume.h"
40 
41 char *hfs_error = "no error";	/* static error string */
42 
43 hfsvol *hfs_mounts;		/* linked list of mounted volumes */
44 hfsvol *hfs_curvol;		/* current volume */
45 
46 /* High-Level Volume Routines ============================================== */
47 
48 /*
49  * NAME:	hfs->mount()
50  * DESCRIPTION:	open an HFS volume; return volume descriptor or 0 (error)
51  */
52 #ifdef APPLE_HYB
53 hfsvol *hfs_mount(hce_mem *hce, int pnum, int flags)
54 #else
55 hfsvol *hfs_mount(char *path, int pnum, int flags)
56 #endif /* APPLE_HYB */
57 {
58   struct stat dev;
59   hfsvol *vol = 0;
60 
61 #ifndef APPLE_HYB
62   /* see if the volume is already mounted */
63 
64   if (stat(path, &dev) >= 0)
65     {
66       struct stat mdev;
67       hfsvol *check;
68 
69       for (check = hfs_mounts; check; check = check->next)
70 	{
71 	  if (fstat(check->fd, &mdev) >= 0 &&
72 	      mdev.st_dev == dev.st_dev &&
73 	      mdev.st_ino == dev.st_ino &&
74 	      (check->pnum == 0 || check->pnum == pnum))
75 	    {
76 	      /* verify compatible read/write mode */
77 
78 	      if (((check->flags & HFS_READONLY) &&
79 		   ! (flags & O_WRONLY)) ||
80 		  (! (check->flags & HFS_READONLY) &&
81 		   (flags & (O_WRONLY | O_RDWR))))
82 		{
83 		  vol = check;
84 		  break;
85 		}
86 	    }
87 	}
88     }
89 #endif /* APPLE_HYB */
90   if (vol == 0)
91     {
92       vol = ALLOC(hfsvol, 1);
93       if (vol == 0)
94 	{
95 	  ERROR(ENOMEM, 0);
96 	  return 0;
97 	}
98 
99       vol->flags  = 0;
100       vol->pnum   = pnum;
101       vol->vstart = 0;
102       vol->vlen   = 0;
103       vol->lpa    = 0;
104       vol->vbm    = 0;
105       vol->cwd    = HFS_CNID_ROOTDIR;
106 
107       vol->refs   = 0;
108       vol->files  = 0;
109       vol->dirs   = 0;
110       vol->prev   = 0;
111       vol->next   = 0;
112 
113       vol->ext.map     = 0;
114       vol->ext.mapsz   = 0;
115       vol->ext.flags   = 0;
116       vol->ext.compare = r_compareextkeys;
117 
118       vol->cat.map     = 0;
119       vol->cat.mapsz   = 0;
120       vol->cat.flags   = 0;
121       vol->cat.compare = r_comparecatkeys;
122 
123       /* open and lock the device */
124 
125 #ifdef APPLE_HYB
126       vol->fd = 3;			/* any +ve number will do? */
127       vol->hce = hce;			/* store the extra with the vol info */
128 #else
129       if (flags & (O_WRONLY | O_RDWR))
130 	{
131 	  vol->fd = open(path, O_RDWR);
132 	  if (vol->fd >= 0 && l_lockvol(vol) < 0)
133 	    {
134 	      close(vol->fd);
135 	      vol->fd = -2;
136 	    }
137 	}
138 
139       if (! (flags & (O_WRONLY | O_RDWR)) ||
140 	  (vol->fd < 0 &&
141 	   (errno == EROFS || errno == EACCES || errno == EAGAIN) &&
142 	   (flags & O_RDWR)))
143 	{
144 	  vol->flags |= HFS_READONLY;
145 	  vol->fd = open(path, O_RDONLY);
146 	  if (vol->fd >= 0 && l_lockvol(vol) < 0)
147 	    {
148 	      close(vol->fd);
149 	      vol->fd = -2;
150 	    }
151 	}
152 
153       if (vol->fd < 0)
154 	{
155 	  if (vol->fd != -2)
156 	    ERROR(errno, "error opening device");
157 
158 	  v_destruct(vol);
159 
160 	  return 0;
161 	}
162 #endif /* APPLE_HYB */
163 
164       /* find out what kind of media this is and read the MDB */
165 
166       if (l_readblock0(vol) < 0 ||
167 	  l_readmdb(vol) < 0)
168 	{
169 #ifndef APPLE_HYB
170 	  close(vol->fd);
171 	  v_destruct(vol);
172 #endif /* APPLE_HYB */
173 	  return 0;
174 	}
175 
176       /* verify this is an HFS volume */
177 
178       if (vol->mdb.drSigWord != 0x4244)
179 	{
180 #ifndef APPLE_HYB
181 	  close(vol->fd);
182 #endif /* APPLE_HYB */
183 	  v_destruct(vol);
184 
185 	  ERROR(EINVAL, "not a Macintosh HFS volume");
186 	  return 0;
187 	}
188 
189       /* do minimal consistency checks */
190 
191       if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
192 	{
193 #ifndef APPLE_HYB
194 	  close(vol->fd);
195 #endif /* APPLE_HYB */
196 	  v_destruct(vol);
197 
198 	  ERROR(EINVAL, "bad volume allocation block size");
199 	  return 0;
200 	}
201 
202       if (vol->vlen == 0)
203 	vol->vlen = vol->mdb.drAlBlSt +
204 	  vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2;
205 
206       /* read the volume bitmap and extents/catalog B*-tree headers */
207 
208       if (l_readvbm(vol) < 0 ||
209 	  bt_readhdr(&vol->ext) < 0 ||
210 	  bt_readhdr(&vol->cat) < 0)
211 	{
212 #ifndef APPLE_HYB
213 	  close(vol->fd);
214 #endif /* APPLE_HYB */
215 	  v_destruct(vol);
216 	  return 0;
217 	}
218 
219       if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
220 	{
221 	  /* volume was not cleanly unmounted; scavenge free-space */
222 
223 	  if (v_scavenge(vol) < 0)
224 	    {
225 #ifndef APPLE_HYB
226 	      close(vol->fd);
227 #endif /* APPLE_HYB */
228 	      v_destruct(vol);
229 	      return 0;
230 	    }
231 	}
232 
233       if (vol->flags & HFS_READONLY)
234 	vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
235       else
236 	vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
237 
238       vol->prev = 0;
239       vol->next = hfs_mounts;
240 
241       if (hfs_mounts)
242 	hfs_mounts->prev = vol;
243 
244       hfs_mounts = vol;
245     }
246 
247   ++vol->refs;
248 
249   return hfs_curvol = vol;
250 }
251 
252 /*
253  * NAME:	hfs->flush()
254  * DESCRIPTION:	flush all pending changes to an HFS volume
255  */
256 int hfs_flush(hfsvol *vol)
257 {
258   hfsfile *file;
259 
260   if (v_getvol(&vol) < 0)
261     return -1;
262 
263   for (file = vol->files; file; file = file->next)
264     {
265       if (f_flush(file) < 0)
266 	return -1;
267     }
268 
269   if (v_flush(vol, 0) < 0)
270     return -1;
271 
272   return 0;
273 }
274 
275 /*
276  * NAME:	hfs->flushall()
277  * DESCRIPTION:	flush all pending changes to all mounted HFS volumes
278  */
279 void hfs_flushall(void)
280 {
281   hfsvol *vol;
282 
283   for (vol = hfs_mounts; vol; vol = vol->next)
284     hfs_flush(vol);
285 }
286 
287 /*
288  * NAME:	hfs->umount()
289  * DESCRIPTION:	close an HFS volume
290  */
291 #ifdef APPLE_HYB
292 /* extra argument used to alter the position of the extents/catalog files */
293 int hfs_umount(hfsvol *vol, long end)
294 #else
295 int hfs_umount(hfsvol *vol)
296 #endif /* APPLE_HYB */
297 {
298   int result = 0;
299 
300   if (v_getvol(&vol) < 0)
301     return -1;
302 
303   if (--vol->refs)
304     return v_flush(vol, 0);
305 
306   /* close all open files and directories */
307 
308   while (vol->files)
309 #ifdef APPLE_HYB
310     hfs_close(vol->files, 0, 0);
311 #else
312     hfs_close(vol->files);
313 #endif /* APPLE_HYB */
314 
315   while (vol->dirs)
316     hfs_closedir(vol->dirs);
317 
318 #ifdef APPLE_HYB
319   if (end)
320   {
321 	/*  move extents and catalog to end of volume ... */
322   	long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096;
323 
324 	/* we are adding this "files" to the end of the ISO volume,
325 	   so calculate this address in HFS speak ... */
326 /*	end -= vol->mdb.drAlBlSt; */
327 	end -= (vol->mdb.drAlBlSt + vol->hce->hfs_map_size);
328 	end /= vol->lpa;
329 
330 	/* catalog file ... */
331 	vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end;
332  	vol->mdb.drXTExtRec[0].xdrStABN = end;
333 
334 	/* move postition to start of extents file */
335 	end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN;
336 
337 	/* extents file ... */
338 	vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end;
339 	vol->mdb.drCTExtRec[0].xdrStABN = end;
340 
341 	/* the volume bitmap is wrong as we have "moved" files
342 	   about - simple just set the whole lot (it's a readonly volume
343 	   anyway!) */
344 	memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ);
345 
346 	/* set the free blocks to zero */
347 	vol->mdb.drFreeBks = 0;
348 
349 	/* flag changes for flushing later */
350 	vol->flags |= HFS_UPDATE_VBM;
351 	vol->flags |= HFS_UPDATE_MDB;
352 	vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
353 	vol->ext.flags |= HFS_UPDATE_BTHDR;
354 	vol->cat.flags |= HFS_UPDATE_BTHDR;
355   }
356 #endif /* APPLE_HYB */
357 
358   if (v_flush(vol, 1) < 0)
359     result = -1;
360 
361 #ifndef APPLE_HYB
362   if (close(vol->fd) < 0 && result == 0)
363     {
364       ERROR(errno, "error closing device");
365       result = -1;
366     }
367 #endif /* APPLE_HYB */
368 
369   if (vol->prev)
370     vol->prev->next = vol->next;
371   if (vol->next)
372     vol->next->prev = vol->prev;
373 
374   if (vol == hfs_mounts)
375     hfs_mounts = vol->next;
376   if (vol == hfs_curvol)
377     hfs_curvol = 0;
378 
379   v_destruct(vol);
380 
381   return result;
382 }
383 
384 /*
385  * NAME:	hfs->umountall()
386  * DESCRIPTION:	unmount all mounted volumes
387  */
388 void hfs_umountall(void)
389 {
390   while (hfs_mounts)
391 #ifdef APPLE_HYB
392     continue;
393 #else
394     hfs_umount(hfs_mounts);
395 #endif /* APPLE_HYB */
396 }
397 
398 /*
399  * NAME:	hfs->getvol()
400  * DESCRIPTION:	return a pointer to a mounted volume
401  */
402 hfsvol *hfs_getvol(char *name)
403 {
404   hfsvol *vol;
405 
406   if (name == 0)
407     return hfs_curvol;
408 
409   for (vol = hfs_mounts; vol; vol = vol->next)
410     {
411       if (d_relstring(name, vol->mdb.drVN) == 0)
412 	return vol;
413     }
414 
415   return 0;
416 }
417 
418 /*
419  * NAME:	hfs->setvol()
420  * DESCRIPTION:	change the current volume
421  */
422 void hfs_setvol(hfsvol *vol)
423 {
424   hfs_curvol = vol;
425 }
426 
427 /*
428  * NAME:	hfs->vstat()
429  * DESCRIPTION:	return volume statistics
430  */
431 int hfs_vstat(hfsvol *vol, hfsvolent *ent)
432 {
433   if (v_getvol(&vol) < 0)
434     return -1;
435 
436   strcpy(ent->name, vol->mdb.drVN);
437 
438   ent->flags     = (vol->flags & HFS_READONLY) ? HFS_ISLOCKED : 0;
439   ent->totbytes  = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
440   ent->freebytes = vol->mdb.drFreeBks  * vol->mdb.drAlBlkSiz;
441   ent->crdate    = d_toutime(vol->mdb.drCrDate);
442   ent->mddate    = d_toutime(vol->mdb.drLsMod);
443 
444   return 0;
445 }
446 
447 /*
448  * NAME:	hfs->format()
449  * DESCRIPTION:	write a new filesystem
450  */
451 #ifdef APPLE_HYB
452 int hfs_format(hce_mem *hce, int pnum, char *vname)
453 #else
454 int hfs_format(char *path, int pnum, char *vname)
455 #endif /* APPLE_HYB */
456 {
457   hfsvol vol;
458   btree *ext = &vol.ext;
459   btree *cat = &vol.cat;
460   unsigned int vbmsz;
461   int i, result = 0;
462   block vbm[16];
463   char *map;
464 
465   if (strchr(vname, ':'))
466     {
467       ERROR(EINVAL, "volume name may not contain colons");
468       return -1;
469     }
470 
471   i = strlen(vname);
472   if (i < 1 || i > HFS_MAX_VLEN)
473     {
474       ERROR(EINVAL, "volume name must be 1-27 chars");
475       return -1;
476     }
477 
478   vol.flags  = 0;
479   vol.pnum   = pnum;
480   vol.vstart = 0;
481   vol.vlen   = 0;
482   vol.lpa    = 0;
483   vol.vbm    = vbm;
484   vol.cwd    = HFS_CNID_ROOTDIR;
485 
486   vol.refs   = 0;
487   vol.files  = 0;
488   vol.dirs   = 0;
489   vol.prev   = 0;
490   vol.next   = 0;
491 
492 #ifndef APPLE_HYB
493   vol.fd = open(path, O_RDWR);
494   if (vol.fd < 0)
495     {
496       ERROR(errno, "error opening device for writing");
497       return -1;
498     }
499 
500   if (l_lockvol(&vol) < 0)
501     {
502       close(vol.fd);
503       return -1;
504     }
505 #endif /* APPLE_HYB */
506   if (pnum > 0)
507     {
508       if (l_readpm(&vol) < 0)
509 	{
510 	  close(vol.fd);
511 	  return -1;
512 	}
513     }
514   else  /* determine size of entire device */
515     {
516 #ifdef APPLE_HYB
517       vol.vlen = hce->hfs_vol_size/HFS_BLOCKSZ;
518 #else
519       unsigned long low, high, mid;
520       block b;
521 
522       for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2)
523 	low = high;
524 
525       while (low < high - 1)
526 	{
527 	  mid = (low + high) / 2;
528 
529 	  if (b_readlb(&vol, mid, &b) < 0)
530 	    high = mid;
531 	  else
532 	    low = mid;
533 	}
534 
535       vol.vlen = low + 1;
536 #endif /* APPLE_HYB */
537     }
538 
539   if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ)
540     {
541 #ifndef APPLE_HYB
542       close(vol.fd);
543 #endif /* APPLE_HYB */
544 
545       ERROR(EINVAL, "volume size must be >= 800K");
546       return -1;
547     }
548 
549   /* initialize volume geometry */
550 
551 #ifdef APPLE_HYB
552   /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated
553      earlier */
554   vol.lpa = hce->Csize/HFS_BLOCKSZ;
555 #else
556   vol.lpa = 1 + vol.vlen / 65536;
557 #endif /* APPLE_HYB */
558 
559   vbmsz = (vol.vlen / vol.lpa + 4095) / 4096;
560 
561   vol.mdb.drSigWord  = 0x4244;
562   vol.mdb.drCrDate   = d_tomtime(time(0));
563   vol.mdb.drLsMod    = vol.mdb.drCrDate;
564   vol.mdb.drAtrb     = 0;
565   vol.mdb.drNmFls    = 0;
566   vol.mdb.drVBMSt    = 3;
567   vol.mdb.drAllocPtr = 0;
568   vol.mdb.drNmAlBlks = (vol.vlen - 5 - vbmsz) / vol.lpa;
569   vol.mdb.drAlBlkSiz = vol.lpa * HFS_BLOCKSZ;
570   vol.mdb.drClpSiz   = vol.mdb.drAlBlkSiz * 4;
571   vol.mdb.drAlBlSt   = 3 + vbmsz;
572 #ifdef APPLE_HYB
573   /* round up start block to a muliple of lpa - important later */
574 /*vol.mdb.drAlBlSt   = ((vol.mdb.drAlBlSt + vol.lpa -  1) / vol.lpa) * vol.lpa;
575 */
576   /* take in accout alignment of files wrt HFS volume start i.e we want
577      drAlBlSt plus hfs_map_size to me a multiple of lpa */
578   vol.mdb.drAlBlSt   = ((vol.mdb.drAlBlSt + hce->hfs_map_size + vol.lpa -  1) / vol.lpa) * vol.lpa;
579   vol.mdb.drAlBlSt   -= hce->hfs_map_size;
580 #endif /* APPLE_HYB */
581   vol.mdb.drNxtCNID  = HFS_CNID_ROOTDIR;  /* modified later */
582   vol.mdb.drFreeBks  = vol.mdb.drNmAlBlks;
583 
584   strcpy(vol.mdb.drVN, vname);
585 
586   vol.mdb.drVolBkUp  = 0;
587   vol.mdb.drVSeqNum  = 0;
588   vol.mdb.drWrCnt    = 0;
589   vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz;
590 #ifdef APPLE_HYB
591   /* adjust size of extents/catalog upwards as we may have rounded up
592      allocation size */
593   i = 1 + vol.vlen / 65536;
594 
595   vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i;
596 
597   /* round up to lpa size */
598   vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) /
599 				vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz;
600 
601   /* ignore above, use what we have already calculated ... */
602   vol.mdb.drXTClpSiz = hce->XTCsize;
603 
604   /* make Catalog file CTC (default twice) as big - prevents further allocation
605      later which we don't want - this seems to work OK ... */
606 /*vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * CTC; */
607   vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * hce->ctc_size;
608 
609   /* we want to put things at the end of the volume later, so we'll
610      cheat here ... shouldn't matter, as we only need the volume read
611      only anyway (we won't be adding files later!) - leave some extra
612      space for the alternative MDB (in the last allocation block) */
613 
614   vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1;
615 #else
616   vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz;
617 #endif /* APPLE_HYB */
618   vol.mdb.drNmRtDirs = 0;
619   vol.mdb.drFilCnt   = 0;
620   vol.mdb.drDirCnt   = -1;  /* incremented when root folder is created */
621 
622   for (i = 0; i < 8; ++i)
623     vol.mdb.drFndrInfo[i] = 0;
624 
625   vol.mdb.drVCSize   = 0;
626   vol.mdb.drVBMCSize = 0;
627   vol.mdb.drCtlCSize = 0;
628 
629   vol.mdb.drXTFlSize = 0;
630   vol.mdb.drCTFlSize = 0;
631 
632   for (i = 0; i < 3; ++i)
633     {
634       vol.mdb.drXTExtRec[i].xdrStABN    = 0;
635       vol.mdb.drXTExtRec[i].xdrNumABlks = 0;
636 
637       vol.mdb.drCTExtRec[i].xdrStABN    = 0;
638       vol.mdb.drCTExtRec[i].xdrNumABlks = 0;
639     }
640 
641   /* initialize volume bitmap */
642 
643   memset(vol.vbm, 0, sizeof(vbm));
644 
645 #ifdef APPLE_HYB
646   /* We don't want to write anything out at the moment, so we allocate
647      memory to hold the HFS "header" info and extents/catalog files.
648      Any reads/writes from/to these parts of the volume are trapped and
649      stored in memory. */
650 
651   /* blocks up to the first unallocated block == HFS "header" info
652      This will be placed in the first 32kb of the ISO volume later */
653   hce->hfs_hdr_size = vol.mdb.drAlBlSt;
654 
655   /* size of the extents and catalog files. This will be added
656      to the end of the ISO volume later */
657   hce->hfs_ce_size =  vol.mdb.drXTClpSiz + vol.mdb.drCTClpSiz;
658 
659   /* we also allocate space for the Desktop file and the alternative
660      MDB while we're here */
661   FREE(hce->hfs_ce);
662   hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz
663 					+ vol.mdb.drAlBlkSiz));
664 
665   /* allocate memory for the map and hdr */
666   FREE(hce->hfs_map);
667   hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size)
668 			*HFS_BLOCKSZ));
669 
670   if (hce->hfs_ce == 0 || hce->hfs_map == 0)
671     {
672       ERROR(ENOMEM, 0);
673       result = -1;
674     }
675 
676   /* hfs_hdr is immediately after the hfs_map */
677   hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ;
678 
679   /* size needed in HFS_BLOCKSZ blocks for later use */
680   hce->hfs_ce_size /= HFS_BLOCKSZ;
681 
682   /* note size of Desktop file */
683   hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ;
684 
685   /* total size of catalog/extents and desktop */
686   hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size;
687 
688   /* alternative MDB in the last alocation block */
689   hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ;
690 
691   /* add the MDB to the total size */
692   hce->hfs_tot_size += vol.lpa;
693 
694   /* store this info in the volume info */
695   vol.hce = hce;
696 
697 #endif /* APPLE_HYB */
698 
699   /* create extents overflow file */
700 
701   ext->f.vol   = &vol;
702   ext->f.parid = 0;
703   strcpy(ext->f.name, "extents overflow");
704 
705   ext->f.cat.cdrType          = cdrFilRec;
706   /* ext->f.cat.cdrResrv2 */
707   ext->f.cat.u.fil.filFlags   = 0;
708   ext->f.cat.u.fil.filTyp     = 0;
709   /* ext->f.cat.u.fil.filUsrWds */
710   ext->f.cat.u.fil.filFlNum   = HFS_CNID_EXT;
711   ext->f.cat.u.fil.filStBlk   = 0;
712   ext->f.cat.u.fil.filLgLen   = 0;
713   ext->f.cat.u.fil.filPyLen   = 0;
714   ext->f.cat.u.fil.filRStBlk  = 0;
715   ext->f.cat.u.fil.filRLgLen  = 0;
716   ext->f.cat.u.fil.filRPyLen  = 0;
717   ext->f.cat.u.fil.filCrDat   = vol.mdb.drCrDate;
718   ext->f.cat.u.fil.filMdDat   = vol.mdb.drLsMod;
719   ext->f.cat.u.fil.filBkDat   = 0;
720   /* ext->f.cat.u.fil.filFndrInfo */
721   ext->f.cat.u.fil.filClpSize = 0;
722 
723   for (i = 0; i < 3; ++i)
724     {
725       ext->f.cat.u.fil.filExtRec[i].xdrStABN     = 0;
726       ext->f.cat.u.fil.filExtRec[i].xdrNumABlks  = 0;
727 
728       ext->f.cat.u.fil.filRExtRec[i].xdrStABN    = 0;
729       ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
730     }
731   /* ext->f.cat.u.fil.filResrv */
732   f_selectfork(&ext->f, 0);
733 
734   ext->f.clump = vol.mdb.drXTClpSiz;
735   ext->f.flags = 0;
736 
737   ext->f.prev = ext->f.next = 0;
738 
739   n_init(&ext->hdrnd, ext, ndHdrNode, 0);
740 
741   ext->hdrnd.nnum       = 0;
742   ext->hdrnd.nd.ndNRecs = 3;
743   ext->hdrnd.roff[1]    = 0x078;
744   ext->hdrnd.roff[2]    = 0x0f8;
745   ext->hdrnd.roff[3]    = 0x1f8;
746 
747   memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128);
748 
749   ext->hdr.bthDepth    = 0;
750   ext->hdr.bthRoot     = 0;
751   ext->hdr.bthNRecs    = 0;
752   ext->hdr.bthFNode    = 0;
753   ext->hdr.bthLNode    = 0;
754   ext->hdr.bthNodeSize = HFS_BLOCKSZ;
755   ext->hdr.bthKeyLen   = 0x07;
756   ext->hdr.bthNNodes   = 0;
757   ext->hdr.bthFree     = 0;
758   for (i = 0; i < 76; ++i)
759     ext->hdr.bthResv[i] = 0;
760 
761   map = ALLOC(char, HFS_MAP1SZ);
762   if (map == 0)
763     {
764       if (result == 0)
765 	{
766 	  ERROR(ENOMEM, 0);
767 	  result = -1;
768 	}
769     }
770   else
771     {
772       memset(map, 0, HFS_MAP1SZ);
773       BMSET(map, 0);
774     }
775 
776   ext->map     = map;
777   ext->mapsz   = HFS_MAP1SZ;
778   ext->flags   = HFS_UPDATE_BTHDR;
779   ext->compare = r_compareextkeys;
780 
781   if (result == 0 && bt_space(ext, 1) < 0)
782     result = -1;
783 
784   --ext->hdr.bthFree;
785 
786   /* create catalog file */
787 
788   cat->f.vol   = &vol;
789   cat->f.parid = 0;
790   strcpy(cat->f.name, "catalog");
791 
792   cat->f.cat.cdrType          = cdrFilRec;
793   /* cat->f.cat.cdrResrv2 */
794   cat->f.cat.u.fil.filFlags   = 0;
795   cat->f.cat.u.fil.filTyp     = 0;
796   /* cat->f.cat.u.fil.filUsrWds */
797   cat->f.cat.u.fil.filFlNum   = HFS_CNID_CAT;
798   cat->f.cat.u.fil.filStBlk   = 0;
799   cat->f.cat.u.fil.filLgLen   = 0;
800   cat->f.cat.u.fil.filPyLen   = 0;
801   cat->f.cat.u.fil.filRStBlk  = 0;
802   cat->f.cat.u.fil.filRLgLen  = 0;
803   cat->f.cat.u.fil.filRPyLen  = 0;
804   cat->f.cat.u.fil.filCrDat   = vol.mdb.drCrDate;
805   cat->f.cat.u.fil.filMdDat   = vol.mdb.drLsMod;
806   cat->f.cat.u.fil.filBkDat   = 0;
807   /* cat->f.cat.u.fil.filFndrInfo */
808   cat->f.cat.u.fil.filClpSize = 0;
809 
810   for (i = 0; i < 3; ++i)
811     {
812       cat->f.cat.u.fil.filExtRec[i].xdrStABN     = 0;
813       cat->f.cat.u.fil.filExtRec[i].xdrNumABlks  = 0;
814 
815       cat->f.cat.u.fil.filRExtRec[i].xdrStABN    = 0;
816       cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
817     }
818   /* cat->f.cat.u.fil.filResrv */
819   f_selectfork(&cat->f, 0);
820 
821   cat->f.clump = vol.mdb.drCTClpSiz;
822   cat->f.flags = 0;
823 
824   cat->f.prev = cat->f.next = 0;
825 
826   n_init(&cat->hdrnd, cat, ndHdrNode, 0);
827 
828   cat->hdrnd.nnum       = 0;
829   cat->hdrnd.nd.ndNRecs = 3;
830   cat->hdrnd.roff[1]    = 0x078;
831   cat->hdrnd.roff[2]    = 0x0f8;
832   cat->hdrnd.roff[3]    = 0x1f8;
833 
834   memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128);
835 
836   cat->hdr.bthDepth    = 0;
837   cat->hdr.bthRoot     = 0;
838   cat->hdr.bthNRecs    = 0;
839   cat->hdr.bthFNode    = 0;
840   cat->hdr.bthLNode    = 0;
841   cat->hdr.bthNodeSize = HFS_BLOCKSZ;
842   cat->hdr.bthKeyLen   = 0x25;
843   cat->hdr.bthNNodes   = 0;
844   cat->hdr.bthFree     = 0;
845   for (i = 0; i < 76; ++i)
846     cat->hdr.bthResv[i] = 0;
847 
848   map = ALLOC(char, HFS_MAP1SZ);
849   if (map == 0)
850     {
851       if (result == 0)
852 	{
853 	  ERROR(ENOMEM, 0);
854 	  result = -1;
855 	}
856     }
857   else
858     {
859       memset(map, 0, HFS_MAP1SZ);
860       BMSET(map, 0);
861     }
862 
863   cat->map     = map;
864   cat->mapsz   = HFS_MAP1SZ;
865   cat->flags   = HFS_UPDATE_BTHDR;
866   cat->compare = r_comparecatkeys;
867 
868   if (result == 0 && bt_space(cat, 1) < 0)
869     result = -1;
870 
871   --cat->hdr.bthFree;
872 
873   /* create root folder */
874 
875   if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0)
876     result = -1;
877 
878   vol.mdb.drNxtCNID = 16;
879 
880   /* finish up */
881 
882   if (result == 0)
883     {
884       block b;
885 
886       /* write boot blocks */
887 
888       memset(&b, 0, sizeof(b));
889       b_writelb(&vol, 0, &b);
890       b_writelb(&vol, 1, &b);
891 
892       /* flush other disk state */
893 
894       vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM;
895 
896       if (v_flush(&vol, 1) < 0)
897 	result = -1;
898     }
899 #ifndef APPLE_HYB
900   if (close(vol.fd) < 0 && result == 0)
901     {
902       ERROR(errno, "error closing device");
903       result = -1;
904     }
905 #endif /* APPLE_HYB */
906   FREE(vol.ext.map);
907   FREE(vol.cat.map);
908 
909   return result;
910 }
911 
912 /* High-Level Directory Routines =========================================== */
913 
914 /*
915  * NAME:	hfs->chdir()
916  * DESCRIPTION:	change current HFS directory
917  */
918 int hfs_chdir(hfsvol *vol, char *path)
919 {
920   CatDataRec data;
921 
922   if (v_getvol(&vol) < 0 ||
923       v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
924     return -1;
925 
926   if (data.cdrType != cdrDirRec)
927     {
928       ERROR(ENOTDIR, 0);
929       return -1;
930     }
931 
932   vol->cwd = data.u.dir.dirDirID;
933 
934   return 0;
935 }
936 
937 /*
938  * NAME:	hfs->getcwd()
939  * DESCRIPTION:	return the current working directory ID
940  */
941 long hfs_getcwd(hfsvol *vol)
942 {
943   if (v_getvol(&vol) < 0)
944     return 0;
945 
946   return vol->cwd;
947 }
948 
949 /*
950  * NAME:	hfs->setcwd()
951  * DESCRIPTION:	set the current working directory ID
952  */
953 int hfs_setcwd(hfsvol *vol, long id)
954 {
955   if (v_getvol(&vol) < 0)
956     return -1;
957 
958   if (id == vol->cwd)
959     return 0;
960 
961   /* make sure the directory exists */
962 
963   if (v_getdthread(vol, id, 0, 0) <= 0)
964     return -1;
965 
966   vol->cwd = id;
967 
968   return 0;
969 }
970 
971 /*
972  * NAME:	hfs->dirinfo()
973  * DESCRIPTION:	given a directory ID, return its (name and) parent ID
974  */
975 int hfs_dirinfo(hfsvol *vol, long *id, char *name)
976 {
977   CatDataRec thread;
978 
979   if (v_getvol(&vol) < 0 ||
980       v_getdthread(vol, *id, &thread, 0) <= 0)
981     return -1;
982 
983   *id = thread.u.dthd.thdParID;
984 
985   if (name)
986     strcpy(name, thread.u.dthd.thdCName);
987 
988   return 0;
989 }
990 
991 /*
992  * NAME:	hfs->opendir()
993  * DESCRIPTION:	prepare to read the contents of a directory
994  */
995 hfsdir *hfs_opendir(hfsvol *vol, char *path)
996 {
997   hfsdir *dir;
998   CatKeyRec key;
999   CatDataRec data;
1000   unsigned char pkey[HFS_CATKEYLEN];
1001 
1002   if (v_getvol(&vol) < 0)
1003     return 0;
1004 
1005   dir = ALLOC(hfsdir, 1);
1006   if (dir == 0)
1007     {
1008       ERROR(ENOMEM, 0);
1009       return 0;
1010     }
1011 
1012   dir->vol = vol;
1013 
1014   if (*path == 0)
1015     {
1016       /* meta-directory containing root dirs from all mounted volumes */
1017 
1018       dir->dirid = 0;
1019       dir->vptr  = hfs_mounts;
1020     }
1021   else
1022     {
1023       if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
1024 	{
1025 	  FREE(dir);
1026 	  return 0;
1027 	}
1028 
1029       if (data.cdrType != cdrDirRec)
1030 	{
1031 	  FREE(dir);
1032 	  ERROR(ENOTDIR, 0);
1033 	  return 0;
1034 	}
1035 
1036       dir->dirid = data.u.dir.dirDirID;
1037       dir->vptr  = 0;
1038 
1039       r_makecatkey(&key, dir->dirid, "");
1040       r_packcatkey(&key, pkey, 0);
1041 
1042       if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
1043 	{
1044 	  FREE(dir);
1045 	  return 0;
1046 	}
1047     }
1048 
1049   dir->prev = 0;
1050   dir->next = vol->dirs;
1051 
1052   if (vol->dirs)
1053     vol->dirs->prev = dir;
1054 
1055   vol->dirs = dir;
1056 
1057   return dir;
1058 }
1059 
1060 /*
1061  * NAME:	hfs->readdir()
1062  * DESCRIPTION:	return the next entry in the directory
1063  */
1064 int hfs_readdir(hfsdir *dir, hfsdirent *ent)
1065 {
1066   CatKeyRec key;
1067   CatDataRec data;
1068   unsigned char *ptr;
1069 
1070   if (dir->dirid == 0)
1071     {
1072       hfsvol *vol;
1073       char cname[HFS_MAX_FLEN + 1];
1074 
1075       for (vol = hfs_mounts; vol; vol = vol->next)
1076 	{
1077 	  if (vol == dir->vptr)
1078 	    break;
1079 	}
1080 
1081       if (vol == 0)
1082 	{
1083 	  ERROR(ENOENT, "no more entries");
1084 	  return -1;
1085 	}
1086 
1087       if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 ||
1088 	  v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
1089 		      &data, cname, 0) < 0)
1090 	return -1;
1091 
1092       r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
1093 
1094       dir->vptr = vol->next;
1095 
1096       return 0;
1097     }
1098 
1099   if (dir->n.rnum == -1)
1100     {
1101       ERROR(ENOENT, "no more entries");
1102       return -1;
1103     }
1104 
1105   while (1)
1106     {
1107       ++dir->n.rnum;
1108 
1109       while (dir->n.rnum >= dir->n.nd.ndNRecs)
1110 	{
1111 	  dir->n.nnum = dir->n.nd.ndFLink;
1112 	  if (dir->n.nnum == 0)
1113 	    {
1114 	      dir->n.rnum = -1;
1115 	      ERROR(ENOENT, "no more entries");
1116 	      return -1;
1117 	    }
1118 
1119 	  if (bt_getnode(&dir->n) < 0)
1120 	    {
1121 	      dir->n.rnum = -1;
1122 	      return -1;
1123 	    }
1124 
1125 	  dir->n.rnum = 0;
1126 	}
1127 
1128       ptr = HFS_NODEREC(dir->n, dir->n.rnum);
1129 
1130       r_unpackcatkey(ptr, &key);
1131 
1132       if (key.ckrParID != dir->dirid)
1133 	{
1134 	  dir->n.rnum = -1;
1135 	  ERROR(ENOENT, "no more entries");
1136 	  return -1;
1137 	}
1138 
1139       r_unpackcatdata(HFS_RECDATA(ptr), &data);
1140 
1141       switch (data.cdrType)
1142 	{
1143 	case cdrDirRec:
1144 	case cdrFilRec:
1145 	  r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
1146 	  return 0;
1147 
1148 	case cdrThdRec:
1149 	case cdrFThdRec:
1150 	  break;
1151 
1152 	default:
1153 	  dir->n.rnum = -1;
1154 
1155 	  ERROR(EIO, "unexpected directory entry found");
1156 	  return -1;
1157 	}
1158     }
1159 }
1160 
1161 /*
1162  * NAME:	hfs->closedir()
1163  * DESCRIPTION:	stop reading a directory
1164  */
1165 int hfs_closedir(hfsdir *dir)
1166 {
1167   hfsvol *vol = dir->vol;
1168 
1169   if (dir->prev)
1170     dir->prev->next = dir->next;
1171   if (dir->next)
1172     dir->next->prev = dir->prev;
1173   if (dir == vol->dirs)
1174     vol->dirs = dir->next;
1175 
1176   FREE(dir);
1177 
1178   return 0;
1179 }
1180 
1181 /* High-Level File Routines ================================================ */
1182 
1183 /*
1184  * NAME:	hfs->open()
1185  * DESCRIPTION:	prepare a file for I/O
1186  */
1187 hfsfile *hfs_open(hfsvol *vol, char *path)
1188 {
1189   hfsfile *file;
1190 
1191   if (v_getvol(&vol) < 0)
1192     return 0;
1193 
1194   file = ALLOC(hfsfile, 1);
1195   if (file == 0)
1196     {
1197       ERROR(ENOMEM, 0);
1198       return 0;
1199     }
1200 
1201   if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0)
1202     {
1203       FREE(file);
1204       return 0;
1205     }
1206 
1207   if (file->cat.cdrType != cdrFilRec)
1208     {
1209       FREE(file);
1210       ERROR(EISDIR, 0);
1211       return 0;
1212     }
1213 
1214   file->vol   = vol;
1215   file->clump = file->cat.u.fil.filClpSize;
1216   file->flags = 0;
1217 
1218   f_selectfork(file, 0);
1219 
1220   file->prev = 0;
1221   file->next = vol->files;
1222 
1223   if (vol->files)
1224     vol->files->prev = file;
1225 
1226   vol->files = file;
1227 
1228   return file;
1229 }
1230 
1231 /*
1232  * NAME:	hfs->setfork()
1233  * DESCRIPTION:	select file fork for I/O operations
1234  */
1235 int hfs_setfork(hfsfile *file, int fork)
1236 {
1237   int result = 0;
1238 
1239   if (! (file->vol->flags & HFS_READONLY) &&
1240       f_trunc(file) < 0)
1241     result = -1;
1242 
1243   f_selectfork(file, fork);
1244 
1245   return result;
1246 }
1247 
1248 /*
1249  * NAME:	hfs->getfork()
1250  * DESCRIPTION:	return the current fork for I/O operations
1251  */
1252 int hfs_getfork(hfsfile *file)
1253 {
1254   return file->fork != fkData;
1255 }
1256 
1257 /*
1258  * NAME:	hfs->read()
1259  * DESCRIPTION:	read from an open file
1260  */
1261 long hfs_read(hfsfile *file, void *buf, unsigned long len)
1262 {
1263   unsigned long *lglen, count;
1264   unsigned char *ptr = buf;
1265 
1266   f_getptrs(file, &lglen, 0, 0);
1267 
1268   if (file->pos + len > *lglen)
1269     len = *lglen - file->pos;
1270 
1271   count = len;
1272   while (count)
1273     {
1274       block b;
1275       unsigned long bnum, offs, chunk;
1276 
1277       bnum  = file->pos / HFS_BLOCKSZ;
1278       offs  = file->pos % HFS_BLOCKSZ;
1279 
1280       chunk = HFS_BLOCKSZ - offs;
1281       if (chunk > count)
1282 	chunk = count;
1283 
1284       if (f_getblock(file, bnum, &b) < 0)
1285 	return -1;
1286 
1287       memcpy(ptr, b + offs, chunk);
1288       ptr += chunk;
1289 
1290       file->pos += chunk;
1291       count     -= chunk;
1292     }
1293 
1294   return len;
1295 }
1296 
1297 /*
1298  * NAME:	hfs->write()
1299  * DESCRIPTION:	write to an open file
1300  */
1301 long hfs_write(hfsfile *file, void *buf, unsigned long len)
1302 {
1303   unsigned long *lglen, *pylen, count;
1304   unsigned char *ptr = buf;
1305 
1306   if (file->vol->flags & HFS_READONLY)
1307     {
1308       ERROR(EROFS, 0);
1309       return -1;
1310     }
1311 
1312   f_getptrs(file, &lglen, &pylen, 0);
1313 
1314   count = len;
1315 
1316   /* set flag to update (at least) the modification time */
1317 
1318   if (count)
1319     {
1320       file->cat.u.fil.filMdDat = d_tomtime(time(0));
1321       file->flags |= HFS_UPDATE_CATREC;
1322     }
1323 
1324   while (count)
1325     {
1326       block b;
1327       unsigned long bnum, offs, chunk;
1328 
1329       bnum  = file->pos / HFS_BLOCKSZ;
1330       offs  = file->pos % HFS_BLOCKSZ;
1331 
1332       chunk = HFS_BLOCKSZ - offs;
1333       if (chunk > count)
1334 	chunk = count;
1335 
1336       if (file->pos + chunk > *pylen)
1337 	{
1338 	  if (bt_space(&file->vol->ext, 1) < 0 ||
1339 	      f_alloc(file) < 0)
1340 	    return -1;
1341 	}
1342 #ifndef APPLE_HYB
1343       /* Ignore this part as we are always writing new files to an empty disk
1344 	 i.e. offs will always be 0 */
1345 
1346       if (offs > 0 || chunk < HFS_BLOCKSZ)
1347 	{
1348 	  if (f_getblock(file, bnum, &b) < 0)
1349 	    return -1;
1350 	}
1351 #endif /* APPLE_HYB */
1352       memcpy(b + offs, ptr, chunk);
1353       ptr += chunk;
1354 
1355       if (f_putblock(file, bnum, &b) < 0)
1356 	return -1;
1357 
1358       file->pos += chunk;
1359       count     -= chunk;
1360 
1361       if (file->pos > *lglen)
1362 	*lglen = file->pos;
1363     }
1364 
1365   return len;
1366 }
1367 
1368 /*
1369  * NAME:	hfs->truncate()
1370  * DESCRIPTION:	truncate an open file
1371  */
1372 int hfs_truncate(hfsfile *file, unsigned long len)
1373 {
1374   unsigned long *lglen;
1375 
1376   f_getptrs(file, &lglen, 0, 0);
1377 
1378   if (*lglen > len)
1379     {
1380       if (file->vol->flags & HFS_READONLY)
1381 	{
1382 	  ERROR(EROFS, 0);
1383 	  return -1;
1384 	}
1385 
1386       *lglen = len;
1387 
1388       file->cat.u.fil.filMdDat = d_tomtime(time(0));
1389       file->flags |= HFS_UPDATE_CATREC;
1390 
1391       if (file->pos > len)
1392 	file->pos = len;
1393     }
1394 
1395   return 0;
1396 }
1397 
1398 /*
1399  * NAME:	hfs->lseek()
1400  * DESCRIPTION:	change file seek pointer
1401  */
1402 long hfs_lseek(hfsfile *file, long offset, int from)
1403 {
1404   unsigned long *lglen;
1405   long newpos;
1406 
1407   f_getptrs(file, &lglen, 0, 0);
1408 
1409   switch (from)
1410     {
1411     case SEEK_SET:
1412       newpos = offset;
1413       break;
1414 
1415     case SEEK_CUR:
1416       newpos = file->pos + offset;
1417       break;
1418 
1419     case SEEK_END:
1420       newpos = *lglen + offset;
1421       break;
1422 
1423     default:
1424       ERROR(EINVAL, 0);
1425       return -1;
1426     }
1427 
1428   if (newpos < 0)
1429     newpos = 0;
1430   else if (newpos > *lglen)
1431     newpos = *lglen;
1432 
1433   file->pos = newpos;
1434 
1435   return newpos;
1436 }
1437 
1438 /*
1439  * NAME:	hfs->close()
1440  * DESCRIPTION:	close a file
1441  */
1442 #ifdef APPLE_HYB
1443 /* extra args are used to set the start of the forks in the ISO volume */
1444 int hfs_close(hfsfile *file, long dext, long rext)
1445 {
1446   int offset;
1447 #else
1448 int hfs_close(hfsfile *file)
1449 {
1450 #endif /* APPLE_HYB */
1451   hfsvol *vol = file->vol;
1452   int result = 0;
1453 
1454   if (f_trunc(file) < 0 ||
1455       f_flush(file) < 0)
1456     result = -1;
1457 
1458 #ifdef APPLE_HYB
1459   /* "start" of file is relative to the first available block */
1460   offset = vol->hce->hfs_hdr_size + vol->hce->hfs_map_size;
1461   /* update the "real" starting extent and re-flush the file */
1462   if (dext)
1463     file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa;
1464 
1465   if (rext)
1466     file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa;
1467 
1468   if (dext || rext)
1469     file->flags |= HFS_UPDATE_CATREC;
1470 
1471   if (f_flush(file) < 0)
1472     result = -1;
1473 #endif /*APPLE_HYB */
1474 
1475   if (file->prev)
1476     file->prev->next = file->next;
1477   if (file->next)
1478     file->next->prev = file->prev;
1479   if (file == vol->files)
1480     vol->files = file->next;
1481 
1482   FREE(file);
1483 
1484   return result;
1485 }
1486 
1487 /* High-Level Catalog Routines ============================================= */
1488 
1489 /*
1490  * NAME:	hfs->stat()
1491  * DESCRIPTION:	return catalog information for an arbitrary path
1492  */
1493 int hfs_stat(hfsvol *vol, char *path, hfsdirent *ent)
1494 {
1495   CatDataRec data;
1496   long parid;
1497   char name[HFS_MAX_FLEN + 1];
1498 
1499   if (v_getvol(&vol) < 0 ||
1500       v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1501     return -1;
1502 
1503   r_unpackdirent(parid, name, &data, ent);
1504 
1505   return 0;
1506 }
1507 
1508 /*
1509  * NAME:	hfs->fstat()
1510  * DESCRIPTION:	return catalog information for an open file
1511  */
1512 int hfs_fstat(hfsfile *file, hfsdirent *ent)
1513 {
1514   r_unpackdirent(file->parid, file->name, &file->cat, ent);
1515 
1516   return 0;
1517 }
1518 
1519 /*
1520  * NAME:	hfs->setattr()
1521  * DESCRIPTION:	change a file's attributes
1522  */
1523 int hfs_setattr(hfsvol *vol, char *path, hfsdirent *ent)
1524 {
1525   CatDataRec data;
1526   node n;
1527 
1528   if (v_getvol(&vol) < 0 ||
1529       v_resolve(&vol, path, &data, 0, 0, &n) <= 0)
1530     return -1;
1531 
1532   if (vol->flags & HFS_READONLY)
1533     {
1534       ERROR(EROFS, 0);
1535       return -1;
1536     }
1537 
1538   r_packdirent(&data, ent);
1539 
1540   if (v_putcatrec(&data, &n) < 0)
1541     return -1;
1542 
1543   return 0;
1544 }
1545 
1546 /*
1547  * NAME:	hfs->fsetattr()
1548  * DESCRIPTION:	change an open file's attributes
1549  */
1550 int hfs_fsetattr(hfsfile *file, hfsdirent *ent)
1551 {
1552   if (file->vol->flags & HFS_READONLY)
1553     {
1554       ERROR(EROFS, 0);
1555       return -1;
1556     }
1557 
1558   r_packdirent(&file->cat, ent);
1559 
1560   file->flags |= HFS_UPDATE_CATREC;
1561 
1562   return 0;
1563 }
1564 
1565 /*
1566  * NAME:	hfs->mkdir()
1567  * DESCRIPTION:	create a new directory
1568  */
1569 int hfs_mkdir(hfsvol *vol, char *path)
1570 {
1571   CatDataRec data;
1572   long parid;
1573   char name[HFS_MAX_FLEN + 1];
1574   int found;
1575 
1576   if (v_getvol(&vol) < 0)
1577     return -1;
1578 
1579   found = v_resolve(&vol, path, &data, &parid, name, 0);
1580   if (found < 0 || parid == 0)
1581     return -1;
1582   else if (found)
1583     {
1584       ERROR(EEXIST, 0);
1585       return -1;
1586     }
1587 
1588   if (parid == HFS_CNID_ROOTPAR)
1589     {
1590       ERROR(EINVAL, 0);
1591       return -1;
1592     }
1593 
1594   if (vol->flags & HFS_READONLY)
1595     {
1596       ERROR(EROFS, 0);
1597       return -1;
1598     }
1599 
1600   if (v_newfolder(vol, parid, name) < 0)
1601     return -1;
1602 
1603   return 0;
1604 }
1605 
1606 /*
1607  * NAME:	hfs->rmdir()
1608  * DESCRIPTION:	delete an empty directory
1609  */
1610 int hfs_rmdir(hfsvol *vol, char *path)
1611 {
1612   CatKeyRec key;
1613   CatDataRec data;
1614   long parid;
1615   char name[HFS_MAX_FLEN + 1];
1616   unsigned char pkey[HFS_CATKEYLEN];
1617 
1618   if (v_getvol(&vol) < 0 ||
1619       v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1620     return -1;
1621 
1622   if (data.cdrType != cdrDirRec)
1623     {
1624       ERROR(ENOTDIR, 0);
1625       return -1;
1626     }
1627 
1628   if (data.u.dir.dirVal != 0)
1629     {
1630       ERROR(ENOTEMPTY, 0);
1631       return -1;
1632     }
1633 
1634   if (parid == HFS_CNID_ROOTPAR)
1635     {
1636       ERROR(EINVAL, 0);
1637       return -1;
1638     }
1639 
1640   if (vol->flags & HFS_READONLY)
1641     {
1642       ERROR(EROFS, 0);
1643       return -1;
1644     }
1645 
1646   /* delete directory record */
1647 
1648   r_makecatkey(&key, parid, name);
1649   r_packcatkey(&key, pkey, 0);
1650 
1651   if (bt_delete(&vol->cat, pkey) < 0)
1652     return -1;
1653 
1654   /* delete thread record */
1655 
1656   r_makecatkey(&key, data.u.dir.dirDirID, "");
1657   r_packcatkey(&key, pkey, 0);
1658 
1659   if (bt_delete(&vol->cat, pkey) < 0 ||
1660       v_adjvalence(vol, parid, 1, -1) < 0)
1661     return -1;
1662 
1663   return 0;
1664 }
1665 
1666 /*
1667  * NAME:	hfs->create()
1668  * DESCRIPTION:	create a new file
1669  */
1670 int hfs_create(hfsvol *vol, char *path, char *type, char *creator)
1671 {
1672   CatKeyRec key;
1673   CatDataRec data;
1674   long id, parid;
1675   char name[HFS_MAX_FLEN + 1];
1676   unsigned char record[HFS_CATRECMAXLEN];
1677   int found, i, reclen;
1678 
1679   if (v_getvol(&vol) < 0)
1680     return -1;
1681 
1682   found = v_resolve(&vol, path, &data, &parid, name, 0);
1683   if (found < 0 || parid == 0)
1684     return -1;
1685   else if (found)
1686     {
1687       ERROR(EEXIST, 0);
1688       return -1;
1689     }
1690 
1691   if (parid == HFS_CNID_ROOTPAR)
1692     {
1693       ERROR(EINVAL, 0);
1694       return -1;
1695     }
1696 
1697   if (vol->flags & HFS_READONLY)
1698     {
1699       ERROR(EROFS, 0);
1700       return -1;
1701     }
1702 
1703   /* create file `name' in parent `parid' */
1704 
1705   if (bt_space(&vol->cat, 1) < 0)
1706     return -1;
1707 
1708   id = vol->mdb.drNxtCNID++;
1709   vol->flags |= HFS_UPDATE_MDB;
1710 
1711   /* create file record */
1712 
1713   data.cdrType   = cdrFilRec;
1714   data.cdrResrv2 = 0;
1715 
1716   data.u.fil.filFlags = 0;
1717   data.u.fil.filTyp   = 0;
1718 
1719   memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds));
1720 
1721   data.u.fil.filUsrWds.fdType    = d_getl((unsigned char *) type);
1722   data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator);
1723 
1724   data.u.fil.filFlNum  = id;
1725   data.u.fil.filStBlk  = 0;
1726   data.u.fil.filLgLen  = 0;
1727   data.u.fil.filPyLen  = 0;
1728   data.u.fil.filRStBlk = 0;
1729   data.u.fil.filRLgLen = 0;
1730   data.u.fil.filRPyLen = 0;
1731   data.u.fil.filCrDat  = d_tomtime(time(0));
1732   data.u.fil.filMdDat  = data.u.fil.filCrDat;
1733   data.u.fil.filBkDat  = 0;
1734 
1735   memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo));
1736 
1737   data.u.fil.filClpSize = 0;
1738 
1739   for (i = 0; i < 3; ++i)
1740     {
1741       data.u.fil.filExtRec[i].xdrStABN     = 0;
1742       data.u.fil.filExtRec[i].xdrNumABlks  = 0;
1743 
1744       data.u.fil.filRExtRec[i].xdrStABN    = 0;
1745       data.u.fil.filRExtRec[i].xdrNumABlks = 0;
1746     }
1747 
1748   data.u.fil.filResrv = 0;
1749 
1750   r_makecatkey(&key, parid, name);
1751   r_packcatkey(&key, record, &reclen);
1752   r_packcatdata(&data, HFS_RECDATA(record), &reclen);
1753 
1754   if (bt_insert(&vol->cat, record, reclen) < 0 ||
1755       v_adjvalence(vol, parid, 0, 1) < 0)
1756     return -1;
1757 
1758   return 0;
1759 }
1760 
1761 /*
1762  * NAME:	hfs->delete()
1763  * DESCRIPTION:	remove both forks of a file
1764  */
1765 int hfs_delete(hfsvol *vol, char *path)
1766 {
1767   hfsfile file;
1768   CatKeyRec key;
1769   unsigned char pkey[HFS_CATKEYLEN];
1770   int found;
1771 
1772   if (v_getvol(&vol) < 0 ||
1773       v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0)
1774     return -1;
1775 
1776   if (file.cat.cdrType != cdrFilRec)
1777     {
1778       ERROR(EISDIR, 0);
1779       return -1;
1780     }
1781 
1782   if (file.parid == HFS_CNID_ROOTPAR)
1783     {
1784       ERROR(EINVAL, 0);
1785       return -1;
1786     }
1787 
1788   if (vol->flags & HFS_READONLY)
1789     {
1790       ERROR(EROFS, 0);
1791       return -1;
1792     }
1793 
1794   /* free disk blocks */
1795 
1796   file.vol   = vol;
1797   file.flags = 0;
1798 
1799   file.cat.u.fil.filLgLen  = 0;
1800   file.cat.u.fil.filRLgLen = 0;
1801 
1802   f_selectfork(&file, 0);
1803   if (f_trunc(&file) < 0)
1804     return -1;
1805 
1806   f_selectfork(&file, 1);
1807   if (f_trunc(&file) < 0)
1808     return -1;
1809 
1810   /* delete file record */
1811 
1812   r_makecatkey(&key, file.parid, file.name);
1813   r_packcatkey(&key, pkey, 0);
1814 
1815   if (bt_delete(&vol->cat, pkey) < 0 ||
1816       v_adjvalence(vol, file.parid, 0, -1) < 0)
1817     return -1;
1818 
1819   /* delete file thread, if any */
1820 
1821   found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0);
1822   if (found < 0)
1823     return -1;
1824 
1825   if (found)
1826     {
1827       r_makecatkey(&key, file.cat.u.fil.filFlNum, "");
1828       r_packcatkey(&key, pkey, 0);
1829 
1830       if (bt_delete(&vol->cat, pkey) < 0)
1831 	return -1;
1832     }
1833 
1834   return 0;
1835 }
1836 
1837 /*
1838  * NAME:	hfs->rename()
1839  * DESCRIPTION:	change the name of and/or move a file or directory
1840  */
1841 int hfs_rename(hfsvol *vol, char *srcpath, char *dstpath)
1842 {
1843   hfsvol *srcvol;
1844   CatDataRec src, dst;
1845   long srcid, dstid;
1846   CatKeyRec key;
1847   char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1];
1848   unsigned char record[HFS_CATRECMAXLEN];
1849   int found, isdir, moving, reclen;
1850   node n;
1851 
1852   if (v_getvol(&vol) < 0 ||
1853       v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0)
1854     return -1;
1855 
1856   isdir  = (src.cdrType == cdrDirRec);
1857   srcvol = vol;
1858 
1859   found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0);
1860   if (found < 0)
1861     return -1;
1862 
1863   if (vol != srcvol)
1864     {
1865       ERROR(EINVAL, "can't move across volumes");
1866       return -1;
1867     }
1868 
1869   if (dstid == 0)
1870     {
1871       ERROR(ENOENT, "bad destination path");
1872       return -1;
1873     }
1874 
1875   if (found &&
1876       dst.cdrType == cdrDirRec &&
1877       dst.u.dir.dirDirID != src.u.dir.dirDirID)
1878     {
1879       dstid = dst.u.dir.dirDirID;
1880       strcpy(dstname, srcname);
1881 
1882       found = v_catsearch(vol, dstid, dstname, 0, 0, 0);
1883       if (found < 0)
1884 	return -1;
1885     }
1886 
1887   moving = (srcid != dstid);
1888 
1889   if (found)
1890     {
1891       char *ptr;
1892 
1893       ptr = strrchr(dstpath, ':');
1894       if (ptr == 0)
1895 	ptr = dstpath;
1896       else
1897 	++ptr;
1898 
1899       if (*ptr)
1900 	strcpy(dstname, ptr);
1901 
1902       if (! moving && strcmp(srcname, dstname) == 0)
1903 	return 0;  /* source and destination are the same */
1904 
1905       if (moving || d_relstring(srcname, dstname))
1906 	{
1907 	  ERROR(EEXIST, "can't use destination name");
1908 	  return -1;
1909 	}
1910     }
1911 
1912   /* can't move anything into the root directory's parent */
1913 
1914   if (moving && dstid == HFS_CNID_ROOTPAR)
1915     {
1916       ERROR(EINVAL, "can't move above root directory");
1917       return -1;
1918     }
1919 
1920   if (moving && isdir)
1921     {
1922       long id;
1923 
1924       /* can't move root directory anywhere */
1925 
1926       if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR)
1927 	{
1928 	  ERROR(EINVAL, "can't move root directory");
1929 	  return -1;
1930 	}
1931 
1932       /* make sure we aren't trying to move a directory inside itself */
1933 
1934       for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID)
1935 	{
1936 	  if (id == src.u.dir.dirDirID)
1937 	    {
1938 	      ERROR(EINVAL, "can't move directory inside itself");
1939 	      return -1;
1940 	    }
1941 
1942 	  if (v_getdthread(vol, id, &dst, 0) <= 0)
1943 	    return -1;
1944 	}
1945     }
1946 
1947   if (vol->flags & HFS_READONLY)
1948     {
1949       ERROR(EROFS, 0);
1950       return -1;
1951     }
1952 
1953   /* change volume name */
1954 
1955   if (dstid == HFS_CNID_ROOTPAR)
1956     {
1957       if (strlen(dstname) > HFS_MAX_VLEN)
1958 	{
1959 	  ERROR(ENAMETOOLONG, 0);
1960 	  return -1;
1961 	}
1962 
1963       strcpy(vol->mdb.drVN, dstname);
1964       vol->flags |= HFS_UPDATE_MDB;
1965     }
1966 
1967   /* remove source record */
1968 
1969   r_makecatkey(&key, srcid, srcname);
1970   r_packcatkey(&key, record, 0);
1971 
1972   if (bt_delete(&vol->cat, record) < 0)
1973     return -1;
1974 
1975   /* insert destination record */
1976 
1977   r_makecatkey(&key, dstid, dstname);
1978   r_packcatkey(&key, record, &reclen);
1979   r_packcatdata(&src, HFS_RECDATA(record), &reclen);
1980 
1981   if (bt_insert(&vol->cat, record, reclen) < 0)
1982     return -1;
1983 
1984   /* update thread record */
1985 
1986   if (isdir)
1987     {
1988       if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0)
1989 	return -1;
1990 
1991       dst.u.dthd.thdParID = dstid;
1992       strcpy(dst.u.dthd.thdCName, dstname);
1993 
1994       if (v_putcatrec(&dst, &n) < 0)
1995 	return -1;
1996     }
1997   else
1998     {
1999       found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n);
2000       if (found < 0)
2001 	return -1;
2002 
2003       if (found)
2004 	{
2005 	  dst.u.fthd.fthdParID = dstid;
2006 	  strcpy(dst.u.fthd.fthdCName, dstname);
2007 
2008 	  if (v_putcatrec(&dst, &n) < 0)
2009 	    return -1;
2010 	}
2011     }
2012 
2013   /* update directory valences */
2014 
2015   if (moving)
2016     {
2017       if (v_adjvalence(vol, srcid, isdir, -1) < 0 ||
2018 	  v_adjvalence(vol, dstid, isdir,  1) < 0)
2019 	return -1;
2020     }
2021 
2022   return 0;
2023 }
2024 #ifdef APPLE_HYB
2025 /*
2026  * NAME:        hfs->hfs_get_drAllocPtr()
2027  * DESCRIPTION: get the current start of next allocation search
2028  */
2029 unsigned short
2030 hfs_get_drAllocPtr(hfsfile *file)
2031 {
2032   return(file->vol->mdb.drAllocPtr);
2033 }
2034 
2035 /*
2036  * NAME:        hfs->hfs_set_drAllocPtr()
2037  * DESCRIPTION: set the current start of next allocation search
2038  */
2039 int
2040 hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size)
2041 {
2042   hfsvol *vol = file->vol;
2043   int result = 0;
2044 
2045   /* truncate the current fork */
2046   if (f_trunc(file) < 0 ||
2047       f_flush(file) < 0)
2048     result = -1;
2049 
2050   /* convert the fork size into allocation blocks */
2051   size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz;
2052 
2053   /* set the start of next allocation search to be after this fork */
2054   vol->mdb.drAllocPtr = drAllocPtr + size;
2055 
2056   vol->flags |= HFS_UPDATE_MDB;
2057 
2058   return result;
2059 }
2060 
2061 /*
2062  * NAME:        hfs->vsetbless()
2063  * DESCRIPTION: set blessed folder
2064  *
2065  * adapted from vsetattr() from v3.2.6
2066  */
2067 void
2068 hfs_vsetbless(hfsvol *vol, unsigned long cnid)
2069 {
2070   vol->mdb.drFndrInfo[0] = cnid;
2071 
2072   vol->flags |= HFS_UPDATE_MDB;
2073 }
2074 #endif /* APPLE_HYB */
2075