📄 hfs.c
字号:
/* * hfsutils - tools for reading and writing Macintosh HFS volumes * Copyright (C) 1996, 1997 Robert Leslie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */#include <mconfig.h>#include <stdxlib.h>#include <unixstd.h>#include <fcntl.h>#include <errno.h>#include <strdefs.h>#include <time.h>#include <ctype.h>#include <sys/stat.h>#include "internal.h"#include "data.h"#include "block.h"#include "low.h"#include "file.h"#include "btree.h"#include "node.h"#include "record.h"#include "volume.h"char *hfs_error = "no error"; /* static error string */hfsvol *hfs_mounts; /* linked list of mounted volumes */hfsvol *hfs_curvol; /* current volume *//* High-Level Volume Routines ============================================== *//* * NAME: hfs->mount() * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) */#ifdef APPLE_HYBhfsvol *hfs_mount(hce, pnum, flags) hce_mem *hce; int pnum; int flags;#elsehfsvol *hfs_mount(path, pnum, flags) char *path; int pnum; int flags;#endif /* APPLE_HYB */{#ifndef APPLE_HYB struct stat dev;#endif hfsvol *vol = 0;#ifndef APPLE_HYB /* see if the volume is already mounted */ if (stat(path, &dev) >= 0) { struct stat mdev; hfsvol *check; for (check = hfs_mounts; check; check = check->next) { if (fstat(check->fd, &mdev) >= 0 && mdev.st_dev == dev.st_dev && mdev.st_ino == dev.st_ino && (check->pnum == 0 || check->pnum == pnum)) { /* verify compatible read/write mode */ if (((check->flags & HFS_READONLY) && ! (flags & O_WRONLY)) || (! (check->flags & HFS_READONLY) && (flags & (O_WRONLY | O_RDWR)))) { vol = check; break; } } } }#endif /* APPLE_HYB */ if (vol == 0) { vol = ALLOC(hfsvol, 1); if (vol == 0) { ERROR(ENOMEM, 0); return 0; } vol->flags = 0; vol->pnum = pnum; vol->vstart = 0; vol->vlen = 0; vol->lpa = 0; vol->vbm = 0; vol->cwd = HFS_CNID_ROOTDIR; vol->refs = 0; vol->files = 0; vol->dirs = 0; vol->prev = 0; vol->next = 0; vol->ext.map = 0; vol->ext.mapsz = 0; vol->ext.flags = 0; vol->ext.compare = r_compareextkeys; vol->cat.map = 0; vol->cat.mapsz = 0; vol->cat.flags = 0; vol->cat.compare = r_comparecatkeys; /* open and lock the device */#ifdef APPLE_HYB vol->fd = 3; /* any +ve number will do? */ vol->hce = hce; /* store the extra with the vol info */#else if (flags & (O_WRONLY | O_RDWR)) { vol->fd = open(path, O_RDWR); if (vol->fd >= 0 && l_lockvol(vol) < 0) { close(vol->fd); vol->fd = -2; } } if (! (flags & (O_WRONLY | O_RDWR)) || (vol->fd < 0 && (errno == EROFS || errno == EACCES || errno == EAGAIN) && (flags & O_RDWR))) { vol->flags |= HFS_READONLY; vol->fd = open(path, O_RDONLY); if (vol->fd >= 0 && l_lockvol(vol) < 0) { close(vol->fd); vol->fd = -2; } } if (vol->fd < 0) { if (vol->fd != -2) ERROR(errno, "error opening device"); v_destruct(vol); return 0; }#endif /* APPLE_HYB */ /* find out what kind of media this is and read the MDB */ if (l_readblock0(vol) < 0 || l_readmdb(vol) < 0) {#ifndef APPLE_HYB close(vol->fd); v_destruct(vol);#endif /* APPLE_HYB */ return 0; } /* verify this is an HFS volume */ if (vol->mdb.drSigWord != 0x4244) {#ifndef APPLE_HYB close(vol->fd);#endif /* APPLE_HYB */ v_destruct(vol); ERROR(EINVAL, "not a Macintosh HFS volume"); return 0; } /* do minimal consistency checks */ if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) {#ifndef APPLE_HYB close(vol->fd);#endif /* APPLE_HYB */ v_destruct(vol); ERROR(EINVAL, "bad volume allocation block size"); return 0; } if (vol->vlen == 0) vol->vlen = vol->mdb.drAlBlSt + vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2; /* read the volume bitmap and extents/catalog B*-tree headers */ if (l_readvbm(vol) < 0 || bt_readhdr(&vol->ext) < 0 || bt_readhdr(&vol->cat) < 0) {#ifndef APPLE_HYB close(vol->fd);#endif /* APPLE_HYB */ v_destruct(vol); return 0; } if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) { /* volume was not cleanly unmounted; scavenge free-space */ if (v_scavenge(vol) < 0) {#ifndef APPLE_HYB close(vol->fd);#endif /* APPLE_HYB */ v_destruct(vol); return 0; } } if (vol->flags & HFS_READONLY) vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; else vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; vol->prev = 0; vol->next = hfs_mounts; if (hfs_mounts) hfs_mounts->prev = vol; hfs_mounts = vol; } ++vol->refs; return hfs_curvol = vol;}/* * NAME: hfs->flush() * DESCRIPTION: flush all pending changes to an HFS volume */int hfs_flush(vol) hfsvol *vol;{ hfsfile *file; if (v_getvol(&vol) < 0) return -1; for (file = vol->files; file; file = file->next) { if (f_flush(file) < 0) return -1; } if (v_flush(vol, 0) < 0) return -1; return 0;}/* * NAME: hfs->flushall() * DESCRIPTION: flush all pending changes to all mounted HFS volumes */void hfs_flushall(){ hfsvol *vol; for (vol = hfs_mounts; vol; vol = vol->next) hfs_flush(vol);}/* * NAME: hfs->umount() * DESCRIPTION: close an HFS volume */#ifdef APPLE_HYB/* extra argument used to alter the position of the extents/catalog files */int hfs_umount(vol, end) hfsvol *vol; long end;#elseint hfs_umount(vol) hfsvol *vol;#endif /* APPLE_HYB */{ int result = 0; if (v_getvol(&vol) < 0) return -1; if (--vol->refs) return v_flush(vol, 0); /* close all open files and directories */ while (vol->files)#ifdef APPLE_HYB hfs_close(vol->files, 0, 0);#else hfs_close(vol->files);#endif /* APPLE_HYB */ while (vol->dirs) hfs_closedir(vol->dirs);#ifdef APPLE_HYB if (end) { /* move extents and catalog to end of volume ... */ long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096; /* we are adding this "files" to the end of the ISO volume, so calculate this address in HFS speak ... *//* end -= vol->mdb.drAlBlSt; */ end -= (vol->mdb.drAlBlSt + vol->hce->hfs_map_size); end /= vol->lpa; /* catalog file ... */ vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end; vol->mdb.drXTExtRec[0].xdrStABN = end; /* move postition to start of extents file */ end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN; /* extents file ... */ vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end; vol->mdb.drCTExtRec[0].xdrStABN = end; /* the volume bitmap is wrong as we have "moved" files about - simple just set the whole lot (it's a readonly volume anyway!) */ memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ); /* set the free blocks to zero */ vol->mdb.drFreeBks = 0; /* flag changes for flushing later */ vol->flags |= HFS_UPDATE_VBM; vol->flags |= HFS_UPDATE_MDB; vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; vol->ext.flags |= HFS_UPDATE_BTHDR; vol->cat.flags |= HFS_UPDATE_BTHDR; }#endif /* APPLE_HYB */ if (v_flush(vol, 1) < 0) result = -1;#ifndef APPLE_HYB if (close(vol->fd) < 0 && result == 0) { ERROR(errno, "error closing device"); result = -1; }#endif /* APPLE_HYB */ if (vol->prev) vol->prev->next = vol->next; if (vol->next) vol->next->prev = vol->prev; if (vol == hfs_mounts) hfs_mounts = vol->next; if (vol == hfs_curvol) hfs_curvol = 0; v_destruct(vol); return result;}/* * NAME: hfs->umountall() * DESCRIPTION: unmount all mounted volumes */void hfs_umountall(){ while (hfs_mounts)#ifdef APPLE_HYB continue;#else hfs_umount(hfs_mounts);#endif /* APPLE_HYB */}/* * NAME: hfs->getvol() * DESCRIPTION: return a pointer to a mounted volume */hfsvol *hfs_getvol(name) char *name;{ hfsvol *vol; if (name == 0) return hfs_curvol; for (vol = hfs_mounts; vol; vol = vol->next) { if (d_relstring(name, vol->mdb.drVN) == 0) return vol; } return 0;}/* * NAME: hfs->setvol() * DESCRIPTION: change the current volume */void hfs_setvol(vol) hfsvol *vol;{ hfs_curvol = vol;}/* * NAME: hfs->vstat() * DESCRIPTION: return volume statistics */int hfs_vstat(vol, ent) hfsvol *vol; hfsvolent *ent;{ if (v_getvol(&vol) < 0) return -1; strcpy(ent->name, vol->mdb.drVN); ent->flags = (vol->flags & HFS_READONLY) ? HFS_ISLOCKED : 0; ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; ent->crdate = d_toutime(vol->mdb.drCrDate); ent->mddate = d_toutime(vol->mdb.drLsMod); return 0;}/* * NAME: hfs->format() * DESCRIPTION: write a new filesystem */#ifdef APPLE_HYBint hfs_format(hce, pnum, vname) hce_mem *hce; int pnum; char *vname;#elseint hfs_format(path, pnum, vname) char *path; int pnum; char *vname;#endif /* APPLE_HYB */{ hfsvol vol; btree *ext = &vol.ext; btree *cat = &vol.cat; unsigned int vbmsz; int i, result = 0; block vbm[16]; char *map; if (strchr(vname, ':')) { ERROR(EINVAL, "volume name may not contain colons"); return -1; } i = strlen(vname); if (i < 1 || i > HFS_MAX_VLEN) { ERROR(EINVAL, "volume name must be 1-27 chars"); return -1; } vol.flags = 0; vol.pnum = pnum; vol.vstart = 0; vol.vlen = 0; vol.lpa = 0; vol.vbm = vbm; vol.cwd = HFS_CNID_ROOTDIR; vol.refs = 0; vol.files = 0; vol.dirs = 0; vol.prev = 0; vol.next = 0;#ifndef APPLE_HYB vol.fd = open(path, O_RDWR); if (vol.fd < 0) { ERROR(errno, "error opening device for writing"); return -1; } if (l_lockvol(&vol) < 0) { close(vol.fd); return -1; }#endif /* APPLE_HYB */ if (pnum > 0) { if (l_readpm(&vol) < 0) { close(vol.fd); return -1; } } else /* determine size of entire device */ {#ifdef APPLE_HYB vol.vlen = hce->hfs_vol_size/HFS_BLOCKSZ;#else unsigned long low, high, mid; block b; for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2) low = high; while (low < high - 1) { mid = (low + high) / 2; if (b_readlb(&vol, mid, &b) < 0) high = mid; else low = mid; } vol.vlen = low + 1;#endif /* APPLE_HYB */ } if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ) {#ifndef APPLE_HYB close(vol.fd);#endif /* APPLE_HYB */ ERROR(EINVAL, "volume size must be >= 800K"); return -1; } /* initialize volume geometry */#ifdef APPLE_HYB /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated earlier */ vol.lpa = hce->Csize/HFS_BLOCKSZ;#else vol.lpa = 1 + vol.vlen / 65536;#endif /* APPLE_HYB */ vbmsz = (vol.vlen / vol.lpa + 4095) / 4096; vol.mdb.drSigWord = 0x4244; vol.mdb.drCrDate = d_tomtime(time(0)); vol.mdb.drLsMod = vol.mdb.drCrDate; vol.mdb.drAtrb = 0; vol.mdb.drNmFls = 0; vol.mdb.drVBMSt = 3; vol.mdb.drAllocPtr = 0; vol.mdb.drNmAlBlks = (vol.vlen - 5 - vbmsz) / vol.lpa; vol.mdb.drAlBlkSiz = vol.lpa * HFS_BLOCKSZ; vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz * 4; vol.mdb.drAlBlSt = 3 + vbmsz;#ifdef APPLE_HYB /* round up start block to a muliple of lpa - important later *//*vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + vol.lpa - 1) / vol.lpa) * vol.lpa; */ /* take in accout alignment of files wrt HFS volume start i.e we want drAlBlSt plus hfs_map_size to me a multiple of lpa */ vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + hce->hfs_map_size + vol.lpa - 1) / vol.lpa) * vol.lpa; vol.mdb.drAlBlSt -= hce->hfs_map_size;#endif /* APPLE_HYB */ vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */ vol.mdb.drFreeBks = vol.mdb.drNmAlBlks; strcpy(vol.mdb.drVN, vname); vol.mdb.drVolBkUp = 0; vol.mdb.drVSeqNum = 0; vol.mdb.drWrCnt = 0; vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz;#ifdef APPLE_HYB /* adjust size of extents/catalog upwards as we may have rounded up allocation size */ i = 1 + vol.vlen / 65536; vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i; /* round up to lpa size */ vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) / vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz; /* ignore above, use what we have already calculated ... */ vol.mdb.drXTClpSiz = hce->XTCsize; /* make Catalog file CTC (default twice) as big - prevents further allocation later which we don't want - this seems to work OK ... *//*vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * CTC; */ vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * hce->ctc_size; /* we want to put things at the end of the volume later, so we'll cheat here ... shouldn't matter, as we only need the volume read only anyway (we won't be adding files later!) - leave some extra space for the alternative MDB (in the last allocation block) */ vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1;#else vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz;#endif /* APPLE_HYB */ vol.mdb.drNmRtDirs = 0; vol.mdb.drFilCnt = 0; vol.mdb.drDirCnt = -1; /* incremented when root folder is created */ for (i = 0; i < 8; ++i) vol.mdb.drFndrInfo[i] = 0; vol.mdb.drVCSize = 0; vol.mdb.drVBMCSize = 0; vol.mdb.drCtlCSize = 0; vol.mdb.drXTFlSize = 0; vol.mdb.drCTFlSize = 0; for (i = 0; i < 3; ++i) { vol.mdb.drXTExtRec[i].xdrStABN = 0; vol.mdb.drXTExtRec[i].xdrNumABlks = 0; vol.mdb.drCTExtRec[i].xdrStABN = 0; vol.mdb.drCTExtRec[i].xdrNumABlks = 0; } /* initialize volume bitmap */ memset(vol.vbm, 0, sizeof(vbm));#ifdef APPLE_HYB /* We don't want to write anything out at the moment, so we allocate memory to hold the HFS "header" info and extents/catalog files. Any reads/writes from/to these parts of the volume are trapped and stored in memory. */ /* blocks up to the first unallocated block == HFS "header" info This will be placed in the first 32kb of the ISO volume later */ hce->hfs_hdr_size = vol.mdb.drAlBlSt; /* size of the extents and catalog files. This will be added to the end of the ISO volume later */ hce->hfs_ce_size = vol.mdb.drXTClpSiz + vol.mdb.drCTClpSiz; /* we also allocate space for the Desktop file and the alternative MDB while we're here */ FREE(hce->hfs_ce); hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz + vol.mdb.drAlBlkSiz)); /* allocate memory for the map and hdr */ FREE(hce->hfs_map); hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size) *HFS_BLOCKSZ)); if (hce->hfs_ce == 0 || hce->hfs_map == 0) { ERROR(ENOMEM, 0); result = -1; } /* hfs_hdr is immediately after the hfs_map */ hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ; /* size needed in HFS_BLOCKSZ blocks for later use */ hce->hfs_ce_size /= HFS_BLOCKSZ; /* note size of Desktop file */ hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ; /* total size of catalog/extents and desktop */ hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size; /* alternative MDB in the last alocation block */ hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ; /* add the MDB to the total size */ hce->hfs_tot_size += vol.lpa;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -