Page MenuHomeFreeBSD

D51096.id157762.diff
No OneTemporary

D51096.id157762.diff

diff --git a/bin/cp/cp.c b/bin/cp/cp.c
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -270,10 +270,9 @@
FTS *ftsp;
FTSENT *curr;
char *recpath = NULL, *sep;
- int atflags, dne, badcp, len, rval;
+ int atflags, dne, badcp, len, level, rval;
mode_t mask, mode;
bool beneath = Rflag && type != FILE_TO_FILE;
- bool skipdp = false;
/*
* Keep an inverted copy of the umask, for use in correcting
@@ -305,6 +304,7 @@
to.dir = -1;
}
+ level = FTS_ROOTLEVEL;
if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
err(1, "fts_open");
for (badcp = rval = 0;
@@ -315,6 +315,20 @@
case FTS_NS:
case FTS_DNR:
case FTS_ERR:
+ if (level > curr->fts_level) {
+ /* leaving a directory; remove its name from to.path */
+ if (type == DIR_TO_DNE &&
+ curr->fts_level == FTS_ROOTLEVEL) {
+ /* this is actually our created root */
+ } else {
+ while (to.end > to.path && *to.end != '/')
+ to.end--;
+ assert(strcmp(to.end + (*to.end == '/'),
+ curr->fts_name) == 0);
+ *to.end = '\0';
+ }
+ level--;
+ }
warnc(curr->fts_errno, "%s", curr->fts_path);
badcp = rval = 1;
continue;
@@ -335,14 +349,6 @@
strlcpy(rootname, curr->fts_name,
sizeof(rootname));
}
- /*
- * If we FTS_SKIP while handling FTS_D, we will
- * immediately get FTS_DP for the same directory.
- * If this happens before we've appended the name
- * to to.path, we need to remember not to perform
- * the reverse operation.
- */
- skipdp = true;
/* we must have a destination! */
if (type == DIR_TO_DNE &&
curr->fts_level == FTS_ROOTLEVEL) {
@@ -410,7 +416,7 @@
}
to.end += len;
}
- skipdp = false;
+ level++;
/*
* We're on the verge of recursing on ourselves.
* Either we need to stop right here (we knowingly
@@ -477,18 +483,19 @@
rval = 1;
}
}
- /* are we leaving a directory we failed to enter? */
- if (skipdp)
- continue;
- /* leaving a directory; remove its name from to.path */
- if (type == DIR_TO_DNE &&
- curr->fts_level == FTS_ROOTLEVEL) {
- /* this is actually our created root */
- } else {
- while (to.end > to.path && *to.end != '/')
- to.end--;
- assert(strcmp(to.end + (*to.end == '/'), curr->fts_name) == 0);
- *to.end = '\0';
+ if (level > curr->fts_level) {
+ /* leaving a directory; remove its name from to.path */
+ if (type == DIR_TO_DNE &&
+ curr->fts_level == FTS_ROOTLEVEL) {
+ /* this is actually our created root */
+ } else {
+ while (to.end > to.path && *to.end != '/')
+ to.end--;
+ assert(strcmp(to.end + (*to.end == '/'),
+ curr->fts_name) == 0);
+ *to.end = '\0';
+ }
+ level--;
}
continue;
default:
@@ -638,6 +645,7 @@
if (vflag && !badcp)
(void)printf("%s -> %s%s\n", curr->fts_path, to.base, to.path);
}
+ assert(level == FTS_ROOTLEVEL);
if (errno)
err(1, "fts_read");
(void)fts_close(ftsp);
diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh
--- a/bin/cp/tests/cp_test.sh
+++ b/bin/cp/tests/cp_test.sh
@@ -618,6 +618,54 @@
(dst=$(cat dst) && rm "/$dst") 2>/dev/null || true
}
+atf_test_case dirloop
+dirloop_body()
+{
+ mkdir -p src/a src/b
+ ln -s ../b src/a
+ ln -s ../a src/b
+ atf_check \
+ -s exit:1 \
+ -e match:"src/a/b/a: directory causes a cycle" \
+ -e match:"src/b/a/b: directory causes a cycle" \
+ cp -r src dst
+ atf_check test -d dst
+ atf_check test -d dst/a
+ atf_check test -d dst/b
+ atf_check test -d dst/a/b
+ atf_check -s exit:1 test -d dst/a/b/a
+ atf_check test -d dst/b/a
+ atf_check -s exit:1 test -d dst/b/a/b
+}
+
+atf_test_case unrdir
+unrdir_body()
+{
+ mkdir -p src/foo
+ echo "bar" >src/bar
+ chmod 0 src/foo
+ atf_check \
+ -s not-exit:0 \
+ -e match:"^cp: src/foo: Permission denied" \
+ cp -R src dst
+ atf_check test -d dst/foo
+ atf_check -o inline:"bar\n" cat dst/bar
+}
+
+atf_test_case unrfile
+unrfile_body()
+{
+ mkdir -p src/foo
+ echo "bar" >src/bar
+ chmod 0 src/bar
+ atf_check \
+ -s not-exit:0 \
+ -e match:"^cp: src/bar: Permission denied" \
+ cp -R src dst
+ atf_check test -d dst/foo
+ atf_check -s not-exit:0 -e ignore cat dst/bar
+}
+
atf_init_test_cases()
{
atf_add_test_case basic
@@ -656,4 +704,7 @@
atf_add_test_case to_link_outside
atf_add_test_case dstmode
atf_add_test_case to_root
+ atf_add_test_case dirloop
+ atf_add_test_case unrdir
+ atf_add_test_case unrfile
}

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 22, 2:15 AM (6 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31946700
Default Alt Text
D51096.id157762.diff (4 KB)

Event Timeline