2 * This file has been modified for the cdrkit suite.
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).
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.
13 /* @(#)hfs.c 1.9 04/06/17 joerg */
15 * hfsutils - tools for reading and writing Macintosh HFS volumes
16 * Copyright (C) 1996, 1997 Robert Leslie
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.
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.
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.
33 /* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */
55 /* High-Level Volume Routines ============================================== */
59 * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
62 hfsvol *hfs_mount(hce_mem *hce, int pnum, int flags)
64 hfsvol *hfs_mount(char *path, int pnum, int flags)
65 #endif /* APPLE_HYB */
73 /* see if the volume is already mounted */
75 if (stat(path, &dev) >= 0)
80 for (check = hfs_mounts; check; check = check->next)
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))
87 /* verify compatible read/write mode */
89 if (((check->flags & HFS_READONLY) &&
90 ! (flags & O_WRONLY)) ||
91 (! (check->flags & HFS_READONLY) &&
92 (flags & (O_WRONLY | O_RDWR))))
100 #endif /* APPLE_HYB */
103 vol = ALLOC(hfsvol, 1);
116 vol->cwd = HFS_CNID_ROOTDIR;
127 vol->ext.compare = r_compareextkeys;
132 vol->cat.compare = r_comparecatkeys;
134 /* open and lock the device */
137 vol->fd = 3; /* any +ve number will do? */
138 vol->hce = hce; /* store the extra with the vol info */
140 if (flags & (O_WRONLY | O_RDWR))
142 vol->fd = open(path, O_RDWR);
143 if (vol->fd >= 0 && l_lockvol(vol) < 0)
150 if (! (flags & (O_WRONLY | O_RDWR)) ||
152 (errno == EROFS || errno == EACCES || errno == EAGAIN) &&
155 vol->flags |= HFS_READONLY;
156 vol->fd = open(path, O_RDONLY);
157 if (vol->fd >= 0 && l_lockvol(vol) < 0)
167 ERROR(errno, "error opening device");
173 #endif /* APPLE_HYB */
175 /* find out what kind of media this is and read the MDB */
177 if (l_readblock0(vol) < 0 ||
183 #endif /* APPLE_HYB */
187 /* verify this is an HFS volume */
189 if (vol->mdb.drSigWord != 0x4244)
193 #endif /* APPLE_HYB */
196 ERROR(EINVAL, "not a Macintosh HFS volume");
200 /* do minimal consistency checks */
202 if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
206 #endif /* APPLE_HYB */
209 ERROR(EINVAL, "bad volume allocation block size");
214 vol->vlen = vol->mdb.drAlBlSt +
215 vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2;
217 /* read the volume bitmap and extents/catalog B*-tree headers */
219 if (l_readvbm(vol) < 0 ||
220 bt_readhdr(&vol->ext) < 0 ||
221 bt_readhdr(&vol->cat) < 0)
225 #endif /* APPLE_HYB */
230 if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
232 /* volume was not cleanly unmounted; scavenge free-space */
234 if (v_scavenge(vol) < 0)
238 #endif /* APPLE_HYB */
244 if (vol->flags & HFS_READONLY)
245 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
247 vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
250 vol->next = hfs_mounts;
253 hfs_mounts->prev = vol;
260 return hfs_curvol = vol;
265 * DESCRIPTION: flush all pending changes to an HFS volume
267 int hfs_flush(hfsvol *vol)
271 if (v_getvol(&vol) < 0)
274 for (file = vol->files; file; file = file->next)
276 if (f_flush(file) < 0)
280 if (v_flush(vol, 0) < 0)
287 * NAME: hfs->flushall()
288 * DESCRIPTION: flush all pending changes to all mounted HFS volumes
294 for (vol = hfs_mounts; vol; vol = vol->next)
299 * NAME: hfs->umount()
300 * DESCRIPTION: close an HFS volume
303 /* extra argument used to alter the position of the extents/catalog files */
304 int hfs_umount(hfsvol *vol, long end, long locked)
306 int hfs_umount(hfsvol *vol)
307 #endif /* APPLE_HYB */
311 if (v_getvol(&vol) < 0)
315 return v_flush(vol, 0);
317 /* close all open files and directories */
321 hfs_close(vol->files, 0, 0);
323 hfs_close(vol->files);
324 #endif /* APPLE_HYB */
327 hfs_closedir(vol->dirs);
332 /* move extents and catalog to end of volume ... */
333 long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096;
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);
341 /* catalog file ... */
342 vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end;
343 vol->mdb.drXTExtRec[0].xdrStABN = end;
345 /* move postition to start of extents file */
346 end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN;
348 /* extents file ... */
349 vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end;
350 vol->mdb.drCTExtRec[0].xdrStABN = end;
352 /* the volume bitmap is wrong as we have "moved" files
353 about - simple just set the whole lot (it's a readonly volume
355 memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ);
357 /* set the free blocks to zero */
358 vol->mdb.drFreeBks = 0;
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;
365 vol->mdb.drAtrb |= HFS_ATRB_SLOCKED;
367 vol->ext.flags |= HFS_UPDATE_BTHDR;
368 vol->cat.flags |= HFS_UPDATE_BTHDR;
370 #endif /* APPLE_HYB */
372 if (v_flush(vol, 1) < 0)
376 if (close(vol->fd) < 0 && result == 0)
378 ERROR(errno, "error closing device");
381 #endif /* APPLE_HYB */
384 vol->prev->next = vol->next;
386 vol->next->prev = vol->prev;
388 if (vol == hfs_mounts)
389 hfs_mounts = vol->next;
390 if (vol == hfs_curvol)
399 * NAME: hfs->umountall()
400 * DESCRIPTION: unmount all mounted volumes
408 hfs_umount(hfs_mounts);
409 #endif /* APPLE_HYB */
413 * NAME: hfs->getvol()
414 * DESCRIPTION: return a pointer to a mounted volume
416 hfsvol *hfs_getvol(char *name)
423 for (vol = hfs_mounts; vol; vol = vol->next)
425 if (d_relstring(name, vol->mdb.drVN) == 0)
433 * NAME: hfs->setvol()
434 * DESCRIPTION: change the current volume
436 void hfs_setvol(hfsvol *vol)
443 * DESCRIPTION: return volume statistics
445 int hfs_vstat(hfsvol *vol, hfsvolent *ent)
447 if (v_getvol(&vol) < 0)
450 strcpy(ent->name, vol->mdb.drVN);
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);
462 * NAME: hfs->format()
463 * DESCRIPTION: write a new filesystem
466 int hfs_format(hce_mem *hce, int pnum, char *vname)
468 int hfs_format(char *path, int pnum, char *vname)
469 #endif /* APPLE_HYB */
472 btree *ext = &vol.ext;
473 btree *cat = &vol.cat;
479 if (strchr(vname, ':'))
481 ERROR(EINVAL, "volume name may not contain colons");
486 if (i < 1 || i > HFS_MAX_VLEN)
488 ERROR(EINVAL, "volume name must be 1-27 chars");
498 vol.cwd = HFS_CNID_ROOTDIR;
507 vol.fd = open(path, O_RDWR);
510 ERROR(errno, "error opening device for writing");
514 if (l_lockvol(&vol) < 0)
519 #endif /* APPLE_HYB */
522 if (l_readpm(&vol) < 0)
528 else /* determine size of entire device */
531 vol.vlen = hce->hfs_vol_size;
533 unsigned long low, high, mid;
536 for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2)
539 while (low < high - 1)
541 mid = (low + high) / 2;
543 if (b_readlb(&vol, mid, &b) < 0)
550 #endif /* APPLE_HYB */
553 if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ)
557 #endif /* APPLE_HYB */
559 ERROR(EINVAL, "volume size must be >= 800K");
563 /* initialize volume geometry */
566 /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated
568 vol.lpa = hce->Csize/HFS_BLOCKSZ;
570 vol.lpa = 1 + vol.vlen / 65536;
571 #endif /* APPLE_HYB */
573 vbmsz = (vol.vlen / vol.lpa + 4095) / 4096;
575 vol.mdb.drSigWord = 0x4244;
576 vol.mdb.drCrDate = d_tomtime(time(0));
577 vol.mdb.drLsMod = vol.mdb.drCrDate;
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;
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;
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;
598 strcpy(vol.mdb.drVN, vname);
600 vol.mdb.drVolBkUp = 0;
601 vol.mdb.drVSeqNum = 0;
603 vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz;
605 /* adjust size of extents/catalog upwards as we may have rounded up
607 i = 1 + vol.vlen / 65536;
609 vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i;
611 /* round up to lpa size */
612 vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) /
613 vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz;
615 /* ignore above, use what we have already calculated ... */
616 vol.mdb.drXTClpSiz = hce->XTCsize;
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;
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) */
628 vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1;
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 */
636 for (i = 0; i < 8; ++i)
637 vol.mdb.drFndrInfo[i] = 0;
639 vol.mdb.drVCSize = 0;
640 vol.mdb.drVBMCSize = 0;
641 vol.mdb.drCtlCSize = 0;
643 vol.mdb.drXTFlSize = 0;
644 vol.mdb.drCTFlSize = 0;
646 for (i = 0; i < 3; ++i)
648 vol.mdb.drXTExtRec[i].xdrStABN = 0;
649 vol.mdb.drXTExtRec[i].xdrNumABlks = 0;
651 vol.mdb.drCTExtRec[i].xdrStABN = 0;
652 vol.mdb.drCTExtRec[i].xdrNumABlks = 0;
655 /* initialize volume bitmap */
657 memset(vol.vbm, 0, sizeof(vbm));
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
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;
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;
673 /* we also allocate space for the Desktop file and the alternative
674 MDB while we're here */
676 hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz
677 + vol.mdb.drAlBlkSiz));
679 /* allocate memory for the map and hdr */
681 hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size)
684 if (hce->hfs_ce == 0 || hce->hfs_map == 0)
690 /* hfs_hdr is immediately after the hfs_map */
691 hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ;
693 /* size needed in HFS_BLOCKSZ blocks for later use */
694 hce->hfs_ce_size /= HFS_BLOCKSZ;
696 /* note size of Desktop file */
697 hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ;
699 /* total size of catalog/extents and desktop */
700 hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size;
702 /* alternative MDB in the last alocation block */
703 hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ;
705 /* add the MDB to the total size */
706 hce->hfs_tot_size += vol.lpa;
708 /* store this info in the volume info */
711 #endif /* APPLE_HYB */
713 /* create extents overflow file */
717 strcpy(ext->f.name, "extents overflow");
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;
737 for (i = 0; i < 3; ++i)
739 ext->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
740 ext->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
742 ext->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
743 ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
745 /* ext->f.cat.u.fil.filResrv */
746 f_selectfork(&ext->f, 0);
748 ext->f.clump = vol.mdb.drXTClpSiz;
751 ext->f.prev = ext->f.next = 0;
753 n_init(&ext->hdrnd, ext, ndHdrNode, 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;
761 memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128);
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;
775 map = ALLOC(char, HFS_MAP1SZ);
786 memset(map, 0, HFS_MAP1SZ);
791 ext->mapsz = HFS_MAP1SZ;
792 ext->flags = HFS_UPDATE_BTHDR;
793 ext->compare = r_compareextkeys;
795 if (result == 0 && bt_space(ext, 1) < 0)
800 /* create catalog file */
804 strcpy(cat->f.name, "catalog");
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;
824 for (i = 0; i < 3; ++i)
826 cat->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
827 cat->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
829 cat->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
830 cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
832 /* cat->f.cat.u.fil.filResrv */
833 f_selectfork(&cat->f, 0);
835 cat->f.clump = vol.mdb.drCTClpSiz;
838 cat->f.prev = cat->f.next = 0;
840 n_init(&cat->hdrnd, cat, ndHdrNode, 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;
848 memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128);
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;
862 map = ALLOC(char, HFS_MAP1SZ);
873 memset(map, 0, HFS_MAP1SZ);
878 cat->mapsz = HFS_MAP1SZ;
879 cat->flags = HFS_UPDATE_BTHDR;
880 cat->compare = r_comparecatkeys;
882 if (result == 0 && bt_space(cat, 1) < 0)
887 /* create root folder */
889 if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0)
892 vol.mdb.drNxtCNID = 16;
900 /* write boot blocks */
902 memset(&b, 0, sizeof(b));
903 b_writelb(&vol, 0, &b);
904 b_writelb(&vol, 1, &b);
906 /* flush other disk state */
908 vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM;
910 if (v_flush(&vol, 1) < 0)
914 if (close(vol.fd) < 0 && result == 0)
916 ERROR(errno, "error closing device");
919 #endif /* APPLE_HYB */
926 /* High-Level Directory Routines =========================================== */
930 * DESCRIPTION: change current HFS directory
932 int hfs_chdir(hfsvol *vol, char *path)
936 if (v_getvol(&vol) < 0 ||
937 v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
940 if (data.cdrType != cdrDirRec)
946 vol->cwd = data.u.dir.dirDirID;
952 * NAME: hfs->getcwd()
953 * DESCRIPTION: return the current working directory ID
955 long hfs_getcwd(hfsvol *vol)
957 if (v_getvol(&vol) < 0)
964 * NAME: hfs->setcwd()
965 * DESCRIPTION: set the current working directory ID
967 int hfs_setcwd(hfsvol *vol, long id)
969 if (v_getvol(&vol) < 0)
975 /* make sure the directory exists */
977 if (v_getdthread(vol, id, 0, 0) <= 0)
986 * NAME: hfs->dirinfo()
987 * DESCRIPTION: given a directory ID, return its (name and) parent ID
989 int hfs_dirinfo(hfsvol *vol, long *id, char *name)
993 if (v_getvol(&vol) < 0 ||
994 v_getdthread(vol, *id, &thread, 0) <= 0)
997 *id = thread.u.dthd.thdParID;
1000 strcpy(name, thread.u.dthd.thdCName);
1006 * NAME: hfs->opendir()
1007 * DESCRIPTION: prepare to read the contents of a directory
1009 hfsdir *hfs_opendir(hfsvol *vol, char *path)
1014 unsigned char pkey[HFS_CATKEYLEN];
1016 if (v_getvol(&vol) < 0)
1019 dir = ALLOC(hfsdir, 1);
1030 /* meta-directory containing root dirs from all mounted volumes */
1033 dir->vptr = hfs_mounts;
1037 if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
1043 if (data.cdrType != cdrDirRec)
1050 dir->dirid = data.u.dir.dirDirID;
1053 r_makecatkey(&key, dir->dirid, "");
1054 r_packcatkey(&key, pkey, 0);
1056 if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
1064 dir->next = vol->dirs;
1067 vol->dirs->prev = dir;
1075 * NAME: hfs->readdir()
1076 * DESCRIPTION: return the next entry in the directory
1078 int hfs_readdir(hfsdir *dir, hfsdirent *ent)
1084 if (dir->dirid == 0)
1087 char cname[HFS_MAX_FLEN + 1];
1089 for (vol = hfs_mounts; vol; vol = vol->next)
1091 if (vol == dir->vptr)
1097 ERROR(ENOENT, "no more entries");
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)
1106 r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
1108 dir->vptr = vol->next;
1113 if (dir->n.rnum == -1)
1115 ERROR(ENOENT, "no more entries");
1123 while (dir->n.rnum >= (int)dir->n.nd.ndNRecs)
1125 dir->n.nnum = dir->n.nd.ndFLink;
1126 if (dir->n.nnum == 0)
1129 ERROR(ENOENT, "no more entries");
1133 if (bt_getnode(&dir->n) < 0)
1142 ptr = HFS_NODEREC(dir->n, dir->n.rnum);
1144 r_unpackcatkey(ptr, &key);
1146 if (key.ckrParID != dir->dirid)
1149 ERROR(ENOENT, "no more entries");
1153 r_unpackcatdata(HFS_RECDATA(ptr), &data);
1155 switch (data.cdrType)
1159 r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
1169 ERROR(EIO, "unexpected directory entry found");
1176 * NAME: hfs->closedir()
1177 * DESCRIPTION: stop reading a directory
1179 int hfs_closedir(hfsdir *dir)
1181 hfsvol *vol = dir->vol;
1184 dir->prev->next = dir->next;
1186 dir->next->prev = dir->prev;
1187 if (dir == vol->dirs)
1188 vol->dirs = dir->next;
1195 /* High-Level File Routines ================================================ */
1199 * DESCRIPTION: prepare a file for I/O
1201 hfsfile *hfs_open(hfsvol *vol, char *path)
1205 if (v_getvol(&vol) < 0)
1208 file = ALLOC(hfsfile, 1);
1215 if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0)
1221 if (file->cat.cdrType != cdrFilRec)
1229 file->clump = file->cat.u.fil.filClpSize;
1232 f_selectfork(file, 0);
1235 file->next = vol->files;
1238 vol->files->prev = file;
1246 * NAME: hfs->setfork()
1247 * DESCRIPTION: select file fork for I/O operations
1249 int hfs_setfork(hfsfile *file, int ffork)
1253 if (! (file->vol->flags & HFS_READONLY) &&
1257 f_selectfork(file, ffork);
1263 * NAME: hfs->getfork()
1264 * DESCRIPTION: return the current fork for I/O operations
1266 int hfs_getfork(hfsfile *file)
1268 return file->fork != fkData;
1273 * DESCRIPTION: read from an open file
1275 long hfs_read(hfsfile *file, void *buf, unsigned long len)
1277 unsigned long *lglen, count;
1278 unsigned char *ptr = buf;
1280 f_getptrs(file, &lglen, 0, 0);
1282 if (file->pos + len > *lglen)
1283 len = *lglen - file->pos;
1289 unsigned long bnum, offs, chunk;
1291 bnum = file->pos / HFS_BLOCKSZ;
1292 offs = file->pos % HFS_BLOCKSZ;
1294 chunk = HFS_BLOCKSZ - offs;
1298 if (f_getblock(file, bnum, &b) < 0)
1301 memcpy(ptr, b + offs, chunk);
1312 * NAME: hfs->write()
1313 * DESCRIPTION: write to an open file
1315 long hfs_write(hfsfile *file, void *buf, unsigned long len)
1317 unsigned long *lglen, *pylen, count;
1318 unsigned char *ptr = buf;
1320 if (file->vol->flags & HFS_READONLY)
1326 f_getptrs(file, &lglen, &pylen, 0);
1330 /* set flag to update (at least) the modification time */
1334 file->cat.u.fil.filMdDat = d_tomtime(time(0));
1335 file->flags |= HFS_UPDATE_CATREC;
1341 unsigned long bnum, offs, chunk;
1343 bnum = file->pos / HFS_BLOCKSZ;
1344 offs = file->pos % HFS_BLOCKSZ;
1346 chunk = HFS_BLOCKSZ - offs;
1350 if (file->pos + chunk > *pylen)
1352 if (bt_space(&file->vol->ext, 1) < 0 ||
1357 /* Ignore this part as we are always writing new files to an empty disk
1358 i.e. offs will always be 0 */
1360 if (offs > 0 || chunk < HFS_BLOCKSZ)
1362 if (f_getblock(file, bnum, &b) < 0)
1365 #endif /* APPLE_HYB */
1366 memcpy(b + offs, ptr, chunk);
1369 if (f_putblock(file, bnum, &b) < 0)
1375 if (file->pos > *lglen)
1383 * NAME: hfs->truncate()
1384 * DESCRIPTION: truncate an open file
1386 int hfs_truncate(hfsfile *file, unsigned long len)
1388 unsigned long *lglen;
1390 f_getptrs(file, &lglen, 0, 0);
1394 if (file->vol->flags & HFS_READONLY)
1402 file->cat.u.fil.filMdDat = d_tomtime(time(0));
1403 file->flags |= HFS_UPDATE_CATREC;
1405 if (file->pos > len)
1413 * NAME: hfs->lseek()
1414 * DESCRIPTION: change file seek pointer
1416 long hfs_lseek(hfsfile *file, long offset, int from)
1418 unsigned long *lglen;
1421 f_getptrs(file, &lglen, 0, 0);
1430 newpos = file->pos + offset;
1434 newpos = *lglen + offset;
1444 else if (newpos > *lglen)
1453 * NAME: hfs->close()
1454 * DESCRIPTION: close a file
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)
1462 int hfs_close(hfsfile *file)
1464 #endif /* APPLE_HYB */
1465 hfsvol *vol = file->vol;
1468 if (f_trunc(file) < 0 ||
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 */
1477 file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa;
1480 file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa;
1483 file->flags |= HFS_UPDATE_CATREC;
1485 if (f_flush(file) < 0)
1487 #endif /*APPLE_HYB */
1490 file->prev->next = file->next;
1492 file->next->prev = file->prev;
1493 if (file == vol->files)
1494 vol->files = file->next;
1501 /* High-Level Catalog Routines ============================================= */
1505 * DESCRIPTION: return catalog information for an arbitrary path
1507 int hfs_stat(hfsvol *vol, char *path, hfsdirent *ent)
1511 char name[HFS_MAX_FLEN + 1];
1513 if (v_getvol(&vol) < 0 ||
1514 v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1517 r_unpackdirent(parid, name, &data, ent);
1523 * NAME: hfs->fstat()
1524 * DESCRIPTION: return catalog information for an open file
1526 int hfs_fstat(hfsfile *file, hfsdirent *ent)
1528 r_unpackdirent(file->parid, file->name, &file->cat, ent);
1534 * NAME: hfs->setattr()
1535 * DESCRIPTION: change a file's attributes
1537 int hfs_setattr(hfsvol *vol, char *path, hfsdirent *ent)
1542 if (v_getvol(&vol) < 0 ||
1543 v_resolve(&vol, path, &data, 0, 0, &n) <= 0)
1546 if (vol->flags & HFS_READONLY)
1552 r_packdirent(&data, ent);
1554 if (v_putcatrec(&data, &n) < 0)
1561 * NAME: hfs->fsetattr()
1562 * DESCRIPTION: change an open file's attributes
1564 int hfs_fsetattr(hfsfile *file, hfsdirent *ent)
1566 if (file->vol->flags & HFS_READONLY)
1572 r_packdirent(&file->cat, ent);
1574 file->flags |= HFS_UPDATE_CATREC;
1580 * NAME: hfs->mkdir()
1581 * DESCRIPTION: create a new directory
1583 int hfs_mkdir(hfsvol *vol, char *path)
1587 char name[HFS_MAX_FLEN + 1];
1590 if (v_getvol(&vol) < 0)
1593 found = v_resolve(&vol, path, &data, &parid, name, 0);
1594 if (found < 0 || parid == 0)
1602 if (parid == HFS_CNID_ROOTPAR)
1608 if (vol->flags & HFS_READONLY)
1614 if (v_newfolder(vol, parid, name) < 0)
1621 * NAME: hfs->rmdir()
1622 * DESCRIPTION: delete an empty directory
1624 int hfs_rmdir(hfsvol *vol, char *path)
1629 char name[HFS_MAX_FLEN + 1];
1630 unsigned char pkey[HFS_CATKEYLEN];
1632 if (v_getvol(&vol) < 0 ||
1633 v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1636 if (data.cdrType != cdrDirRec)
1642 if (data.u.dir.dirVal != 0)
1644 ERROR(ENOTEMPTY, 0);
1648 if (parid == HFS_CNID_ROOTPAR)
1654 if (vol->flags & HFS_READONLY)
1660 /* delete directory record */
1662 r_makecatkey(&key, parid, name);
1663 r_packcatkey(&key, pkey, 0);
1665 if (bt_delete(&vol->cat, pkey) < 0)
1668 /* delete thread record */
1670 r_makecatkey(&key, data.u.dir.dirDirID, "");
1671 r_packcatkey(&key, pkey, 0);
1673 if (bt_delete(&vol->cat, pkey) < 0 ||
1674 v_adjvalence(vol, parid, 1, -1) < 0)
1681 * NAME: hfs->create()
1682 * DESCRIPTION: create a new file
1684 int hfs_create(hfsvol *vol, char *path, char *type, char *creator)
1689 char name[HFS_MAX_FLEN + 1];
1690 unsigned char record[HFS_CATRECMAXLEN];
1691 int found, i, reclen;
1693 if (v_getvol(&vol) < 0)
1696 found = v_resolve(&vol, path, &data, &parid, name, 0);
1697 if (found < 0 || parid == 0)
1705 if (parid == HFS_CNID_ROOTPAR)
1711 if (vol->flags & HFS_READONLY)
1717 /* create file `name' in parent `parid' */
1719 if (bt_space(&vol->cat, 1) < 0)
1722 id = vol->mdb.drNxtCNID++;
1723 vol->flags |= HFS_UPDATE_MDB;
1725 /* create file record */
1727 data.cdrType = cdrFilRec;
1730 data.u.fil.filFlags = 0;
1731 data.u.fil.filTyp = 0;
1733 memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds));
1735 data.u.fil.filUsrWds.fdType = d_getl((unsigned char *) type);
1736 data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator);
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;
1749 memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo));
1751 data.u.fil.filClpSize = 0;
1753 for (i = 0; i < 3; ++i)
1755 data.u.fil.filExtRec[i].xdrStABN = 0;
1756 data.u.fil.filExtRec[i].xdrNumABlks = 0;
1758 data.u.fil.filRExtRec[i].xdrStABN = 0;
1759 data.u.fil.filRExtRec[i].xdrNumABlks = 0;
1762 data.u.fil.filResrv = 0;
1764 r_makecatkey(&key, parid, name);
1765 r_packcatkey(&key, record, &reclen);
1766 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
1768 if (bt_insert(&vol->cat, record, reclen) < 0 ||
1769 v_adjvalence(vol, parid, 0, 1) < 0)
1776 * NAME: hfs->delete()
1777 * DESCRIPTION: remove both forks of a file
1779 int hfs_delete(hfsvol *vol, char *path)
1783 unsigned char pkey[HFS_CATKEYLEN];
1786 if (v_getvol(&vol) < 0 ||
1787 v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0)
1790 if (file.cat.cdrType != cdrFilRec)
1796 if (file.parid == HFS_CNID_ROOTPAR)
1802 if (vol->flags & HFS_READONLY)
1808 /* free disk blocks */
1813 file.cat.u.fil.filLgLen = 0;
1814 file.cat.u.fil.filRLgLen = 0;
1816 f_selectfork(&file, 0);
1817 if (f_trunc(&file) < 0)
1820 f_selectfork(&file, 1);
1821 if (f_trunc(&file) < 0)
1824 /* delete file record */
1826 r_makecatkey(&key, file.parid, file.name);
1827 r_packcatkey(&key, pkey, 0);
1829 if (bt_delete(&vol->cat, pkey) < 0 ||
1830 v_adjvalence(vol, file.parid, 0, -1) < 0)
1833 /* delete file thread, if any */
1835 found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0);
1841 r_makecatkey(&key, file.cat.u.fil.filFlNum, "");
1842 r_packcatkey(&key, pkey, 0);
1844 if (bt_delete(&vol->cat, pkey) < 0)
1852 * NAME: hfs->rename()
1853 * DESCRIPTION: change the name of and/or move a file or directory
1855 int hfs_rename(hfsvol *vol, char *srcpath, char *dstpath)
1858 CatDataRec src, dst;
1861 char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1];
1862 unsigned char record[HFS_CATRECMAXLEN];
1863 int found, isdir, moving, reclen;
1866 if (v_getvol(&vol) < 0 ||
1867 v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0)
1870 isdir = (src.cdrType == cdrDirRec);
1873 found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0);
1879 ERROR(EINVAL, "can't move across volumes");
1885 ERROR(ENOENT, "bad destination path");
1890 dst.cdrType == cdrDirRec &&
1891 dst.u.dir.dirDirID != src.u.dir.dirDirID)
1893 dstid = dst.u.dir.dirDirID;
1894 strcpy(dstname, srcname);
1896 found = v_catsearch(vol, dstid, dstname, 0, 0, 0);
1901 moving = (srcid != dstid);
1907 ptr = strrchr(dstpath, ':');
1914 strcpy(dstname, ptr);
1916 if (! moving && strcmp(srcname, dstname) == 0)
1917 return 0; /* source and destination are the same */
1919 if (moving || d_relstring(srcname, dstname))
1921 ERROR(EEXIST, "can't use destination name");
1926 /* can't move anything into the root directory's parent */
1928 if (moving && dstid == HFS_CNID_ROOTPAR)
1930 ERROR(EINVAL, "can't move above root directory");
1934 if (moving && isdir)
1938 /* can't move root directory anywhere */
1940 if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR)
1942 ERROR(EINVAL, "can't move root directory");
1946 /* make sure we aren't trying to move a directory inside itself */
1948 for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID)
1950 if (id == src.u.dir.dirDirID)
1952 ERROR(EINVAL, "can't move directory inside itself");
1956 if (v_getdthread(vol, id, &dst, 0) <= 0)
1961 if (vol->flags & HFS_READONLY)
1967 /* change volume name */
1969 if (dstid == HFS_CNID_ROOTPAR)
1971 if (strlen(dstname) > HFS_MAX_VLEN)
1973 ERROR(ENAMETOOLONG, 0);
1977 strcpy(vol->mdb.drVN, dstname);
1978 vol->flags |= HFS_UPDATE_MDB;
1981 /* remove source record */
1983 r_makecatkey(&key, srcid, srcname);
1984 r_packcatkey(&key, record, 0);
1986 if (bt_delete(&vol->cat, record) < 0)
1989 /* insert destination record */
1991 r_makecatkey(&key, dstid, dstname);
1992 r_packcatkey(&key, record, &reclen);
1993 r_packcatdata(&src, HFS_RECDATA(record), &reclen);
1995 if (bt_insert(&vol->cat, record, reclen) < 0)
1998 /* update thread record */
2002 if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0)
2005 dst.u.dthd.thdParID = dstid;
2006 strcpy(dst.u.dthd.thdCName, dstname);
2008 if (v_putcatrec(&dst, &n) < 0)
2013 found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n);
2019 dst.u.fthd.fthdParID = dstid;
2020 strcpy(dst.u.fthd.fthdCName, dstname);
2022 if (v_putcatrec(&dst, &n) < 0)
2027 /* update directory valences */
2031 if (v_adjvalence(vol, srcid, isdir, -1) < 0 ||
2032 v_adjvalence(vol, dstid, isdir, 1) < 0)
2040 * NAME: hfs->hfs_get_drAllocPtr()
2041 * DESCRIPTION: get the current start of next allocation search
2044 hfs_get_drAllocPtr(hfsfile *file)
2046 return(file->vol->mdb.drAllocPtr);
2050 * NAME: hfs->hfs_set_drAllocPtr()
2051 * DESCRIPTION: set the current start of next allocation search
2055 hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size)
2058 hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size)
2061 hfsvol *vol = file->vol;
2064 /* truncate the current fork */
2065 if (f_trunc(file) < 0 ||
2069 /* convert the fork size into allocation blocks */
2070 size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz;
2072 /* set the start of next allocation search to be after this fork */
2073 vol->mdb.drAllocPtr = drAllocPtr + size;
2075 vol->flags |= HFS_UPDATE_MDB;
2081 * NAME: hfs->vsetbless()
2082 * DESCRIPTION: set blessed folder
2084 * adapted from vsetattr() from v3.2.6
2088 hfs_vsetbless(hfsvol *vol, unsigned long cnid)
2091 hfs_vsetbless(hfsvol *vol, unsigned long cnid)
2094 vol->mdb.drFndrInfo[0] = cnid;
2096 vol->flags |= HFS_UPDATE_MDB;
2098 #endif /* APPLE_HYB */