#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <zlib.h>
#include "hfsmeta.h"
#include "Data.h"
struct HFSDataObject {
int64_t offset;
int64_t size;
};
typedef struct HFSDataObject HFSDataObject;
enum {
kHFSInfoHeaderVersion = 1,
};
#define MIN(a, b) \
({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
__a < __b ? __a : __b; })
struct HFSInfoHeader {
uint32_t version;
uint32_t deviceBlockSize;
int64_t rawDeviceSize;
int32_t size; int32_t objectCount;
};
static ssize_t
WriteExtent(gzFile outf, DeviceInfo_t *devp, off_t start, off_t len)
{
const size_t bufSize = 1024 * 1024;
uint8_t buffer[bufSize];
off_t total = 0;
while (total < len) {
ssize_t nread;
size_t amt = MIN(bufSize, len - total);
ssize_t nwritten;
nread = pread(devp->fd, buffer, amt, start + total);
if (nread == -1) {
warn("Cannot read from device at offset %lld", start + total);
return -1;
}
if (nread != amt) {
warnx("Tried to read %zu bytes, only read %zd", amt, nread);
}
nwritten = gzwrite(outf, (char*)buffer, amt);
if (nwritten == -1) {
warn("tried to gzwrite %zu bytes", amt);
return -1;
} else if (nwritten != amt) {
warnx("tried to gzwrite %zu bytes, only wrote %u", amt, nwritten);
total += nwritten;
} else
total += amt;
}
return 0;
}
void
WriteGatheredData(const char *pathname, VolumeObjects_t *vop)
{
int fd;
gzFile outf;
struct HFSInfoHeader hdr = { 0 };
HFSDataObject *objs = NULL, *op;
ExtentList_t *ep;
int i;
hdr.version = S32(kHFSInfoHeaderVersion);
hdr.deviceBlockSize = S32((uint32_t)vop->devp->blockSize);
hdr.rawDeviceSize = S64(vop->devp->size);
hdr.objectCount = S32(vop->count);
hdr.size = S32(sizeof(hdr) + sizeof(HFSDataObject) * vop->count);
objs = malloc(sizeof(HFSDataObject) * vop->count);
if (objs == NULL) {
warn("Unable to allocate space for data objects (%zu bytes)", sizeof(HFSDataObject)* vop->count);
goto done;
}
op = objs;
for (ep = vop->list;
ep;
ep = ep->next) {
int i;
for (i = 0; i < ep->count; i++) {
op->offset = S64(ep->extents[i].base);
op->size = S64(ep->extents[i].length);
op++;
}
}
fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
warn("cannot create gather file %s", pathname);
goto done;
}
outf = gzdopen(fd, "wb");
if (outf == NULL) {
warn("Cannot create gz descriptor from file %s", pathname);
close(fd);
goto done;
}
gzwrite(outf, &hdr, sizeof(hdr));
gzwrite(outf, objs, sizeof(HFSDataObject) * vop->count);
int count = 0;
for (ep = vop->list;
ep;
ep = ep->next) {
int i;
for (i = 0; i < ep->count; i++) {
if (verbose)
fprintf(stderr, "Writing extent <%lld, %lld>\n", ep->extents[i].base, ep->extents[i].length);
if (WriteExtent(outf, vop->devp, ep->extents[i].base, ep->extents[i].length) == -1) {
if (verbose)
fprintf(stderr, "\tWrite failed\n");
break;
}
count++;
}
}
gzclose(outf);
if (count != vop->count)
fprintf(stderr, "WHOAH! we're short by %zd objects!\n", vop->count - count);
done:
if (objs)
free(objs);
return;
}