]>
Commit | Line | Data |
---|---|---|
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__ | |
22 | ssize_t | |
23 | GetBlock(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; | |
37 | done: | |
38 | return retval; | |
39 | } | |
40 | ||
41 | /* | |
42 | * Initialize a VolumeObject. Simple function. | |
43 | */ | |
44 | __private_extern__ | |
45 | VolumeObjects_t * | |
46 | InitVolumeObject(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 | ||
59 | done: | |
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__ | |
73 | int | |
74 | AddExtent(VolumeObjects_t *vdp, off_t start, off_t length) | |
75 | { | |
76 | return AddExtentForFile(vdp, start, length, 0); | |
77 | } | |
78 | ||
79 | __private_extern__ | |
80 | int | |
81 | AddExtentForFile(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 | ||
114 | done: | |
115 | return retval; | |
116 | } | |
117 | ||
118 | // Debugging function | |
119 | __private_extern__ | |
120 | void | |
121 | PrintVolumeObject(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__ | |
153 | int | |
154 | CopyObjectsToDest(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 | } |