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