1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini mv implementation for busybox 4 * 5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu> 6 * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp> 7 * 8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 9 */ 10 11/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) 12 * 13 * Size reduction and improved error checking. 14 */ 15 16#include <sys/types.h> 17#include <sys/stat.h> 18#include <dirent.h> 19#include <getopt.h> /* struct option */ 20#include "libbb.h" 21#include "libcoreutils/coreutils.h" 22 23#if ENABLE_FEATURE_MV_LONG_OPTIONS 24static const char mv_longopts[] ALIGN1 = 25 "interactive\0" No_argument "i" 26 "force\0" No_argument "f" 27 ; 28#endif 29 30#define OPT_FILEUTILS_FORCE 1 31#define OPT_FILEUTILS_INTERACTIVE 2 32 33static const char fmt[] ALIGN1 = 34 "cannot overwrite %sdirectory with %sdirectory"; 35 36int mv_main(int argc, char **argv); 37int mv_main(int argc, char **argv) 38{ 39 struct stat dest_stat; 40 const char *last; 41 const char *dest; 42 unsigned long flags; 43 int dest_exists; 44 int status = 0; 45 int copy_flag = 0; 46 47#if ENABLE_FEATURE_MV_LONG_OPTIONS 48 applet_long_options = mv_longopts; 49#endif 50 opt_complementary = "f-i:i-f"; 51 flags = getopt32(argv, "fi"); 52 if (optind + 2 > argc) { 53 bb_show_usage(); 54 } 55 56 last = argv[argc - 1]; 57 argv += optind; 58 59 if (optind + 2 == argc) { 60 dest_exists = cp_mv_stat(last, &dest_stat); 61 if (dest_exists < 0) { 62 return 1; 63 } 64 65 if (!(dest_exists & 2)) { 66 dest = last; 67 goto DO_MOVE; 68 } 69 } 70 71 do { 72 dest = concat_path_file(last, bb_get_last_path_component(*argv)); 73 dest_exists = cp_mv_stat(dest, &dest_stat); 74 if (dest_exists < 0) { 75 goto RET_1; 76 } 77 78DO_MOVE: 79 80 if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) && 81 ((access(dest, W_OK) < 0 && isatty(0)) || 82 (flags & OPT_FILEUTILS_INTERACTIVE))) { 83 if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { 84 goto RET_1; /* Ouch! fprintf failed! */ 85 } 86 if (!bb_ask_confirmation()) { 87 goto RET_0; 88 } 89 } 90 if (rename(*argv, dest) < 0) { 91 struct stat source_stat; 92 int source_exists; 93 94 if (errno != EXDEV || 95 (source_exists = cp_mv_stat(*argv, &source_stat)) < 1) { 96 bb_perror_msg("cannot rename '%s'", *argv); 97 } else { 98 if (dest_exists) { 99 if (dest_exists == 3) { 100 if (source_exists != 3) { 101 bb_error_msg(fmt, "", "non-"); 102 goto RET_1; 103 } 104 } else { 105 if (source_exists == 3) { 106 bb_error_msg(fmt, "non-", ""); 107 goto RET_1; 108 } 109 } 110 if (unlink(dest) < 0) { 111 bb_perror_msg("cannot remove '%s'", dest); 112 goto RET_1; 113 } 114 } 115 copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; 116#if ENABLE_SELINUX 117 copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; 118#endif 119 if ((copy_file(*argv, dest, copy_flag) >= 0) && 120 (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) { 121 goto RET_0; 122 } 123 } 124RET_1: 125 status = 1; 126 } 127RET_0: 128 if (dest != last) { 129 free((void *) dest); 130 } 131 } while (*++argv != last); 132 133 return status; 134} 135