20 |
|
|
21 |
/* This is a NOEXEC applet. Be very careful! */ |
/* This is a NOEXEC applet. Be very careful! */ |
22 |
|
|
|
|
|
23 |
int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
24 |
int cp_main(int argc, char **argv) |
int cp_main(int argc, char **argv) |
25 |
{ |
{ |
30 |
int s_flags; |
int s_flags; |
31 |
int d_flags; |
int d_flags; |
32 |
int flags; |
int flags; |
33 |
int status = 0; |
int status; |
34 |
enum { |
enum { |
35 |
OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), |
OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), |
36 |
OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), |
OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), |
37 |
OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), |
OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), |
38 |
OPT_H = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), |
OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), |
39 |
|
#if ENABLE_FEATURE_CP_LONG_OPTIONS |
40 |
|
OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), |
41 |
|
#endif |
42 |
}; |
}; |
43 |
|
|
44 |
// Need at least two arguments |
// Need at least two arguments |
47 |
// -r and -R are the same |
// -r and -R are the same |
48 |
// -R (and therefore -r) turns on -d (coreutils does this) |
// -R (and therefore -r) turns on -d (coreutils does this) |
49 |
// -a = -pdR |
// -a = -pdR |
50 |
opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR:HL"; |
opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR"; |
51 |
flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPH"); |
#if ENABLE_FEATURE_CP_LONG_OPTIONS |
52 |
|
applet_long_options = |
53 |
|
"archive\0" No_argument "a" |
54 |
|
"force\0" No_argument "f" |
55 |
|
"interactive\0" No_argument "i" |
56 |
|
"link\0" No_argument "l" |
57 |
|
"dereference\0" No_argument "L" |
58 |
|
"no-dereference\0" No_argument "P" |
59 |
|
"recursive\0" No_argument "R" |
60 |
|
"symbolic-link\0" No_argument "s" |
61 |
|
"verbose\0" No_argument "v" |
62 |
|
"parents\0" No_argument "\xff" |
63 |
|
; |
64 |
|
#endif |
65 |
|
// -v (--verbose) is ignored |
66 |
|
flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); |
67 |
|
/* Options of cp from GNU coreutils 6.10: |
68 |
|
* -a, --archive |
69 |
|
* -f, --force |
70 |
|
* -i, --interactive |
71 |
|
* -l, --link |
72 |
|
* -L, --dereference |
73 |
|
* -P, --no-dereference |
74 |
|
* -R, -r, --recursive |
75 |
|
* -s, --symbolic-link |
76 |
|
* -v, --verbose |
77 |
|
* -H follow command-line symbolic links in SOURCE |
78 |
|
* -d same as --no-dereference --preserve=links |
79 |
|
* -p same as --preserve=mode,ownership,timestamps |
80 |
|
* -c same as --preserve=context |
81 |
|
* --parents |
82 |
|
* use full source file name under DIRECTORY |
83 |
|
* NOT SUPPORTED IN BBOX: |
84 |
|
* --backup[=CONTROL] |
85 |
|
* make a backup of each existing destination file |
86 |
|
* -b like --backup but does not accept an argument |
87 |
|
* --copy-contents |
88 |
|
* copy contents of special files when recursive |
89 |
|
* --preserve[=ATTR_LIST] |
90 |
|
* preserve attributes (default: mode,ownership,timestamps), |
91 |
|
* if possible additional attributes: security context,links,all |
92 |
|
* --no-preserve=ATTR_LIST |
93 |
|
* --remove-destination |
94 |
|
* remove each existing destination file before attempting to open |
95 |
|
* --sparse=WHEN |
96 |
|
* control creation of sparse files |
97 |
|
* --strip-trailing-slashes |
98 |
|
* remove any trailing slashes from each SOURCE argument |
99 |
|
* -S, --suffix=SUFFIX |
100 |
|
* override the usual backup suffix |
101 |
|
* -t, --target-directory=DIRECTORY |
102 |
|
* copy all SOURCE arguments into DIRECTORY |
103 |
|
* -T, --no-target-directory |
104 |
|
* treat DEST as a normal file |
105 |
|
* -u, --update |
106 |
|
* copy only when the SOURCE file is newer than the destination |
107 |
|
* file or when the destination file is missing |
108 |
|
* -x, --one-file-system |
109 |
|
* stay on this file system |
110 |
|
* -Z, --context=CONTEXT |
111 |
|
* (SELinux) set SELinux security context of copy to CONTEXT |
112 |
|
*/ |
113 |
argc -= optind; |
argc -= optind; |
114 |
argv += optind; |
argv += optind; |
115 |
flags ^= FILEUTILS_DEREFERENCE; /* the sense of this flag was reversed */ |
/* Reverse this bit. If there is -d, bit is not set: */ |
116 |
|
flags ^= FILEUTILS_DEREFERENCE; |
117 |
/* coreutils 6.9 compat: |
/* coreutils 6.9 compat: |
118 |
* by default, "cp" derefs symlinks (creates regular dest files), |
* by default, "cp" derefs symlinks (creates regular dest files), |
119 |
* but "cp -R" does not. We switch off deref if -r or -R (see above). |
* but "cp -R" does not. We switch off deref if -r or -R (see above). |
120 |
* However, "cp -RL" must still deref symlinks: */ |
* However, "cp -RL" must still deref symlinks: */ |
121 |
if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ |
if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ |
122 |
flags |= FILEUTILS_DEREFERENCE; |
flags |= FILEUTILS_DEREFERENCE; |
|
/* The behavior of -H is *almost* like -L, but not quite, so let's |
|
|
* just ignore it too for fun. TODO. |
|
|
if (flags & OPT_H) ... // deref command-line params only |
|
|
*/ |
|
123 |
|
|
124 |
#if ENABLE_SELINUX |
#if ENABLE_SELINUX |
125 |
if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { |
if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { |
127 |
} |
} |
128 |
#endif |
#endif |
129 |
|
|
130 |
|
status = EXIT_SUCCESS; |
131 |
last = argv[argc - 1]; |
last = argv[argc - 1]; |
132 |
/* If there are only two arguments and... */ |
/* If there are only two arguments and... */ |
133 |
if (argc == 2) { |
if (argc == 2) { |
134 |
s_flags = cp_mv_stat2(*argv, &source_stat, |
s_flags = cp_mv_stat2(*argv, &source_stat, |
135 |
(flags & FILEUTILS_DEREFERENCE) ? stat : lstat); |
(flags & FILEUTILS_DEREFERENCE) ? stat : lstat); |
136 |
if (s_flags < 0) |
if (s_flags < 0) |
137 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
138 |
d_flags = cp_mv_stat(last, &dest_stat); |
d_flags = cp_mv_stat(last, &dest_stat); |
139 |
if (d_flags < 0) |
if (d_flags < 0) |
140 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
141 |
|
|
142 |
/* ...if neither is a directory or... */ |
#if ENABLE_FEATURE_CP_LONG_OPTIONS |
143 |
if ( !((s_flags | d_flags) & 2) || |
if (flags & OPT_parents) { |
144 |
/* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */ |
if (!(d_flags & 2)) { |
145 |
((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) |
bb_error_msg_and_die("with --parents, the destination must be a directory"); |
146 |
|
} |
147 |
|
} |
148 |
|
#endif |
149 |
|
|
150 |
|
/* ...if neither is a directory... */ |
151 |
|
if (!((s_flags | d_flags) & 2) |
152 |
|
/* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ |
153 |
|
|| ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) |
154 |
) { |
) { |
155 |
/* ...do a simple copy. */ |
/* Do a simple copy */ |
156 |
dest = last; |
dest = last; |
157 |
goto DO_COPY; /* NB: argc==2 -> *++argv==last */ |
goto DO_COPY; /* NB: argc==2 -> *++argv==last */ |
158 |
} |
} |
159 |
} |
} |
160 |
|
|
161 |
while (1) { |
while (1) { |
162 |
|
#if ENABLE_FEATURE_CP_LONG_OPTIONS |
163 |
|
if (flags & OPT_parents) { |
164 |
|
char *dest_dup; |
165 |
|
char *dest_dir; |
166 |
|
dest = concat_path_file(last, *argv); |
167 |
|
dest_dup = xstrdup(dest); |
168 |
|
dest_dir = dirname(dest_dup); |
169 |
|
if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) { |
170 |
|
return EXIT_FAILURE; |
171 |
|
} |
172 |
|
free(dest_dup); |
173 |
|
goto DO_COPY; |
174 |
|
} |
175 |
|
#endif |
176 |
dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); |
dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); |
177 |
DO_COPY: |
DO_COPY: |
178 |
if (copy_file(*argv, dest, flags) < 0) { |
if (copy_file(*argv, dest, flags) < 0) { |
179 |
status = 1; |
status = EXIT_FAILURE; |
180 |
} |
} |
181 |
if (*++argv == last) { |
if (*++argv == last) { |
182 |
/* possibly leaking dest... */ |
/* possibly leaking dest... */ |
183 |
break; |
break; |
184 |
} |
} |
185 |
|
/* don't move up: dest may be == last and not malloced! */ |
186 |
free((void*)dest); |
free((void*)dest); |
187 |
} |
} |
188 |
|
|