summaryrefslogtreecommitdiff
path: root/bin/cp
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2003-01-06 01:52:53 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2003-01-06 01:52:53 +0000
commit7c65cb083dd598aff0f35602409f6d8f0deee196 (patch)
tree5fe6b2254e15dcedaee4bcbd5b161b9ec5e7c5e5 /bin/cp
parent54fb0f57a499eb9d2e2a155b72ef42d0052c4f3a (diff)
Set directory mode/user/times on the post-order pass, not pre-order.
Fixes a problem with "cp -R" (and probably cp -p) with directories that do not grant the owner write permission. However, we need to know whether or not we created the directory ourselves so stash that fact in fts_pointer (treat it as a boolean) but add a macro to access it for readability. This also allows me to remove the duplicated file times setting code for "cp -p". Closes PR 3058; deraadt@ OK
Diffstat (limited to 'bin/cp')
-rw-r--r--bin/cp/cp.c76
1 files changed, 39 insertions, 37 deletions
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
index daafd581640..80222030835 100644
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cp.c,v 1.20 2002/07/04 04:26:39 deraadt Exp $ */
+/* $OpenBSD: cp.c,v 1.21 2003/01/06 01:52:52 millert Exp $ */
/* $NetBSD: cp.c,v 1.14 1995/09/07 06:14:51 jtc Exp $ */
/*
@@ -47,7 +47,7 @@ static char copyright[] =
#if 0
static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95";
#else
-static char rcsid[] = "$OpenBSD: cp.c,v 1.20 2002/07/04 04:26:39 deraadt Exp $";
+static char rcsid[] = "$OpenBSD: cp.c,v 1.21 2003/01/06 01:52:52 millert Exp $";
#endif
#endif /* not lint */
@@ -89,6 +89,8 @@ static char rcsid[] = "$OpenBSD: cp.c,v 1.20 2002/07/04 04:26:39 deraadt Exp $";
*--(p).p_end = '\0'; \
}
+#define fts_dne(_x) (_x->fts_pointer != NULL)
+
PATH_T to = { to.p_path, "" };
uid_t myuid;
@@ -279,7 +281,7 @@ copy(char *argv[], enum op type, int fts_options)
struct stat to_stat;
FTS *ftsp;
FTSENT *curr;
- int base, dne, nlen, rval;
+ int base, nlen, rval;
char *p, *target_mid;
#ifdef lint
base = 0;
@@ -357,25 +359,37 @@ copy(char *argv[], enum op type, int fts_options)
if (stat(to.p_path, &to_stat) == -1) {
if (curr->fts_info == FTS_DP)
continue;
- dne = 1;
+ /*
+ * We use fts_pointer as a boolean to indicate that
+ * we created this directory ourselves. We'll use
+ * this later on via the fts_dne macro to decide
+ * whether or not to set the directory mode during
+ * the post-order pass.
+ */
+ curr->fts_pointer = (void *)1;
} else {
/*
- * For -p mode, we need to reset the directory
- * times in the post-order pass since the times
- * will have been changed when we added files to
- * the directory in the pre-order pass.
+ * Set directory mode/user/times on the post-order
+ * pass. We can't do this earlier because the mode
+ * may not allow us write permission. Furthermore,
+ * if we set the times during the pre-order pass,
+ * the they will get changed later when the directory
+ * is populated.
*/
if (curr->fts_info == FTS_DP) {
- if (pflag && S_ISDIR(to_stat.st_mode)) {
- struct timeval tv[2];
-
- TIMESPEC_TO_TIMEVAL(&tv[0],
- &curr->fts_statp->st_atimespec);
- TIMESPEC_TO_TIMEVAL(&tv[1],
- &curr->fts_statp->st_mtimespec);
- if (utimes(to.p_path, tv))
- warn("utimes: %s", to.p_path);
- }
+ if (!S_ISDIR(to_stat.st_mode))
+ continue;
+ /*
+ * If not -p and directory didn't exist, set
+ * it to be the same as the from directory,
+ * unmodified by the umask; arguably wrong,
+ * but it's been that way forever.
+ */
+ if (pflag && setfile(curr->fts_statp, 0))
+ rval = 1;
+ else if (fts_dne(curr))
+ (void)chmod(to.p_path,
+ curr->fts_statp->st_mode);
continue;
}
if (to_stat.st_dev == curr->fts_statp->st_dev &&
@@ -394,12 +408,11 @@ copy(char *argv[], enum op type, int fts_options)
rval = 1;
continue;
}
- dne = 0;
}
switch (curr->fts_statp->st_mode & S_IFMT) {
case S_IFLNK:
- if (copy_link(curr, !dne))
+ if (copy_link(curr, !fts_dne(curr)))
rval = 1;
break;
case S_IFDIR:
@@ -418,7 +431,7 @@ copy(char *argv[], enum op type, int fts_options)
* 555) and not causing a permissions race. If the
* umask blocks owner writes, we fail..
*/
- if (dne) {
+ if (fts_dne(curr)) {
if (mkdir(to.p_path,
curr->fts_statp->st_mode | S_IRWXU) < 0)
err(1, "%s", to.p_path);
@@ -426,37 +439,26 @@ copy(char *argv[], enum op type, int fts_options)
errno = ENOTDIR;
err(1, "%s", to.p_path);
}
- /*
- * If not -p and directory didn't exist, set it to be
- * the same as the from directory, unmodified by the
- * umask; arguably wrong, but it's been that way
- * forever.
- */
- if (pflag && setfile(curr->fts_statp, 0))
- rval = 1;
- else if (dne)
- (void)chmod(to.p_path,
- curr->fts_statp->st_mode);
break;
case S_IFBLK:
case S_IFCHR:
if (Rflag) {
- if (copy_special(curr->fts_statp, !dne))
+ if (copy_special(curr->fts_statp, !fts_dne(curr)))
rval = 1;
} else
- if (copy_file(curr, dne))
+ if (copy_file(curr, fts_dne(curr)))
rval = 1;
break;
case S_IFIFO:
if (Rflag) {
- if (copy_fifo(curr->fts_statp, !dne))
+ if (copy_fifo(curr->fts_statp, !fts_dne(curr)))
rval = 1;
} else
- if (copy_file(curr, dne))
+ if (copy_file(curr, fts_dne(curr)))
rval = 1;
break;
default:
- if (copy_file(curr, dne))
+ if (copy_file(curr, fts_dne(curr)))
rval = 1;
break;
}