3 |
* Mini mv implementation for busybox |
* Mini mv implementation for busybox |
4 |
* |
* |
5 |
* Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu> |
* 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. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 |
*/ |
*/ |
13 |
* Size reduction and improved error checking. |
* Size reduction and improved error checking. |
14 |
*/ |
*/ |
15 |
|
|
16 |
#include <sys/types.h> |
#include "libbb.h" |
|
#include <sys/stat.h> |
|
|
#include <unistd.h> |
|
|
#include <dirent.h> |
|
|
#include <errno.h> |
|
|
#include <stdlib.h> |
|
|
#include <getopt.h> /* struct option */ |
|
|
#include "busybox.h" |
|
17 |
#include "libcoreutils/coreutils.h" |
#include "libcoreutils/coreutils.h" |
18 |
|
|
19 |
#if ENABLE_FEATURE_MV_LONG_OPTIONS |
#if ENABLE_FEATURE_MV_LONG_OPTIONS |
20 |
static const struct option mv_long_options[] = { |
static const char mv_longopts[] ALIGN1 = |
21 |
{ "interactive", 0, NULL, 'i' }, |
"interactive\0" No_argument "i" |
22 |
{ "force", 0, NULL, 'f' }, |
"force\0" No_argument "f" |
23 |
{ 0, 0, 0, 0 } |
; |
|
}; |
|
24 |
#endif |
#endif |
25 |
|
|
26 |
#define OPT_FILEUTILS_FORCE 1 |
#define OPT_FILEUTILS_FORCE 1 |
27 |
#define OPT_FILEUTILS_INTERACTIVE 2 |
#define OPT_FILEUTILS_INTERACTIVE 2 |
28 |
|
|
29 |
static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory"; |
int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
|
|
|
30 |
int mv_main(int argc, char **argv) |
int mv_main(int argc, char **argv) |
31 |
{ |
{ |
32 |
struct stat dest_stat; |
struct stat dest_stat; |
33 |
const char *last; |
const char *last; |
34 |
const char *dest; |
const char *dest; |
35 |
unsigned long flags; |
unsigned flags; |
36 |
int dest_exists; |
int dest_exists; |
37 |
int status = 0; |
int status = 0; |
38 |
|
int copy_flag = 0; |
39 |
|
|
40 |
#if ENABLE_FEATURE_MV_LONG_OPTIONS |
#if ENABLE_FEATURE_MV_LONG_OPTIONS |
41 |
applet_long_options = mv_long_options; |
applet_long_options = mv_longopts; |
42 |
#endif |
#endif |
43 |
opt_complementary = "f-i:i-f"; |
// Need at least two arguments |
44 |
flags = getopt32(argc, argv, "fi"); |
// -f unsets -i, -i unsets -f |
45 |
if (optind + 2 > argc) { |
opt_complementary = "-2:f-i:i-f"; |
46 |
bb_show_usage(); |
flags = getopt32(argv, "fi"); |
47 |
} |
argc -= optind; |
|
|
|
|
last = argv[argc - 1]; |
|
48 |
argv += optind; |
argv += optind; |
49 |
|
last = argv[argc - 1]; |
50 |
|
|
51 |
if (optind + 2 == argc) { |
if (argc == 2) { |
52 |
dest_exists = cp_mv_stat(last, &dest_stat); |
dest_exists = cp_mv_stat(last, &dest_stat); |
53 |
if (dest_exists < 0) { |
if (dest_exists < 0) { |
54 |
return 1; |
return EXIT_FAILURE; |
55 |
} |
} |
56 |
|
|
57 |
if (!(dest_exists & 2)) { |
if (!(dest_exists & 2)) { /* last is not a directory */ |
58 |
dest = last; |
dest = last; |
59 |
goto DO_MOVE; |
goto DO_MOVE; |
60 |
} |
} |
61 |
} |
} |
62 |
|
|
63 |
do { |
do { |
64 |
dest = concat_path_file(last, bb_get_last_path_component(*argv)); |
dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); |
65 |
dest_exists = cp_mv_stat(dest, &dest_stat); |
dest_exists = cp_mv_stat(dest, &dest_stat); |
66 |
if (dest_exists < 0) { |
if (dest_exists < 0) { |
67 |
goto RET_1; |
goto RET_1; |
68 |
} |
} |
69 |
|
|
70 |
DO_MOVE: |
DO_MOVE: |
71 |
|
if (dest_exists |
72 |
if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) && |
&& !(flags & OPT_FILEUTILS_FORCE) |
73 |
((access(dest, W_OK) < 0 && isatty(0)) || |
&& ((access(dest, W_OK) < 0 && isatty(0)) |
74 |
(flags & OPT_FILEUTILS_INTERACTIVE))) { |
|| (flags & OPT_FILEUTILS_INTERACTIVE)) |
75 |
|
) { |
76 |
if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { |
if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { |
77 |
goto RET_1; /* Ouch! fprintf failed! */ |
goto RET_1; /* Ouch! fprintf failed! */ |
78 |
} |
} |
84 |
struct stat source_stat; |
struct stat source_stat; |
85 |
int source_exists; |
int source_exists; |
86 |
|
|
87 |
if (errno != EXDEV || |
if (errno != EXDEV |
88 |
(source_exists = cp_mv_stat(*argv, &source_stat)) < 1) { |
|| (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 |
89 |
|
) { |
90 |
bb_perror_msg("cannot rename '%s'", *argv); |
bb_perror_msg("cannot rename '%s'", *argv); |
91 |
} else { |
} else { |
92 |
|
static const char fmt[] ALIGN1 = |
93 |
|
"cannot overwrite %sdirectory with %sdirectory"; |
94 |
|
|
95 |
if (dest_exists) { |
if (dest_exists) { |
96 |
if (dest_exists == 3) { |
if (dest_exists == 3) { |
97 |
if (source_exists != 3) { |
if (source_exists != 3) { |
109 |
goto RET_1; |
goto RET_1; |
110 |
} |
} |
111 |
} |
} |
112 |
if ((copy_file(*argv, dest, |
/* FILEUTILS_RECUR also prevents nasties like |
113 |
FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 0) && |
* "read from device and write contents to dst" |
114 |
(remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) { |
* instead of "create same device node" */ |
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 |
|
) { |
122 |
goto RET_0; |
goto RET_0; |
123 |
} |
} |
124 |
} |
} |
125 |
RET_1: |
RET_1: |
126 |
status = 1; |
status = 1; |
127 |
} |
} |
128 |
RET_0: |
RET_0: |
129 |
if (dest != last) { |
if (dest != last) { |
130 |
free((void *) dest); |
free((void *) dest); |
131 |
} |
} |