forked from rmyorston/busybox-w32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cp.c
196 lines (184 loc) · 5.65 KB
/
cp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* vi: set sw=4 ts=4: */
/*
* Mini cp implementation for busybox
*
* Copyright (C) 2000 by Matt Kraai <[email protected]>
* SELinux support by Yuichi Nakamura <[email protected]>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
/* Mar 16, 2003 Manuel Novoa III ([email protected])
*
* Size reduction.
*/
#include "libbb.h"
#include "libcoreutils/coreutils.h"
/* This is a NOEXEC applet. Be very careful! */
int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int cp_main(int argc, char **argv)
{
struct stat source_stat;
struct stat dest_stat;
const char *last;
const char *dest;
int s_flags;
int d_flags;
int flags;
int status;
enum {
OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
#if ENABLE_FEATURE_CP_LONG_OPTIONS
OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
#endif
};
// Need at least two arguments
// Soft- and hardlinking doesn't mix
// -P and -d are the same (-P is POSIX, -d is GNU)
// -r and -R are the same
// -R (and therefore -r) turns on -d (coreutils does this)
// -a = -pdR
opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR";
#if ENABLE_FEATURE_CP_LONG_OPTIONS
applet_long_options =
"archive\0" No_argument "a"
"force\0" No_argument "f"
"interactive\0" No_argument "i"
"link\0" No_argument "l"
"dereference\0" No_argument "L"
"no-dereference\0" No_argument "P"
"recursive\0" No_argument "R"
"symbolic-link\0" No_argument "s"
"verbose\0" No_argument "v"
"parents\0" No_argument "\xff"
;
#endif
// -v (--verbose) is ignored
flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv");
/* Options of cp from GNU coreutils 6.10:
* -a, --archive
* -f, --force
* -i, --interactive
* -l, --link
* -L, --dereference
* -P, --no-dereference
* -R, -r, --recursive
* -s, --symbolic-link
* -v, --verbose
* -H follow command-line symbolic links in SOURCE
* -d same as --no-dereference --preserve=links
* -p same as --preserve=mode,ownership,timestamps
* -c same as --preserve=context
* --parents
* use full source file name under DIRECTORY
* NOT SUPPORTED IN BBOX:
* --backup[=CONTROL]
* make a backup of each existing destination file
* -b like --backup but does not accept an argument
* --copy-contents
* copy contents of special files when recursive
* --preserve[=ATTR_LIST]
* preserve attributes (default: mode,ownership,timestamps),
* if possible additional attributes: security context,links,all
* --no-preserve=ATTR_LIST
* --remove-destination
* remove each existing destination file before attempting to open
* --sparse=WHEN
* control creation of sparse files
* --strip-trailing-slashes
* remove any trailing slashes from each SOURCE argument
* -S, --suffix=SUFFIX
* override the usual backup suffix
* -t, --target-directory=DIRECTORY
* copy all SOURCE arguments into DIRECTORY
* -T, --no-target-directory
* treat DEST as a normal file
* -u, --update
* copy only when the SOURCE file is newer than the destination
* file or when the destination file is missing
* -x, --one-file-system
* stay on this file system
* -Z, --context=CONTEXT
* (SELinux) set SELinux security context of copy to CONTEXT
*/
argc -= optind;
argv += optind;
/* Reverse this bit. If there is -d, bit is not set: */
flags ^= FILEUTILS_DEREFERENCE;
/* coreutils 6.9 compat:
* by default, "cp" derefs symlinks (creates regular dest files),
* but "cp -R" does not. We switch off deref if -r or -R (see above).
* However, "cp -RL" must still deref symlinks: */
if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */
flags |= FILEUTILS_DEREFERENCE;
#if ENABLE_SELINUX
if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) {
selinux_or_die();
}
#endif
status = EXIT_SUCCESS;
last = argv[argc - 1];
/* If there are only two arguments and... */
if (argc == 2) {
#if ENABLE_PLATFORM_MINGW32
/* stat can't be aliased, and MinGW uses lstat anyway */
s_flags = cp_mv_stat2(*argv, &source_stat, lstat);
#else
s_flags = cp_mv_stat2(*argv, &source_stat,
(flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
#endif
if (s_flags < 0)
return EXIT_FAILURE;
d_flags = cp_mv_stat(last, &dest_stat);
if (d_flags < 0)
return EXIT_FAILURE;
#if ENABLE_FEATURE_CP_LONG_OPTIONS
if (flags & OPT_parents) {
if (!(d_flags & 2)) {
bb_error_msg_and_die("with --parents, the destination must be a directory");
}
}
#endif
/* ...if neither is a directory... */
if (!((s_flags | d_flags) & 2)
/* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */
|| ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
) {
/* Do a simple copy */
dest = last;
goto DO_COPY; /* NB: argc==2 -> *++argv==last */
}
}
while (1) {
#if ENABLE_FEATURE_CP_LONG_OPTIONS
if (flags & OPT_parents) {
char *dest_dup;
char *dest_dir;
dest = concat_path_file(last, *argv);
dest_dup = xstrdup(dest);
dest_dir = dirname(dest_dup);
if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) {
return EXIT_FAILURE;
}
free(dest_dup);
goto DO_COPY;
}
#endif
dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
DO_COPY:
if (copy_file(*argv, dest, flags) < 0) {
status = EXIT_FAILURE;
}
if (*++argv == last) {
/* possibly leaking dest... */
break;
}
/* don't move up: dest may be == last and not malloced! */
free((void*)dest);
}
/* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */
return status;
}