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 | |
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>
-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) { |