]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/main.c
hfs-366.1.1.tar.gz
[apple/hfs.git] / CopyHFSMeta / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <fcntl.h>
6 #include <err.h>
7 #include <errno.h>
8 #include <sys/stat.h>
9 #include <sys/disk.h>
10 #include <sys/sysctl.h>
11 #include <hfs/hfs_mount.h>
12 #include <Block.h>
13 #include "hfsmeta.h"
14 #include "Data.h"
15 #include "Sparse.h"
16
17 /*
18 * Used to automatically run a corruption program after the
19 * copying is done. Only used during development. Uncomment
20 * to use.
21 */
22 //#define TESTINJECT 1
23
24 static const char *kAppleInternal = "/AppleInternal";
25 static const char *kTestProgram = "HC-Inject-Errors";
26
27 int verbose;
28 int debug;
29 int printProgress;
30
31
32 /*
33 * Compare two volume headers to see if they're the same. Some fields
34 * we may not care about, so we only compare specific fields. Note that
35 * since we're looking for equality, we don't need to byte swap.
36 * (The function CompareVolumeHeaders() will compare the two volume
37 * headers to see if the extents they describe are the same.)
38 */
39 static int
40 CheckVolumeHeaders(HFSPlusVolumeHeader *left, HFSPlusVolumeHeader *right)
41 {
42 if (left->signature != right->signature ||
43 left->version != right->version ||
44 left->modifyDate != right->modifyDate ||
45 left->fileCount != right->fileCount ||
46 left->folderCount != right->folderCount ||
47 left->nextAllocation != right->nextAllocation ||
48 left->nextCatalogID != right->nextCatalogID ||
49 left->writeCount != right->writeCount)
50 return 0;
51 return 1;
52 }
53
54 static void
55 usage(const char *progname)
56 {
57
58 errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r <bytes>] <src device> <destination>", progname);
59 }
60
61
62 main(int ac, char **av)
63 {
64 char *src = NULL;
65 char *dst = NULL;
66 DeviceInfo_t *devp = NULL;
67 VolumeDescriptor_t *vdp = NULL;
68 VolumeObjects_t *vop = NULL;
69 IOWrapper_t *wrapper = NULL;
70 int ch;
71 off_t restart = 0;
72 int printEstimate = 0;
73 const char *progname = av[0];
74 char *gather = NULL;
75 int force = 0;
76 int retval = kGoodExit;
77 int find_all_metadata = 0;
78
79 while ((ch = getopt(ac, av, "fvdg:Spr:CA")) != -1) {
80 switch (ch) {
81 case 'A': find_all_metadata = 1; break;
82 case 'v': verbose++; break;
83 case 'd': debug++; verbose++; break;
84 case 'S': printEstimate = 1; break;
85 case 'p': printProgress = 1; break;
86 case 'r': restart = strtoull(optarg, NULL, 0); break;
87 case 'g': gather = strdup(optarg); break;
88 case 'f': force = 1; break;
89 default: usage(progname);
90 }
91 }
92
93 ac -= optind;
94 av += optind;
95
96 if (ac == 0 || ac > 2) {
97 usage(progname);
98 }
99 src = av[0];
100 if (ac == 2)
101 dst = av[1];
102
103 // Start by opening the input device
104 devp = OpenDevice(src, 1);
105 if (devp == NULL) {
106 errx(kBadExit, "cannot get device information for %s", src);
107 }
108
109 // Get the volume information.
110 vdp = VolumeInfo(devp);
111
112 // Start creating the in-core volume list
113 vop = InitVolumeObject(devp, vdp);
114
115 // Add the volume headers
116 if (AddHeaders(vop, 0) == 0) {
117 errx(kBadExit, "Invalid volume header(s) for %s", src);
118 }
119 // Add the journal and file extents
120 AddJournal(vop);
121 AddFileExtents(vop);
122
123 /*
124 * find_all_metadata requires scanning through
125 * the catalog and attributes files, looking for
126 * other bits treated as metadata.
127 */
128 if (find_all_metadata)
129 FindOtherMetadata(vop, ^(int fid, off_t start, off_t len) {
130 AddExtentForFile(vop, start, len, fid);
131 // fprintf(stderr, "AddExtentForFile(%p, %llu, %llu, %u)\n", vop, start, len, fid);
132 return 0;
133 });
134
135 if (debug)
136 PrintVolumeObject(vop);
137
138 if (printEstimate) {
139 printf("Estimate %llu\n", vop->byteCount);
140 }
141
142 // Create a gatherHFS-compatible file, if requested.
143 if (gather) {
144 WriteGatheredData(gather, vop);
145 }
146
147 /*
148 * If we're given a destination, initialize it.
149 */
150 if (dst) {
151 wrapper = InitSparseBundle(dst, devp);
152 }
153
154 if (wrapper) {
155 // See if we're picking up from a previous copy
156 if (restart == 0) {
157 restart = wrapper->getprog(wrapper);
158 if (debug) {
159 fprintf(stderr, "auto-restarting at offset %lld\n", restart);
160 }
161 }
162 // "force" in this case means try even if the space estimate says we won't succeed.
163 if (force == 0) {
164 struct statfs sfs;
165 if (statfs(dst, &sfs) != -1) {
166 off_t freeSpace = (off_t)sfs.f_bsize * (off_t)sfs.f_bfree;
167 if (freeSpace < (vop->byteCount - restart)) {
168 errx(kNoSpaceExit, "free space (%lld) < required space (%lld)", freeSpace, vop->byteCount - restart);
169 }
170 }
171 }
172
173 /*
174 * If we're restarting, we need to compare the volume headers and see if
175 * they're the same. If they're not, we need to start from the beginning.
176 */
177 if (restart) {
178 HFSPlusVolumeHeader priHeader, altHeader;
179
180 if (wrapper->reader(wrapper, 1024, &priHeader, sizeof(priHeader)) != -1) {
181 if (CheckVolumeHeaders(&priHeader, &vop->vdp->priHeader) == 0) {
182 restart = 0;
183 } else {
184 if (wrapper->reader(wrapper, vop->vdp->altOffset, &altHeader, sizeof(altHeader)) != -1) {
185 if (CheckVolumeHeaders(&altHeader, &vop->vdp->altHeader) == 0) {
186 restart = 0;
187 }
188 }
189 }
190 }
191 if (restart == 0) {
192 if (verbose)
193 warnx("Destination volume does not match source, starting from beginning");
194 }
195 }
196
197 // And start copying the objects.
198 if (CopyObjectsToDest(vop, wrapper, restart) == -1) {
199 if (errno == EIO)
200 retval = kCopyIOExit;
201 else if (errno == EINTR)
202 retval = kIntrExit;
203 else
204 retval = kBadExit;
205 err(retval, "CopyObjectsToDest failed");
206 } else {
207 #if TESTINJECT
208 // Copy finished, let's see if we should run a test program
209 if (access(kAppleInternal, 0) != -1) {
210 char *home = getenv("HOME");
211 if (home) {
212 char *pName;
213 pName = malloc(strlen(home) + strlen(kTestProgram) + 2); // '/' and NUL
214 if (pName) {
215 sprintf(pName, "%s/%s", home, kTestProgram);
216 execl(pName, kTestProgram, dst, NULL);
217 }
218 }
219 }
220 #endif
221 }
222 }
223
224 return retval;
225 }
226