]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/main.c
hfs-366.50.19.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 int
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 int ch;
70 off_t restart = 0;
71 int printEstimate = 0;
72 const char *progname = av[0];
73 char *gather = NULL;
74 int force = 0;
75 int retval = kGoodExit;
76 int find_all_metadata = 0;
77
78 while ((ch = getopt(ac, av, "fvdg:Spr:CA")) != -1) {
79 switch (ch) {
80 case 'A': find_all_metadata = 1; break;
81 case 'v': verbose++; break;
82 case 'd': debug++; verbose++; break;
83 case 'S': printEstimate = 1; break;
84 case 'p': printProgress = 1; break;
85 case 'r': restart = strtoull(optarg, NULL, 0); break;
86 case 'g': gather = strdup(optarg); break;
87 case 'f': force = 1; break;
88 default: usage(progname);
89 }
90 }
91
92 ac -= optind;
93 av += optind;
94
95 if (ac == 0 || ac > 2) {
96 usage(progname);
97 }
98 src = av[0];
99 if (ac == 2)
100 dst = av[1];
101
102 // Start by opening the input device
103 devp = OpenDevice(src, 1);
104 if (devp == NULL) {
105 errx(kBadExit, "cannot get device information for %s", src);
106 }
107
108 // Get the volume information.
109 vdp = VolumeInfo(devp);
110
111 // Start creating the in-core volume list
112 vop = InitVolumeObject(devp, vdp);
113
114 // Add the volume headers
115 if (AddHeaders(vop, 0) == 0) {
116 errx(kBadExit, "Invalid volume header(s) for %s", src);
117 }
118 // Add the journal and file extents
119 AddJournal(vop);
120 AddFileExtents(vop);
121
122 /*
123 * find_all_metadata requires scanning through
124 * the catalog and attributes files, looking for
125 * other bits treated as metadata.
126 */
127 if (find_all_metadata)
128 FindOtherMetadata(vop, ^(int fid, off_t start, off_t len) {
129 AddExtentForFile(vop, start, len, fid);
130 // fprintf(stderr, "AddExtentForFile(%p, %llu, %llu, %u)\n", vop, start, len, fid);
131 return 0;
132 });
133
134 if (debug)
135 PrintVolumeObject(vop);
136
137 if (printEstimate) {
138 printf("Estimate %llu\n", vop->byteCount);
139 }
140
141 // Create a gatherHFS-compatible file, if requested.
142 if (gather) {
143 WriteGatheredData(gather, vop);
144 }
145
146 /*
147 * If we're given a destination, initialize it.
148 */
149 if (dst) {
150 IOWrapper_t *wrapper = InitSparseBundle(dst, devp);
151
152 if (wrapper == NULL) {
153 err(kBadExit, "cannot initialize destination container %s", dst);
154 }
155
156 // See if we're picking up from a previous copy
157 if (restart == 0) {
158 restart = wrapper->getprog(wrapper);
159 if (debug) {
160 fprintf(stderr, "auto-restarting at offset %lld\n", restart);
161 }
162 }
163 // "force" in this case means try even if the space estimate says we won't succeed.
164 if (force == 0) {
165 struct statfs sfs;
166 if (statfs(dst, &sfs) != -1) {
167 off_t freeSpace = (off_t)sfs.f_bsize * (off_t)sfs.f_bfree;
168 if (freeSpace < (vop->byteCount - restart)) {
169 errx(kNoSpaceExit, "free space (%lld) < required space (%lld)", freeSpace, vop->byteCount - restart);
170 }
171 }
172 }
173
174 /*
175 * If we're restarting, we need to compare the volume headers and see if
176 * they're the same. If they're not, we need to start from the beginning.
177 */
178 if (restart) {
179 HFSPlusVolumeHeader priHeader, altHeader;
180
181 if (wrapper->reader(wrapper, 1024, &priHeader, sizeof(priHeader)) != -1) {
182 if (CheckVolumeHeaders(&priHeader, &vop->vdp->priHeader) == 0) {
183 restart = 0;
184 } else {
185 if (wrapper->reader(wrapper, vop->vdp->altOffset, &altHeader, sizeof(altHeader)) != -1) {
186 if (CheckVolumeHeaders(&altHeader, &vop->vdp->altHeader) == 0) {
187 restart = 0;
188 }
189 }
190 }
191 }
192 if (restart == 0) {
193 if (verbose)
194 warnx("Destination volume does not match source, starting from beginning");
195 }
196 }
197
198 // And start copying the objects.
199 if (CopyObjectsToDest(vop, wrapper, restart) == -1) {
200 if (errno == EIO)
201 retval = kCopyIOExit;
202 else if (errno == EINTR)
203 retval = kIntrExit;
204 else
205 retval = kBadExit;
206 err(retval, "CopyObjectsToDest failed");
207 } else {
208 #if TESTINJECT
209 // Copy finished, let's see if we should run a test program
210 if (access(kAppleInternal, 0) != -1) {
211 char *home = getenv("HOME");
212 if (home) {
213 char *pName;
214 pName = malloc(strlen(home) + strlen(kTestProgram) + 2); // '/' and NUL
215 if (pName) {
216 sprintf(pName, "%s/%s", home, kTestProgram);
217 execl(pName, kTestProgram, dst, NULL);
218 }
219 }
220 }
221 #endif
222 }
223 }
224
225 return retval;
226 }
227