int main(int argc, char **argv) { struct stat sb; int opt, i = 0; const char *input_filename = NULL; int use_input_file = 0; char *buf = NULL; size_t buf_len; int recurse; /* Recursive descent. */ const char *base; int mass_relabel = 0, errors = 0; const char *ropts = "e:f:hilno:pqrsvFRW0"; const char *sopts = "c:de:f:hilno:pqr:svFR:W0"; const char *opts; memset(&r_opts, 0, sizeof(r_opts)); /* Initialize variables */ r_opts.progress = 0; r_opts.count = 0; r_opts.nfile = 0; r_opts.debug = 0; r_opts.change = 1; r_opts.verbose = 0; r_opts.logging = 0; r_opts.rootpath = NULL; r_opts.rootpathlen = 0; r_opts.outfile = NULL; r_opts.force = 0; r_opts.hard_links = 1; altpath = NULL; r_opts.progname = strdup(argv[0]); if (!r_opts.progname) { fprintf(stderr, "%s: Out of memory!\n", argv[0]); exit(-1); } base = basename(r_opts.progname); if (!strcmp(base, SETFILES)) { /* * setfiles: * Recursive descent, * Does not expand paths via realpath, * Aborts on errors during the file tree walk, * Try to track inode associations for conflict detection, * Does not follow mounts, * Validates all file contexts at init time. */ iamrestorecon = 0; recurse = 1; r_opts.expand_realpath = 0; r_opts.abort_on_error = 1; r_opts.add_assoc = 1; r_opts.fts_flags = FTS_PHYSICAL | FTS_XDEV; ctx_validate = 1; opts = sopts; } else { /* * restorecon: * No recursive descent unless -r/-R, * Expands paths via realpath, * Do not abort on errors during the file tree walk, * Do not try to track inode associations for conflict detection, * Follows mounts, * Does lazy validation of contexts upon use. */ if (strcmp(base, RESTORECON) && !r_opts.quiet) printf("Executed with an unrecognized name (%s), defaulting to %s behavior.\n", base, RESTORECON); iamrestorecon = 1; recurse = 0; r_opts.expand_realpath = 1; r_opts.abort_on_error = 0; r_opts.add_assoc = 0; r_opts.fts_flags = FTS_PHYSICAL; ctx_validate = 0; opts = ropts; /* restorecon only: silent exit if no SELinux. Allows unconditional execution by scripts. */ if (is_selinux_enabled() <= 0) exit(0); } /* This must happen before getopt. */ r_opts.nfile = exclude_non_seclabel_mounts(); if (iamrestorecon) opts = ropts; else opts = sopts; /* Process any options. */ while ((opt = getopt(argc, argv, opts)) > 0) { switch (opt) { case 'c': { FILE *policystream; if (iamrestorecon) usage(argv[0]); policyfile = optarg; policystream = fopen(policyfile, "r"); if (!policystream) { fprintf(stderr, "Error opening %s: %s\n", policyfile, strerror(errno)); exit(-1); } __fsetlocking(policystream, FSETLOCKING_BYCALLER); if (sepol_set_policydb_from_file(policystream) < 0) { fprintf(stderr, "Error reading policy %s: %s\n", policyfile, strerror(errno)); exit(-1); } fclose(policystream); ctx_validate = 1; break; } case 'e': remove_exclude(optarg); if (lstat(optarg, &sb) < 0 && errno != EACCES) { fprintf(stderr, "Can't stat exclude path \"%s\", %s - ignoring.\n", optarg, strerror(errno)); break; } if (add_exclude(optarg)) exit(-1); break; case 'f': use_input_file = 1; input_filename = optarg; break; case 'd': if (iamrestorecon) usage(argv[0]); r_opts.debug = 1; break; case 'i': r_opts.ignore_enoent = 1; break; case 'l': r_opts.logging = 1; break; case 'F': r_opts.force = 1; break; case 'n': r_opts.change = 0; break; case 'o': if (strcmp(optarg, "-") == 0) { r_opts.outfile = stdout; break; } r_opts.outfile = fopen(optarg, "w"); if (!r_opts.outfile) { fprintf(stderr, "Error opening %s: %s\n", optarg, strerror(errno)); usage(argv[0]); } __fsetlocking(r_opts.outfile, FSETLOCKING_BYCALLER); break; case 'q': r_opts.quiet = 1; break; case 'R': case 'r': if (iamrestorecon) { recurse = 1; break; } if (NULL != r_opts.rootpath) { fprintf(stderr, "%s: only one -r can be specified\n", argv[0]); exit(-1); } set_rootpath(optarg); break; case 's': use_input_file = 1; input_filename = "-"; r_opts.add_assoc = 0; break; case 'v': if (r_opts.progress) { fprintf(stderr, "Progress and Verbose mutually exclusive\n"); usage(argv[0]); } r_opts.verbose++; break; case 'p': if (r_opts.verbose) { fprintf(stderr, "Progress and Verbose mutually exclusive\n"); usage(argv[0]); } r_opts.progress++; break; case 'W': warn_no_match = 1; break; case '0': null_terminated = 1; break; case 'h': case '?': usage(argv[0]); } } for (i = optind; i < argc; i++) { if (!strcmp(argv[i], "/")) { mass_relabel = 1; if (r_opts.progress) r_opts.progress++; } } if (!iamrestorecon) { if (policyfile) { if (optind != (argc - 1)) usage(argv[0]); } else if (use_input_file) { if (optind != (argc - 1)) { /* Cannot mix with pathname arguments. */ usage(argv[0]); } } else { if (optind > (argc - 2)) usage(argv[0]); } /* Use our own invalid context checking function so that we can support either checking against the active policy or checking against a binary policy file. */ selinux_set_callback(SELINUX_CB_VALIDATE, (union selinux_callback)&canoncon); if (stat(argv[optind], &sb) < 0) { perror(argv[optind]); exit(-1); } if (!S_ISREG(sb.st_mode)) { fprintf(stderr, "%s: spec file %s is not a regular file.\n", argv[0], argv[optind]); exit(-1); } altpath = argv[optind]; optind++; } else if (argc == 1) usage(argv[0]); /* Load the file contexts configuration and check it. */ r_opts.selabel_opt_validate = (ctx_validate ? (char *)1 : NULL); r_opts.selabel_opt_path = altpath; if (nerr) exit(-1); restore_init(&r_opts); if (use_input_file) { FILE *f = stdin; ssize_t len; int delim; if (strcmp(input_filename, "-") != 0) f = fopen(input_filename, "r"); if (f == NULL) { fprintf(stderr, "Unable to open %s: %s\n", input_filename, strerror(errno)); usage(argv[0]); } __fsetlocking(f, FSETLOCKING_BYCALLER); delim = (null_terminated != 0) ? '\0' : '\n'; while ((len = getdelim(&buf, &buf_len, delim, f)) > 0) { buf[len - 1] = 0; if (!strcmp(buf, "/")) mass_relabel = 1; errors |= process_glob(buf, recurse) < 0; } if (strcmp(input_filename, "-") != 0) fclose(f); } else { for (i = optind; i < argc; i++) errors |= process_glob(argv[i], recurse) < 0; } maybe_audit_mass_relabel(mass_relabel, errors); if (warn_no_match) selabel_stats(r_opts.hnd); selabel_close(r_opts.hnd); restore_finish(); if (r_opts.outfile) fclose(r_opts.outfile); if (r_opts.progress && r_opts.count >= STAR_COUNT) printf("\n"); exit(errors ? -1: 0); }
int copy_main (int argc, char **argv) { char source_a[PATH_MAX], source_b[PATH_MAX]; char dest_a[PATH_MAX], dest_b[PATH_MAX]; char *tmp; struct backup_info *info_b, *info_r; int opt, loop, thresh = 0, threshopt = 0; unsigned int varsize = 0, swapsize = 0; unsigned int bflags = BF_BACKUPVAR, rflags = 0; int quiet = 0; int bswap = 0; int expand = 0; int expandscale = 2; tivo_partition_direct (); while ((opt = getopt (argc, argv, "hqf:L:tTaspxr:v:S:lbBzE")) > 0) { switch (opt) { case 'q': quiet++; break; case 's': bflags |= BF_SHRINK; break; case 'E': bflags |= BF_TRUNCATED; break; case 'f': if (threshopt) { fprintf (stderr, "%s: -f and -%c cannot be used together\n", argv[0], threshopt); return 1; } threshopt = loop; thresh = strtoul (optarg, &tmp, 10); if (*tmp) { fprintf (stderr, "%s: Non integer argument to -f\n", argv[0]); return 1; } break; case 'L': if (threshopt) { fprintf (stderr, "%s: -l and -%c cannot be used together\n", argv[0], threshopt); return 1; } threshopt = loop; thresh = strtoul (optarg, &tmp, 10); thresh *= 1024 * 2; bflags |= BF_THRESHSIZE; if (*tmp) { fprintf (stderr, "%s: Non integer argument to -L\n", argv[0]); return 1; } break; case 't': bflags |= BF_THRESHTOT; break; case 'T': bflags |= BF_STREAMTOT; break; case 'a': if (threshopt) { fprintf (stderr, "%s: -a and -%c cannot be used together\n", argv[0], threshopt); return 1; } threshopt = loop; thresh = ~0; break; case 'v': bflags &= ~BF_BACKUPVAR; varsize = strtoul (optarg, &tmp, 10); varsize *= 1024 * 2; if (tmp && *tmp) { fprintf (stderr, "%s: Integer argument expected for -v.\n", argv[0]); return 1; } break; case 'S': swapsize = strtoul (optarg, &tmp, 10); swapsize *= 1024 * 2; if (tmp && *tmp) { fprintf (stderr, "%s: Integer argument expected for -s.\n", argv[0]); return 1; } break; case 'z': rflags |= RF_ZEROPART; break; case 'b': if (bswap != 0) { fprintf (stderr, "%s: Only one byte swapping option (-b/-B) allowed.\n", argv[0]); return 1; } bswap = -1; break; case 'B': if (bswap != 0) { fprintf (stderr, "%s: Only one byte swapping option (-b/-B) allowed.\n", argv[0]); return 1; } bswap = 1; break; case 'p': rflags |= RF_BALANCE; break; case 'l': rflags |= RF_NOFILL; break; case 'x': expand = 1; break; case 'r': expandscale = strtoul (optarg, &tmp, 10); if (tmp && *tmp) { fprintf (stderr, "%s: Integer argument expected for -r.\n", argv[0]); return 1; } if (expandscale < 0 || expandscale > 4) { fprintf (stderr, "%s: Scale value for -r must be in the range 0 to 4.\n", argv[0]); return 1; } break; default: copy_usage (argv[0]); return 1; } } // Split out the drive names source_a[0] = 0; source_b[0] = 0; dest_a[0] = 0; dest_b[0] = 0; if (argc - optind < 4) { if (optind < argc) { get_drives (argv[optind++], source_a, source_b); } if (optind < argc) { get_drives (argv[optind++], dest_a, dest_b); } } else { // Special case for convenience - 2 source and 2 target named strcpy (source_a, argv[optind++]); strcpy (source_b, argv[optind++]); strcpy (dest_a, argv[optind++]); strcpy (dest_b, argv[optind++]); } if (optind < argc || !*source_a || !*dest_a) { copy_usage (argv[0]); return 1; } if (expand > 0) rflags |= RF_NOFILL; info_b = init_backup (source_a, source_b, bflags); // Try to continue anyway despite error. if (bflags & BF_TRUNCATED && backup_has_error (info_b)) { backup_perror (info_b, "WARNING"); fprintf (stderr, "Attempting copy anyway\n"); backup_check_truncated_volume (info_b); if (info_b && backup_has_error (info_b)) { backup_perror (info_b, "Copy source"); return 1; } } if (info_b && backup_has_error (info_b)) { backup_perror (info_b, "Copy source"); fprintf (stderr, "To attempt copy anyway, try again with -E. -s is implied by -E.\n"); return 1; } info_r = init_restore (rflags); if (info_r && restore_has_error (info_r)) { restore_perror (info_r, "Copy target"); return 1; } if (!info_b || !info_r) { fprintf (stderr, "%s: Copy failed to start. Make sure you specified the right\ndevices, and that the drives are not locked.\n", argv[0]); return 1; } else { unsigned starttime; char buf[BUFSIZE]; unsigned int curcount = 0; int nread, nwrit; if (threshopt) backup_set_thresh (info_b, thresh); if (varsize) restore_set_varsize (info_r, varsize); if (swapsize) restore_set_swapsize (info_r, swapsize); if (bswap) restore_set_bswap (info_r, bswap); if (quiet < 2) fprintf (stderr, "Scanning source drive. Please wait a moment.\n"); if (backup_start (info_b) < 0) { if (backup_has_error (info_b)) backup_perror (info_b, "Copy source"); else fprintf (stderr, "Copy source failed.\n"); return 1; } // Fill the buffer up to start. Restore needs some information to bootstrap // the process. while (curcount < BUFSIZE && (nread = backup_read (info_b, buf, BUFSIZE - curcount)) > 0) { curcount += nread; } if (curcount < BUFSIZE) { if (backup_has_error (info_b)) backup_perror (info_b, "Copy source"); else fprintf (stderr, "Copy source failed.\n"); return 1; } nread = curcount; nwrit = restore_write (info_r, buf, nread); if (nwrit < 0) { if (restore_has_error (info_r)) restore_perror (info_r, "Copy target"); else fprintf (stderr, "Copy target failed.\n"); return 1; } if (restore_trydev (info_r, dest_a, dest_b) < 0) { if (restore_has_error (info_r)) restore_perror (info_r, "Copy target"); else fprintf (stderr, "Copy target failed.\n"); return 1; } if (restore_start (info_r) < 0) { if (restore_has_error (info_r)) restore_perror (info_r, "Copy target"); else fprintf (stderr, "Copy target failed.\n"); return 1; } if (restore_write (info_r, buf + nwrit, nread - nwrit) != nread - nwrit) { if (restore_has_error (info_r)) restore_perror (info_r, "Copy target"); else fprintf (stderr, "Copy target failed.\n"); return 1; } starttime = time (NULL); fprintf (stderr, "Starting copy\nSize: %d megabytes\n", info_r->nsectors / 2048); while ((curcount = backup_read (info_b, buf, BUFSIZE)) > 0) { unsigned int prcnt, compr; if (restore_write (info_r, buf, curcount) != curcount) { if (quiet < 1) fprintf (stderr, "\n"); if (restore_has_error (info_r)) restore_perror (info_r, "Copy source"); else fprintf (stderr, "Copy source failed.\n"); return 1; } prcnt = get_percent (info_r->cursector, info_r->nsectors); if (quiet < 1) { unsigned timedelta = time(NULL) - starttime; fprintf (stderr, "\rCopying %d of %d mb (%d.%02d%%)", info_r->cursector / 2048, info_r->nsectors / 2048, prcnt / 100, prcnt % 100); if (prcnt > 100 && timedelta > 15) { unsigned ETA = timedelta * (10000 - prcnt) / prcnt; fprintf (stderr, " %d mb/sec (ETA %d:%02d:%02d)", info_r->cursector / timedelta / 2048, ETA / 3600, ETA / 60 % 60, ETA % 60); } } } if (quiet < 1) fprintf (stderr, "\n"); if (backup_has_error (info_b)) { backup_perror (info_b, "Copy source"); return 1; } if (restore_has_error (info_r)) { restore_perror (info_r, "Copy target"); return 1; } } if (backup_finish (info_b) < 0) { if (backup_has_error (info_b)) backup_perror (info_b, "Copy source"); else fprintf (stderr, "Copy source failed.\n"); return 1; } if (info_b->back_flags & BF_TRUNCATED) { fprintf (stderr, "***WARNING***\nCopy was made of an incomplete volume. While the copy succeeded,\nit is possible there was some required data missing. Verify your copy.\n"); } if (quiet < 2) fprintf (stderr, "Cleaning up target. Please wait a moment.\n"); if (restore_finish (info_r) < 0) { if (restore_has_error (info_r)) restore_perror (info_r, "Copy target"); else fprintf (stderr, "Copy target failed.\n"); return 1; } if (quiet < 2) fprintf (stderr, "Copy done!\n"); if (expand > 0) { int blocksize = 0x800; struct mfs_handle *mfshnd; expand = 0; mfshnd = mfs_init (dest_a, dest_b, O_RDWR); if (!mfshnd) { fprintf (stderr, "Drive expansion failed.\n"); return 1; } if (mfs_has_error (mfshnd)) { mfs_perror (mfshnd, "Target expand"); return 1; } while (expandscale-- > 0) blocksize *= 2; if (tivo_partition_largest_free (dest_a) > 1024 * 1024 * 2) { if (expand_drive (mfshnd, "/dev/hda", dest_a, blocksize) < 0) { if (mfs_has_error (mfshnd)) mfs_perror (mfshnd, "Expand drive A"); else fprintf (stderr, "Drive A expansion failed.\n"); return 1; } expand++; } if (dest_b[0] && tivo_partition_largest_free (dest_b) > 1024 * 1024 * 2) { if (expand_drive (mfshnd, "/dev/hdb", dest_b, blocksize) < 0) { if (mfs_has_error (mfshnd)) mfs_perror (mfshnd, "Expand drive B"); else fprintf (stderr, "Drive B expansion failed.\n"); return 1; } expand++; } if (!expand) { fprintf (stderr, "Not enough extra space to expand on A drive%s.\n", dest_b[0]? " or B drive": ""); } } return 0; }