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" @@ -95,26 +96,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 @@ -193,25 +174,19 @@ 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 if (dest_desc != -1) { if (fchown (dest_desc, uid, gid) == 0) - { - *chown_succeeded = true; - return true; - } + return true; } else #endif { if (chown (dst_name, uid, gid) == 0) - { - *chown_succeeded = true; - return true; - } + return true; } if (! chown_failure_ok (x)) { @@ -261,7 +236,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; @@ -521,8 +495,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; @@ -531,27 +504,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 - if (fchmod (dest_desc, get_dest_mode (x, src_sb->st_mode)) == 0) - goto close_src_and_dst_desc; -#else - if (chmod (dst_name, get_dest_mode (x, src_sb->st_mode)) == 0) - goto close_src_and_dst_desc; -#endif - 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: @@ -989,12 +951,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) @@ -1510,22 +1473,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)); @@ -1602,16 +1585,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; @@ -1622,7 +1603,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)); @@ -1737,20 +1718,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 @@ -22,6 +22,7 @@ # include #endif +#include #include #include #ifndef S_ISLNK @@ -29,21 +30,31 @@ #endif #include "acl.h" +#include "error.h" +#include "quote.h" #include #ifndef ENOSYS # define ENOSYS (-1) #endif +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#endif + #ifndef MIN_ACL_ENTRIES # define MIN_ACL_ENTRIES 4 #endif -/* Return 1 if FILE has a nontrivial access control list, 0 if not, - and -1 (setting errno) if an error is encountered. */ +/* 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 @@ -52,12 +63,57 @@ file_has_acl (char const *file, struct s fix-related ideas. */ #if HAVE_ACL && defined GETACLCNT - if (! S_ISLNK (filestat->st_mode)) + 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); } #endif return 0; } + +/* Copy the access control list of (SOURCE_DESC or SRC_NAME) to + (DEST_DESC or DEST_NAME). Use file handle operations if SOURCE_DESC + or DEST_DESC are unequal to -1, and name based operations otherwise. + If access control lists are not available, fchmod DEST_DESC (or + chmod DEST_NAME) to MODE. Also sets the non-permission bits of the + destination file (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if + they 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; +#ifdef HAVE_FCHMOD + if (dest_desc != -1) + ret = fchmod (dest_desc, mode); + else +#else + ret = chmod (dst_name, mode); +#endif + if (ret) + error (0, errno, _("preserving permissions for %s"), quote (dst_name)); + return ret; +} + +/* Set the access control list of (DESC or NAME) to the equivalent of MODE. + Use file handle operations if DESC is unequal to -1, and name based + operations otherwise. Also sets the non-permission bits of the + destination file (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if + they are set. System call return value semantics. */ + +int +set_acl (char const *name, int desc, mode_t mode) +{ + int ret; +#ifdef HAVE_FCHMOD + ret = fchmod (desc, mode); +#else + ret = chmod (name, mode); +#endif + if (ret) + error (0, errno, _("setting permissions for %s"), quote (name)); + return ret; +} 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/po/POTFILES.in =================================================================== --- coreutils.orig/po/POTFILES.in +++ coreutils/po/POTFILES.in @@ -2,6 +2,7 @@ # Copyright (C) 1996-2005 Free Software Foundation, Inc. # These are nominally temporary... +lib/acl.c lib/argmatch.c lib/closeout.c lib/error.c