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 |
