1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <err.h>
8#include <errno.h>
9#include <sys/stat.h>
10#include <sys/disk.h>
11
12#include "hfsmeta.h"
13
14#define MIN(a, b) \
15	({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
16		__a < __b ? __a : __b; })
17
18/*
19 * Get a block from a given input device.
20 */
21__private_extern__
22ssize_t
23GetBlock(DeviceInfo_t *devp, off_t offset, uint8_t *buffer)
24{
25	ssize_t retval = -1;
26	off_t baseOffset = (offset / devp->blockSize) * devp->blockSize;
27
28	retval = pread(devp->fd, buffer, devp->blockSize, baseOffset);
29	if (retval != devp->blockSize) {
30		warn("GetBlock: pread returned %zd", retval);
31	}
32	if (offset != baseOffset) {
33		size_t off = offset % devp->blockSize;
34		memmove(buffer, buffer + off, devp->blockSize - off);
35	}
36	retval = 0;
37done:
38	return retval;
39}
40
41/*
42 * Initialize a VolumeObject.  Simple function.
43 */
44__private_extern__
45VolumeObjects_t *
46InitVolumeObject(struct DeviceInfo *devp, struct VolumeDescriptor *vdp)
47{
48	VolumeObjects_t *retval = NULL;
49
50	retval = malloc(sizeof(*retval));
51	if (retval) {
52		retval->devp = devp;
53		retval->vdp = vdp;
54		retval->count = 0;
55		retval->byteCount = 0;
56		retval->list = NULL;
57	}
58
59done:
60	return retval;
61}
62
63/*
64 * Add an extent (<start, length> pair) to a volume list.
65 * Note that this doesn't try to see if an extent is already
66 * in the list; the presumption is that an fsck_hfs run will
67 * note overlapping extents in that case.  It adds the extents
68 * in groups of kExtentCount; the goal here is to minimize the
69 * number of objects we allocate, while still trying to keep
70 * the waste memory allocation low.
71 */
72__private_extern__
73int
74AddExtent(VolumeObjects_t *vdp, off_t start, off_t length)
75{
76	return AddExtentForFile(vdp, start, length, 0);
77}
78
79__private_extern__
80int
81AddExtentForFile(VolumeObjects_t *vdp, off_t start, off_t length, unsigned int fid)
82{
83	int retval = 0;
84	size_t indx;
85	ExtentList_t **ep = &vdp->list;
86
87	if (debug) printf("AddExtent(%p, %lld, %lld) (file id %u)\n", vdp, start, length, fid);
88	while (*ep) {
89		if ((*ep)->count < kExtentCount) {
90			indx = (*ep)->count;
91			(*ep)->extents[indx].base = start;
92			(*ep)->extents[indx].length = length;
93			(*ep)->extents[indx].fid = fid;
94			(*ep)->count++;
95			break;
96		} else {
97			ep = &(*ep)->next;
98		}
99	}
100	if (*ep == NULL) {
101		*ep = malloc(sizeof(ExtentList_t));
102		if (*ep == NULL) {
103			err(1, "cannot allocate a new ExtentList object");
104		}
105		(*ep)->count = 1;
106		(*ep)->extents[0].base = start;
107		(*ep)->extents[0].length = length;
108		(*ep)->extents[0].fid = fid;
109		(*ep)->next = NULL;
110	}
111	vdp->count++;
112	vdp->byteCount += length;
113
114done:
115	return retval;
116}
117
118// Debugging function
119__private_extern__
120void
121PrintVolumeObject(VolumeObjects_t *vop)
122{
123	ExtentList_t *exts;
124
125	printf("Volume Information\n");
126	if (vop->devp) {
127		printf("\tDevice %s\n", vop->devp->devname);
128		printf("\t\tSize %lld\n", vop->devp->size);
129		printf("\t\tBlock size %d\n", vop->devp->blockSize);
130		printf("\t\tBlock Count %lld\n", vop->devp->blockCount);
131	}
132	printf("\tObject count %zu\n", vop->count);
133	printf("\tByte count %lld\n", vop->byteCount);
134	printf("\tExtent list:\n");
135	for (exts = vop->list;
136	     exts;
137	     exts = exts->next) {
138		int indx;
139		for (indx = 0; indx < exts->count; indx++) {
140			printf("\t\t<%lld, %lld> (file %u)\n", exts->extents[indx].base, exts->extents[indx].length, exts->extents[indx].fid);
141		}
142	}
143	return;
144}
145
146/*
147 * The main routine:  given a Volume descriptor, copy the metadata from it
148 * to the given destination object (a device or sparse bundle).  It keeps
149 * track of progress, and also takes an amount to skip (which happens if it's
150 * resuming an earlier, interrupted copy).
151 */
152__private_extern__
153int
154CopyObjectsToDest(VolumeObjects_t *vop, struct IOWrapper *wrapper, off_t skip)
155{
156	ExtentList_t *exts;
157	off_t total = 0;
158
159	if (skip == 0) {
160		wrapper->cleanup(wrapper);
161	}
162	for (exts = vop->list;
163	     exts;
164	     exts = exts->next) {
165		int indx;
166		for (indx = 0; indx < exts->count; indx++) {
167			off_t start = exts->extents[indx].base;
168			off_t len = exts->extents[indx].length;
169			if (skip < len) {
170				__block off_t totalWritten;
171				void (^bp)(off_t);
172
173				if (skip) {
174					off_t amt = MIN(skip, len);
175					len -= amt;
176					start += amt;
177					total += amt;
178					skip -= amt;
179					wrapper->setprog(wrapper, total);
180					if (debug)
181						printf("* * * Wrote %lld of %lld\n", total, vop->byteCount);
182					else
183						printf("%d%%\n", (int)((total * 100) / vop->byteCount));
184					fflush(stdout);
185				}
186				totalWritten = total;
187				if (printProgress) {
188					bp = ^(off_t amt) {
189						totalWritten += amt;
190						wrapper->setprog(wrapper, totalWritten);
191						if (debug)
192							printf("* * Wrote %lld of %lld (%d%%)\n", totalWritten, vop->byteCount, (int)((totalWritten * 100) / vop->byteCount));
193						else
194							printf("%d%%\n", (int)((totalWritten * 100) / vop->byteCount));
195						fflush(stdout);
196						return;
197					};
198				} else {
199					bp = ^(off_t amt) {
200						totalWritten += amt;
201						return;
202					};
203				}
204				if (wrapper->writer(wrapper, vop->devp, start, len, bp) == -1) {
205					int t = errno;
206					if (verbose)
207						warnx("Writing extent <%lld, %lld> failed", start, len);
208					errno = t;
209					return -1;
210				}
211				total = totalWritten;
212			} else {
213				skip -= len;
214				total += len;
215				if (printProgress) {
216					wrapper->setprog(wrapper, total);
217					if (debug)
218						printf("Wrote %lld of %lld\n", total, vop->byteCount);
219					else
220						printf("%d%%\n", (int)((total * 100) / vop->byteCount));
221					fflush(stdout);
222				}
223			}
224		}
225	}
226
227	if (total == vop->byteCount) {
228		wrapper->setprog(wrapper, 0);	// remove progress
229	}
230
231	return 0;
232}
233