historical/m0-applesillicon.git/xnu-qemu-arm64-5.1.0/roms/openbios/fs/hfs/volume.c
2024-01-16 11:20:27 -06:00

612 lines
12 KiB
C

/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 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., 51 Franklin Street - Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* $Id: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $
*/
#include "config.h"
#include "libhfs.h"
#include "volume.h"
#include "data.h"
#include "block.h"
#include "low.h"
#include "medium.h"
#include "file.h"
#include "btree.h"
#include "record.h"
#include "os.h"
#include "libc/byteorder.h"
/*
* NAME: vol->init()
* DESCRIPTION: initialize volume structure
*/
void v_init(hfsvol *vol, int flags)
{
btree *ext = &vol->ext;
btree *cat = &vol->cat;
vol->os_fd = 0;
vol->flags = flags & HFS_VOL_OPT_MASK;
vol->pnum = -1;
vol->vstart = 0;
vol->vlen = 0;
vol->lpa = 0;
vol->cache = NULL;
vol->vbm = NULL;
vol->vbmsz = 0;
f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
ext->map = NULL;
ext->mapsz = 0;
ext->flags = 0;
ext->keyunpack = (keyunpackfunc) r_unpackextkey;
ext->keycompare = (keycomparefunc) r_compareextkeys;
f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
cat->map = NULL;
cat->mapsz = 0;
cat->flags = 0;
cat->keyunpack = (keyunpackfunc) r_unpackcatkey;
cat->keycompare = (keycomparefunc) r_comparecatkeys;
vol->cwd = HFS_CNID_ROOTDIR;
vol->refs = 0;
vol->files = NULL;
vol->dirs = NULL;
vol->prev = NULL;
vol->next = NULL;
}
/*
* NAME: vol->open()
* DESCRIPTION: open volume source and lock against concurrent updates
*/
int v_open(hfsvol *vol, int os_fd )
{
if (vol->flags & HFS_VOL_OPEN)
ERROR(EINVAL, "volume already open");
vol->flags |= HFS_VOL_OPEN;
vol->os_fd = os_fd;
/* initialize volume block cache (OK to fail) */
if (! (vol->flags & HFS_OPT_NOCACHE) &&
b_init(vol) != -1)
vol->flags |= HFS_VOL_USINGCACHE;
return 0;
fail:
return -1;
}
/*
* NAME: vol->close()
* DESCRIPTION: close access path to volume source
*/
int v_close(hfsvol *vol)
{
int result = 0;
if (! (vol->flags & HFS_VOL_OPEN))
goto done;
if ((vol->flags & HFS_VOL_USINGCACHE) &&
b_finish(vol) == -1)
result = -1;
vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
/* free dynamically allocated structures */
FREE(vol->vbm);
vol->vbm = NULL;
vol->vbmsz = 0;
FREE(vol->ext.map);
FREE(vol->cat.map);
vol->ext.map = NULL;
vol->cat.map = NULL;
done:
return result;
}
/*
* NAME: vol->same()
* DESCRIPTION: return 1 iff path is same as open volume
*/
int v_same(hfsvol *vol, int os_fd )
{
return vol->os_fd == os_fd;
}
/*
* NAME: vol->geometry()
* DESCRIPTION: determine volume location and size (possibly in a partition)
*/
int v_geometry(hfsvol *vol, int pnum)
{
Partition map;
unsigned long bnum = 0;
int found;
vol->pnum = pnum;
if (pnum == 0)
{
vol->vstart = 0;
vol->vlen = b_size(vol);
if (vol->vlen == 0)
goto fail;
}
else
{
while (pnum--)
{
found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
if (found == -1 || ! found)
goto fail;
}
vol->vstart = map.pmPyPartStart;
vol->vlen = map.pmPartBlkCnt;
if (map.pmDataCnt)
{
if ((unsigned long) map.pmLgDataStart +
(unsigned long) map.pmDataCnt > vol->vlen)
ERROR(EINVAL, "partition data overflows partition");
vol->vstart += (unsigned long) map.pmLgDataStart;
vol->vlen = map.pmDataCnt;
}
if (vol->vlen == 0)
ERROR(EINVAL, "volume partition is empty");
}
if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
ERROR(EINVAL, "volume is smaller than 800K");
return 0;
fail:
return -1;
}
/*
* NAME: vol->readmdb()
* DESCRIPTION: load Master Directory Block into memory
*/
int v_readmdb(hfsvol *vol)
{
if (l_getmdb(vol, &vol->mdb, 0) == -1)
goto fail;
if (vol->mdb.drSigWord != HFS_SIGWORD)
{
if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
ERROR(EINVAL, "MFS volume format not supported");
else
ERROR(EINVAL, "not a Macintosh HFS volume");
}
if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
ERROR(EINVAL, "bad volume allocation block size");
vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
/* extents pseudo-file structs */
vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN;
vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize;
vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize;
vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
memcpy(&vol->ext.f.cat.u.fil.filExtRec,
&vol->mdb.drXTExtRec, sizeof(ExtDataRec));
f_selectfork(&vol->ext.f, fkData);
/* catalog pseudo-file structs */
vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN;
vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize;
vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize;
vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
memcpy(&vol->cat.f.cat.u.fil.filExtRec,
&vol->mdb.drCTExtRec, sizeof(ExtDataRec));
f_selectfork(&vol->cat.f, fkData);
return 0;
fail:
return -1;
}
/*
* NAME: vol->readvbm()
* DESCRIPTION: read volume bitmap into memory
*/
int v_readvbm(hfsvol *vol)
{
unsigned int vbmst = vol->mdb.drVBMSt;
unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
block *bp;
ASSERT(vol->vbm == 0);
if (vol->mdb.drAlBlSt - vbmst < vbmsz)
ERROR(EIO, "volume bitmap collides with volume data");
vol->vbm = ALLOC(block, vbmsz);
if (vol->vbm == NULL)
ERROR(ENOMEM, NULL);
vol->vbmsz = vbmsz;
for (bp = vol->vbm; vbmsz--; ++bp)
{
if (b_readlb(vol, vbmst++, bp) == -1)
goto fail;
}
return 0;
fail:
FREE(vol->vbm);
vol->vbm = NULL;
vol->vbmsz = 0;
return -1;
}
/*
* NAME: vol->mount()
* DESCRIPTION: load volume information into memory
*/
int v_mount(hfsvol *vol)
{
/* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
if (v_readmdb(vol) == -1 ||
v_readvbm(vol) == -1 ||
bt_readhdr(&vol->ext) == -1 ||
bt_readhdr(&vol->cat) == -1)
goto fail;
if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED)
vol->flags |= HFS_VOL_READONLY;
else if (vol->flags & HFS_VOL_READONLY)
vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
else
vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
vol->flags |= HFS_VOL_MOUNTED;
return 0;
fail:
return -1;
}
/*
* NAME: vol->catsearch()
* DESCRIPTION: search catalog tree
*/
int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
CatDataRec *data, char *cname, node *np)
{
CatKeyRec key;
byte pkey[HFS_CATKEYLEN];
const byte *ptr;
node n;
int found;
if (np == NULL)
np = &n;
r_makecatkey(&key, parid, name);
r_packcatkey(&key, pkey, NULL);
found = bt_search(&vol->cat, pkey, np);
if (found <= 0)
return found;
ptr = HFS_NODEREC(*np, np->rnum);
if (cname)
{
r_unpackcatkey(ptr, &key);
strcpy(cname, key.ckrCName);
}
if (data)
r_unpackcatdata(HFS_RECDATA(ptr), data);
return 1;
}
/*
* NAME: vol->extsearch()
* DESCRIPTION: search extents tree
*/
int v_extsearch(hfsfile *file, unsigned int fabn,
ExtDataRec *data, node *np)
{
ExtKeyRec key;
ExtDataRec extsave;
unsigned int fabnsave;
byte pkey[HFS_EXTKEYLEN];
const byte *ptr;
node n;
int found;
if (np == NULL)
np = &n;
r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
r_packextkey(&key, pkey, NULL);
/* in case bt_search() clobbers these */
memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
fabnsave = file->fabn;
found = bt_search(&file->vol->ext, pkey, np);
memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
file->fabn = fabnsave;
if (found <= 0)
return found;
if (data)
{
ptr = HFS_NODEREC(*np, np->rnum);
r_unpackextdata(HFS_RECDATA(ptr), data);
}
return 1;
}
/*
* NAME: vol->getthread()
* DESCRIPTION: retrieve catalog thread information for a file or directory
*/
int v_getthread(hfsvol *vol, unsigned long id,
CatDataRec *thread, node *np, int type)
{
CatDataRec rec;
int found;
if (thread == NULL)
thread = &rec;
found = v_catsearch(vol, id, "", thread, NULL, np);
if (found == 1 && thread->cdrType != type)
ERROR(EIO, "bad thread record");
return found;
fail:
return -1;
}
/*
* NAME: vol->resolve()
* DESCRIPTION: translate a pathname; return catalog information
*/
int v_resolve(hfsvol **vol, const char *path,
CatDataRec *data, unsigned long *parid, char *fname, node *np)
{
unsigned long dirid;
char name[HFS_MAX_FLEN + 1], *nptr;
int found = 0;
if (*path == 0)
ERROR(ENOENT, "empty path");
if (parid)
*parid = 0;
nptr = strchr(path, ':');
if (*path == ':' || nptr == NULL)
{
dirid = (*vol)->cwd; /* relative path */
if (*path == ':')
++path;
if (*path == 0)
{
found = v_getdthread(*vol, dirid, data, NULL);
if (found == -1)
goto fail;
if (found)
{
if (parid)
*parid = data->u.dthd.thdParID;
found = v_catsearch(*vol, data->u.dthd.thdParID,
data->u.dthd.thdCName, data, fname, np);
if (found == -1)
goto fail;
}
goto done;
}
}
else
{
hfsvol *check;
dirid = HFS_CNID_ROOTPAR; /* absolute path */
if (nptr - path > HFS_MAX_VLEN)
ERROR(ENAMETOOLONG, NULL);
strncpy(name, path, nptr - path);
name[nptr - path] = 0;
for (check = hfs_mounts; check; check = check->next)
{
if (d_relstring(check->mdb.drVN, name) == 0)
{
*vol = check;
break;
}
}
}
while (1)
{
while (*path == ':')
{
++path;
found = v_getdthread(*vol, dirid, data, NULL);
if (found == -1)
goto fail;
else if (! found)
goto done;
dirid = data->u.dthd.thdParID;
}
if (*path == 0)
{
found = v_getdthread(*vol, dirid, data, NULL);
if (found == -1)
goto fail;
if (found)
{
if (parid)
*parid = data->u.dthd.thdParID;
found = v_catsearch(*vol, data->u.dthd.thdParID,
data->u.dthd.thdCName, data, fname, np);
if (found == -1)
goto fail;
}
goto done;
}
nptr = name;
while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
*nptr++ = *path++;
if (*path && *path != ':')
ERROR(ENAMETOOLONG, NULL);
*nptr = 0;
if (*path == ':')
++path;
if (parid)
*parid = dirid;
found = v_catsearch(*vol, dirid, name, data, fname, np);
if (found == -1)
goto fail;
if (! found)
{
if (*path && parid)
*parid = 0;
if (*path == 0 && fname)
strcpy(fname, name);
goto done;
}
switch (data->cdrType)
{
case cdrDirRec:
if (*path == 0)
goto done;
dirid = data->u.dir.dirDirID;
break;
case cdrFilRec:
if (*path == 0)
goto done;
ERROR(ENOTDIR, "invalid pathname");
default:
ERROR(EIO, "unexpected catalog record");
}
}
done:
return found;
fail:
return -1;
}
/* Determine whether the volume is a HFS volume */
int
v_probe(int fd, long long offset)
{
MDB *mdb;
mdb = (MDB*)malloc(2 * 512);
os_seek_offset( fd, 2 * 512 + offset );
os_read(fd, mdb, 2, 9);
if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) {
free(mdb);
return 0;
}
free(mdb);
return -1;
}