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 /libbb | |
| 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.
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/copy_file.c | 37 |
1 files changed, 29 insertions, 8 deletions
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... */ |
