Index: coreutils/src/copy.c =================================================================== --- coreutils.orig/src/copy.c +++ coreutils/src/copy.c @@ -30,6 +30,7 @@ #endif #include "system.h" +#include "acl.h" #include "backupfile.h" #include "buffer-lcm.h" #include "copy.h" @@ -52,9 +53,6 @@ #include "xreadlink.h" #include "yesno.h" -#ifndef HAVE_FCHMOD -# define HAVE_FCHMOD false -#endif #ifndef HAVE_FCHOWN # define HAVE_FCHOWN false # define fchown(fd, uid, gid) (-1) @@ -103,26 +101,6 @@ static char const *top_level_dst_name; /* The invocation name of this program. */ extern char *program_name; -/* Encapsulate selection of the file mode to be applied to - new non-directories. */ - -static mode_t -get_dest_mode (const struct cp_options *option, mode_t mode) -{ - /* In some applications (e.g., install), use precisely the - specified mode. */ - if (option->set_mode) - return option->mode; - - /* Honor the umask for `cp', but not for `mv' or `cp -p'. - In addition, `cp' without -p must clear the set-user-ID and set-group-ID - bits. POSIX requires it do that when creating new files. */ - if (!option->move_mode && !option->preserve_mode) - mode &= (option->umask_kill & ~(S_ISUID | S_ISGID)); - - return mode; -} - /* FIXME: describe */ /* FIXME: rewrite this to use a hash table so we avoid the quadratic performance hit that's probably noticeable only on trees deeper @@ -201,23 +179,17 @@ copy_dir (char const *src_name_in, char static bool set_owner (const struct cp_options *x, char const *dst_name, int dest_desc, - uid_t uid, gid_t gid, bool *chown_succeeded) + uid_t uid, gid_t gid) { if (HAVE_FCHOWN && dest_desc != -1) { if (fchown (dest_desc, uid, gid) == 0) - { - *chown_succeeded = true; - return true; - } + return true; } else { if (chown (dst_name, uid, gid) == 0) - { - *chown_succeeded = true; - return true; - } + return true; } if (! chown_failure_ok (x)) { @@ -267,7 +239,6 @@ preserve_author (const char *dst_name, i static bool copy_reg (char const *src_name, char const *dst_name, const struct cp_options *x, mode_t dst_mode, bool *new_dst, - bool *chown_succeeded, struct stat const *src_sb) { char *buf; @@ -527,8 +498,7 @@ copy_reg (char const *src_name, char con if (x->preserve_ownership && ! SAME_OWNER_AND_GROUP (*src_sb, sb)) { - if (! set_owner (x, dst_name, dest_desc, src_sb->st_uid, src_sb->st_gid, - chown_succeeded)) + if (! set_owner (x, dst_name, dest_desc, src_sb->st_uid, src_sb->st_gid)) { return_val = false; goto close_src_and_dst_desc; @@ -537,25 +507,16 @@ copy_reg (char const *src_name, char con preserve_author (dst_name, dest_desc, src_sb); - /* Permissions of newly-created regular files were set upon `open'. - But don't return early if there were any special bits and chown - succeeded, because the chown must have reset those bits. */ - if (!(*new_dst - && !(*chown_succeeded && (src_sb->st_mode & ~S_IRWXUGO))) - && (x->preserve_mode || *new_dst) - && (x->copy_as_regular || S_ISREG (src_sb->st_mode))) - { - if ((HAVE_FCHMOD - ? fchmod (dest_desc, get_dest_mode (x, src_sb->st_mode)) - : chmod (dst_name, get_dest_mode (x, src_sb->st_mode))) == 0) - goto close_src_and_dst_desc; - - error (0, errno, _("setting permissions for %s"), quote (dst_name)); - if (x->set_mode || x->require_preserve) - { - return_val = false; - goto close_src_and_dst_desc; - } + if (x->preserve_mode || x->move_mode) + { + if (copy_acl (src_name, source_desc, dst_name, dest_desc, + src_sb->st_mode) != 0 && x->require_preserve) + return_val = false; + } + else if (x->set_mode) + { + if (set_acl (dst_name, dest_desc, x->mode) != 0) + return_val = false; } close_src_and_dst_desc: @@ -993,12 +954,13 @@ copy_internal (char const *src_name, cha struct stat dst_sb; mode_t src_mode; mode_t src_type; + mode_t dst_mode IF_LINT (= 0); + bool restore_dst_mode = false; char *earlier_file = NULL; char *dst_backup = NULL; bool backup_succeeded = false; bool delayed_ok; bool copied_as_regular = false; - bool chown_succeeded = false; bool preserve_metadata; if (x->move_mode && rename_succeeded) @@ -1514,22 +1476,42 @@ copy_internal (char const *src_name, cha if (new_dst || !S_ISDIR (dst_sb.st_mode)) { - /* Create the new directory writable and searchable, so - we can create new entries in it. */ - - if (mkdir (dst_name, (src_mode & x->umask_kill) | S_IRWXU) != 0) + if (mkdir (dst_name, src_mode) != 0) { error (0, errno, _("cannot create directory %s"), quote (dst_name)); goto un_backup; } + /* We need search and write permissions to the new directory + for writing the directory's contents. Check if these + permissions are there. */ + + if (lstat (dst_name, &dst_sb) != 0) + { + error (0, errno, _("cannot stat %s"), quote (dst_name)); + goto un_backup; + } + else if ((dst_sb.st_mode & S_IRWXU) != S_IRWXU) + { + /* Make the new directory searchable and writable. */ + + dst_mode = dst_sb.st_mode; + restore_dst_mode = true; + + if (chmod (dst_name, dst_mode | S_IRWXU)) + { + error (0, errno, _("setting permissions for %s"), + quote (dst_name)); + goto un_backup; + } + } + /* Insert the created directory's inode and device numbers into the search structure, so that we can avoid copying it again. */ - if (! remember_created (dst_name)) - goto un_backup; + remember_copied (dst_name, dst_sb.st_ino, dst_sb.st_dev); if (x->verbose) printf ("%s -> %s\n", quote_n (0, src_name), quote_n (1, dst_name)); @@ -1606,16 +1588,14 @@ copy_internal (char const *src_name, cha /* POSIX says the permission bits of the source file must be used as the 3rd argument in the open call, but that's not consistent with historical practice. */ - if (! copy_reg (src_name, dst_name, x, - get_dest_mode (x, src_mode), &new_dst, &chown_succeeded, - &src_sb)) + if (! copy_reg (src_name, dst_name, x, src_mode, &new_dst, &src_sb)) goto un_backup; } else #ifdef S_ISFIFO if (S_ISFIFO (src_type)) { - if (mkfifo (dst_name, get_dest_mode (x, src_mode))) + if (mkfifo (dst_name, src_mode)) { error (0, errno, _("cannot create fifo %s"), quote (dst_name)); goto un_backup; @@ -1626,7 +1606,7 @@ copy_internal (char const *src_name, cha if (S_ISBLK (src_type) || S_ISCHR (src_type) || S_ISSOCK (src_type)) { - if (mknod (dst_name, get_dest_mode (x, src_mode), src_sb.st_rdev)) + if (mknod (dst_name, src_mode, src_sb.st_rdev)) { error (0, errno, _("cannot create special file %s"), quote (dst_name)); @@ -1741,20 +1721,30 @@ copy_internal (char const *src_name, cha if (x->preserve_ownership && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb))) { - if (! set_owner (x, dst_name, -1, src_sb.st_uid, src_sb.st_gid, - &chown_succeeded)) + if (! set_owner (x, dst_name, -1, src_sb.st_uid, src_sb.st_gid)) return false; } preserve_author (dst_name, -1, &src_sb); - if ((x->preserve_mode || new_dst) - && (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type))) + if (x->preserve_mode || x->move_mode) + { + if (copy_acl (src_name, -1, dst_name, -1, src_mode) != 0 + && x->require_preserve) + return false; + } + else if (x->set_mode) { - if (chmod (dst_name, get_dest_mode (x, src_mode)) != 0) + if (set_acl (dst_name, -1, x->mode) != 0) + return false; + } + else if (restore_dst_mode) + { + if (chmod (dst_name, dst_mode)) { - error (0, errno, _("setting permissions for %s"), quote (dst_name)); - if (x->set_mode || x->require_preserve) + error (0, errno, _("preserving permissions for %s"), + quote (dst_name)); + if (x->require_preserve) return false; } } Index: coreutils/doc/coreutils.texi =================================================================== --- coreutils.orig/doc/coreutils.texi +++ coreutils/doc/coreutils.texi @@ -5688,9 +5688,14 @@ Otherwise. @end table Following the permission bits is a single character that specifies -whether an alternate access method applies to the file. When that -character is a space, there is no alternate access method. When it -is a printing character (e.g., @samp{+}), then there is such a method. +whether an alternate access method such as an access control list +applies to the file. When the character following the permissions is a +space, there is no alternate access method. When it is a printing +character, then there is such a method. + +For a file with an extended access control list, a @samp{+} character is +listed. Basic access control lists are equivalent to the permissions +listed, and are not considered an alternate access method. @item -n @itemx --numeric-uid-gid @@ -6527,7 +6532,7 @@ of one or more of the following strings: @table @samp @itemx mode -Preserve the permission attributes. +Preserve the permission attributes, including access control lists. @itemx ownership Preserve the owner and group. On most modern systems, only the super-user may change the owner of a file, and regular users @@ -6543,7 +6548,6 @@ any links between corresponding source f @itemx all Preserve all file attributes. Equivalent to specifying all of the above. address@hidden Mention ACLs here. @end table Using @option{--preserve} with no @var{attribute_list} is equivalent Index: coreutils/lib/acl.c =================================================================== --- coreutils.orig/lib/acl.c +++ coreutils/lib/acl.c @@ -16,48 +16,394 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - Written by Paul Eggert. */ + Written by Paul Eggert and Andreas Gruenbacher. */ #ifdef HAVE_CONFIG_H # include #endif +#include +#include +#include #include #include #ifndef S_ISLNK # define S_ISLNK(Mode) 0 #endif +#ifdef HAVE_ACL_LIBACL_H +# include +#endif + #include "acl.h" +#include "error.h" +#include "quote.h" #include #ifndef ENOSYS # define ENOSYS (-1) #endif +#ifndef ENOTSUP +# define ENOTSUP (-1) +#endif -#ifndef MIN_ACL_ENTRIES -# define MIN_ACL_ENTRIES 4 +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text #endif -/* Return 1 if FILE has a nontrivial access control list, 0 if not, - and -1 (setting errno) if an error is encountered. */ +#ifndef HAVE_FCHMOD +# define HAVE_FCHMOD false +# define fchmod(fd, mode) (-1) +#endif + +/* POSIX 1003.1e (draft 17) */ +#ifndef HAVE_ACL_GET_FD +# define HAVE_ACL_GET_FD false +# define acl_get_fd(fd) (NULL) +#endif + +/* POSIX 1003.1e (draft 17) */ +#ifndef HAVE_ACL_SET_FD +# define HAVE_ACL_SET_FD false +# define acl_set_fd(fd, acl) (-1) +#endif + +/* Linux-specific */ +#ifndef HAVE_ACL_EXTENDED_FILE +# define HAVE_ACL_EXTENDED_FILE false +# define acl_extended_file(name) (-1) +#endif + +/* Linux-specific */ +#ifndef HAVE_ACL_FROM_MODE +# define HAVE_ACL_FROM_MODE false +# define acl_from_mode(mode) (NULL) +#endif + +/* We detect presence of POSIX 1003.1e (draft 17 -- abandoned) support + by checking for HAVE_ACL_GET_FILE, HAVE_ACL_SET_FILE, and HAVE_ACL_FREE. + Systems that have acl_get_file, acl_set_file, and acl_free must also + have acl_to_text, acl_from_text, and acl_delete_def_file (all defined + in the draft); systems that don't would hit #error statements here. */ + +#if USE_ACL && HAVE_ACL_GET_FILE && !HAVE_ACL_ENTRIES +# ifndef HAVE_ACL_TO_TEXT +# error Must have acl_to_text (see POSIX 1003.1e draft 17). +# endif + +/* Return the number of entries in ACL. Linux implements acl_entries + as a more efficient extension than using this workaround. */ + +static int +acl_entries (acl_t acl) +{ + char *text = acl_to_text (acl, NULL), *t; + int entries; + if (text == NULL) + return -1; + for (entries = 0, t = text; ; t++, entries++) { + t = strchr (t, '\n'); + if (t == NULL) + break; + } + acl_free (text); + return entries; +} +#endif + +/* If DESC is a valid file descriptor use fchmod to change the + file's mode to MODE on systems that have fchown. On systems + that don't have fchown and if DESC is invalid, use chown on + NAME instead. */ + +static int +chmod_or_fchmod (const char *name, int desc, mode_t mode) +{ + if (HAVE_FCHMOD && desc != -1) + return fchmod (desc, mode); + else + return chmod (name, mode); +} + +/* Return 1 if NAME has a nontrivial access control list, 0 if + NAME only has no or a base access control list, and -1 on + error. SB must be set to the stat buffer of FILE. */ int -file_has_acl (char const *file, struct stat const *filestat) +file_has_acl (char const *name, struct stat const *sb) { - /* FIXME: This implementation should work on recent-enough versions - of HP-UX, Solaris, and Unixware, but it simply returns 0 with - POSIX 1003.1e (draft 17 -- abandoned), AIX, GNU/Linux, Irix, and - Tru64. Please see Samba's source/lib/sysacls.c file for - fix-related ideas. */ +#if USE_ACL && HAVE_ACL && defined GETACLCNT + /* This implementation should work on recent-enough versions of HP-UX, + Solaris, and Unixware. */ -#if HAVE_ACL && defined GETACLCNT - if (! S_ISLNK (filestat->st_mode)) +# ifndef MIN_ACL_ENTRIES +# define MIN_ACL_ENTRIES 4 +# endif + + if (! S_ISLNK (sb->st_mode)) { - int n = acl (file, GETACLCNT, 0, NULL); + int n = acl (name, GETACLCNT, 0, NULL); return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n); } +#elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + + if (! S_ISLNK (sb->st_mode)) + { + int ret; + + if (HAVE_ACL_EXTENDED_FILE) + ret = acl_extended_file (name); + else + { + acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); + if (acl) + { + ret = (3 < acl_entries (acl)); + acl_free (acl); + if (ret == 0 && S_ISDIR (sb->st_mode)) + { + acl = acl_get_file (name, ACL_TYPE_DEFAULT); + if (acl) + { + ret = (0 < acl_entries (acl)); + acl_free (acl); + } + else + ret = -1; + } + } + else + ret = -1; + } + if (ret < 0) + return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1; + return ret; + } #endif + /* FIXME: Add support for AIX, Irix, and Tru64. Please see Samba's + source/lib/sysacls.c file for fix-related ideas. */ + return 0; } + +/* Copy access control lists from one file to another. If SOURCE_DESC is + a valid file descriptor, use file descriptor operations, else use + filename based operations on SRC_NAME. Likewise for DEST_DESC and + DEST_NAME. + If access control lists are not available, fchmod the target file to + MODE. Also sets the non-permission bits of the destination file + (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. + System call return value semantics. */ + +int +copy_acl (const char *src_name, int source_desc, const char *dst_name, + int dest_desc, mode_t mode) +{ + int ret; + +#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + + acl_t acl; + if (HAVE_ACL_GET_FD && source_desc != -1) + acl = acl_get_fd (source_desc); + else + acl = acl_get_file (src_name, ACL_TYPE_ACCESS); + if (acl == NULL) + { + if (errno == ENOSYS || errno == ENOTSUP) + return set_acl (dst_name, dest_desc, mode); + else + { + error (0, errno, "%s", quote (src_name)); + return -1; + } + } + + if (HAVE_ACL_SET_FD && dest_desc != -1) + ret = acl_set_fd (dest_desc, acl); + else + ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); + if (ret != 0) + { + int saved_errno = errno; + + if (errno == ENOSYS || errno == ENOTSUP) + { + int n = acl_entries (acl); + + acl_free (acl); + if (n == 3) + { + if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) + saved_errno = errno; + else + return 0; + } + else + chmod_or_fchmod (dst_name, dest_desc, mode); + } + else + { + acl_free (acl); + chmod_or_fchmod (dst_name, dest_desc, mode); + } + error (0, saved_errno, _("preserving permissions for %s"), + quote (dst_name)); + return -1; + } + else + acl_free (acl); + + 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 (dst_name, dest_desc, mode) != 0) + { + error (0, errno, _("preserving permissions for %s"), + quote (dst_name)); + return -1; + } + } + + if (S_ISDIR (mode)) + { + acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); + if (acl == NULL) + { + error (0, errno, "%s", quote (src_name)); + return -1; + } + + if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) + { + error (0, errno, _("preserving permissions for %s"), + quote (dst_name)); + acl_free (acl); + return -1; + } + else + acl_free (acl); + } + return 0; +#else + ret = chmod_or_fchmod (dst_name, dest_desc, mode); + if (ret != 0) + error (0, errno, _("preserving permissions for %s"), quote (dst_name)); + return ret; +#endif +} + +/* Set the access control lists of a file. If DESC is a valid file + descriptor, use file descriptor operations where available, else use + filename based operations on NAME. If access control lists are not + available, fchmod the target file to MODE. Also sets the + non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX) + to those from MODE if any are set. System call return value + semantics. */ + +int +set_acl (char const *name, int desc, mode_t mode) +{ +#if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE + /* POSIX 1003.1e draft 17 (abandoned) specific version. */ + + /* We must also have have_acl_from_text and acl_delete_def_file. + (acl_delete_def_file could be emulated with acl_init followed + by acl_set_file, but acl_set_file with an empty acl is + unspecified.) */ + +# ifndef HAVE_ACL_FROM_TEXT +# error Must have acl_from_text (see POSIX 1003.1e draft 17). +# endif +# ifndef HAVE_ACL_DELETE_DEF_FILE +# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). +# endif + + acl_t acl; + int ret; + + if (HAVE_ACL_FROM_MODE) + { + acl = acl_from_mode (mode); + if (!acl) + { + error (0, errno, "%s", quote (name)); + 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) + { + error (0, errno, "%s", quote (name)); + 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 (errno == ENOTSUP || errno == ENOSYS) + { + if (chmod_or_fchmod (name, desc, mode) != 0) + saved_errno = errno; + else + return 0; + } + error (0, saved_errno, _("setting permissions for %s"), quote (name)); + return -1; + } + else + acl_free (acl); + + if (S_ISDIR (mode) && acl_delete_def_file (name)) + { + error (0, errno, _("setting permissions for %s"), quote (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)) + { + error (0, errno, _("preserving permissions for %s"), quote (name)); + return -1; + } + } + return 0; +#else + int ret = chmod_or_fchmod (name, desc, mode); + if (ret) + error (0, errno, _("setting permissions for %s"), quote (name)); + return ret; +#endif +} Index: coreutils/lib/acl.h =================================================================== --- coreutils.orig/lib/acl.h +++ coreutils/lib/acl.h @@ -18,11 +18,13 @@ Written by Paul Eggert. */ -#if HAVE_SYS_ACL_H && HAVE_ACL +#if HAVE_SYS_ACL_H # include #endif -#if ! defined GETACLCNT && defined ACL_CNT +#if defined HAVE_ACL && ! defined GETACLCNT && defined ACL_CNT # define GETACLCNT ACL_CNT #endif int file_has_acl (char const *, struct stat const *); +int copy_acl (char const *, int, char const *, int, mode_t); +int set_acl (char const *, int, mode_t); Index: coreutils/src/copy.h =================================================================== --- coreutils.orig/src/copy.h +++ coreutils/src/copy.h @@ -165,9 +165,6 @@ struct cp_options Create destination directories as usual. */ bool symbolic_link; - /* The bits to preserve in created files' modes. */ - mode_t umask_kill; - /* If true, do not copy a nondirectory that has an existing destination with the same or newer modification time. */ bool update; Index: coreutils/src/cp.c =================================================================== --- coreutils.orig/src/cp.c +++ coreutils/src/cp.c @@ -35,6 +35,7 @@ #include "quotearg.h" #include "stat-time.h" #include "utimens.h" +#include "acl.h" #define ASSIGN_BASENAME_STRDUPA(Dest, File_name) \ do \ @@ -56,7 +57,8 @@ need to be fixed after copying. */ struct dir_attr { - bool is_new_dir; + mode_t mode; + bool restore_mode; size_t slash_offset; struct dir_attr *next; }; @@ -327,9 +329,14 @@ re_protect (char const *const_dst_name, } } - if (x->preserve_mode | p->is_new_dir) + if (x->preserve_mode) { - if (chmod (dst_name, src_sb.st_mode & x->umask_kill)) + if (copy_acl (src_name, -1, dst_name, -1, src_sb.st_mode)) + return false; + } + else if (p->restore_mode) + { + if (chmod (dst_name, p->mode)) { error (0, errno, _("failed to preserve permissions for %s"), quote (dst_name)); @@ -347,8 +354,7 @@ re_protect (char const *const_dst_name, SRC_OFFSET is the index in CONST_DIR (which is a destination directory) of the beginning of the source directory name. - Create any leading directories that don't already exist, - giving them permissions MODE. + Create any leading directories that don't already exist. If VERBOSE_FMT_STRING is nonzero, use it as a printf format string for printing a message after successfully making a directory. The format should take two string arguments: the names of the @@ -364,9 +370,9 @@ re_protect (char const *const_dst_name, static bool make_dir_parents_private (char const *const_dir, size_t src_offset, - mode_t mode, char const *verbose_fmt_string, + char const *verbose_fmt_string, struct dir_attr **attr_list, bool *new_dst, - int (*xstat) ()) + const struct cp_options *x) { struct stat stats; char *dir; /* A copy of CONST_DIR we can change. */ @@ -385,7 +391,7 @@ make_dir_parents_private (char const *co *attr_list = NULL; - if ((*xstat) (dst_dir, &stats)) + if (XSTAT (x, dst_dir, &stats)) { /* A parent of CONST_DIR does not exist. Make all missing intermediate directories. */ @@ -400,20 +406,30 @@ make_dir_parents_private (char const *co fixing later. */ struct dir_attr *new = xmalloc (sizeof *new); new->slash_offset = slash - dir; + new->restore_mode = false; new->next = *attr_list; *attr_list = new; *slash = '\0'; - if ((*xstat) (dir, &stats)) + if (XSTAT (x, dir, &stats)) { + mode_t src_mode; + /* This component does not exist. We must set - *new_dst and new->is_new_dir inside this loop because, + *new_dst and new->mode inside this loop because, for example, in the command `cp --parents ../a/../b/c e_dir', make_dir_parents_private creates only e_dir/../a if ./b already exists. */ *new_dst = true; - new->is_new_dir = true; - if (mkdir (dir, mode)) + if (XSTAT (x, src, &stats)) + { + error (0, errno, _("failed to get attributes of %s"), + quote (src)); + return false; + } + src_mode = stats.st_mode; + + if (mkdir (dir, src_mode)) { error (0, errno, _("cannot make directory %s"), quote (dir)); @@ -424,6 +440,41 @@ make_dir_parents_private (char const *co if (verbose_fmt_string != NULL) printf (verbose_fmt_string, src, dir); } + + /* We need search and write permissions to the new directory + for writing the directory's contents. Check if these + permissions are there. */ + + if (lstat (dir, &stats)) + { + error (0, errno, _("failed to get attributes of %s"), + quote (dir)); + return false; + } + else + { + if (x->preserve_mode) + { + new->mode = src_mode; + new->restore_mode = (src_mode != stats.st_mode); + } + + if ((stats.st_mode & S_IRWXU) != S_IRWXU) + { + /* Make the new directory searchable and writable. The + original permissions will be restored later. */ + + new->mode = stats.st_mode; + new->restore_mode = true; + + if (chmod (dir, stats.st_mode | S_IRWXU)) + { + error (0, errno, _("setting permissions for %s"), + quote (dir)); + return false; + } + } + } } else if (!S_ISDIR (stats.st_mode)) { @@ -432,10 +483,7 @@ make_dir_parents_private (char const *co return false; } else - { - new->is_new_dir = false; - *new_dst = false; - } + *new_dst = false; *slash++ = '/'; /* Avoid unnecessary calls to `stat' when given @@ -536,10 +584,6 @@ do_copy (int n_files, char **file, const Copy the files `file1' through `filen' to the existing directory `edir'. */ int i; - int (*xstat)() = (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS - || x->dereference == DEREF_ALWAYS - ? stat - : lstat); /* Initialize these hash tables only if we'll need them. The problems they're used to detect can arise only if @@ -585,9 +629,9 @@ do_copy (int n_files, char **file, const leading directories. */ parent_exists = (make_dir_parents_private - (dst_name, arg_in_concat - dst_name, S_IRWXU, + (dst_name, arg_in_concat - dst_name, (x->verbose ? "%s -> %s\n" : NULL), - &attr_list, &new_dst, xstat)); + &attr_list, &new_dst, x)); } else { @@ -697,12 +741,6 @@ cp_option_init (struct cp_options *x) /* Not used. */ x->stdin_tty = false; - /* Find out the current file creation mask, to knock the right bits - when using chmod. The creation mask is set to be liberal, so - that created directories can be written, even if it would not - have been allowed with the mask this process was started with. */ - x->umask_kill = ~ umask (0); - x->update = false; x->verbose = false; x->dest_info = NULL; @@ -987,9 +1025,6 @@ main (int argc, char **argv) version_control_string) : no_backups); - if (x.preserve_mode) - x.umask_kill = ~ (mode_t) 0; - if (x.dereference == DEREF_UNDEFINED) { if (x.recursive) Index: coreutils/src/install.c =================================================================== --- coreutils.orig/src/install.c +++ coreutils/src/install.c @@ -153,7 +153,6 @@ cp_option_init (struct cp_options *x) x->mode = S_IRUSR | S_IWUSR; x->stdin_tty = false; - x->umask_kill = 0; x->update = false; x->verbose = false; x->dest_info = NULL; Index: coreutils/src/ls.c =================================================================== --- coreutils.orig/src/ls.c +++ coreutils/src/ls.c @@ -193,13 +193,13 @@ struct fileinfo enum filetype filetype; -#if HAVE_ACL +#if USE_ACL /* For long listings, true if the file has an access control list. */ bool have_acl; #endif }; -#if HAVE_ACL +#if USE_ACL # define FILE_HAS_ACL(F) ((F)->have_acl) #else # define FILE_HAS_ACL(F) 0 @@ -334,7 +334,7 @@ static int current_time_ns = -1; /* Whether any of the files has an ACL. This affects the width of the mode column. */ -#if HAVE_ACL +#if USE_ACL static bool any_has_acl; #else enum { any_has_acl = false }; @@ -2477,7 +2477,7 @@ clear_files (void) } files_index = 0; -#if HAVE_ACL +#if USE_ACL any_has_acl = false; #endif inode_number_width = 0; @@ -2588,7 +2588,7 @@ gobble_file (char const *name, enum file return 0; } -#if HAVE_ACL +#if USE_ACL if (format == long_format) { int n = file_has_acl (absolute_name, &f->stat); Index: coreutils/src/mv.c =================================================================== --- coreutils.orig/src/mv.c +++ coreutils/src/mv.c @@ -134,12 +134,6 @@ cp_option_init (struct cp_options *x) x->mode = 0; x->stdin_tty = isatty (STDIN_FILENO); - /* Find out the current file creation mask, to knock the right bits - when using chmod. The creation mask is set to be liberal, so - that created directories can be written, even if it would not - have been allowed with the mask this process was started with. */ - x->umask_kill = ~ umask (0); - x->update = false; x->verbose = false; x->dest_info = NULL; Index: coreutils/m4/acl.m4 =================================================================== --- coreutils.orig/m4/acl.m4 +++ coreutils/m4/acl.m4 @@ -14,5 +14,22 @@ AC_DEFUN([AC_FUNC_ACL], dnl Prerequisites of lib/acl.c. AC_CHECK_HEADERS(sys/acl.h) + if test "$ac_cv_header_sys_acl_h" = yes; then + use_acl=1 + else + use_acl=0 + fi + AC_DEFINE_UNQUOTED(USE_ACL, $use_acl, + [Define if you want access control list support.]) AC_CHECK_FUNCS(acl) + ac_save_LIBS="$LIBS" + AC_SEARCH_LIBS(acl_get_file, acl, + [test "$ac_cv_search_acl_get_file" = "none required" || + LIB_ACL=$ac_cv_search_acl_get_file]) + AC_SUBST(LIB_ACL) + AC_CHECK_HEADERS(acl/libacl.h) + AC_CHECK_FUNCS(acl_get_file acl_get_fd acl_set_file acl_set_fd \ + acl_free acl_from_mode acl_from_text acl_to_text \ + acl_delete_def_file acl_entries acl_extended_file) + LIBS="$ac_save_LIBS" ]) Index: coreutils/src/Makefile.am =================================================================== --- coreutils.orig/src/Makefile.am +++ coreutils/src/Makefile.am @@ -93,6 +93,13 @@ uptime_LDADD = $(LDADD) $(GETLOADAVG_LIB su_LDADD = $(LDADD) $(LIB_CRYPT) +dir_LDADD += $(LIB_ACL) +ls_LDADD += $(LIB_ACL) +vdir_LDADD += $(LIB_ACL) +cp_LDADD += $(LIB_ACL) +mv_LDADD += $(LIB_ACL) +ginstall_LDADD += $(LIB_ACL) + $(PROGRAMS): ../lib/libcoreutils.a SUFFIXES = .sh Index: coreutils/ChangeLog =================================================================== --- coreutils.orig/ChangeLog +++ coreutils/ChangeLog @@ -1,4 +1,42 @@ -2005-12-05 Andreas Gruenbacher +2005-12-05 Andreas Gruenbacher + + * lib/acl.h: Fix for POSIX ACLs. + * lib/acl.h (copy_acl, set_acl): Add declarations. + * lib/acl.c (acl_entries): Add fallback implementation for POSIX ACL + systems other than Linux. + * lib/acl.c (chmod_or_fchmod): Add function that does fchmod when + possible and chmod otherwise. + * lib/acl.c (file_has_acl): Add a POSIX ACL implementation, with a + Linux-specific subcase. + * lib/acl.c (copy_acl): Add: copy an acl and S_ISUID, S_ISGID, and + S_ISVTX from one file to another. Fall back to fchmod/chmod when + acls are unsupported. + * lib/acl.c (set_acl): Add: set a file's acl and S_ISUID, S_ISGID, and + S_ISVTX to a defined value. Fall back to fchmod/chmod when acls + are unsupported. + * src/ls.c: Switch back from HAVE_ACL to USE_ACL: The acl() syscall + is no requirement for ACL support; particularly, it does not exist + on systems that have POSIX ACLs. + * src/cp.c, src/mv.c, src/install.c (umask_kill): With default acls, + the umask is not to be applied. Remove umask_kill, don't change the + process umask, and let the kernel apply the umask where appropriate. + * src/cp.c (make_path_private): Fix logic for POSIX ACLs. + * src/copy.c (get_dest_mode): Remove; it is obsolete after removing + umask_kill. + * src/copy.c (copy_reg, copy_internal): Use copy_acl and set_acl + instead of fchown/chown. Fix the logic for POSIX ACLs. + * src/copy.c (chown_succeded): Remove; we now always copy acls and + preserve S_ISUID, S_ISGID, and S_ISVTX when needed, no matter if we + did a chown before or not. + * doc/coreutils.texi (ls): Clarify the Alternate Access Method + description. + * doc/coreutils.texi (cp): Clarify that --preserve=mode also preserves + acls. + * src/Makefile.am: On systems that have one, add the acl library to + *_LDADD for the utilities that need it. + * m4/acl.m4: Add POSIX ACL and Linux-specific acl tests. + +2005-12-05 Andreas Gruenbacher * Version 6.0-cvs.