2172 lines
42 KiB
C
2172 lines
42 KiB
C
/* @(#)hfs.c 1.15 09/11/07 joerg */
|
|
#include <schily/mconfig.h>
|
|
#ifndef lint
|
|
static UConst char sccsid[] =
|
|
"@(#)hfs.c 1.15 09/11/07 joerg";
|
|
#endif
|
|
/*
|
|
* 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 <schily/stdlib.h>
|
|
#include <schily/unistd.h>
|
|
#include <schily/fcntl.h>
|
|
#include <schily/errno.h>
|
|
#include <schily/string.h>
|
|
#include <schily/ctype.h>
|
|
#include <schily/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"
|
|
|
|
/* High-Level Volume Routines ============================================== */
|
|
|
|
/*
|
|
* NAME: hfs->mount()
|
|
* DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
|
|
*/
|
|
#ifdef APPLE_HYB
|
|
hfsvol *hfs_mount(hce, pnum, flags)
|
|
hce_mem *hce;
|
|
int pnum;
|
|
int flags;
|
|
#else
|
|
hfsvol *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, locked)
|
|
hfsvol *vol;
|
|
long end;
|
|
long locked;
|
|
#else
|
|
int 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;
|
|
if (locked) {
|
|
vol->mdb.drAtrb |= HFS_ATRB_SLOCKED;
|
|
}
|
|
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
|
|
hfs_umount(hfs_mounts, 0L, 0L);
|
|
#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_HYB
|
|
int hfs_format(hce, pnum, vname)
|
|
hce_mem *hce;
|
|
int pnum;
|
|
char *vname;
|
|
#else
|
|
int 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;
|
|
#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;
|
|
|
|
/* store this info in the volume info */
|
|
vol.hce = hce;
|
|
|
|
#endif /* APPLE_HYB */
|
|
|
|
/* create extents overflow file */
|
|
|
|
ext->f.vol = &vol;
|
|
ext->f.parid = 0;
|
|
strcpy(ext->f.name, "extents overflow");
|
|
|
|
ext->f.cat.cdrType = cdrFilRec;
|
|
/* ext->f.cat.cdrResrv2 */
|
|
ext->f.cat.u.fil.filFlags = 0;
|
|
ext->f.cat.u.fil.filTyp = 0;
|
|
/* ext->f.cat.u.fil.filUsrWds */
|
|
ext->f.cat.u.fil.filFlNum = HFS_CNID_EXT;
|
|
ext->f.cat.u.fil.filStBlk = 0;
|
|
ext->f.cat.u.fil.filLgLen = 0;
|
|
ext->f.cat.u.fil.filPyLen = 0;
|
|
ext->f.cat.u.fil.filRStBlk = 0;
|
|
ext->f.cat.u.fil.filRLgLen = 0;
|
|
ext->f.cat.u.fil.filRPyLen = 0;
|
|
ext->f.cat.u.fil.filCrDat = vol.mdb.drCrDate;
|
|
ext->f.cat.u.fil.filMdDat = vol.mdb.drLsMod;
|
|
ext->f.cat.u.fil.filBkDat = 0;
|
|
/* ext->f.cat.u.fil.filFndrInfo */
|
|
ext->f.cat.u.fil.filClpSize = 0;
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
ext->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
|
|
ext->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
|
|
|
|
ext->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
|
|
ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
|
|
}
|
|
/* ext->f.cat.u.fil.filResrv */
|
|
f_selectfork(&ext->f, 0);
|
|
|
|
ext->f.clump = vol.mdb.drXTClpSiz;
|
|
ext->f.flags = 0;
|
|
|
|
ext->f.prev = ext->f.next = 0;
|
|
|
|
n_init(&ext->hdrnd, ext, ndHdrNode, 0);
|
|
|
|
ext->hdrnd.nnum = 0;
|
|
ext->hdrnd.nd.ndNRecs = 3;
|
|
ext->hdrnd.roff[1] = 0x078;
|
|
ext->hdrnd.roff[2] = 0x0f8;
|
|
ext->hdrnd.roff[3] = 0x1f8;
|
|
|
|
memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128);
|
|
|
|
ext->hdr.bthDepth = 0;
|
|
ext->hdr.bthRoot = 0;
|
|
ext->hdr.bthNRecs = 0;
|
|
ext->hdr.bthFNode = 0;
|
|
ext->hdr.bthLNode = 0;
|
|
ext->hdr.bthNodeSize = HFS_BLOCKSZ;
|
|
ext->hdr.bthKeyLen = 0x07;
|
|
ext->hdr.bthNNodes = 0;
|
|
ext->hdr.bthFree = 0;
|
|
for (i = 0; i < 76; ++i)
|
|
ext->hdr.bthResv[i] = 0;
|
|
|
|
map = ALLOC(char, HFS_MAP1SZ);
|
|
if (map == 0)
|
|
{
|
|
if (result == 0)
|
|
{
|
|
ERROR(ENOMEM, 0);
|
|
result = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(map, 0, HFS_MAP1SZ);
|
|
BMSET(map, 0);
|
|
}
|
|
|
|
ext->map = map;
|
|
ext->mapsz = HFS_MAP1SZ;
|
|
ext->flags = HFS_UPDATE_BTHDR;
|
|
ext->compare = r_compareextkeys;
|
|
|
|
if (result == 0 && bt_space(ext, 1) < 0)
|
|
result = -1;
|
|
|
|
--ext->hdr.bthFree;
|
|
|
|
/* create catalog file */
|
|
|
|
cat->f.vol = &vol;
|
|
cat->f.parid = 0;
|
|
strcpy(cat->f.name, "catalog");
|
|
|
|
cat->f.cat.cdrType = cdrFilRec;
|
|
/* cat->f.cat.cdrResrv2 */
|
|
cat->f.cat.u.fil.filFlags = 0;
|
|
cat->f.cat.u.fil.filTyp = 0;
|
|
/* cat->f.cat.u.fil.filUsrWds */
|
|
cat->f.cat.u.fil.filFlNum = HFS_CNID_CAT;
|
|
cat->f.cat.u.fil.filStBlk = 0;
|
|
cat->f.cat.u.fil.filLgLen = 0;
|
|
cat->f.cat.u.fil.filPyLen = 0;
|
|
cat->f.cat.u.fil.filRStBlk = 0;
|
|
cat->f.cat.u.fil.filRLgLen = 0;
|
|
cat->f.cat.u.fil.filRPyLen = 0;
|
|
cat->f.cat.u.fil.filCrDat = vol.mdb.drCrDate;
|
|
cat->f.cat.u.fil.filMdDat = vol.mdb.drLsMod;
|
|
cat->f.cat.u.fil.filBkDat = 0;
|
|
/* cat->f.cat.u.fil.filFndrInfo */
|
|
cat->f.cat.u.fil.filClpSize = 0;
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
cat->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
|
|
cat->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
|
|
|
|
cat->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
|
|
cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
|
|
}
|
|
/* cat->f.cat.u.fil.filResrv */
|
|
f_selectfork(&cat->f, 0);
|
|
|
|
cat->f.clump = vol.mdb.drCTClpSiz;
|
|
cat->f.flags = 0;
|
|
|
|
cat->f.prev = cat->f.next = 0;
|
|
|
|
n_init(&cat->hdrnd, cat, ndHdrNode, 0);
|
|
|
|
cat->hdrnd.nnum = 0;
|
|
cat->hdrnd.nd.ndNRecs = 3;
|
|
cat->hdrnd.roff[1] = 0x078;
|
|
cat->hdrnd.roff[2] = 0x0f8;
|
|
cat->hdrnd.roff[3] = 0x1f8;
|
|
|
|
memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128);
|
|
|
|
cat->hdr.bthDepth = 0;
|
|
cat->hdr.bthRoot = 0;
|
|
cat->hdr.bthNRecs = 0;
|
|
cat->hdr.bthFNode = 0;
|
|
cat->hdr.bthLNode = 0;
|
|
cat->hdr.bthNodeSize = HFS_BLOCKSZ;
|
|
cat->hdr.bthKeyLen = 0x25;
|
|
cat->hdr.bthNNodes = 0;
|
|
cat->hdr.bthFree = 0;
|
|
for (i = 0; i < 76; ++i)
|
|
cat->hdr.bthResv[i] = 0;
|
|
|
|
map = ALLOC(char, HFS_MAP1SZ);
|
|
if (map == 0)
|
|
{
|
|
if (result == 0)
|
|
{
|
|
ERROR(ENOMEM, 0);
|
|
result = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(map, 0, HFS_MAP1SZ);
|
|
BMSET(map, 0);
|
|
}
|
|
|
|
cat->map = map;
|
|
cat->mapsz = HFS_MAP1SZ;
|
|
cat->flags = HFS_UPDATE_BTHDR;
|
|
cat->compare = r_comparecatkeys;
|
|
|
|
if (result == 0 && bt_space(cat, 1) < 0)
|
|
result = -1;
|
|
|
|
--cat->hdr.bthFree;
|
|
|
|
/* create root folder */
|
|
|
|
if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0)
|
|
result = -1;
|
|
|
|
vol.mdb.drNxtCNID = 16;
|
|
|
|
/* finish up */
|
|
|
|
if (result == 0)
|
|
{
|
|
block b;
|
|
|
|
/* write boot blocks */
|
|
|
|
memset(b, 0, sizeof(b));
|
|
b_writelb(&vol, 0, &b);
|
|
b_writelb(&vol, 1, &b);
|
|
|
|
/* flush other disk state */
|
|
|
|
vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM;
|
|
|
|
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 */
|
|
FREE(vol.ext.map);
|
|
FREE(vol.cat.map);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* High-Level Directory Routines =========================================== */
|
|
|
|
/*
|
|
* NAME: hfs->chdir()
|
|
* DESCRIPTION: change current HFS directory
|
|
*/
|
|
int hfs_chdir(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
CatDataRec data;
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
|
|
return -1;
|
|
|
|
if (data.cdrType != cdrDirRec)
|
|
{
|
|
ERROR(ENOTDIR, 0);
|
|
return -1;
|
|
}
|
|
|
|
vol->cwd = data.u.dir.dirDirID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->getcwd()
|
|
* DESCRIPTION: return the current working directory ID
|
|
*/
|
|
long hfs_getcwd(vol)
|
|
hfsvol *vol;
|
|
{
|
|
if (v_getvol(&vol) < 0)
|
|
return 0;
|
|
|
|
return vol->cwd;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->setcwd()
|
|
* DESCRIPTION: set the current working directory ID
|
|
*/
|
|
int hfs_setcwd(vol, id)
|
|
hfsvol *vol;
|
|
long id;
|
|
{
|
|
if (v_getvol(&vol) < 0)
|
|
return -1;
|
|
|
|
if (id == vol->cwd)
|
|
return 0;
|
|
|
|
/* make sure the directory exists */
|
|
|
|
if (v_getdthread(vol, id, 0, 0) <= 0)
|
|
return -1;
|
|
|
|
vol->cwd = id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->dirinfo()
|
|
* DESCRIPTION: given a directory ID, return its (name and) parent ID
|
|
*/
|
|
int hfs_dirinfo(vol, id, name)
|
|
hfsvol *vol;
|
|
long *id;
|
|
char *name;
|
|
{
|
|
CatDataRec thread;
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_getdthread(vol, *id, &thread, 0) <= 0)
|
|
return -1;
|
|
|
|
*id = thread.u.dthd.thdParID;
|
|
|
|
if (name)
|
|
strcpy(name, thread.u.dthd.thdCName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->opendir()
|
|
* DESCRIPTION: prepare to read the contents of a directory
|
|
*/
|
|
hfsdir *hfs_opendir(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
hfsdir *dir;
|
|
CatKeyRec key;
|
|
CatDataRec data;
|
|
unsigned char pkey[HFS_CATKEYLEN];
|
|
|
|
if (v_getvol(&vol) < 0)
|
|
return 0;
|
|
|
|
dir = ALLOC(hfsdir, 1);
|
|
if (dir == 0)
|
|
{
|
|
ERROR(ENOMEM, 0);
|
|
return 0;
|
|
}
|
|
|
|
dir->vol = vol;
|
|
|
|
if (*path == 0)
|
|
{
|
|
/* meta-directory containing root dirs from all mounted volumes */
|
|
|
|
dir->dirid = 0;
|
|
dir->vptr = hfs_mounts;
|
|
}
|
|
else
|
|
{
|
|
if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
|
|
{
|
|
FREE(dir);
|
|
return 0;
|
|
}
|
|
|
|
if (data.cdrType != cdrDirRec)
|
|
{
|
|
FREE(dir);
|
|
ERROR(ENOTDIR, 0);
|
|
return 0;
|
|
}
|
|
|
|
dir->dirid = data.u.dir.dirDirID;
|
|
dir->vptr = 0;
|
|
|
|
r_makecatkey(&key, dir->dirid, "");
|
|
r_packcatkey(&key, pkey, 0);
|
|
|
|
if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
|
|
{
|
|
FREE(dir);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dir->prev = 0;
|
|
dir->next = vol->dirs;
|
|
|
|
if (vol->dirs)
|
|
vol->dirs->prev = dir;
|
|
|
|
vol->dirs = dir;
|
|
|
|
return dir;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->readdir()
|
|
* DESCRIPTION: return the next entry in the directory
|
|
*/
|
|
int hfs_readdir(dir, ent)
|
|
hfsdir *dir;
|
|
hfsdirent *ent;
|
|
{
|
|
CatKeyRec key;
|
|
CatDataRec data;
|
|
unsigned char *ptr;
|
|
|
|
if (dir->dirid == 0)
|
|
{
|
|
hfsvol *vol;
|
|
char cname[HFS_MAX_FLEN + 1];
|
|
|
|
for (vol = hfs_mounts; vol; vol = vol->next)
|
|
{
|
|
if (vol == dir->vptr)
|
|
break;
|
|
}
|
|
|
|
if (vol == 0)
|
|
{
|
|
ERROR(ENOENT, "no more entries");
|
|
return -1;
|
|
}
|
|
|
|
if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 ||
|
|
v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
|
|
&data, cname, 0) < 0)
|
|
return -1;
|
|
|
|
r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
|
|
|
|
dir->vptr = vol->next;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (dir->n.rnum == -1)
|
|
{
|
|
ERROR(ENOENT, "no more entries");
|
|
return -1;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
++dir->n.rnum;
|
|
|
|
while (dir->n.rnum >= (int)dir->n.nd.ndNRecs)
|
|
{
|
|
dir->n.nnum = dir->n.nd.ndFLink;
|
|
if (dir->n.nnum == 0)
|
|
{
|
|
dir->n.rnum = -1;
|
|
ERROR(ENOENT, "no more entries");
|
|
return -1;
|
|
}
|
|
|
|
if (bt_getnode(&dir->n) < 0)
|
|
{
|
|
dir->n.rnum = -1;
|
|
return -1;
|
|
}
|
|
|
|
dir->n.rnum = 0;
|
|
}
|
|
|
|
ptr = HFS_NODEREC(dir->n, dir->n.rnum);
|
|
|
|
r_unpackcatkey(ptr, &key);
|
|
|
|
if (key.ckrParID != dir->dirid)
|
|
{
|
|
dir->n.rnum = -1;
|
|
ERROR(ENOENT, "no more entries");
|
|
return -1;
|
|
}
|
|
|
|
r_unpackcatdata(HFS_RECDATA(ptr), &data);
|
|
|
|
switch (data.cdrType)
|
|
{
|
|
case cdrDirRec:
|
|
case cdrFilRec:
|
|
r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
|
|
return 0;
|
|
|
|
case cdrThdRec:
|
|
case cdrFThdRec:
|
|
break;
|
|
|
|
default:
|
|
dir->n.rnum = -1;
|
|
|
|
ERROR(EIO, "unexpected directory entry found");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->closedir()
|
|
* DESCRIPTION: stop reading a directory
|
|
*/
|
|
int hfs_closedir(dir)
|
|
hfsdir *dir;
|
|
{
|
|
hfsvol *vol = dir->vol;
|
|
|
|
if (dir->prev)
|
|
dir->prev->next = dir->next;
|
|
if (dir->next)
|
|
dir->next->prev = dir->prev;
|
|
if (dir == vol->dirs)
|
|
vol->dirs = dir->next;
|
|
|
|
FREE(dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* High-Level File Routines ================================================ */
|
|
|
|
/*
|
|
* NAME: hfs->open()
|
|
* DESCRIPTION: prepare a file for I/O
|
|
*/
|
|
hfsfile *hfs_open(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
hfsfile *file;
|
|
|
|
if (v_getvol(&vol) < 0)
|
|
return 0;
|
|
|
|
file = ALLOC(hfsfile, 1);
|
|
if (file == 0)
|
|
{
|
|
ERROR(ENOMEM, 0);
|
|
return 0;
|
|
}
|
|
|
|
if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0)
|
|
{
|
|
FREE(file);
|
|
return 0;
|
|
}
|
|
|
|
if (file->cat.cdrType != cdrFilRec)
|
|
{
|
|
FREE(file);
|
|
ERROR(EISDIR, 0);
|
|
return 0;
|
|
}
|
|
|
|
file->vol = vol;
|
|
file->clump = file->cat.u.fil.filClpSize;
|
|
file->flags = 0;
|
|
|
|
f_selectfork(file, 0);
|
|
|
|
file->prev = 0;
|
|
file->next = vol->files;
|
|
|
|
if (vol->files)
|
|
vol->files->prev = file;
|
|
|
|
vol->files = file;
|
|
|
|
return file;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->setfork()
|
|
* DESCRIPTION: select file fork for I/O operations
|
|
*/
|
|
int hfs_setfork(file, ffork)
|
|
hfsfile *file;
|
|
int ffork;
|
|
{
|
|
int result = 0;
|
|
|
|
if (! (file->vol->flags & HFS_READONLY) &&
|
|
f_trunc(file) < 0)
|
|
result = -1;
|
|
|
|
f_selectfork(file, ffork);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->getfork()
|
|
* DESCRIPTION: return the current fork for I/O operations
|
|
*/
|
|
int hfs_getfork(file)
|
|
hfsfile *file;
|
|
{
|
|
return file->fork != fkData;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->read()
|
|
* DESCRIPTION: read from an open file
|
|
*/
|
|
long hfs_read(file, buf, len)
|
|
hfsfile *file;
|
|
void *buf;
|
|
unsigned long len;
|
|
{
|
|
unsigned long *lglen, count;
|
|
unsigned char *ptr = buf;
|
|
|
|
f_getptrs(file, &lglen, 0, 0);
|
|
|
|
if (file->pos + len > *lglen)
|
|
len = *lglen - file->pos;
|
|
|
|
count = len;
|
|
while (count)
|
|
{
|
|
block b;
|
|
unsigned long bnum, offs, chunk;
|
|
|
|
bnum = file->pos / HFS_BLOCKSZ;
|
|
offs = file->pos % HFS_BLOCKSZ;
|
|
|
|
chunk = HFS_BLOCKSZ - offs;
|
|
if (chunk > count)
|
|
chunk = count;
|
|
|
|
if (f_getblock(file, bnum, &b) < 0)
|
|
return -1;
|
|
|
|
memcpy(ptr, b + offs, chunk);
|
|
ptr += chunk;
|
|
|
|
file->pos += chunk;
|
|
count -= chunk;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->write()
|
|
* DESCRIPTION: write to an open file
|
|
*/
|
|
long hfs_write(file, buf, len)
|
|
hfsfile *file;
|
|
void *buf;
|
|
unsigned long len;
|
|
{
|
|
unsigned long *lglen, *pylen, count;
|
|
unsigned char *ptr = buf;
|
|
|
|
if (file->vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
f_getptrs(file, &lglen, &pylen, 0);
|
|
|
|
count = len;
|
|
|
|
/* set flag to update (at least) the modification time */
|
|
|
|
if (count)
|
|
{
|
|
file->cat.u.fil.filMdDat = d_tomtime(time(0));
|
|
file->flags |= HFS_UPDATE_CATREC;
|
|
}
|
|
|
|
while (count)
|
|
{
|
|
block b;
|
|
unsigned long bnum, offs, chunk;
|
|
|
|
bnum = file->pos / HFS_BLOCKSZ;
|
|
offs = file->pos % HFS_BLOCKSZ;
|
|
|
|
chunk = HFS_BLOCKSZ - offs;
|
|
if (chunk > count)
|
|
chunk = count;
|
|
|
|
if (file->pos + chunk > *pylen)
|
|
{
|
|
if (bt_space(&file->vol->ext, 1) < 0 ||
|
|
f_alloc(file) < 0)
|
|
return -1;
|
|
}
|
|
#ifndef APPLE_HYB
|
|
/* Ignore this part as we are always writing new files to an empty disk
|
|
i.e. offs will always be 0 */
|
|
|
|
if (offs > 0 || chunk < HFS_BLOCKSZ)
|
|
{
|
|
if (f_getblock(file, bnum, &b) < 0)
|
|
return -1;
|
|
}
|
|
#endif /* APPLE_HYB */
|
|
memcpy(b + offs, ptr, chunk);
|
|
ptr += chunk;
|
|
|
|
if (f_putblock(file, bnum, &b) < 0)
|
|
return -1;
|
|
|
|
file->pos += chunk;
|
|
count -= chunk;
|
|
|
|
if (file->pos > *lglen)
|
|
*lglen = file->pos;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->truncate()
|
|
* DESCRIPTION: truncate an open file
|
|
*/
|
|
int hfs_truncate(file, len)
|
|
hfsfile *file;
|
|
unsigned long len;
|
|
{
|
|
unsigned long *lglen;
|
|
|
|
f_getptrs(file, &lglen, 0, 0);
|
|
|
|
if (*lglen > len)
|
|
{
|
|
if (file->vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
*lglen = len;
|
|
|
|
file->cat.u.fil.filMdDat = d_tomtime(time(0));
|
|
file->flags |= HFS_UPDATE_CATREC;
|
|
|
|
if (file->pos > len)
|
|
file->pos = len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->lseek()
|
|
* DESCRIPTION: change file seek pointer
|
|
*/
|
|
long hfs_lseek(file, offset, from)
|
|
hfsfile *file;
|
|
long offset;
|
|
int from;
|
|
{
|
|
unsigned long *lglen;
|
|
long newpos;
|
|
|
|
f_getptrs(file, &lglen, 0, 0);
|
|
|
|
switch (from)
|
|
{
|
|
case SEEK_SET:
|
|
newpos = offset;
|
|
break;
|
|
|
|
case SEEK_CUR:
|
|
newpos = file->pos + offset;
|
|
break;
|
|
|
|
case SEEK_END:
|
|
newpos = *lglen + offset;
|
|
break;
|
|
|
|
default:
|
|
ERROR(EINVAL, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (newpos < 0)
|
|
newpos = 0;
|
|
else if (newpos > *lglen)
|
|
newpos = *lglen;
|
|
|
|
file->pos = newpos;
|
|
|
|
return newpos;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->close()
|
|
* DESCRIPTION: close a file
|
|
*/
|
|
#ifdef APPLE_HYB
|
|
/* extra args are used to set the start of the forks in the ISO volume */
|
|
int hfs_close(file, dext, rext)
|
|
hfsfile *file;
|
|
long dext;
|
|
long rext;
|
|
{
|
|
int offset;
|
|
#else
|
|
int hfs_close(hfsfile *file)
|
|
{
|
|
#endif /* APPLE_HYB */
|
|
hfsvol *vol = file->vol;
|
|
int result = 0;
|
|
|
|
if (f_trunc(file) < 0 ||
|
|
f_flush(file) < 0)
|
|
result = -1;
|
|
|
|
#ifdef APPLE_HYB
|
|
/* "start" of file is relative to the first available block */
|
|
offset = vol->hce->hfs_hdr_size + vol->hce->hfs_map_size;
|
|
/* update the "real" starting extent and re-flush the file */
|
|
if (dext)
|
|
file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa;
|
|
|
|
if (rext)
|
|
file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa;
|
|
|
|
if (dext || rext)
|
|
file->flags |= HFS_UPDATE_CATREC;
|
|
|
|
if (f_flush(file) < 0)
|
|
result = -1;
|
|
#endif /*APPLE_HYB */
|
|
|
|
if (file->prev)
|
|
file->prev->next = file->next;
|
|
if (file->next)
|
|
file->next->prev = file->prev;
|
|
if (file == vol->files)
|
|
vol->files = file->next;
|
|
|
|
FREE(file);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* High-Level Catalog Routines ============================================= */
|
|
|
|
/*
|
|
* NAME: hfs->stat()
|
|
* DESCRIPTION: return catalog information for an arbitrary path
|
|
*/
|
|
int hfs_stat(vol, path, ent)
|
|
hfsvol *vol;
|
|
char *path;
|
|
hfsdirent *ent;
|
|
{
|
|
CatDataRec data;
|
|
long parid;
|
|
char name[HFS_MAX_FLEN + 1];
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
|
|
return -1;
|
|
|
|
r_unpackdirent(parid, name, &data, ent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->fstat()
|
|
* DESCRIPTION: return catalog information for an open file
|
|
*/
|
|
int hfs_fstat(file, ent)
|
|
hfsfile *file;
|
|
hfsdirent *ent;
|
|
{
|
|
r_unpackdirent(file->parid, file->name, &file->cat, ent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->setattr()
|
|
* DESCRIPTION: change a file's attributes
|
|
*/
|
|
int hfs_setattr(vol, path, ent)
|
|
hfsvol *vol;
|
|
char *path;
|
|
hfsdirent *ent;
|
|
{
|
|
CatDataRec data;
|
|
node n;
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, path, &data, 0, 0, &n) <= 0)
|
|
return -1;
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
r_packdirent(&data, ent);
|
|
|
|
if (v_putcatrec(&data, &n) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->fsetattr()
|
|
* DESCRIPTION: change an open file's attributes
|
|
*/
|
|
int hfs_fsetattr(file, ent)
|
|
hfsfile *file;
|
|
hfsdirent *ent;
|
|
{
|
|
if (file->vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
r_packdirent(&file->cat, ent);
|
|
|
|
file->flags |= HFS_UPDATE_CATREC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->mkdir()
|
|
* DESCRIPTION: create a new directory
|
|
*/
|
|
int hfs_mkdir(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
CatDataRec data;
|
|
long parid;
|
|
char name[HFS_MAX_FLEN + 1];
|
|
int found;
|
|
|
|
if (v_getvol(&vol) < 0)
|
|
return -1;
|
|
|
|
found = v_resolve(&vol, path, &data, &parid, name, 0);
|
|
if (found < 0 || parid == 0)
|
|
return -1;
|
|
else if (found)
|
|
{
|
|
ERROR(EEXIST, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (parid == HFS_CNID_ROOTPAR)
|
|
{
|
|
ERROR(EINVAL, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (v_newfolder(vol, parid, name) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->rmdir()
|
|
* DESCRIPTION: delete an empty directory
|
|
*/
|
|
int hfs_rmdir(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
CatKeyRec key;
|
|
CatDataRec data;
|
|
long parid;
|
|
char name[HFS_MAX_FLEN + 1];
|
|
unsigned char pkey[HFS_CATKEYLEN];
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
|
|
return -1;
|
|
|
|
if (data.cdrType != cdrDirRec)
|
|
{
|
|
ERROR(ENOTDIR, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (data.u.dir.dirVal != 0)
|
|
{
|
|
ERROR(ENOTEMPTY, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (parid == HFS_CNID_ROOTPAR)
|
|
{
|
|
ERROR(EINVAL, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* delete directory record */
|
|
|
|
r_makecatkey(&key, parid, name);
|
|
r_packcatkey(&key, pkey, 0);
|
|
|
|
if (bt_delete(&vol->cat, pkey) < 0)
|
|
return -1;
|
|
|
|
/* delete thread record */
|
|
|
|
r_makecatkey(&key, data.u.dir.dirDirID, "");
|
|
r_packcatkey(&key, pkey, 0);
|
|
|
|
if (bt_delete(&vol->cat, pkey) < 0 ||
|
|
v_adjvalence(vol, parid, 1, -1) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->create()
|
|
* DESCRIPTION: create a new file
|
|
*/
|
|
int hfs_create(vol, path, type, creator)
|
|
hfsvol *vol;
|
|
char *path;
|
|
char *type;
|
|
char *creator;
|
|
{
|
|
CatKeyRec key;
|
|
CatDataRec data;
|
|
long id, parid;
|
|
char name[HFS_MAX_FLEN + 1];
|
|
unsigned char record[HFS_CATRECMAXLEN];
|
|
int found, i, reclen;
|
|
|
|
if (v_getvol(&vol) < 0)
|
|
return -1;
|
|
|
|
found = v_resolve(&vol, path, &data, &parid, name, 0);
|
|
if (found < 0 || parid == 0)
|
|
return -1;
|
|
else if (found)
|
|
{
|
|
ERROR(EEXIST, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (parid == HFS_CNID_ROOTPAR)
|
|
{
|
|
ERROR(EINVAL, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* create file `name' in parent `parid' */
|
|
|
|
if (bt_space(&vol->cat, 1) < 0)
|
|
return -1;
|
|
|
|
id = vol->mdb.drNxtCNID++;
|
|
vol->flags |= HFS_UPDATE_MDB;
|
|
|
|
/* create file record */
|
|
|
|
data.cdrType = cdrFilRec;
|
|
data.cdrResrv2 = 0;
|
|
|
|
data.u.fil.filFlags = 0;
|
|
data.u.fil.filTyp = 0;
|
|
|
|
memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds));
|
|
|
|
data.u.fil.filUsrWds.fdType = d_getl((unsigned char *) type);
|
|
data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator);
|
|
|
|
data.u.fil.filFlNum = id;
|
|
data.u.fil.filStBlk = 0;
|
|
data.u.fil.filLgLen = 0;
|
|
data.u.fil.filPyLen = 0;
|
|
data.u.fil.filRStBlk = 0;
|
|
data.u.fil.filRLgLen = 0;
|
|
data.u.fil.filRPyLen = 0;
|
|
data.u.fil.filCrDat = d_tomtime(time(0));
|
|
data.u.fil.filMdDat = data.u.fil.filCrDat;
|
|
data.u.fil.filBkDat = 0;
|
|
|
|
memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo));
|
|
|
|
data.u.fil.filClpSize = 0;
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
data.u.fil.filExtRec[i].xdrStABN = 0;
|
|
data.u.fil.filExtRec[i].xdrNumABlks = 0;
|
|
|
|
data.u.fil.filRExtRec[i].xdrStABN = 0;
|
|
data.u.fil.filRExtRec[i].xdrNumABlks = 0;
|
|
}
|
|
|
|
data.u.fil.filResrv = 0;
|
|
|
|
r_makecatkey(&key, parid, name);
|
|
r_packcatkey(&key, record, &reclen);
|
|
r_packcatdata(&data, HFS_RECDATA(record), &reclen);
|
|
|
|
if (bt_insert(&vol->cat, record, reclen) < 0 ||
|
|
v_adjvalence(vol, parid, 0, 1) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->delete()
|
|
* DESCRIPTION: remove both forks of a file
|
|
*/
|
|
int hfs_delete(vol, path)
|
|
hfsvol *vol;
|
|
char *path;
|
|
{
|
|
hfsfile file;
|
|
CatKeyRec key;
|
|
unsigned char pkey[HFS_CATKEYLEN];
|
|
int found;
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0)
|
|
return -1;
|
|
|
|
if (file.cat.cdrType != cdrFilRec)
|
|
{
|
|
ERROR(EISDIR, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (file.parid == HFS_CNID_ROOTPAR)
|
|
{
|
|
ERROR(EINVAL, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* free disk blocks */
|
|
|
|
file.vol = vol;
|
|
file.flags = 0;
|
|
|
|
file.cat.u.fil.filLgLen = 0;
|
|
file.cat.u.fil.filRLgLen = 0;
|
|
|
|
f_selectfork(&file, 0);
|
|
if (f_trunc(&file) < 0)
|
|
return -1;
|
|
|
|
f_selectfork(&file, 1);
|
|
if (f_trunc(&file) < 0)
|
|
return -1;
|
|
|
|
/* delete file record */
|
|
|
|
r_makecatkey(&key, file.parid, file.name);
|
|
r_packcatkey(&key, pkey, 0);
|
|
|
|
if (bt_delete(&vol->cat, pkey) < 0 ||
|
|
v_adjvalence(vol, file.parid, 0, -1) < 0)
|
|
return -1;
|
|
|
|
/* delete file thread, if any */
|
|
|
|
found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0);
|
|
if (found < 0)
|
|
return -1;
|
|
|
|
if (found)
|
|
{
|
|
r_makecatkey(&key, file.cat.u.fil.filFlNum, "");
|
|
r_packcatkey(&key, pkey, 0);
|
|
|
|
if (bt_delete(&vol->cat, pkey) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->rename()
|
|
* DESCRIPTION: change the name of and/or move a file or directory
|
|
*/
|
|
int hfs_rename(vol, srcpath, dstpath)
|
|
hfsvol *vol;
|
|
char *srcpath;
|
|
char *dstpath;
|
|
{
|
|
hfsvol *srcvol;
|
|
CatDataRec src, dst;
|
|
long srcid, dstid;
|
|
CatKeyRec key;
|
|
char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1];
|
|
unsigned char record[HFS_CATRECMAXLEN];
|
|
int found, isdir, moving, reclen;
|
|
node n;
|
|
|
|
if (v_getvol(&vol) < 0 ||
|
|
v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0)
|
|
return -1;
|
|
|
|
isdir = (src.cdrType == cdrDirRec);
|
|
srcvol = vol;
|
|
|
|
found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0);
|
|
if (found < 0)
|
|
return -1;
|
|
|
|
if (vol != srcvol)
|
|
{
|
|
ERROR(EINVAL, "can't move across volumes");
|
|
return -1;
|
|
}
|
|
|
|
if (dstid == 0)
|
|
{
|
|
ERROR(ENOENT, "bad destination path");
|
|
return -1;
|
|
}
|
|
|
|
if (found &&
|
|
dst.cdrType == cdrDirRec &&
|
|
dst.u.dir.dirDirID != src.u.dir.dirDirID)
|
|
{
|
|
dstid = dst.u.dir.dirDirID;
|
|
strcpy(dstname, srcname);
|
|
|
|
found = v_catsearch(vol, dstid, dstname, 0, 0, 0);
|
|
if (found < 0)
|
|
return -1;
|
|
}
|
|
|
|
moving = (srcid != dstid);
|
|
|
|
if (found)
|
|
{
|
|
char *ptr;
|
|
|
|
ptr = strrchr(dstpath, ':');
|
|
if (ptr == 0)
|
|
ptr = dstpath;
|
|
else
|
|
++ptr;
|
|
|
|
if (*ptr)
|
|
strcpy(dstname, ptr);
|
|
|
|
if (! moving && strcmp(srcname, dstname) == 0)
|
|
return 0; /* source and destination are the same */
|
|
|
|
if (moving || d_relstring(srcname, dstname))
|
|
{
|
|
ERROR(EEXIST, "can't use destination name");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* can't move anything into the root directory's parent */
|
|
|
|
if (moving && dstid == HFS_CNID_ROOTPAR)
|
|
{
|
|
ERROR(EINVAL, "can't move above root directory");
|
|
return -1;
|
|
}
|
|
|
|
if (moving && isdir)
|
|
{
|
|
long id;
|
|
|
|
/* can't move root directory anywhere */
|
|
|
|
if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR)
|
|
{
|
|
ERROR(EINVAL, "can't move root directory");
|
|
return -1;
|
|
}
|
|
|
|
/* make sure we aren't trying to move a directory inside itself */
|
|
|
|
for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID)
|
|
{
|
|
if (id == src.u.dir.dirDirID)
|
|
{
|
|
ERROR(EINVAL, "can't move directory inside itself");
|
|
return -1;
|
|
}
|
|
|
|
if (v_getdthread(vol, id, &dst, 0) <= 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (vol->flags & HFS_READONLY)
|
|
{
|
|
ERROR(EROFS, 0);
|
|
return -1;
|
|
}
|
|
|
|
/* change volume name */
|
|
|
|
if (dstid == HFS_CNID_ROOTPAR)
|
|
{
|
|
if (strlen(dstname) > HFS_MAX_VLEN)
|
|
{
|
|
ERROR(ENAMETOOLONG, 0);
|
|
return -1;
|
|
}
|
|
|
|
strcpy(vol->mdb.drVN, dstname);
|
|
vol->flags |= HFS_UPDATE_MDB;
|
|
}
|
|
|
|
/* remove source record */
|
|
|
|
r_makecatkey(&key, srcid, srcname);
|
|
r_packcatkey(&key, record, 0);
|
|
|
|
if (bt_delete(&vol->cat, record) < 0)
|
|
return -1;
|
|
|
|
/* insert destination record */
|
|
|
|
r_makecatkey(&key, dstid, dstname);
|
|
r_packcatkey(&key, record, &reclen);
|
|
r_packcatdata(&src, HFS_RECDATA(record), &reclen);
|
|
|
|
if (bt_insert(&vol->cat, record, reclen) < 0)
|
|
return -1;
|
|
|
|
/* update thread record */
|
|
|
|
if (isdir)
|
|
{
|
|
if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0)
|
|
return -1;
|
|
|
|
dst.u.dthd.thdParID = dstid;
|
|
strcpy(dst.u.dthd.thdCName, dstname);
|
|
|
|
if (v_putcatrec(&dst, &n) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n);
|
|
if (found < 0)
|
|
return -1;
|
|
|
|
if (found)
|
|
{
|
|
dst.u.fthd.fthdParID = dstid;
|
|
strcpy(dst.u.fthd.fthdCName, dstname);
|
|
|
|
if (v_putcatrec(&dst, &n) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* update directory valences */
|
|
|
|
if (moving)
|
|
{
|
|
if (v_adjvalence(vol, srcid, isdir, -1) < 0 ||
|
|
v_adjvalence(vol, dstid, isdir, 1) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#ifdef APPLE_HYB
|
|
/*
|
|
* NAME: hfs->hfs_get_drAllocPtr()
|
|
* DESCRIPTION: get the current start of next allocation search
|
|
*/
|
|
unsigned short
|
|
hfs_get_drAllocPtr(file)
|
|
hfsfile *file;
|
|
{
|
|
return(file->vol->mdb.drAllocPtr);
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->hfs_set_drAllocPtr()
|
|
* DESCRIPTION: set the current start of next allocation search
|
|
*/
|
|
#ifdef PROTOTYPES
|
|
int
|
|
hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size)
|
|
#else
|
|
int
|
|
hfs_set_drAllocPtr(file, drAllocPtr, size)
|
|
hfsfile *file;
|
|
unsigned short drAllocPtr;
|
|
int size;
|
|
#endif
|
|
{
|
|
hfsvol *vol = file->vol;
|
|
int result = 0;
|
|
|
|
/* truncate the current fork */
|
|
if (f_trunc(file) < 0 ||
|
|
f_flush(file) < 0)
|
|
result = -1;
|
|
|
|
/* convert the fork size into allocation blocks */
|
|
size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz;
|
|
|
|
/* set the start of next allocation search to be after this fork */
|
|
vol->mdb.drAllocPtr = drAllocPtr + size;
|
|
|
|
vol->flags |= HFS_UPDATE_MDB;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* NAME: hfs->vsetbless()
|
|
* DESCRIPTION: set blessed folder
|
|
*
|
|
* adapted from vsetattr() from v3.2.6
|
|
*/
|
|
#ifdef PROTOTYPES
|
|
void
|
|
hfs_vsetbless(hfsvol *vol, unsigned long cnid)
|
|
#else
|
|
void
|
|
hfs_vsetbless(vol, cnid)
|
|
hfsvol *vol;
|
|
unsigned long cnid;
|
|
#endif
|
|
{
|
|
vol->mdb.drFndrInfo[0] = cnid;
|
|
|
|
vol->flags |= HFS_UPDATE_MDB;
|
|
}
|
|
#endif /* APPLE_HYB */
|