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
;
69 IOWrapper_t
*wrapper
= NULL
;
72 int printEstimate
= 0;
73 const char *progname
= av
[0];
76 int retval
= kGoodExit
;
77 int find_all_metadata
= 0;
79 while ((ch
= getopt(ac
, av
, "fvdg:Spr:CA")) != -1) {
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
);
96 if (ac
== 0 || ac
> 2) {
103 // Start by opening the input device
104 devp
= OpenDevice(src
, 1);
106 errx(kBadExit
, "cannot get device information for %s", src
);
109 // Get the volume information.
110 vdp
= VolumeInfo(devp
);
112 // Start creating the in-core volume list
113 vop
= InitVolumeObject(devp
, vdp
);
115 // Add the volume headers
116 if (AddHeaders(vop
, 0) == 0) {
117 errx(kBadExit
, "Invalid volume header(s) for %s", src
);
119 // Add the journal and file extents
124 * find_all_metadata requires scanning through
125 * the catalog and attributes files, looking for
126 * other bits treated as metadata.
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);
136 PrintVolumeObject(vop
);
139 printf("Estimate %llu\n", vop
->byteCount
);
142 // Create a gatherHFS-compatible file, if requested.
144 WriteGatheredData(gather
, vop
);
148 * If we're given a destination, initialize it.
151 wrapper
= InitSparseBundle(dst
, devp
);
155 // See if we're picking up from a previous copy
157 restart
= wrapper
->getprog(wrapper
);
159 fprintf(stderr
, "auto-restarting at offset %lld\n", restart
);
162 // "force" in this case means try even if the space estimate says we won't succeed.
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
);
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.
178 HFSPlusVolumeHeader priHeader
, altHeader
;
180 if (wrapper
->reader(wrapper
, 1024, &priHeader
, sizeof(priHeader
)) != -1) {
181 if (CheckVolumeHeaders(&priHeader
, &vop
->vdp
->priHeader
) == 0) {
184 if (wrapper
->reader(wrapper
, vop
->vdp
->altOffset
, &altHeader
, sizeof(altHeader
)) != -1) {
185 if (CheckVolumeHeaders(&altHeader
, &vop
->vdp
->altHeader
) == 0) {
193 warnx("Destination volume does not match source, starting from beginning");
197 // And start copying the objects.
198 if (CopyObjectsToDest(vop
, wrapper
, restart
) == -1) {
200 retval
= kCopyIOExit
;
201 else if (errno
== EINTR
)
205 err(retval
, "CopyObjectsToDest failed");
208 // Copy finished, let's see if we should run a test program
209 if (access(kAppleInternal
, 0) != -1) {
210 char *home
= getenv("HOME");
213 pName
= malloc(strlen(home
) + strlen(kTestProgram
) + 2); // '/' and NUL
215 sprintf(pName
, "%s/%s", home
, kTestProgram
);
216 execl(pName
, kTestProgram
, dst
, NULL
);