#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include "hfsmeta.h"
#include "Data.h"
static unsigned int
FindBlock(HFSPlusExtentRecord *erp, unsigned int blockNum)
{
unsigned int lba = 0;
unsigned int base = 0;
HFSPlusExtentDescriptor *ep = &(*erp)[0];
int i;
for (i = 0; i < kHFSPlusExtentDensity; i++) {
if (ep->startBlock == 0 || ep->blockCount == 0)
break;
if ((base + S32(ep->blockCount)) > blockNum) {
lba = S32(ep->startBlock) + (blockNum - base);
break;
}
base += S32(ep->blockCount);
ep++;
}
return lba;
}
static int
GetNode(DeviceInfo_t *devp, HFSPlusVolumeHeader *hp, int nodeNum, int blocksPerNode, void *nodePtr)
{
int retval = 0;
unsigned char *ptr, *endPtr;
unsigned int offset;
HFSPlusExtentRecord *erp = &hp->extentsFile.extents;
ptr = nodePtr;
endPtr = ptr + (blocksPerNode * S32(hp->blockSize));
offset = nodeNum * blocksPerNode;
while (ptr < endPtr) {
ssize_t rv;
off_t lba;
int i;
lba = FindBlock(erp, offset);
if (lba == 0) {
warnx("Cannot find block %u in extents overflow file", offset);
return -1;
}
lba = lba * S32(hp->blockSize);
for (i = 0; i < S32(hp->blockSize) / devp->blockSize; i++) {
rv = GetBlock(devp, lba + (i * devp->blockSize), ptr);
if (rv == -1) {
warnx("Cannot read block %u in extents overflow file", lba + i);
return -1;
}
ptr += devp->blockSize;
}
offset++;
}
done:
return retval;
}
static unsigned int
ScanNode(VolumeObjects_t *vop, uint8_t *nodePtr, size_t nodeSize, off_t blockSize)
{
u_int16_t *offsetPtr;
BTNodeDescriptor *descp;
int indx;
int numRecords;
HFSPlusExtentKey *keyp;
HFSPlusExtentRecord *datap;
uint8_t *recp;
unsigned int retval;
descp = (BTNodeDescriptor*)nodePtr;
if (descp->kind != kBTLeafNode)
return 0;
numRecords = S16(descp->numRecords);
offsetPtr = (u_int16_t*)((uint8_t*)nodePtr + nodeSize);
retval = S32(descp->fLink);
for (indx = 1; indx <= numRecords; indx++) {
int recOffset = S16(offsetPtr[-indx]);
recp = nodePtr + recOffset;
if (recp > (nodePtr + nodeSize)) {
return -1; }
keyp = (HFSPlusExtentKey*)recp;
datap = (HFSPlusExtentRecord*)(recp + sizeof(HFSPlusExtentKey));
if (S32(keyp->fileID) >= kHFSFirstUserCatalogNodeID) {
if (debug) printf("Done scanning extents overflow file\n");
retval = 0;
break;
} else {
int i;
for (i = 0; i < kHFSPlusExtentDensity; i++) {
off_t start = S32((*datap)[i].startBlock) * (off_t)blockSize;
off_t len = S32((*datap)[i].blockCount) * (off_t)blockSize;
if (start && len)
AddExtent(vop, start, len);
}
}
}
return retval;
}
int
ScanExtents(VolumeObjects_t *vop, int useAltHdr)
{
int retval = -1;
ssize_t rv;
char buffer[vop->devp->blockSize];
struct RootNode {
BTNodeDescriptor desc;
BTHeaderRec header;
} *headerNode;
HFSPlusVolumeHeader *hp;
HFSPlusExtentRecord *erp;
off_t vBlockSize;
size_t tBlockSize;
int blocksPerNode;
void *nodePtr = NULL;
unsigned int nodeNum = 0;
hp = useAltHdr ? &vop->vdp->altHeader : & vop->vdp->priHeader;
vBlockSize = S32(hp->blockSize);
rv = GetBlock(vop->devp, S32(hp->extentsFile.extents[0].startBlock) * vBlockSize, buffer);
if (rv == -1) {
warnx("Cannot get btree header node for extents file for %s header", useAltHdr ? "alternate" : "primary");
retval = -1;
goto done;
}
headerNode = (struct RootNode*)buffer;
if (headerNode->desc.kind != kBTHeaderNode) {
warnx("Root node is not a header node (%x)", headerNode->desc.kind);
goto done;
}
tBlockSize = S16(headerNode->header.nodeSize);
blocksPerNode = tBlockSize / vBlockSize;
nodePtr = malloc(tBlockSize);
if (nodePtr == NULL) {
warn("cannot allocate buffer for node");
goto done;
}
nodeNum = S32(headerNode->header.firstLeafNode);
if (debug) printf("nodenum = %u\n", nodeNum);
while (nodeNum != 0) {
if (debug) printf("Getting node %u\n", nodeNum);
rv = GetNode(vop->devp, hp, nodeNum, blocksPerNode, nodePtr);
if (rv == -1) {
warnx("Cannot get node %u", nodeNum);
retval = -1;
goto done;
}
nodeNum = ScanNode(vop, nodePtr, tBlockSize, vBlockSize);
}
retval = 0;
done:
if (nodePtr)
free(nodePtr);
return retval;
}