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 24static const char *kAppleInternal = "/AppleInternal"; 25static const char *kTestProgram = "HC-Inject-Errors"; 26 27int verbose; 28int debug; 29int 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 */ 39static int 40CheckVolumeHeaders(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 54static void 55usage(const char *progname) 56{ 57 58 errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r <bytes>] <src device> <destination>", progname); 59} 60 61 62main(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 227