10 #include <sys/sysctl.h>
11 #include <hfs/hfs_mount.h>
18 * Used to automatically run a corruption program after the
19 * copying is done. Only used during development. Uncomment
22 //#define TESTINJECT 1
24 static const char *kAppleInternal
= "/AppleInternal";
25 static const char *kTestProgram
= "HC-Inject-Errors";
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.)
40 CheckVolumeHeaders(HFSPlusVolumeHeader
*left
, HFSPlusVolumeHeader
*right
)
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
)
55 usage(const char *progname
)
58 errx(kBadExit
, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r <bytes>] <src device> <destination>", progname
);
62 main(int ac
, char **av
)
66 DeviceInfo_t
*devp
= NULL
;
67 VolumeDescriptor_t
*vdp
= NULL
;
68 VolumeObjects_t
*vop
= NULL
;
71 int printEstimate
= 0;
72 const char *progname
= av
[0];
75 int retval
= kGoodExit
;
76 int find_all_metadata
= 0;
78 while ((ch
= getopt(ac
, av
, "fvdg:Spr:CA")) != -1) {
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
);
95 if (ac
== 0 || ac
> 2) {
102 // Start by opening the input device
103 devp
= OpenDevice(src
, 1);
105 errx(kBadExit
, "cannot get device information for %s", src
);
108 // Get the volume information.
109 vdp
= VolumeInfo(devp
);
111 // Start creating the in-core volume list
112 vop
= InitVolumeObject(devp
, vdp
);
114 // Add the volume headers
115 if (AddHeaders(vop
, 0) == 0) {
116 errx(kBadExit
, "Invalid volume header(s) for %s", src
);
118 // Add the journal and file extents
123 * find_all_metadata requires scanning through
124 * the catalog and attributes files, looking for
125 * other bits treated as metadata.
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);
135 PrintVolumeObject(vop
);
138 printf("Estimate %llu\n", vop
->byteCount
);
141 // Create a gatherHFS-compatible file, if requested.
143 WriteGatheredData(gather
, vop
);
147 * If we're given a destination, initialize it.
150 IOWrapper_t
*wrapper
= InitSparseBundle(dst
, devp
);
152 if (wrapper
== NULL
) {
153 err(kBadExit
, "cannot initialize destination container %s", dst
);
156 // See if we're picking up from a previous copy
158 restart
= wrapper
->getprog(wrapper
);
160 fprintf(stderr
, "auto-restarting at offset %lld\n", restart
);
163 // "force" in this case means try even if the space estimate says we won't succeed.
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
);
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.
179 HFSPlusVolumeHeader priHeader
, altHeader
;
181 if (wrapper
->reader(wrapper
, 1024, &priHeader
, sizeof(priHeader
)) != -1) {
182 if (CheckVolumeHeaders(&priHeader
, &vop
->vdp
->priHeader
) == 0) {
185 if (wrapper
->reader(wrapper
, vop
->vdp
->altOffset
, &altHeader
, sizeof(altHeader
)) != -1) {
186 if (CheckVolumeHeaders(&altHeader
, &vop
->vdp
->altHeader
) == 0) {
194 warnx("Destination volume does not match source, starting from beginning");
198 // And start copying the objects.
199 if (CopyObjectsToDest(vop
, wrapper
, restart
) == -1) {
201 retval
= kCopyIOExit
;
202 else if (errno
== EINTR
)
206 err(retval
, "CopyObjectsToDest failed");
209 // Copy finished, let's see if we should run a test program
210 if (access(kAppleInternal
, 0) != -1) {
211 char *home
= getenv("HOME");
214 pName
= malloc(strlen(home
) + strlen(kTestProgram
) + 2); // '/' and NUL
216 sprintf(pName
, "%s/%s", home
, kTestProgram
);
217 execl(pName
, kTestProgram
, dst
, NULL
);