diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-28 17:49:31 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-28 17:49:31 +0000 |
commit | b9ad75fa602dd72b042b2ea08ce86da50746ab30 (patch) | |
tree | 19445163e0f30e8015eeafbcad091309407ab050 | |
parent | a38ba59cc3e78db0234cf4c224de6749d8ce759d (diff) | |
download | busybox-w32-b9ad75fa602dd72b042b2ea08ce86da50746ab30.tar.gz busybox-w32-b9ad75fa602dd72b042b2ea08ce86da50746ab30.tar.bz2 busybox-w32-b9ad75fa602dd72b042b2ea08ce86da50746ab30.zip |
copy_file: handle "cp /dev/foo file" (almost) compatibly to coreutils.
(almost because we do not copy mode, which is probably wasn't intended).
+61 bytes.
-rw-r--r-- | coreutils/mv.c | 6 | ||||
-rw-r--r-- | include/libbb.h | 34 | ||||
-rw-r--r-- | libbb/copy_file.c | 37 | ||||
-rw-r--r-- | testsuite/cp/cp-a-files-to-dir | 3 | ||||
-rw-r--r-- | testsuite/cp/cp-dev-file | 2 | ||||
-rw-r--r-- | testsuite/cp/cp-does-not-copy-unreadable-file | 7 |
6 files changed, 63 insertions, 26 deletions
diff --git a/coreutils/mv.c b/coreutils/mv.c index 5d02196e3..613d4ac46 100644 --- a/coreutils/mv.c +++ b/coreutils/mv.c | |||
@@ -71,7 +71,8 @@ int mv_main(int argc, char **argv) | |||
71 | } | 71 | } |
72 | 72 | ||
73 | DO_MOVE: | 73 | DO_MOVE: |
74 | if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) | 74 | if (dest_exists |
75 | && !(flags & OPT_FILEUTILS_FORCE) | ||
75 | && ((access(dest, W_OK) < 0 && isatty(0)) | 76 | && ((access(dest, W_OK) < 0 && isatty(0)) |
76 | || (flags & OPT_FILEUTILS_INTERACTIVE)) | 77 | || (flags & OPT_FILEUTILS_INTERACTIVE)) |
77 | ) { | 78 | ) { |
@@ -108,6 +109,9 @@ int mv_main(int argc, char **argv) | |||
108 | goto RET_1; | 109 | goto RET_1; |
109 | } | 110 | } |
110 | } | 111 | } |
112 | /* FILEUTILS_RECUR also prevents nasties like | ||
113 | * "read from device and write contents to dst" | ||
114 | * instead of "create same device node" */ | ||
111 | copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; | 115 | copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; |
112 | #if ENABLE_SELINUX | 116 | #if ENABLE_SELINUX |
113 | copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; | 117 | copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; |
diff --git a/include/libbb.h b/include/libbb.h index 3638b7e15..afc053efb 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -223,8 +223,27 @@ extern char *skip_non_whitespace(const char *); | |||
223 | //TODO: supply a pointer to char[11] buffer (avoid statics)? | 223 | //TODO: supply a pointer to char[11] buffer (avoid statics)? |
224 | extern const char *bb_mode_string(mode_t mode); | 224 | extern const char *bb_mode_string(mode_t mode); |
225 | extern int is_directory(const char *name, int followLinks, struct stat *statBuf); | 225 | extern int is_directory(const char *name, int followLinks, struct stat *statBuf); |
226 | enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ | ||
227 | FILEUTILS_PRESERVE_STATUS = 1, | ||
228 | FILEUTILS_DEREFERENCE = 2, | ||
229 | FILEUTILS_RECUR = 4, | ||
230 | FILEUTILS_FORCE = 8, | ||
231 | FILEUTILS_INTERACTIVE = 0x10, | ||
232 | FILEUTILS_MAKE_HARDLINK = 0x20, | ||
233 | FILEUTILS_MAKE_SOFTLINK = 0x40, | ||
234 | #if ENABLE_SELINUX | ||
235 | FILEUTILS_PRESERVE_SECURITY_CONTEXT = 0x80, | ||
236 | FILEUTILS_SET_SECURITY_CONTEXT = 0x100 | ||
237 | #endif | ||
238 | }; | ||
239 | #define FILEUTILS_CP_OPTSTR "pdRfils" USE_SELINUX("c") | ||
226 | extern int remove_file(const char *path, int flags); | 240 | extern int remove_file(const char *path, int flags); |
241 | /* NB: without FILEUTILS_RECUR in flags, it will basically "cat" | ||
242 | * the source, not copy (unless "source" is a directory). | ||
243 | * This makes "cp /dev/null file" and "install /dev/null file" (!!!) | ||
244 | * work coreutils-compatibly. */ | ||
227 | extern int copy_file(const char *source, const char *dest, int flags); | 245 | extern int copy_file(const char *source, const char *dest, int flags); |
246 | |||
228 | enum { | 247 | enum { |
229 | ACTION_RECURSE = (1 << 0), | 248 | ACTION_RECURSE = (1 << 0), |
230 | ACTION_FOLLOWLINKS = (1 << 1), | 249 | ACTION_FOLLOWLINKS = (1 << 1), |
@@ -1170,21 +1189,6 @@ void *md5_end(void *resbuf, md5_ctx_t *ctx); | |||
1170 | uint32_t *crc32_filltable(uint32_t *tbl256, int endian); | 1189 | uint32_t *crc32_filltable(uint32_t *tbl256, int endian); |
1171 | 1190 | ||
1172 | 1191 | ||
1173 | enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ | ||
1174 | FILEUTILS_PRESERVE_STATUS = 1, | ||
1175 | FILEUTILS_DEREFERENCE = 2, | ||
1176 | FILEUTILS_RECUR = 4, | ||
1177 | FILEUTILS_FORCE = 8, | ||
1178 | FILEUTILS_INTERACTIVE = 0x10, | ||
1179 | FILEUTILS_MAKE_HARDLINK = 0x20, | ||
1180 | FILEUTILS_MAKE_SOFTLINK = 0x40, | ||
1181 | #if ENABLE_SELINUX | ||
1182 | FILEUTILS_PRESERVE_SECURITY_CONTEXT = 0x80, | ||
1183 | FILEUTILS_SET_SECURITY_CONTEXT = 0x100 | ||
1184 | #endif | ||
1185 | }; | ||
1186 | |||
1187 | #define FILEUTILS_CP_OPTSTR "pdRfils" USE_SELINUX("c") | ||
1188 | extern const char *applet_name; | 1192 | extern const char *applet_name; |
1189 | /* "BusyBox vN.N.N (timestamp or extra_version)" */ | 1193 | /* "BusyBox vN.N.N (timestamp or extra_version)" */ |
1190 | extern const char bb_banner[]; | 1194 | extern const char bb_banner[]; |
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index d37d51562..3b83e1216 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
@@ -81,6 +81,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
81 | signed char dest_exists = 0; | 81 | signed char dest_exists = 0; |
82 | signed char ovr; | 82 | signed char ovr; |
83 | 83 | ||
84 | /* Inverse of cp -d ("cp without -d") */ | ||
84 | #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) | 85 | #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) |
85 | 86 | ||
86 | if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { | 87 | if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { |
@@ -229,12 +230,22 @@ int copy_file(const char *source, const char *dest, int flags) | |||
229 | return 0; | 230 | return 0; |
230 | } | 231 | } |
231 | 232 | ||
232 | if (S_ISREG(source_stat.st_mode) | 233 | if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */ |
233 | /* DEREF uses stat, which never returns S_ISLNK() == true. */ | 234 | !(flags & FILEUTILS_RECUR) |
235 | /* "cp [-opts] regular_file thing2" */ | ||
236 | || S_ISREG(source_stat.st_mode) | ||
237 | /* DEREF uses stat, which never returns S_ISLNK() == true. | ||
238 | * So the below is never true: */ | ||
234 | /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ | 239 | /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ |
235 | ) { | 240 | ) { |
236 | int src_fd; | 241 | int src_fd; |
237 | int dst_fd; | 242 | int dst_fd; |
243 | mode_t new_mode; | ||
244 | |||
245 | if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) { | ||
246 | /* "cp -d symlink dst": create a link */ | ||
247 | goto dont_cat; | ||
248 | } | ||
238 | 249 | ||
239 | if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { | 250 | if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { |
240 | const char *link_target; | 251 | const char *link_target; |
@@ -258,18 +269,24 @@ int copy_file(const char *source, const char *dest, int flags) | |||
258 | if (src_fd < 0) | 269 | if (src_fd < 0) |
259 | return -1; | 270 | return -1; |
260 | 271 | ||
272 | /* Do not try to open with weird mode fields */ | ||
273 | new_mode = source_stat.st_mode; | ||
274 | if (!S_ISREG(source_stat.st_mode)) | ||
275 | new_mode = 0666; | ||
276 | |||
261 | /* POSIX way is a security problem versus symlink attacks, | 277 | /* POSIX way is a security problem versus symlink attacks, |
262 | * we do it only for non-symlinks, and only for non-recursive, | 278 | * we do it only for non-symlinks, and only for non-recursive, |
263 | * non-interactive cp. NB: it is still racy | 279 | * non-interactive cp. NB: it is still racy |
264 | * for "cp file /home/bad_user/file" case | 280 | * for "cp file /home/bad_user/file" case |
265 | * (user can rm file and create a link to /etc/passwd) */ | 281 | * (user can rm file and create a link to /etc/passwd) */ |
266 | if (DO_POSIX_CP | 282 | if (DO_POSIX_CP |
267 | || (dest_exists && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE)) | 283 | || (dest_exists |
284 | && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE)) | ||
268 | && !S_ISLNK(dest_stat.st_mode)) | 285 | && !S_ISLNK(dest_stat.st_mode)) |
269 | ) { | 286 | ) { |
270 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); | 287 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); |
271 | } else /* safe way: */ | 288 | } else /* safe way: */ |
272 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode); | 289 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); |
273 | if (dst_fd == -1) { | 290 | if (dst_fd == -1) { |
274 | ovr = ask_and_unlink(dest, flags); | 291 | ovr = ask_and_unlink(dest, flags); |
275 | if (ovr <= 0) { | 292 | if (ovr <= 0) { |
@@ -277,7 +294,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
277 | return ovr; | 294 | return ovr; |
278 | } | 295 | } |
279 | /* It shouldn't exist. If it exists, do not open (symlink attack?) */ | 296 | /* It shouldn't exist. If it exists, do not open (symlink attack?) */ |
280 | dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode); | 297 | dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); |
281 | if (dst_fd < 0) { | 298 | if (dst_fd < 0) { |
282 | close(src_fd); | 299 | close(src_fd); |
283 | return -1; | 300 | return -1; |
@@ -285,8 +302,7 @@ int copy_file(const char *source, const char *dest, int flags) | |||
285 | } | 302 | } |
286 | 303 | ||
287 | #if ENABLE_SELINUX | 304 | #if ENABLE_SELINUX |
288 | if (((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) | 305 | if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) |
289 | || (flags & FILEUTILS_SET_SECURITY_CONTEXT)) | ||
290 | && is_selinux_enabled() > 0 | 306 | && is_selinux_enabled() > 0 |
291 | ) { | 307 | ) { |
292 | security_context_t con; | 308 | security_context_t con; |
@@ -313,8 +329,13 @@ int copy_file(const char *source, const char *dest, int flags) | |||
313 | } | 329 | } |
314 | /* ...but read size is already checked by bb_copyfd_eof */ | 330 | /* ...but read size is already checked by bb_copyfd_eof */ |
315 | close(src_fd); | 331 | close(src_fd); |
332 | /* "cp /dev/something new_file" should not | ||
333 | * copy mode of /dev/something */ | ||
334 | if (!S_ISREG(source_stat.st_mode)) | ||
335 | return retval; | ||
316 | goto preserve_mode_ugid_time; | 336 | goto preserve_mode_ugid_time; |
317 | } | 337 | } |
338 | dont_cat: | ||
318 | 339 | ||
319 | /* Source is a symlink or a special file */ | 340 | /* Source is a symlink or a special file */ |
320 | /* We are lazy here, a bit lax with races... */ | 341 | /* We are lazy here, a bit lax with races... */ |
diff --git a/testsuite/cp/cp-a-files-to-dir b/testsuite/cp/cp-a-files-to-dir index 39f8f8103..abdbdf77d 100644 --- a/testsuite/cp/cp-a-files-to-dir +++ b/testsuite/cp/cp-a-files-to-dir | |||
@@ -2,7 +2,8 @@ echo file number one > file1 | |||
2 | echo file number two > file2 | 2 | echo file number two > file2 |
3 | ln -s file2 link1 | 3 | ln -s file2 link1 |
4 | mkdir dir1 | 4 | mkdir dir1 |
5 | touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3 | 5 | # why??? |
6 | #touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3 | ||
6 | mkdir there | 7 | mkdir there |
7 | busybox cp -a file1 file2 link1 dir1 there | 8 | busybox cp -a file1 file2 link1 dir1 there |
8 | test -f there/file1 | 9 | test -f there/file1 |
diff --git a/testsuite/cp/cp-dev-file b/testsuite/cp/cp-dev-file new file mode 100644 index 000000000..055f0d9ee --- /dev/null +++ b/testsuite/cp/cp-dev-file | |||
@@ -0,0 +1,2 @@ | |||
1 | busybox cp /dev/null foo | ||
2 | test -f foo | ||
diff --git a/testsuite/cp/cp-does-not-copy-unreadable-file b/testsuite/cp/cp-does-not-copy-unreadable-file index ce11bfab0..e17e8e66d 100644 --- a/testsuite/cp/cp-does-not-copy-unreadable-file +++ b/testsuite/cp/cp-does-not-copy-unreadable-file | |||
@@ -1,6 +1,11 @@ | |||
1 | touch foo | 1 | touch foo |
2 | chmod a-r foo | 2 | chmod a-r foo |
3 | set +e | 3 | set +e |
4 | busybox cp foo bar | 4 | if test `id -u` = 0; then |
5 | # run as user with nonzero uid | ||
6 | setuidgid 1 busybox cp foo bar | ||
7 | else | ||
8 | busybox cp foo bar | ||
9 | fi | ||
5 | set -e | 10 | set -e |
6 | test ! -f bar | 11 | test ! -f bar |