diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-07-05 13:24:17 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-07-05 13:24:17 +0200 |
| commit | 6f58be07485fd2fbac1b828e5f2c631e7d5bc9e4 (patch) | |
| tree | a364f5241ce39c3ddbb25d7dbc679639562a38ab /libbb | |
| parent | 2d7b5bfa9bd0b8096bf1ec5069fb6638d538a028 (diff) | |
| download | busybox-w32-6f58be07485fd2fbac1b828e5f2c631e7d5bc9e4.tar.gz busybox-w32-6f58be07485fd2fbac1b828e5f2c631e7d5bc9e4.tar.bz2 busybox-w32-6f58be07485fd2fbac1b828e5f2c631e7d5bc9e4.zip | |
cp: make "non-POSIX" cp a bit more consistent
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/copy_file.c | 62 |
1 files changed, 28 insertions, 34 deletions
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index 4d2f17aa5..ae70cbc0a 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp> | 6 | * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp> |
| 7 | * | 7 | * |
| 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
| 9 | * | ||
| 10 | */ | 9 | */ |
| 11 | #include "libbb.h" | 10 | #include "libbb.h" |
| 12 | 11 | ||
| @@ -16,31 +15,33 @@ | |||
| 16 | // Then open w/o EXCL (yes, not unlink!). | 15 | // Then open w/o EXCL (yes, not unlink!). |
| 17 | // If open still fails and -f, try unlink, then try open again. | 16 | // If open still fails and -f, try unlink, then try open again. |
| 18 | // Result: a mess: | 17 | // Result: a mess: |
| 19 | // If dest is a softlink, we overwrite softlink's destination! | 18 | // If dest is a (sym)link, we overwrite link destination! |
| 20 | // (or fail, if it points to dir/nonexistent location/etc). | 19 | // (or fail, if it points to dir/nonexistent location/etc). |
| 21 | // This is strange, but POSIX-correct. | 20 | // This is strange, but POSIX-correct. |
| 22 | // coreutils cp has --remove-destination to override this... | 21 | // coreutils cp has --remove-destination to override this... |
| 23 | // | ||
| 24 | // NB: we have special code which still allows for "cp file /dev/node" | ||
| 25 | // to work POSIX-ly (the only realistic case where it makes sense) | ||
| 26 | 22 | ||
| 27 | // errno must be set to relevant value ("why we cannot create dest?") | 23 | /* Called if open of destination, link creation etc fails. |
| 28 | // for POSIX mode to give reasonable error message | 24 | * errno must be set to relevant value ("why we cannot create dest?") |
| 25 | * to give reasonable error message */ | ||
| 29 | static int ask_and_unlink(const char *dest, int flags) | 26 | static int ask_and_unlink(const char *dest, int flags) |
| 30 | { | 27 | { |
| 31 | int e = errno; | 28 | int e = errno; |
| 29 | |||
| 32 | #if !ENABLE_FEATURE_NON_POSIX_CP | 30 | #if !ENABLE_FEATURE_NON_POSIX_CP |
| 33 | if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { | 31 | if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { |
| 34 | // Either it exists, or the *path* doesnt exist | 32 | /* Either it exists, or the *path* doesnt exist */ |
| 35 | bb_perror_msg("cannot create '%s'", dest); | 33 | bb_perror_msg("cannot create '%s'", dest); |
| 36 | return -1; | 34 | return -1; |
| 37 | } | 35 | } |
| 38 | #endif | 36 | #endif |
| 39 | // If ENABLE_FEATURE_NON_POSIX_CP, act as if -f is always in effect | 37 | // else: act as if -f is always in effect. |
| 40 | // - we don't want "cannot create" msg, we want unlink to be done | 38 | // We don't want "cannot create" msg, we want unlink to be done |
| 41 | // (silently unless -i). | 39 | // (silently unless -i). Why? POSIX cp usually succeeds with |
| 40 | // O_TRUNC open of existing file, and user is left ignorantly happy. | ||
| 41 | // With above block unconditionally enabled, non-POSIX cp | ||
| 42 | // will complain a lot more than POSIX one. | ||
| 42 | 43 | ||
| 43 | // TODO: maybe we should do it only if ctty is present? | 44 | /* TODO: maybe we should do it only if ctty is present? */ |
| 44 | if (flags & FILEUTILS_INTERACTIVE) { | 45 | if (flags & FILEUTILS_INTERACTIVE) { |
| 45 | // We would not do POSIX insanity. -i asks, | 46 | // We would not do POSIX insanity. -i asks, |
| 46 | // then _unlinks_ the offender. Presto. | 47 | // then _unlinks_ the offender. Presto. |
| @@ -48,7 +49,7 @@ static int ask_and_unlink(const char *dest, int flags) | |||
| 48 | // Or else we will end up having 3 open()s! | 49 | // Or else we will end up having 3 open()s! |
| 49 | fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); | 50 | fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); |
| 50 | if (!bb_ask_confirmation()) | 51 | if (!bb_ask_confirmation()) |
| 51 | return 0; // not allowed to overwrite | 52 | return 0; /* not allowed to overwrite */ |
| 52 | } | 53 | } |
| 53 | if (unlink(dest) < 0) { | 54 | if (unlink(dest) < 0) { |
| 54 | #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE | 55 | #if ENABLE_FEATURE_VERBOSE_CP_MESSAGE |
| @@ -56,14 +57,14 @@ static int ask_and_unlink(const char *dest, int flags) | |||
| 56 | /* e == ENOTDIR is similar: path has non-dir component, | 57 | /* e == ENOTDIR is similar: path has non-dir component, |
| 57 | * but in this case we don't even reach copy_file() */ | 58 | * but in this case we don't even reach copy_file() */ |
| 58 | bb_error_msg("cannot create '%s': Path does not exist", dest); | 59 | bb_error_msg("cannot create '%s': Path does not exist", dest); |
| 59 | return -1; // error | 60 | return -1; /* error */ |
| 60 | } | 61 | } |
| 61 | #endif | 62 | #endif |
| 62 | errno = e; | 63 | errno = e; /* do not use errno from unlink */ |
| 63 | bb_perror_msg("cannot create '%s'", dest); | 64 | bb_perror_msg("cannot create '%s'", dest); |
| 64 | return -1; // error | 65 | return -1; /* error */ |
| 65 | } | 66 | } |
| 66 | return 1; // ok (to try again) | 67 | return 1; /* ok (to try again) */ |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | /* Return: | 70 | /* Return: |
| @@ -85,8 +86,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
| 85 | #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) | 86 | #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) |
| 86 | 87 | ||
| 87 | if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { | 88 | if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { |
| 88 | // This may be a dangling symlink. | 89 | /* This may be a dangling symlink. |
| 89 | // Making [sym]links to dangling symlinks works, so... | 90 | * Making [sym]links to dangling symlinks works, so... */ |
| 90 | if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) | 91 | if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) |
| 91 | goto make_links; | 92 | goto make_links; |
| 92 | bb_perror_msg("cannot stat '%s'", source); | 93 | bb_perror_msg("cannot stat '%s'", source); |
| @@ -212,9 +213,9 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
| 212 | if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { | 213 | if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { |
| 213 | int (*lf)(const char *oldpath, const char *newpath); | 214 | int (*lf)(const char *oldpath, const char *newpath); |
| 214 | make_links: | 215 | make_links: |
| 215 | // Hmm... maybe | 216 | /* Hmm... maybe |
| 216 | // if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? | 217 | * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? |
| 217 | // (but realpath returns NULL on dangling symlinks...) | 218 | * (but realpath returns NULL on dangling symlinks...) */ |
| 218 | lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; | 219 | lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; |
| 219 | if (lf(source, dest) < 0) { | 220 | if (lf(source, dest) < 0) { |
| 220 | ovr = ask_and_unlink(dest, flags); | 221 | ovr = ask_and_unlink(dest, flags); |
| @@ -226,7 +227,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
| 226 | } | 227 | } |
| 227 | } | 228 | } |
| 228 | /* _Not_ jumping to preserve_mode_ugid_time: | 229 | /* _Not_ jumping to preserve_mode_ugid_time: |
| 229 | * hard/softlinks don't have those */ | 230 | * (sym)links don't have those */ |
| 230 | return 0; | 231 | return 0; |
| 231 | } | 232 | } |
| 232 | 233 | ||
| @@ -274,19 +275,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
| 274 | if (!S_ISREG(source_stat.st_mode)) | 275 | if (!S_ISREG(source_stat.st_mode)) |
| 275 | new_mode = 0666; | 276 | new_mode = 0666; |
| 276 | 277 | ||
| 277 | /* POSIX way is a security problem versus symlink attacks, | 278 | // POSIX way is a security problem versus (sym)link attacks |
| 278 | * we do it only for non-symlinks, and only for non-recursive, | 279 | if (!ENABLE_FEATURE_NON_POSIX_CP) { |
| 279 | * non-interactive cp. NB: it is still racy | ||
| 280 | * for "cp file /home/bad_user/file" case | ||
| 281 | * (user can rm file and create a link to /etc/passwd) */ | ||
| 282 | if (!ENABLE_FEATURE_NON_POSIX_CP | ||
| 283 | || (dest_exists | ||
| 284 | && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE)) | ||
| 285 | && !S_ISLNK(dest_stat.st_mode)) | ||
| 286 | ) { | ||
| 287 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); | 280 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); |
| 288 | } else /* safe way: */ | 281 | } else { /* safe way: */ |
| 289 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); | 282 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); |
| 283 | } | ||
| 290 | if (dst_fd == -1) { | 284 | if (dst_fd == -1) { |
| 291 | ovr = ask_and_unlink(dest, flags); | 285 | ovr = ask_and_unlink(dest, flags); |
| 292 | if (ovr <= 0) { | 286 | if (ovr <= 0) { |
