/*
* This file has been modified for the cdrkit suite.
*
* The behaviour and appearence of the program code below can differ to a major
* extent from the version distributed by the original author(s).
*
* For details, see Changelog file distributed with the cdrkit package. If you
* received this file from another source then ask the distributing person for
* a log of modifications.
*
*/
/* @(#)low.c 1.4 04/06/17 joerg */
/*
* 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.
*/
#include <mconfig.h>
#include <strdefs.h>
#include <stdxlib.h>
#include <errno.h>
#include <unixstd.h>
#include <fctldefs.h>
#include "internal.h"
#include "data.h"
#include "block.h"
#include "low.h"
#include "file.h"
/*
* NAME: low->lockvol()
* DESCRIPTION: prevent destructive simultaneous access
*/
int l_lockvol(hfsvol *vol)
{
# ifndef NODEVLOCKS
struct flock lock;
lock.l_type = (vol->flags & HFS_READONLY) ? F_RDLCK : F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(vol->fd, F_SETLK, &lock) < 0)
{
ERROR(errno, "unable to obtain lock for device");
return -1;
}
# endif
return 0;
}
/*
* NAME: low->readblock0()
* DESCRIPTION: read the first sector and get bearings
*/
int l_readblock0(hfsvol *vol)
{
block b;
unsigned char *ptr = b;
Block0 rec;
if (b_readlb(vol, 0, &b) < 0)
return -1;
d_fetchw(&ptr, &rec.sbSig);
d_fetchw(&ptr, &rec.sbBlkSize);
d_fetchl(&ptr, &rec.sbBlkCount);
d_fetchw(&ptr, &rec.sbDevType);
d_fetchw(&ptr, &rec.sbDevId);
d_fetchl(&ptr, &rec.sbData);
d_fetchw(&ptr, &rec.sbDrvrCount);
d_fetchl(&ptr, &rec.ddBlock);
d_fetchw(&ptr, &rec.ddSize);
d_fetchw(&ptr, &rec.ddType);
switch (rec.sbSig)
{
case 0x4552: /* block device with a partition table */
{
if (rec.sbBlkSize != HFS_BLOCKSZ)
{
ERROR(EINVAL, "unsupported block size");
return -1;
}
vol->vlen = rec.sbBlkCount;
if (l_readpm(vol) < 0)
return -1;
}
break;
case 0x4c4b: /* bootable floppy */
vol->pnum = 0;
break;
default: /* non-bootable floppy or something else */
/* some miscreant media may also be partitioned;
we attempt to read a partition map, but ignore any failure */
if (l_readpm(vol) < 0)
vol->pnum = 0;
}
return 0;
}
/*
* NAME: low->readpm()
* DESCRIPTION: read the partition map and locate an HFS volume
*/
int l_readpm(hfsvol *vol)
{
block b;
unsigned char *ptr;
Partition map;
unsigned long bnum;
int pnum;
bnum = 1;
pnum = vol->pnum;
for (;;)
{
if (b_readlb(vol, bnum, &b) < 0)
return -1;
ptr = b;
d_fetchw(&ptr, &map.pmSig);
d_fetchw(&ptr, &map.pmSigPad);
d_fetchl(&ptr, &map.pmMapBlkCnt);
d_fetchl(&ptr, &map.pmPyPartStart);
d_fetchl(&ptr, &map.pmPartBlkCnt);
memcpy(map.pmPartName, ptr, 32);
map.pmPartName[32] = 0;
ptr += 32;
memcpy(map.pmParType, ptr, 32);
map.pmParType[32] = 0;
ptr += 32;
d_fetchl(&ptr, &map.pmLgDataStart);
d_fetchl(&ptr, &map.pmDataCnt);
d_fetchl(&ptr, &map.pmPartStatus);
d_fetchl(&ptr, &map.pmLgBootStart);
d_fetchl(&ptr, &map.pmBootSize);
d_fetchl(&ptr, &map.pmBootAddr);
d_fetchl(&ptr, &map.pmBootAddr2);
d_fetchl(&ptr, &map.pmBootEntry);
d_fetchl(&ptr, &map.pmBootEntry2);
d_fetchl(&ptr, &map.pmBootCksum);
memcpy(map.pmProcessor, ptr, 16);
map.pmProcessor[16] = 0;
ptr += 16;
if (map.pmSig == 0x5453)
{
/* old partition map sig */
ERROR(EINVAL, "unsupported partition map signature");
return -1;
}
if (map.pmSig != 0x504d)
{
ERROR(EINVAL, "bad partition map");
return -1;
}
if (strcmp((char *) map.pmParType, "Apple_HFS") == 0 && --pnum == 0)
{
if (map.pmLgDataStart != 0)
{
ERROR(EINVAL, "unsupported start of partition logical data");
return -1;
}
vol->vstart = map.pmPyPartStart;
vol->vlen = map.pmPartBlkCnt;
return 0;
}
if (bnum >= map.pmMapBlkCnt)
{
ERROR(EINVAL, "can't find HFS partition");
return -1;
}
++bnum;
}
}
/*
* NAME: low->readmdb()
* DESCRIPTION: read the master directory block into memory
*/
int l_readmdb(hfsvol *vol)
{
block b;
unsigned char *ptr = b;
MDB *mdb = &vol->mdb;
hfsfile *ext = &vol->ext.f;
hfsfile *cat = &vol->cat.f;
int i;
if (b_readlb(vol, 2, &b) < 0)
return -1;
d_fetchw(&ptr, &mdb->drSigWord);
d_fetchl(&ptr, &mdb->drCrDate);
d_fetchl(&ptr, &mdb->drLsMod);
d_fetchw(&ptr, &mdb->drAtrb);
d_fetchw(&ptr, (short *) &mdb->drNmFls);
d_fetchw(&ptr, (short *) &mdb->drVBMSt);
d_fetchw(&ptr, (short *) &mdb->drAllocPtr);
d_fetchw(&ptr, (short *) &mdb->drNmAlBlks);
d_fetchl(&ptr, (long *) &mdb->drAlBlkSiz);
d_fetchl(&ptr, (long *) &mdb->drClpSiz);
d_fetchw(&ptr, (short *) &mdb->drAlBlSt);
d_fetchl(&ptr, &mdb->drNxtCNID);
d_fetchw(&ptr, (short *) &mdb->drFreeBks);
d_fetchs(&ptr, mdb->drVN, sizeof(mdb->drVN));
if (ptr - b != 64)
abort();
d_fetchl(&ptr, &mdb->drVolBkUp);
d_fetchw(&ptr, &mdb->drVSeqNum);
d_fetchl(&ptr, (long *) &mdb->drWrCnt);
d_fetchl(&ptr, (long *) &mdb->drXTClpSiz);
d_fetchl(&ptr, (long *) &mdb->drCTClpSiz);
d_fetchw(&ptr, (short *) &mdb->drNmRtDirs);
d_fetchl(&ptr, (long *) &mdb->drFilCnt);
d_fetchl(&ptr, (long *) &mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_fetchl(&ptr, &mdb->drFndrInfo[i]);
if (ptr - b != 124)
abort();
d_fetchw(&ptr, (short *) &mdb->drVCSize);
d_fetchw(&ptr, (short *) &mdb->drVBMCSize);
d_fetchw(&ptr, (short *) &mdb->drCtlCSize);
d_fetchl(&ptr, (long *) &mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrStABN);
d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrNumABlks);
}
if (ptr - b != 146)
abort();
d_fetchl(&ptr, (long *) &mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrStABN);
d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrNumABlks);
}
if (ptr - b != 162)
abort();
vol->lpa = mdb->drAlBlkSiz / HFS_BLOCKSZ;
/* extents pseudo-file structs */
ext->vol = vol;
ext->parid = 0;
strcpy(ext->name, "extents overflow");
ext->cat.cdrType = cdrFilRec;
/* ext->cat.cdrResrv2 */
ext->cat.u.fil.filFlags = 0;
ext->cat.u.fil.filTyp = 0;
/* ext->cat.u.fil.filUsrWds */
ext->cat.u.fil.filFlNum = HFS_CNID_EXT;
ext->cat.u.fil.filStBlk = mdb->drXTExtRec[0].xdrStABN;
ext->cat.u.fil.filLgLen = mdb->drXTFlSize;
ext->cat.u.fil.filPyLen = mdb->drXTFlSize;
ext->cat.u.fil.filRStBlk = 0;
ext->cat.u.fil.filRLgLen = 0;
ext->cat.u.fil.filRPyLen = 0;
ext->cat.u.fil.filCrDat = mdb->drCrDate;
ext->cat.u.fil.filMdDat = mdb->drLsMod;
ext->cat.u.fil.filBkDat = 0;
/* ext->cat.u.fil.filFndrInfo */
ext->cat.u.fil.filClpSize = 0;
memcpy(ext->cat.u.fil.filExtRec, mdb->drXTExtRec, sizeof(ExtDataRec));
for (i = 0; i < 3; ++i)
{
ext->cat.u.fil.filRExtRec[i].xdrStABN = 0;
ext->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
}
f_selectfork(ext, 0);
ext->clump = mdb->drXTClpSiz;
ext->flags = 0;
ext->prev = ext->next = 0;
/* catalog pseudo-file structs */
cat->vol = vol;
cat->parid = 0;
strcpy(cat->name, "catalog");
cat->cat.cdrType = cdrFilRec;
/* cat->cat.cdrResrv2 */
cat->cat.u.fil.filFlags = 0;
cat->cat.u.fil.filTyp = 0;
/* cat->cat.u.fil.filUsrWds */
cat->cat.u.fil.filFlNum = HFS_CNID_CAT;
cat->cat.u.fil.filStBlk = mdb->drCTExtRec[0].xdrStABN;
cat->cat.u.fil.filLgLen = mdb->drCTFlSize;
cat->cat.u.fil.filPyLen = mdb->drCTFlSize;
cat->cat.u.fil.filRStBlk = 0;
cat->cat.u.fil.filRLgLen = 0;
cat->cat.u.fil.filRPyLen = 0;
cat->cat.u.fil.filCrDat = mdb->drCrDate;
cat->cat.u.fil.filMdDat = mdb->drLsMod;
cat->cat.u.fil.filBkDat = 0;
/* cat->cat.u.fil.filFndrInfo */
cat->cat.u.fil.filClpSize = 0;
memcpy(cat->cat.u.fil.filExtRec, mdb->drCTExtRec, sizeof(ExtDataRec));
for (i = 0; i < 3; ++i)
{
cat->cat.u.fil.filRExtRec[i].xdrStABN = 0;
cat->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
}
f_selectfork(cat, 0);
cat->clump = mdb->drCTClpSiz;
cat->flags = 0;
cat->prev = cat->next = 0;
return 0;
}
/*
* NAME: low->writemdb()
* DESCRIPTION: write the master directory block to disk
*/
int l_writemdb(hfsvol *vol)
{
block b;
unsigned char *ptr = b;
MDB *mdb = &vol->mdb;
hfsfile *ext = &vol->ext.f;
hfsfile *cat = &vol->cat.f;
int i;
memset(&b, 0, sizeof(b));
mdb->drXTFlSize = ext->cat.u.fil.filPyLen;
mdb->drXTClpSiz = ext->clump;
memcpy(mdb->drXTExtRec, ext->cat.u.fil.filExtRec, sizeof(ExtDataRec));
mdb->drCTFlSize = cat->cat.u.fil.filPyLen;
mdb->drCTClpSiz = cat->clump;
memcpy(mdb->drCTExtRec, cat->cat.u.fil.filExtRec, sizeof(ExtDataRec));
d_storew(&ptr, mdb->drSigWord);
d_storel(&ptr, mdb->drCrDate);
d_storel(&ptr, mdb->drLsMod);
d_storew(&ptr, mdb->drAtrb);
d_storew(&ptr, mdb->drNmFls);
d_storew(&ptr, mdb->drVBMSt);
d_storew(&ptr, mdb->drAllocPtr);
d_storew(&ptr, mdb->drNmAlBlks);
d_storel(&ptr, mdb->drAlBlkSiz);
d_storel(&ptr, mdb->drClpSiz);
d_storew(&ptr, mdb->drAlBlSt);
d_storel(&ptr, mdb->drNxtCNID);
d_storew(&ptr, mdb->drFreeBks);
d_stores(&ptr, mdb->drVN, sizeof(mdb->drVN));
if (ptr - b != 64)
abort();
d_storel(&ptr, mdb->drVolBkUp);
d_storew(&ptr, mdb->drVSeqNum);
d_storel(&ptr, mdb->drWrCnt);
d_storel(&ptr, mdb->drXTClpSiz);
d_storel(&ptr, mdb->drCTClpSiz);
d_storew(&ptr, mdb->drNmRtDirs);
d_storel(&ptr, mdb->drFilCnt);
d_storel(&ptr, mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_storel(&ptr, mdb->drFndrInfo[i]);
if (ptr - b != 124)
abort();
d_storew(&ptr, mdb->drVCSize);
d_storew(&ptr, mdb->drVBMCSize);
d_storew(&ptr, mdb->drCtlCSize);
d_storel(&ptr, mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_storew(&ptr, mdb->drXTExtRec[i].xdrStABN);
d_storew(&ptr, mdb->drXTExtRec[i].xdrNumABlks);
}
if (ptr - b != 146)
abort();
d_storel(&ptr, mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_storew(&ptr, mdb->drCTExtRec[i].xdrStABN);
d_storew(&ptr, mdb->drCTExtRec[i].xdrNumABlks);
}
if (ptr - b != 162)
abort();
if (b_writelb(vol, 2, &b) < 0)
return -1;
if (vol->flags & HFS_UPDATE_ALTMDB)
{
#ifdef APPLE_HYB
/* "write" alternative MDB to memory copy */
memcpy(vol->hce->hfs_alt_mdb, &b, sizeof(b));
#else
if (b_writelb(vol, vol->vlen - 2, &b) < 0)
return -1;
#endif /* APPLE_HYB */
}
vol->flags &= ~(HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB);
return 0;
}
/*
* NAME: low->readvbm()
* DESCRIPTION: read the volume bit map into memory
*/
int l_readvbm(hfsvol *vol)
{
int vbmst = vol->mdb.drVBMSt;
int vbmsz = (vol->mdb.drNmAlBlks + 4095) / (unsigned)4096;
block *bp;
if ((int)(vol->mdb.drAlBlSt - vbmst) < vbmsz)
{
ERROR(EIO, "volume bitmap collides with volume data");
return -1;
}
bp = ALLOC(block, vbmsz);
if (bp == 0)
{
ERROR(ENOMEM, 0);
return -1;
}
vol->vbm = bp;
while (vbmsz--)
{
if (b_readlb(vol, vbmst++, bp++) < 0)
{
FREE(vol->vbm);
vol->vbm = 0;
return -1;
}
}
return 0;
}
/*
* NAME: low->writevbm()
* DESCRIPTION: write the volume bit map to disk
*/
int l_writevbm(hfsvol *vol)
{
int vbmst = vol->mdb.drVBMSt;
int vbmsz = (vol->mdb.drNmAlBlks + 4095) / (unsigned)4096;
block *bp = vol->vbm;
while (vbmsz--)
{
if (b_writelb(vol, vbmst++, bp++) < 0)
return -1;
}
vol->flags &= ~HFS_UPDATE_VBM;
return 0;
}