Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F154746749
D51096.id157822.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D51096.id157822.diff
View Options
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,76 @@
(dst=$(cat dst) && rm "/$dst") 2>/dev/null || true
}
+atf_test_case dirloop
+dirloop_head()
+{
+ atf_set "descr" "Test cycle detection when recursing"
+}
+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 test ! -e dst/a/b/a
+ atf_check test -d dst/b/a
+ atf_check test ! -e dst/b/a/b
+}
+
+atf_test_case unrdir
+unrdir_head()
+{
+ atf_set "descr" "Test handling of unreadable directories"
+}
+unrdir_body()
+{
+ for d in a b c ; do
+ mkdir -p src/$d
+ echo "$d" >src/$d/f
+ done
+ chmod 0 src/b
+ atf_check \
+ -s exit:1 \
+ -e match:"^cp: src/b: Permission denied" \
+ cp -R src dst
+ atf_check test -d dst/a
+ atf_check cmp src/a/f dst/a/f
+ atf_check test -d dst/b
+ atf_check test ! -e dst/b/f
+ atf_check test -d dst/c
+ atf_check cmp src/c/f dst/c/f
+}
+
+atf_test_case unrfile
+unrdir_head()
+{
+ atf_set "descr" "Test handling of unreadable files"
+}
+unrfile_body()
+{
+ mkdir src
+ for d in a b c ; do
+ echo "$d" >src/$d
+ done
+ chmod 0 src/b
+ atf_check \
+ -s exit:1 \
+ -e match:"^cp: src/b: Permission denied" \
+ cp -R src dst
+ atf_check test -d dst
+ atf_check cmp src/a dst/a
+ atf_check test ! -e dst/b
+ atf_check cmp src/c dst/c
+}
+
atf_init_test_cases()
{
atf_add_test_case basic
@@ -656,4 +726,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
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 30, 9:04 AM (10 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32483151
Default Alt Text
D51096.id157822.diff (4 KB)
Attached To
Mode
D51096: cp: Don't rely on FTS_DP to keep track of depth.
Attached
Detach File
Event Timeline
Log In to Comment