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