static void confirm_or_abort(void) { fprintf(stderr, "Continue? [y/N] "); fflush_all(); if (!bb_ask_confirmation()) bb_error_msg_and_die("aborting"); }
static int retry_overwrite(const char *dest, int flags) { if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { fprintf(stderr, "'%s' exists\n", dest); return -1; } if (flags & FILEUTILS_INTERACTIVE) { fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); if (!bb_ask_confirmation()) return 0; // not allowed to overwrite } if (unlink(dest) < 0) { bb_perror_msg("cannot remove '%s'", dest); return -1; // error } return 1; // ok (to try again) }
/* Called if open of destination, link creation etc fails. * errno must be set to relevant value ("why we cannot create dest?") * to give reasonable error message */ static int ask_and_unlink(const char *dest, int flags) { int e = errno; #if !ENABLE_FEATURE_NON_POSIX_CP if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { /* Either it exists, or the *path* doesnt exist */ bb_perror_msg("can't create '%s'", dest); return -1; } #endif // else: act as if -f is always in effect. // We don't want "can't create" msg, we want unlink to be done // (silently unless -i). Why? POSIX cp usually succeeds with // O_TRUNC open of existing file, and user is left ignorantly happy. // With above block unconditionally enabled, non-POSIX cp // will complain a lot more than POSIX one. /* TODO: maybe we should do it only if ctty is present? */ if (flags & FILEUTILS_INTERACTIVE) { // We would not do POSIX insanity. -i asks, // then _unlinks_ the offender. Presto. // (No "opening without O_EXCL", no "unlink only if -f") // Or else we will end up having 3 open()s! fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); if (!bb_ask_confirmation()) return 0; /* not allowed to overwrite */ } if (unlink(dest) < 0) { #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE if (e == errno && e == ENOENT) { /* e == ENOTDIR is similar: path has non-dir component, * but in this case we don't even reach copy_file() */ bb_error_msg("can't create '%s': Path does not exist", dest); return -1; /* error */ } #endif errno = e; /* do not use errno from unlink */ bb_perror_msg("can't create '%s'", dest); return -1; /* error */ } return 1; /* ok (to try again) */ }
// errno must be set to relevant value ("why we cannot create dest?") // for POSIX mode to give reasonable error message static int ask_and_unlink(const char *dest, int flags) { int e = errno; #if DO_POSIX_CP if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { // Either it exists, or the *path* doesnt exist bb_perror_msg("cannot create '%s'", dest); return -1; } #endif // If !DO_POSIX_CP, act as if -f is always in effect - we don't want // "cannot create" msg, we want unlink to be done (silently unless -i). // TODO: maybe we should do it only if ctty is present? if (flags & FILEUTILS_INTERACTIVE) { // We would not do POSIX insanity. -i asks, // then _unlinks_ the offender. Presto. // (No "opening without O_EXCL", no "unlink only if -f") // Or else we will end up having 3 open()s! fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); if (!bb_ask_confirmation()) return 0; // not allowed to overwrite } if (unlink(dest) < 0) { #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE if (e == errno && e == ENOENT) { /* e == ENOTDIR is similar: path has non-dir component, * but in this case we don't even reach copy_file() */ bb_error_msg("cannot create '%s': Path does not exist", dest); return -1; // error } #endif errno = e; bb_perror_msg("cannot create '%s'", dest); return -1; // error } return 1; // ok (to try again) }
int copy_file(const char *source, const char *dest, int flags) { struct stat source_stat; struct stat dest_stat; int dest_exists = 0; int status = 0; if ((!(flags & FILEUTILS_DEREFERENCE) && lstat(source, &source_stat) < 0) || ((flags & FILEUTILS_DEREFERENCE) && stat(source, &source_stat) < 0)) { bb_perror_msg("%s", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("unable to stat `%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino) { bb_error_msg("`%s' and `%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("%s: omitting directory", source); return -1; } /* Create DEST. */ if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("`%s' is not a directory", dest); return -1; } } else { mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("cannot create directory `%s'", dest); return -1; } umask(saved_umask); } /* Recursively copy files in SOURCE. */ if ((dp = opendir(source)) == NULL) { bb_perror_msg("unable to open directory `%s'", source); status = -1; goto end; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if(new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags) < 0) status = -1; free(new_source); free(new_dest); } /* closedir have only EBADF error, but "dp" not changes */ closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { bb_perror_msg("unable to change permissions of `%s'", dest); status = -1; } } else if (S_ISREG(source_stat.st_mode)) { int src_fd; int dst_fd = -1; #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS char *link_name; if (!(flags & FILEUTILS_DEREFERENCE) && is_in_ino_dev_hashtable(&source_stat, &link_name)) { if (link(link_name, dest) < 0) { bb_perror_msg("unable to link `%s'", dest); return -1; } return 0; } #endif src_fd = open(source, O_RDONLY); if (src_fd == -1) { bb_perror_msg("unable to open `%s'", source); return(-1); } if (dest_exists) { if (flags & FILEUTILS_INTERACTIVE) { fprintf(stderr, "%s: overwrite `%s'? ", bb_applet_name, dest); if (!bb_ask_confirmation()) { close (src_fd); return 0; } } dst_fd = open(dest, O_WRONLY|O_TRUNC); if (dst_fd == -1) { if (!(flags & FILEUTILS_FORCE)) { bb_perror_msg("unable to open `%s'", dest); close(src_fd); return -1; } if (unlink(dest) < 0) { bb_perror_msg("unable to remove `%s'", dest); close(src_fd); return -1; } dest_exists = 0; } } if (!dest_exists) { dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode); if (dst_fd == -1) { bb_perror_msg("unable to open `%s'", dest); close(src_fd); return(-1); } } if (bb_copyfd_eof(src_fd, dst_fd) == -1) status = -1; if (close(dst_fd) < 0) { bb_perror_msg("unable to close `%s'", dest); status = -1; } if (close(src_fd) < 0) { bb_perror_msg("unable to close `%s'", source); status = -1; } } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) || S_ISLNK(source_stat.st_mode)) { if (dest_exists) { if((flags & FILEUTILS_FORCE) == 0) { fprintf(stderr, "`%s' exists\n", dest); return -1; } if(unlink(dest) < 0) { bb_perror_msg("unable to remove `%s'", dest); return -1; } } } else { bb_error_msg("internal error: unrecognized file type"); return -1; } if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode)) { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("unable to create `%s'", dest); return -1; } } else if (S_ISFIFO(source_stat.st_mode)) { if (mkfifo(dest, source_stat.st_mode) < 0) { bb_perror_msg("cannot create fifo `%s'", dest); return -1; } } else if (S_ISLNK(source_stat.st_mode)) { char *lpath; lpath = xreadlink(source); if (symlink(lpath, dest) < 0) { bb_perror_msg("cannot create symlink `%s'", dest); return -1; } free(lpath); #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("unable to preserve ownership of `%s'", dest); #endif #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS add_to_ino_dev_hashtable(&source_stat, dest); #endif return 0; } #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS if (! S_ISDIR(source_stat.st_mode)) { add_to_ino_dev_hashtable(&source_stat, dest); } #endif end: if (flags & FILEUTILS_PRESERVE_STATUS) { struct utimbuf times; times.actime = source_stat.st_atime; times.modtime = source_stat.st_mtime; if (utime(dest, ×) < 0) bb_perror_msg("unable to preserve times of `%s'", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("unable to preserve ownership of `%s'", dest); } if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("unable to preserve permissions of `%s'", dest); } return status; }
int FAST_FUNC remove_file(const char *path, int flags) { struct stat path_stat; if (lstat(path, &path_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("can't stat '%s'", path); return -1; } if (!(flags & FILEUTILS_FORCE)) { bb_perror_msg("can't remove '%s'", path); return -1; } return 0; } if (S_ISDIR(path_stat.st_mode)) { DIR *dp; struct dirent *d; int status = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("%s: is a directory", path); return -1; } if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0)) || (flags & FILEUTILS_INTERACTIVE) ) { fprintf(stderr, "%s: descend into directory '%s'? ", applet_name, path); if (!bb_ask_confirmation()) return 0; } dp = opendir(path); if (dp == NULL) { return -1; } while ((d = readdir(dp)) != NULL) { char *new_path; new_path = concat_subpath_file(path, d->d_name); if (new_path == NULL) continue; if (remove_file(new_path, flags) < 0) status = -1; free(new_path); } if (closedir(dp) < 0) { bb_perror_msg("can't close '%s'", path); return -1; } if (flags & FILEUTILS_INTERACTIVE) { fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path); if (!bb_ask_confirmation()) return status; } if (rmdir(path) < 0) { bb_perror_msg("can't remove '%s'", path); return -1; } return status; } /* !ISDIR */ if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && !S_ISLNK(path_stat.st_mode) && isatty(0)) || (flags & FILEUTILS_INTERACTIVE) ) { fprintf(stderr, "%s: remove '%s'? ", applet_name, path); if (!bb_ask_confirmation()) return 0; } if (unlink(path) < 0) { bb_perror_msg("can't remove '%s'", path); return -1; } return 0; }
void proceed_question(void) { fputs("Proceed anyway? (y,n) ", stdout); if (bb_ask_confirmation() == 0) exit(1); }
int mv_main(int argc, char **argv) { struct stat dest_stat; const char *last; const char *dest; unsigned flags; int dest_exists; int status = 0; int copy_flag = 0; #if ENABLE_FEATURE_MV_LONG_OPTIONS applet_long_options = mv_longopts; #endif /* Need at least two arguments. * If more than one of -f, -i, -n is specified , only the final one * takes effect (it unsets previous options). */ opt_complementary = "-2:f-in:i-fn:n-fi"; flags = getopt32(argv, "finv"); argc -= optind; argv += optind; last = argv[argc - 1]; if (argc == 2) { dest_exists = cp_mv_stat(last, &dest_stat); if (dest_exists < 0) { return EXIT_FAILURE; } if (!(dest_exists & 2)) { /* last is not a directory */ dest = last; goto DO_MOVE; } } do { dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); dest_exists = cp_mv_stat(dest, &dest_stat); if (dest_exists < 0) { goto RET_1; } DO_MOVE: if (dest_exists) { if (flags & OPT_NOCLOBBER) goto RET_0; if (!(flags & OPT_FORCE) && ((access(dest, W_OK) < 0 && isatty(0)) || (flags & OPT_INTERACTIVE)) ) { if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { goto RET_1; /* Ouch! fprintf failed! */ } if (!bb_ask_confirmation()) { goto RET_0; } } } if (rename(*argv, dest) < 0) { struct stat source_stat; int source_exists; if (errno != EXDEV || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 ) { bb_perror_msg("can't rename '%s'", *argv); } else { static const char fmt[] ALIGN1 = "can't overwrite %sdirectory with %sdirectory"; if (dest_exists) { if (dest_exists == 3) { if (source_exists != 3) { bb_error_msg(fmt, "", "non-"); goto RET_1; } } else { if (source_exists == 3) { bb_error_msg(fmt, "non-", ""); goto RET_1; } } if (unlink(dest) < 0) { bb_perror_msg("can't remove '%s'", dest); goto RET_1; } } /* FILEUTILS_RECUR also prevents nasties like * "read from device and write contents to dst" * instead of "create same device node" */ copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; #if ENABLE_SELINUX copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; #endif if ((copy_file(*argv, dest, copy_flag) >= 0) && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) ) { goto RET_0; } } RET_1: status = 1; } RET_0: if (flags & OPT_VERBOSE) { printf("'%s' -> '%s'\n", *argv, dest); } if (dest != last) { free((void *) dest); } } while (*++argv != last); return status; }
extern int mv_main(int argc, char **argv) { struct stat source_stat; struct stat dest_stat; const char *last; const char *dest; int dest_exists; int source_exists; unsigned long flags; int status = 0; bb_applet_long_options = mv_long_options; bb_opt_complementaly = "f-i:i-f"; flags = bb_getopt_ulflags(argc, argv, mv_getopt_short_option); if (optind + 2 > argc) bb_show_usage(); last = argv[argc - 1]; argv += optind; if (optind + 2 == argc) { if ((dest_exists = cp_mv_stat(last, &dest_stat)) < 0) { return 1; } if (!(dest_exists & 2)) { dest = last; goto DO_MOVE; } } do { dest = concat_path_file(last, bb_get_last_path_component(*argv)); if ((dest_exists = cp_mv_stat(dest, &dest_stat)) < 0) { goto RET_1; } DO_MOVE: if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) && ((access(dest, W_OK) < 0 && isatty(0)) || (flags & OPT_FILEUTILS_INTERACTIVE))) { if (fprintf(stderr, "mv: overwrite `%s'? ", dest) < 0) { goto RET_1; /* Ouch! fprintf failed! */ } if (!bb_ask_confirmation()) goto RET_0; } if (rename(*argv, dest) < 0) { if (errno != EXDEV) { bb_perror_msg("unable to rename `%s'", *argv); } else if ((source_exists = cp_mv_stat(*argv, &source_stat)) >= 0) { if (dest_exists) { if (dest_exists & 2) { if (!(source_exists & 2)) { bb_error_msg(fmt, "", "non-"); goto RET_1; } } else { if (source_exists & 2) { bb_error_msg(fmt, "non-", ""); goto RET_1; } } if (unlink(dest) < 0) { bb_perror_msg("cannot remove `%s'", dest); goto RET_1; } } if ((copy_file(*argv, dest, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 0) && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) ) { goto RET_0; } } RET_1: status = 1; } RET_0: if (dest != last) { free((void *) dest); } } while (*++argv != last); exit(status); }
int mv_main(int argc, char **argv) { struct stat dest_stat; const char *last; const char *dest; unsigned long flags; int dest_exists; int status = 0; int copy_flag = 0; #if ENABLE_FEATURE_MV_LONG_OPTIONS applet_long_options = mv_longopts; #endif opt_complementary = "f-i:i-f"; flags = getopt32(argv, "fi"); if (optind + 2 > argc) { bb_show_usage(); } last = argv[argc - 1]; argv += optind; if (optind + 2 == argc) { dest_exists = cp_mv_stat(last, &dest_stat); if (dest_exists < 0) { return 1; } if (!(dest_exists & 2)) { dest = last; goto DO_MOVE; } } do { dest = concat_path_file(last, bb_get_last_path_component(*argv)); dest_exists = cp_mv_stat(dest, &dest_stat); if (dest_exists < 0) { goto RET_1; } DO_MOVE: if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) && ((access(dest, W_OK) < 0 && isatty(0)) || (flags & OPT_FILEUTILS_INTERACTIVE))) { if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { goto RET_1; /* Ouch! fprintf failed! */ } if (!bb_ask_confirmation()) { goto RET_0; } } if (rename(*argv, dest) < 0) { struct stat source_stat; int source_exists; if (errno != EXDEV || (source_exists = cp_mv_stat(*argv, &source_stat)) < 1) { bb_perror_msg("cannot rename '%s'", *argv); } else { if (dest_exists) { if (dest_exists == 3) { if (source_exists != 3) { bb_error_msg(fmt, "", "non-"); goto RET_1; } } else { if (source_exists == 3) { bb_error_msg(fmt, "non-", ""); goto RET_1; } } if (unlink(dest) < 0) { bb_perror_msg("cannot remove '%s'", dest); goto RET_1; } } copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; #if ENABLE_SELINUX copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; #endif if ((copy_file(*argv, dest, copy_flag) >= 0) && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) { goto RET_0; } } RET_1: status = 1; } RET_0: if (dest != last) { free((void *) dest); } } while (*++argv != last); return status; }