Skip to content

Commit 48578dc

Browse files
committed
cp: Fix issues with destination directory mode.
Ensure that we are able to enter the destination directory after we create it, even if the current umask would normally prevent it, and that it has the expected permissions once we are done, even if we had to tweak them to be able to enter it. Fixes: 82fc0d0 Sponsored by: Klara, Inc. Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D50266
1 parent be78391 commit 48578dc

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

bin/cp/cp.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,18 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
331331
assert(to.dir < 0);
332332
assert(root_stat == NULL);
333333
mode = curr_stat->st_mode | S_IRWXU;
334+
/*
335+
* Will our umask prevent us from entering
336+
* the directory after we create it?
337+
*/
338+
if (~mask & S_IRWXU)
339+
umask(~mask & ~S_IRWXU);
334340
if (mkdir(to.base, mode) != 0) {
335341
warn("%s", to.base);
336342
fts_set(ftsp, curr, FTS_SKIP);
337343
badcp = rval = 1;
344+
if (~mask & S_IRWXU)
345+
umask(~mask);
338346
continue;
339347
}
340348
to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
@@ -343,6 +351,8 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
343351
(void)rmdir(to.base);
344352
fts_set(ftsp, curr, FTS_SKIP);
345353
badcp = rval = 1;
354+
if (~mask & S_IRWXU)
355+
umask(~mask);
346356
continue;
347357
}
348358
if (fstat(to.dir, &created_root_stat) != 0) {
@@ -352,9 +362,14 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
352362
fts_set(ftsp, curr, FTS_SKIP);
353363
to.dir = -1;
354364
badcp = rval = 1;
365+
if (~mask & S_IRWXU)
366+
umask(~mask);
355367
continue;
356368
}
369+
if (~mask & S_IRWXU)
370+
umask(~mask);
357371
root_stat = &created_root_stat;
372+
curr->fts_number = 1;
358373
} else {
359374
/* entering a directory; append its name to to.path */
360375
len = snprintf(to.end, END(to.path) - to.end, "%s%s",
@@ -432,9 +447,7 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
432447
} else if (curr->fts_number) {
433448
const char *path = *to.path ? to.path : dot;
434449
mode = curr_stat->st_mode;
435-
if (((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
436-
((mode | S_IRWXU) & mask) != (mode & mask)) &&
437-
fchmodat(to.dir, path, mode & mask, 0) != 0) {
450+
if (fchmodat(to.dir, path, mode & mask, 0) != 0) {
438451
warn("chmod: %s/%s", to.base, to.path);
439452
rval = 1;
440453
}
@@ -538,12 +551,22 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
538551
*/
539552
if (dne) {
540553
mode = curr_stat->st_mode | S_IRWXU;
554+
/*
555+
* Will our umask prevent us from entering
556+
* the directory after we create it?
557+
*/
558+
if (~mask & S_IRWXU)
559+
umask(~mask & ~S_IRWXU);
541560
if (mkdirat(to.dir, to.path, mode) != 0) {
542561
warn("%s/%s", to.base, to.path);
543562
fts_set(ftsp, curr, FTS_SKIP);
544563
badcp = rval = 1;
564+
if (~mask & S_IRWXU)
565+
umask(~mask);
545566
break;
546567
}
568+
if (~mask & S_IRWXU)
569+
umask(~mask);
547570
} else if (!S_ISDIR(to_stat.st_mode)) {
548571
warnc(ENOTDIR, "%s/%s", to.base, to.path);
549572
fts_set(ftsp, curr, FTS_SKIP);
@@ -554,8 +577,10 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
554577
* Arrange to correct directory attributes later
555578
* (in the post-order phase) if this is a new
556579
* directory, or if the -p flag is in effect.
580+
* Note that fts_number may already be set if this
581+
* is the newly created destination directory.
557582
*/
558-
curr->fts_number = pflag || dne;
583+
curr->fts_number |= pflag || dne;
559584
break;
560585
case S_IFBLK:
561586
case S_IFCHR:

bin/cp/tests/cp_test.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,28 @@ to_link_outside_body()
557557
cp -r dir dst
558558
}
559559

560+
atf_test_case dstmode
561+
dstmode_body()
562+
{
563+
mkdir -m 0755 dir
564+
echo "foo" >dir/file
565+
umask 0177
566+
#atf_check cp -R dir dst
567+
#begin
568+
# atf-check stupidly refuses to work if the current umask is
569+
# weird, instead of just dealing with the situation
570+
cp -R dir dst >stdout 2>stderr
571+
rc=$?
572+
umask 022
573+
atf_check_equal 0 $rc
574+
atf_check cat stdout
575+
atf_check cat stderr
576+
#end
577+
atf_check -o inline:"40600\n" stat -f%p dst
578+
atf_check chmod 0750 dst
579+
atf_check cmp dir/file dst/file
580+
}
581+
560582
atf_init_test_cases()
561583
{
562584
atf_add_test_case basic
@@ -593,4 +615,5 @@ atf_init_test_cases()
593615
atf_add_test_case to_dirlink
594616
atf_add_test_case to_deaddirlink
595617
atf_add_test_case to_link_outside
618+
atf_add_test_case dstmode
596619
}

0 commit comments

Comments
 (0)