2) cp/file-perm-race.log can be reproduced like this: terminal1$ ../../src/mkfifo fifo terminal2$ ../../src/cp -p --copy-contents fifo fifo-copy terminal1$ echo foo > fifo Now on terminal2: ../../src/cp: „fifo“: No such file or directory
Looking in more detail at the "../../src/cp -p --copy-contents fifo fifo-copy" process: $ gdb ../../src/cp 0x00004605 in copy_reg (src_name=0xbffff81d "fifo", dst_name=0xbffff822 "fifo-copy", x=0xbffff6b4, dst_mode=420, omitted_permissions=36, new_dst=0xbffff27c, src_sb=0xbffff3c4) at copy.c:340 340 source_desc = open (src_name, Value returned is $1 = 4 (gdb) next 343 if (source_desc < 0) (gdb) 349 if (fstat (source_desc, &src_open_sb) != 0) (gdb) 358 if (! SAME_INODE (*src_sb, src_open_sb)) (gdb) 369 if (! *new_dst) (gdb) 426 if (*new_dst) (gdb) 428 int open_flags = O_WRONLY | O_CREAT | O_BINARY; (gdb) 429 dest_desc = open (dst_name, open_flags | O_EXCL , (gdb) 431 dest_errno = errno; (gdb) 442 if (dest_desc < 0 && dest_errno == EEXIST && ! x->move_mode) (gdb) 467 if (dest_desc < 0) (gdb) 475 if (fstat (dest_desc, &sb) != 0) (gdb) 484 off_t n_read_total = 0; (gdb) 487 size_t buf_alignment = lcm (getpagesize (), sizeof (word)); (gdb) 488 size_t buf_alignment_slop = sizeof (word) + buf_alignment - 1; (gdb) 489 size_t buf_size = ST_BLKSIZE (sb); (gdb) 492 bool last_write_made_hole = false; (gdb) 493 bool make_holes = false; (gdb) 495 if (S_ISREG (sb.st_mode)) (gdb) 499 if (x->sparse_mode == SPARSE_ALWAYS) (gdb) 507 if (x->sparse_mode == SPARSE_AUTO && S_ISREG (src_open_sb.st_mode) (gdb) 515 if (! make_holes) (gdb) 525 size_t blcm_max = MIN (SIZE_MAX, SSIZE_MAX) - buf_alignment_slop; (gdb) 527 blcm_max); (gdb) 530 buf_size = MAX (SMALL_BUF_SIZE, blcm); (gdb) 534 if (S_ISREG (src_open_sb.st_mode) && src_open_sb.st_size < buf_size) (gdb) 540 buf_size += blcm - 1; (gdb) 541 buf_size -= buf_size % blcm; (gdb) 542 if (buf_size == 0 || blcm_max < buf_size) (gdb) 547 buf_alloc = xmalloc (buf_size + buf_alignment_slop); (gdb) 548 buf = ptr_align (buf_alloc, buf_alignment); (gdb) 552 word *wp = NULL; (gdb) 554 ssize_t n_read = read (source_desc, buf, buf_size); (gdb) 555 if (n_read < 0) (gdb) 565 if (n_read == 0) (gdb) 568 n_read_total += n_read; (gdb) 570 if (make_holes) (gdb) 615 if (!wp) (gdb) 617 size_t n = n_read; (gdb) 618 if (full_write (dest_desc, buf, n) != n) (gdb) 624 last_write_made_hole = false; (gdb) 627 if (n_read != buf_size && S_ISREG (src_open_sb.st_mode)) (gdb) 552 word *wp = NULL; (gdb) 554 ssize_t n_read = read (source_desc, buf, buf_size); (gdb) 555 if (n_read < 0) (gdb) 565 if (n_read == 0) (gdb) 638 if (last_write_made_hole) (gdb) 655 if (x->preserve_timestamps) (gdb) 658 timespec[0] = get_stat_atime (src_sb); (gdb) 659 timespec[1] = get_stat_mtime (src_sb); (gdb) 661 if (gl_futimens (dest_desc, dst_name, timespec) != 0) (gdb) 672 if (x->preserve_ownership && ! SAME_OWNER_AND_GROUP (*src_sb, sb)) (gdb) 686 set_author (dst_name, dest_desc, src_sb); (gdb) 688 if (x->preserve_mode || x->move_mode) (gdb) 690 if (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) != 0 (gdb) step copy_acl (src_name=0xbffff81d "fifo", source_desc=4, dst_name=0xbffff822 "fifo-copy", dest_desc=5, mode=4516) at acl.c:59 59 if (HAVE_ACL_GET_FD && source_desc != -1) (gdb) next 60 acl = acl_get_fd (source_desc); (gdb) 63 if (acl == NULL) (gdb) 65 if (ACL_NOT_WELL_SUPPORTED (errno)) (gdb) print *(int*)__error() $3 = 2 (gdb) next 69 error (0, errno, "%s", quote (src_name)); So the first problem is that acl_get_fd fails with errno = ENOENT, but ACL_NOT_WELL_SUPPORTED does not recognize this errno. Once this is fixed, we get a different error message: $ LC_ALL=C ../../src/cp -p --copy-contents fifo fifo-copy ../../src/cp: setting permissions for `fifo-copy': Invalid argument The cause is here: 65 if (ACL_NOT_WELL_SUPPORTED (errno)) (gdb) 66 return set_acl (dst_name, dest_desc, mode); (gdb) step set_acl (name=0xbffff822 "fifo-copy", desc=5, mode=4516) at acl.c:316 316 int r = qset_acl (name, desc, mode); (gdb) step qset_acl (name=0xbffff822 "fifo-copy", desc=5, mode=4516) at acl.c:220 220 char acl_text[] = "u::---,g::---,o::---"; (gdb) next 222 if (mode & S_IRUSR) acl_text[ 3] = 'r'; (gdb) 223 if (mode & S_IWUSR) acl_text[ 4] = 'w'; (gdb) 224 if (mode & S_IXUSR) acl_text[ 5] = 'x'; (gdb) 225 if (mode & S_IRGRP) acl_text[10] = 'r'; (gdb) 226 if (mode & S_IWGRP) acl_text[11] = 'w'; (gdb) 227 if (mode & S_IXGRP) acl_text[12] = 'x'; (gdb) 228 if (mode & S_IROTH) acl_text[17] = 'r'; (gdb) 229 if (mode & S_IWOTH) acl_text[18] = 'w'; (gdb) 230 if (mode & S_IXOTH) acl_text[19] = 'x'; (gdb) 232 acl = acl_from_text (acl_text); (gdb) print acl_text $1 = "u::rw-,g::r--,o::r--" (gdb) next 233 if (!acl) (gdb) print acl $2 = (acl_t) 0x0 (gdb) print *(int*)__error() $3 = 22 (gdb) next 234 return -1; (gdb) 309 } (gdb) set_acl (name=0xbffff822 "fifo-copy", desc=5, mode=4516) at acl.c:317 317 if (r != 0) (gdb) 318 error (0, errno, _("setting permissions for %s"), quote (name)); So the second problem is that acl_from_text does not accept the carefully crafted string in Solaris syntax. When I have a file like this: $ echo > foo.data $ /bin/chmod +a "guest allow write" foo.data $ /bin/ls -le foo.data -rw-r--r--+ 1 bruno staff 1 Apr 20 23:37 foo.data 0: group:_guest allow write then acl_get_fd of the file, followed by acl_to_text, yields a string consisting of two lines: !#acl 1 group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF000000C9:_guest:201:allow:write When I change the permissions, e.g. $ chmod g+w foo.data the retrievable ACL is the same. I conclude that the normal permissions are not part of the ACL, and therefore to reset the ACL there are two possibilities: - Set the ACL to empty. The string "!#acl 1\n" must be used. (The number 1 is a sort of magic/version number, not the number of ACLs.) - Set the ACL to absent. How to do this? I cannot find an 'acl_delete' function. The attached patch fixes the following test failures: FAIL: help-version.log FAIL: help-version.log (exit: 1) FAIL: ginstall FAIL: file-perm-race.log FAIL: parent-perm-race.log FAIL: existing-perm-race.log FAIL: backup-dir.log FAIL: src-base-dot.log FAIL: preserve-2.log FAIL: fail-perm.log FAIL: cp-parents.log FAIL: parent-perm.log (exit: 1) FAIL: file-perm-race.log (exit: 1) FAIL: parent-perm-race.log (exit: 1) FAIL: existing-perm-race.log (exit: 1) FAIL: backup-dir.log (exit: 1) FAIL: src-base-dot.log (exit: 1) FAIL: preserve-2.log (exit: 1) FAIL: fail-perm.log (exit: 1) FAIL: cp-parents.log (exit: 1) FAIL: basic-1.log FAIL: create-leading.log FAIL: basic-1.log (exit: 1) FAIL: create-leading.log (exit: 1) FAIL: misc.log FAIL: misc.log (exit: 1) and gladly does not introduce new failures. But it needs more work to integrate this code with the older POSIX/Solaris code and to make sure that it does not cause regressions on older MacOS X versions. Would you or Paul like to take over from here, or should I provide a safe patch? Bruno *** lib/acl-internal.h.bak 2007-10-17 15:47:25.000000000 +0200 --- lib/acl-internal.h 2008-04-20 23:10:16.000000000 +0200 *************** *** 1,6 **** /* Internal implementation of access control lists. ! Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by --- 1,6 ---- /* Internal implementation of access control lists. ! Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by *************** *** 76,82 **** #endif #define ACL_NOT_WELL_SUPPORTED(Err) \ ! ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY) /* Define a replacement for acl_entries if needed. */ #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE && !HAVE_ACL_ENTRIES --- 76,82 ---- #endif #define ACL_NOT_WELL_SUPPORTED(Err) \ ! ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT) /* Define a replacement for acl_entries if needed. */ #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE && !HAVE_ACL_ENTRIES *** lib/acl.c.bak 2008-01-31 19:37:18.000000000 +0100 --- lib/acl.c 2008-04-21 00:48:36.000000000 +0200 *************** *** 1,6 **** /* acl.c - access control lists ! Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by --- 1,6 ---- /* acl.c - access control lists ! Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by *************** *** 209,272 **** acl_t acl; int ret; ! if (HAVE_ACL_FROM_MODE) { ! acl = acl_from_mode (mode); ! if (!acl) ! return -1; ! } ! else ! { ! char acl_text[] = "u::---,g::---,o::---"; ! ! if (mode & S_IRUSR) acl_text[ 3] = 'r'; ! if (mode & S_IWUSR) acl_text[ 4] = 'w'; ! if (mode & S_IXUSR) acl_text[ 5] = 'x'; ! if (mode & S_IRGRP) acl_text[10] = 'r'; ! if (mode & S_IWGRP) acl_text[11] = 'w'; ! if (mode & S_IXGRP) acl_text[12] = 'x'; ! if (mode & S_IROTH) acl_text[17] = 'r'; ! if (mode & S_IWOTH) acl_text[18] = 'w'; ! if (mode & S_IXOTH) acl_text[19] = 'x'; acl = acl_from_text (acl_text); ! if (!acl) ! return -1; ! } ! if (HAVE_ACL_SET_FD && desc != -1) ! ret = acl_set_fd (desc, acl); ! else ! ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); ! if (ret != 0) ! { ! int saved_errno = errno; ! acl_free (acl); ! ! if (ACL_NOT_WELL_SUPPORTED (errno)) { ! if (chmod_or_fchmod (name, desc, mode) != 0) ! saved_errno = errno; ! else ! return 0; } - errno = saved_errno; - return -1; } - else - acl_free (acl); ! if (S_ISDIR (mode) && acl_delete_def_file (name)) ! return -1; ! ! if (mode & (S_ISUID | S_ISGID | S_ISVTX)) ! { ! /* We did not call chmod so far, so the special bits have not yet ! been set. */ ! ! if (chmod_or_fchmod (name, desc, mode)) ! return -1; ! } ! return 0; #else # if USE_ACL && defined ACL_NO_TRIVIAL --- 209,242 ---- acl_t acl; int ret; ! if (desc != -1 ? acl_get_fd (desc) : acl_get_file (name, ACL_TYPE_EXTENDED)) { ! /* Remove existing ACLs. */ ! static char acl_text[] = "!#acl 1\n"; acl = acl_from_text (acl_text); ! if (acl) { ! ret = (desc != -1 ? acl_set_fd (desc, acl) : acl_set_file (name, ACL_TYPE_EXTENDED, acl)); ! if (ret != 0) ! { ! int saved_errno = errno; ! acl_free (acl); ! ! if (ACL_NOT_WELL_SUPPORTED (errno)) ! { ! if (chmod_or_fchmod (name, desc, mode) != 0) ! saved_errno = errno; ! else ! return 0; ! } ! errno = saved_errno; ! return -1; ! } } } ! return chmod_or_fchmod (name, desc, mode); #else # if USE_ACL && defined ACL_NO_TRIVIAL _______________________________________________ Bug-coreutils mailing list Bug-coreutils@gnu.org http://lists.gnu.org/mailman/listinfo/bug-coreutils