1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <err.h>
6#include <errno.h>
7#include <zlib.h>
8
9#include "hfsmeta.h"
10#include "Data.h"
11
12struct HFSDataObject {
13	int64_t	offset;
14	int64_t	size;
15};
16typedef struct HFSDataObject HFSDataObject;
17
18enum {
19	kHFSInfoHeaderVersion = 1,
20};
21
22
23#define MIN(a, b) \
24	({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
25		__a < __b ? __a : __b; })
26
27struct HFSInfoHeader {
28	uint32_t	version;
29	uint32_t	deviceBlockSize;
30	int64_t	rawDeviceSize;
31	int32_t	size;	// Size of header
32	int32_t	objectCount;
33};
34
35static ssize_t
36WriteExtent(gzFile outf, DeviceInfo_t *devp, off_t start, off_t len)
37{
38	const size_t bufSize = 1024 * 1024;
39	uint8_t buffer[bufSize];
40	off_t total = 0;
41
42	while (total < len) {
43		ssize_t nread;
44		size_t amt = MIN(bufSize, len - total);
45		ssize_t nwritten;
46		nread = pread(devp->fd, buffer, amt, start + total);
47		if (nread == -1) {
48			warn("Cannot read from device at offset %lld", start + total);
49			return -1;
50		}
51		if (nread != amt) {
52			warnx("Tried to read %zu bytes, only read %zd", amt, nread);
53		}
54		nwritten = gzwrite(outf, (char*)buffer, amt);
55		if (nwritten == -1) {
56			warn("tried to gzwrite %zu bytes", amt);
57			return -1;
58		} else if (nwritten != amt) {
59			warnx("tried to gzwrite %zu bytes, only wrote %u", amt, nwritten);
60			total += nwritten;
61		} else
62			total += amt;
63	}
64	return 0;
65}
66
67void
68WriteGatheredData(const char *pathname, VolumeObjects_t *vop)
69{
70	int fd;
71	gzFile outf;
72	struct HFSInfoHeader hdr = { 0 };
73	HFSDataObject *objs = NULL, *op;
74	ExtentList_t *ep;
75	int i;
76
77	hdr.version = S32(kHFSInfoHeaderVersion);
78	hdr.deviceBlockSize = S32((uint32_t)vop->devp->blockSize);
79	hdr.rawDeviceSize = S64(vop->devp->size);
80	hdr.objectCount = S32(vop->count);
81	hdr.size = S32(sizeof(hdr) + sizeof(HFSDataObject) * vop->count);
82
83	objs = malloc(sizeof(HFSDataObject) * vop->count);
84	if (objs == NULL) {
85		warn("Unable to allocate space for data objects (%zu bytes)", sizeof(HFSDataObject)* vop->count);
86		goto done;
87	}
88
89	op = objs;
90	for (ep = vop->list;
91	     ep;
92	     ep = ep->next) {
93		int i;
94		for (i = 0; i < ep->count; i++) {
95			op->offset = S64(ep->extents[i].base);
96			op->size = S64(ep->extents[i].length);
97			op++;
98		}
99	}
100
101	fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
102	if (fd == -1) {
103		warn("cannot create gather file %s", pathname);
104		goto done;
105	}
106	outf = gzdopen(fd, "wb");
107	if (outf == NULL) {
108		warn("Cannot create gz descriptor from file %s", pathname);
109		close(fd);
110		goto done;
111	}
112
113	gzwrite(outf, &hdr, sizeof(hdr));
114	gzwrite(outf, objs, sizeof(HFSDataObject) * vop->count);
115
116	int count = 0;
117	for (ep = vop->list;
118	     ep;
119	     ep = ep->next) {
120		int i;
121		for (i = 0; i < ep->count; i++) {
122			if (verbose)
123				fprintf(stderr, "Writing extent <%lld, %lld>\n", ep->extents[i].base, ep->extents[i].length);
124			if (WriteExtent(outf, vop->devp, ep->extents[i].base, ep->extents[i].length) == -1) {
125				if (verbose)
126					fprintf(stderr, "\tWrite failed\n");
127				break;
128			}
129			count++;
130		}
131	}
132	gzclose(outf);
133	if (count != vop->count)
134		fprintf(stderr, "WHOAH!  we're short by %zd objects!\n", vop->count - count);
135
136
137done:
138	if (objs)
139		free(objs);
140	return;
141
142}
143