# --- ROCK-COPYRIGHT-NOTE-BEGIN --- # # This copyright note is auto-generated by ./scripts/Create-CopyPatch. # Please add additional copyright information _after_ the line containing # the ROCK-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by # the ./scripts/Create-CopyPatch script. Do not edit this copyright text! # # ROCK Linux: rock-src/package/base/coreutils/acl-xattr.diff # ROCK Linux is Copyright (C) 1998 - 2003 Clifford Wolf # # 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 # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. A copy of the GNU General Public # License can be found at Documentation/COPYING. # # Many people helped and are helping developing ROCK Linux. Please # have a look at http://www.rocklinux.org/ and the Documentation/TEAM # file for details. # # --- ROCK-COPYRIGHT-NOTE-END --- 2003-04-09 Clifford Wolf * This patch is created using combinediff based on some patches from the linux acl homepage at http://acl.bestbits.at/. * Fixed some includes (lib/acl.c and src/copy.c) 2002-08-15 Andreas Gruenbacher * lib/acl.h: Add copy_acl() and set_acl(). Fix for POSIX/Linux ACLs. * lib/acl.c: Add dummy copy_acl() and set_acl() implementations. * src/copy.[hc]: Remove umask_kill; this makes no sense with POSIX ACLs. Use copy_acl() instead of chmod(). Fix the logic for POSIX ACLs. * src/ls.c: Switch back from HAVE_ACL to USE_ACL: The acl() syscall is no requirement for ACL support. * src/cp.c: Remove umask_kill. Fix logic in make_path_private() for POSIX ACLs. * src/cp.c, src/mv.c, src/install.c: Remove umask_kill. * m4/acl.m4: Add POSIX/Linux ACL tests. * lib/acl.c: Add POSIX/Linux ACL support. --- coreutils-4.5.6/lib/acl.c 2003-02-13 21:39:29.000000000 +0100 +++ coreutils-4.5.6/lib/acl.c 2003-02-13 21:42:45.000000000 +0100 @@ -27,12 +27,29 @@ # define S_ISLNK(Mode) 0 #endif +#include +#include "error.h" +#include "quote.h" #include "acl.h" +#ifdef HAVE_ACL_LIBACL_H +# include +#endif + #include #ifndef ENOSYS # define ENOSYS (-1) #endif +#ifndef ENOTSUP +# define ENOTSUP (-1) +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#endif #ifndef MIN_ACL_ENTRIES # define MIN_ACL_ENTRIES 4 @@ -46,17 +62,196 @@ { - /* 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 (pathstat->st_mode)) { int n = acl (path, GETACLCNT, 0, NULL); return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n); } +#elif USE_ACL && HAVE_ACL_EXTENDED_FILE + /* Linux specific version. */ + + if (! S_ISLNK (pathstat->st_mode)) + { + int ret = acl_extended_file (path); + if (ret < 0) + return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1; + return ret; + } +#endif + + /* FIXME: Add support for POSIX 1003.1e (draft 17 -- abandoned), AIX, Irix, + and Tru64. Please see Samba's source/lib/sysacls.c file for fix-related + ideas. */ + + return 0; +} + +/* Copy the access control list of src_path to dst_path. Fall back to + src_st.st_mode if access control lists are not supported for either + file. */ +int +copy_acl (char const *src_path, char const *dst_path, mode_t mode) +{ +#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && \ + HAVE_ACL_FREE && HAVE_ACL_ENTRIES + /* Linux specific. Will work on all POSIX 1003.1e draft 17 (abandoned) + systems if the Linux specific acl_entries() function is substituted. */ + + acl_t acl = acl_get_file (src_path, ACL_TYPE_ACCESS); + if (acl == NULL) + { + if (errno == ENOSYS || errno == ENOTSUP) + return set_acl (dst_path, mode); + else + { + error (0, errno, "%s", quote (src_path)); + return -1; + } + } + + if (acl_set_file (dst_path, ACL_TYPE_ACCESS, acl)) + { + int saved_errno = errno; + + if (errno == ENOSYS || errno == ENOTSUP) + { + int n = acl_entries (acl); + + acl_free (acl); + if (n == 3) + { + if (chmod (dst_path, mode)) + saved_errno = errno; + else + return 0; + } + else + chmod (dst_path, mode); + } + else + { + acl_free (acl); + chmod (dst_path, mode); + } + error (0, saved_errno, _("preserving permissions for %s"), + quote (dst_path)); + 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 (dst_path, mode)) + { + error (0, errno, _("preserving permissions for %s"), + quote (dst_path)); + return -1; + } + } + + if (S_ISDIR (mode)) + { + acl = acl_get_file (src_path, ACL_TYPE_DEFAULT); + if (acl == NULL) + { + error (0, errno, "%s", quote (src_path)); + return -1; + } + + if (acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl)) + { + error (0, errno, _("preserving permissions for %s"), + quote (dst_path)); + acl_free(acl); + return -1; + } + else + acl_free(acl); + } + return 0; +#else + int ret = chmod (dst_path, mode); + if (ret) + error (0, errno, _("preserving permissions for %s"), quote (dst_path)); + return ret; #endif +} + +/* Set the access control list of path to the permissions defined by mode. */ +int +set_acl (char const *path, mode_t mode) +{ +#if USE_ACL && HAVE_ACL_FROM_TEXT && HAVE_ACL_SET_FILE && HAVE_ACL_FREE && \ + HAVE_ACL_DELETE_DEF_FILE + /* POSIX 1003.1e draft 17 (abandoned) specific version. */ + char acl_text[] = "u::---,g::---,o::---"; + acl_t acl; + + 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 (path)); + return -1; + } + + if (acl_set_file(path, ACL_TYPE_ACCESS, acl)) + { + int saved_errno = errno; + acl_free (acl); + + if (errno == ENOTSUP || errno == ENOSYS) + { + if (chmod (path, mode)) + saved_errno = errno; + else + return 0; + } + error (0, saved_errno, _("setting permissions for %s"), quote (path)); + 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 (path, mode)) + { + error (0, errno, _("preserving permissions for %s"), + quote (path)); + return -1; + } + } + + if (S_ISDIR (mode) && acl_delete_def_file (path)) + { + error (0, errno, _("setting permissions for %s"), quote (path)); + return -1; + } return 0; +#else + int ret = chmod (path, mode); + if (ret) + error (0, errno, _("setting permissions for %s"), quote (path)); + return ret; +#endif } --- coreutils-4.5.6~coreutils-acl/lib/acl.h 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/lib/acl.h 2003-02-13 21:28:21.000000000 +0100 @@ -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 *, char const *, mode_t); +int set_acl(char const *, mode_t); --- coreutils-4.5.6~coreutils-acl/src/copy.c 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/copy.c 2003-02-13 21:39:46.000000000 +0100 @@ -45,6 +45,7 @@ #include "quote.h" #include "same.h" #include "xreadlink.h" +#include "acl.h" #define DO_CHOWN(Chown, File, New_uid, New_gid) \ (Chown (File, New_uid, New_gid) \ @@ -99,26 +100,6 @@ /* 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 @@ -793,12 +774,13 @@ struct stat dst_sb; mode_t src_mode; mode_t src_type; + mode_t dst_mode IF_LINT(= 0); + int dst_mode_valid = 0; char *earlier_file = NULL; char *dst_backup = NULL; int backup_succeeded = 0; int delayed_fail; int copied_as_regular = 0; - int ran_chown = 0; int preserve_metadata; if (x->move_mode && rename_succeeded) @@ -1262,15 +1244,36 @@ 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_path, (src_mode & x->umask_kill) | S_IRWXU)) + if (mkdir (dst_path, src_mode)) { error (0, errno, _("cannot create directory %s"), quote (dst_path)); 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_path, &dst_sb)) + { + error (0, errno, _("cannot stat %s"), quote (dst_path)); + delayed_fail = 1; + } + else if ((dst_sb.st_mode & S_IRWXU) != S_IRWXU) + { + /* Make the new directory searchable and writable. The + original permissions will be restored later. */ + + dst_mode = dst_sb.st_mode; + dst_mode_valid = 1; + + if (chmod (dst_path, dst_mode | S_IRWXU)) + { + error (0, errno, _("setting permissions for %s"), + quote (dst_path)); + goto un_backup; + } + } /* Insert the created directory's inode and device numbers into the search structure, so that we can @@ -1358,15 +1361,14 @@ /* 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_path, dst_path, x, - get_dest_mode (x, src_mode), &new_dst, &src_sb)) + if (copy_reg (src_path, dst_path, x, src_mode, &new_dst, &src_sb)) goto un_backup; } else #ifdef S_ISFIFO if (S_ISFIFO (src_type)) { - if (mkfifo (dst_path, get_dest_mode (x, src_mode))) + if (mkfifo (dst_path, src_mode)) { error (0, errno, _("cannot create fifo %s"), quote (dst_path)); goto un_backup; @@ -1380,7 +1382,7 @@ #endif ) { - if (mknod (dst_path, get_dest_mode (x, src_mode), src_sb.st_rdev)) + if (mknod (dst_path, src_mode, src_sb.st_rdev)) { error (0, errno, _("cannot create special file %s"), quote (dst_path)); @@ -1495,7 +1497,8 @@ if (x->preserve_ownership && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb))) { - ran_chown = 1; + /* The chown() system call may clear the SUID and SGID bits, so we + may need to set them again later. */ if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid)) { error (0, errno, _("failed to preserve ownership for %s"), @@ -1516,20 +1519,27 @@ } #endif - /* Permissions of newly-created regular files were set upon `open' in - copy_reg. But don't return early if there were any special bits and - we had to run chown, because the chown must have reset those bits. */ - if ((new_dst && copied_as_regular) - && !(ran_chown && (src_mode & ~S_IRWXUGO))) - return delayed_fail; - - 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_path, dst_path, src_mode) && x->require_preserve) + return 1; + } + else if (x->set_mode) + { + if (chmod (dst_path, x->mode)) + { + error (0, errno, _("preserving permissions for %s"), + quote (dst_path)); + if (x->require_preserve) + return 1; + } + } + else if (dst_mode_valid) { - if (chmod (dst_path, get_dest_mode (x, src_mode))) + if (chmod (dst_path, dst_mode)) { - error (0, errno, _("setting permissions for %s"), quote (dst_path)); - if (x->set_mode || x->require_preserve) + error (0, errno, _("setting permissions for %s"), quote(dst_path)); + if (x->require_preserve) return 1; } } --- coreutils-4.5.6~coreutils-acl/src/copy.h 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/copy.h 2003-02-13 21:28:21.000000000 +0100 @@ -139,9 +139,6 @@ Create destination directories as usual. */ int symbolic_link; - /* The bits to preserve in created files' modes. */ - mode_t umask_kill; - /* If nonzero, do not copy a nondirectory that has an existing destination with the same or newer modification time. */ int update; --- coreutils-4.5.6~coreutils-acl/src/cp.c 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/cp.c 2003-02-13 21:35:58.000000000 +0100 @@ -36,6 +36,7 @@ #include "dirname.h" #include "path-concat.h" #include "quote.h" +#include "acl.h" #define ASSIGN_BASENAME_STRDUPA(Dest, File_name) \ do \ @@ -61,7 +62,8 @@ need to be fixed after copying. */ struct dir_attr { - int is_new_dir; + mode_t mode; + int mode_valid; int slash_offset; struct dir_attr *next; }; @@ -287,6 +289,7 @@ char *dst_path; /* A copy of CONST_DST_PATH we can change. */ char *src_path; /* The source name in `dst_path'. */ uid_t myeuid = geteuid (); + mode_t umask_kill = ~umask (0); dst_path = (char *) alloca (strlen (const_dst_path) + 1); strcpy (dst_path, const_dst_path); @@ -342,9 +345,14 @@ } } - if (x->preserve_mode || p->is_new_dir) + if (x->preserve_mode) { - if (chmod (dst_path, src_sb.st_mode & x->umask_kill)) + if (copy_acl (src_path, dst_path, src_sb.st_mode)) + return 1; + } + else if (p->mode_valid) + { + if (chmod (dst_path, p->mode)) { error (0, errno, _("failed to preserve permissions for %s"), quote (dst_path)); @@ -362,8 +370,7 @@ SRC_OFFSET is the index in CONST_DIRPATH (which is a destination path) 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 @@ -378,9 +385,9 @@ /* FIXME: find a way to synch this function with the one in lib/makepath.c. */ static int -make_path_private (const char *const_dirpath, int src_offset, int mode, +make_path_private (const char *const_dirpath, int src_offset, const char *verbose_fmt_string, struct dir_attr **attr_list, - int *new_dst, int (*xstat)()) + int *new_dst, struct cp_options const *x) { struct stat stats; char *dirpath; /* A copy of CONST_DIRPATH we can change. */ @@ -400,7 +407,7 @@ *attr_list = NULL; - if ((*xstat) (dst_dirname, &stats)) + if ((*x->xstat) (dst_dirname, &stats)) { /* Parent of CONST_DIRNAME does not exist. Make all missing intermediate directories. */ @@ -420,16 +427,26 @@ *attr_list = new; *slash = '\0'; - if ((*xstat) (dirpath, &stats)) + if ((*x->xstat) (dirpath, &stats)) { + mode_t src_mode; + /* This element of the path 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_path_private creates only e_dir/../a if ./b already exists. */ *new_dst = 1; - new->is_new_dir = 1; - if (mkdir (dirpath, mode)) + + if ((*x->xstat) (src, &stats)) + { + error (0, errno, _("failed to get attributes of %s"), + quote (src)); + return 1; + } + src_mode = stats.st_mode; + + if (mkdir (dirpath, src_mode)) { error (0, errno, _("cannot make directory %s"), quote (dirpath)); @@ -440,6 +457,42 @@ if (verbose_fmt_string != NULL) printf (verbose_fmt_string, src, dirpath); } + + /* We need search and write permissions to the new directory + for writing the directory's contents. Check if these + permissions are there. */ + + if (lstat (dirpath, &stats)) + { + error (0, errno, _("failed to get attributes of %s"), + quote (dirpath)); + return 1; + } + else + { + if (x->preserve_mode) { + new->mode = src_mode; + new->mode_valid = (src_mode != stats.st_mode); + } else { + new->mode = stats.st_mode; + new->mode_valid = 0; + } + + if ((stats.st_mode & S_IRWXU) != S_IRWXU) + { + /* Make the new directory searchable and writable. The + original permissions will be restored later. */ + + new->mode_valid = 1; + + if (chmod (dirpath, stats.st_mode | S_IRWXU)) + { + error (0, errno, _("setting permissions for %s"), + quote (dirpath)); + return 1; + } + } + } } else if (!S_ISDIR (stats.st_mode)) { @@ -449,7 +502,7 @@ } else { - new->is_new_dir = 0; + new->mode_valid = 0; *new_dst = 0; } *slash++ = '/'; @@ -600,11 +653,9 @@ leading directories. */ parent_exists = !make_path_private (dst_path, arg_in_concat - dst_path, - S_IRWXU, (x->verbose ? "%s -> %s\n" : NULL), - &attr_list, &new_dst, - x->xstat); + &attr_list, &new_dst, x); } else { @@ -739,12 +790,6 @@ /* Not used. */ x->stdin_tty = 0; - /* 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 = 0; x->verbose = 0; x->dest_info = NULL; @@ -1017,9 +1062,6 @@ version_control_string) : none); - if (x.preserve_mode == 1) - x.umask_kill = ~ (mode_t) 0; - if (x.dereference == DEREF_UNDEFINED) { if (x.recursive) --- coreutils-4.5.6~coreutils-acl/src/install.c 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/install.c 2003-02-13 21:28:21.000000000 +0100 @@ -165,7 +165,6 @@ x->mode = S_IRUSR | S_IWUSR; x->stdin_tty = 0; - x->umask_kill = 0; x->update = 0; x->verbose = 0; x->xstat = stat; --- coreutils-4.5.6~coreutils-acl/src/ls.c 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/ls.c 2003-02-13 21:28:21.000000000 +0100 @@ -223,13 +223,13 @@ 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 @@ -2400,7 +2400,7 @@ return 0; } -#if HAVE_ACL +#if USE_ACL if (format == long_format) { int n = file_has_acl (path, &files[files_index].stat); --- coreutils-4.5.6~coreutils-acl/src/mv.c 2003-02-13 21:15:48.000000000 +0100 +++ coreutils-4.5.6/src/mv.c 2003-02-13 21:28:21.000000000 +0100 @@ -137,12 +137,6 @@ 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 = 0; x->verbose = 0; x->xstat = lstat; --- coreutils-4.5.6~coreutils-acl+posix/m4/acl.m4 2003-02-13 21:39:29.000000000 +0100 +++ coreutils-4.5.6/m4/acl.m4 2003-02-13 21:49:04.000000000 +0100 @@ -20,4 +20,22 @@ AC_DEFUN([AC_FUNC_ACL], [AC_CHECK_HEADERS(sys/acl.h) - AC_CHECK_FUNCS(acl)]) + 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) + LIB_ACL=`echo "$LIBS" | \ + awk -- "{ print substr(\\$_, length(\"$ac_save_LIBS\")+1)}"` + AC_SUBST(LIB_ACL) + AC_CHECK_HEADERS(acl/libacl.h) + AC_CHECK_FUNCS(acl_get_file acl_set_file acl_free acl_to_text \ + acl_from_text acl_delete_def_file \ + acl_entries acl_extended_file) + LIBS="$ac_save_LIBS" + ]) --- coreutils-4.5.6~coreutils-acl+posix/src/Makefile.am 2003-02-13 21:39:29.000000000 +0100 +++ coreutils-4.5.6/src/Makefile.am 2003-02-13 21:40:44.000000000 +0100 @@ -34,10 +34,13 @@ # replacement functions defined in libfetish.a. LDADD = ../lib/libfetish.a @LIBINTL@ ../lib/libfetish.a -dir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ls_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ +dir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ @LIB_ACL@ +ls_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ @LIB_ACL@ shred_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -vdir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ +vdir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ @LIB_ACL@ +cp_LDADD = $(LDADD) @LIB_ACL@ +mv_LDADD = $(LDADD) @LIB_ACL@ +ginstall_LDADD = $(LDADD) @LIB_ACL@ ## If necessary, add -lm to resolve use of pow in lib/strtod.c. sort_LDADD = $(LDADD) @POW_LIB@ --- coreutils-4.5.8/configure.ac +++ coreutils-4.5.8/configure.ac @@ -245,6 +245,9 @@ # For src/kill.c. AC_CHECK_DECLS([strsignal, strtoimax, sys_siglist, _sys_siglist]) +# Extended attribute copying. +AC_FUNC_XATTR + jm_LIB_CHECK AM_GNU_GETTEXT([external], [need-ngettext]) --- coreutils-4.5.8/doc/coreutils.texi +++ coreutils-4.5.8/doc/coreutils.texi @@ -5859,6 +5859,19 @@ directory in a different order). Equivalent to @option{-dpPR}. +@itemx @w{@kbd{--attributes}=@var{regex}} +@opindex --attributes +Preserve extended attributes whose names match the specified regular +expression. The default behavior or @command{cp} if no +@option{--attributes} option is given is to preserve all extended +attributes except file permissions. If @var{regex} is ``@samp{-}'', no +extended attributes are preserved. + +This option does not affect the preservation of discretionary file +permissions (i.e., file mode permission bits and access control lists). +To control the preservation of file permissions, the @option{-p} or +@option{--preserve=mode} options are used. + @item -b @itemx @w{@kbd{--backup}[=@var{method}]} @opindex -b @@ -5962,7 +5975,7 @@ @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 @@ -5978,7 +5991,6 @@ @itemx all Preserve all file attributes. Equivalent to specifying all of the above. -@c Mention ACLs here. @end table Using @option{--preserve} with no @var{attribute_list} is equivalent --- coreutils-4.5.8/m4/xattr.m4 +++ coreutils-4.5.8/m4/xattr.m4 @@ -0,0 +1,38 @@ +# xattr.m4 - check for Extended Attributes (Linux) + +# Copyright (C) 2003 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 +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +# Written by Andreas Gruenbacher. + +AC_DEFUN([AC_FUNC_XATTR], +[ + AC_CHECK_HEADERS(attr/error_context.h attr/libattr.h) + if test "$ac_cv_header_attr_libattr_h" = yes \ + && test "$ac_cv_header_attr_error_context_h" = yes; then + use_xattr=1 + else + use_xattr=0 + fi + AC_DEFINE_UNQUOTED(USE_XATTR, $use_xattr, + [Define if you want extended attribute support.]) + xattr_saved_LIBS=$LIBS + AC_SEARCH_LIBS(attr_copy_file, attr, + [LIB_XATTR=$ac_cv_search_attr_copy_file]) + AC_SUBST(LIB_XATTR) + AC_CHECK_FUNCS(attr_copy_file) + LIBS=$xattr_saved_LIBS +]) --- coreutils-4.5.8/man/cp.1 +++ coreutils-4.5.8/man/cp.1 @@ -60,6 +60,14 @@ mode,ownership,timestamps), if possible additional attributes: links, all .TP +\fB\-\-attributes\fR=\fIregex\fR +preserve extended attributes whose name +matches the specified regular expression +(defaults to preserving all extended +.IP +attributes except file permissions; +regex=`-' preserves no extended attributes). +.TP \fB\-\-no\-preserve\fR=\fIATTR_LIST\fR don't preserve the specified attributes .TP --- coreutils-4.5.8/src/Makefile.am +++ coreutils-4.5.8/src/Makefile.am @@ -38,9 +38,9 @@ ls_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ @LIB_ACL@ shred_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ vdir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ @LIB_ACL@ -cp_LDADD = $(LDADD) @LIB_ACL@ -mv_LDADD = $(LDADD) @LIB_ACL@ -ginstall_LDADD = $(LDADD) @LIB_ACL@ +cp_LDADD = $(LDADD) @LIB_ACL@ @LIB_XATTR@ +mv_LDADD = $(LDADD) @LIB_ACL@ @LIB_XATTR@ +ginstall_LDADD = $(LDADD) @LIB_ACL@ @LIB_XATTR@ ## If necessary, add -lm to resolve use of pow in lib/strtod.c. sort_LDADD = $(LDADD) @POW_LIB@ --- coreutils-4.5.8/src/copy.c +++ coreutils-4.5.8/src/copy.c @@ -47,6 +47,14 @@ #include "xreadlink.h" #include "acl.h" +#ifdef USE_XATTR +# include "regex.h" +# include +# include +# include +# include +#endif + #define DO_CHOWN(Chown, File, New_uid, New_gid) \ (Chown (File, New_uid, New_gid) \ /* If non-root uses -p, it's ok if we can't preserve ownership. \ @@ -117,6 +124,104 @@ return 0; } +#if USE_XATTR +static void +copy_attr_error (struct error_context *ctx, const char *fmt, ...) +{ + int err = errno; + va_list ap; + int len; + char *buffer; + + /* There is no error function that takes a va_list argument, + so we print the message in a buffer first. */ + + va_start (ap, fmt); + len = vsnprintf (NULL, 0, fmt, ap); + if (len > 0) + { + buffer = xmalloc (len + 1); + vsnprintf (buffer, len + 1, fmt, ap); + error (0, err, "%s", buffer); + free (buffer); + } + va_end (ap); +} + +static const char * +copy_attr_quote (struct error_context *ctx, const char *str) +{ + return xstrdup (quote (str)); +} + +static void +copy_attr_free (struct error_context *ctx, const char *str) +{ + free ((void *) str); +} + +struct copy_attr_context + { + struct error_context ctx; + const char *re_pattern; + struct re_pattern_buffer re_compiled; + } copy_attr_ctx = { + { copy_attr_error, + copy_attr_quote, + copy_attr_free } + }; + +static int +copy_attr_filter (const char *name, struct error_context *ctx) +{ + struct copy_attr_context *copy_ctx = (struct copy_attr_context *) ctx; + + return (attr_copy_check_permissions (name, ctx) + && copy_ctx->re_pattern != NULL + && re_search (©_ctx->re_compiled, name, strlen (name), 0, + strlen (name), NULL) >= 0); +} +#endif /* USE_XATTR */ + +static int +copy_extended_attributes (const char *src_path, const char *dst_path, + const struct cp_options *x) +{ +#if USE_XATTR + if (x->attr_pattern == NULL) + return 0; + + if (copy_attr_ctx.re_pattern != x->attr_pattern) + { + struct re_pattern_buffer *c = ©_attr_ctx.re_compiled; + size_t len = strlen (x->attr_pattern); + const char *err; + + free (c->fastmap); + free (c->buffer); + + copy_attr_ctx.re_pattern = x->attr_pattern; + c->allocated = 2 * len; + c->buffer = xmalloc (c->allocated); + c->fastmap = xmalloc (256); + c->translate = 0; + err = re_compile_pattern (x->attr_pattern, len, c); + if (err) + { + free (c->fastmap); + free (c->buffer); + copy_attr_ctx.re_pattern = NULL; + error (EXIT_FAILURE, 0, _("%s: invalid regular expression: %s"), + x->attr_pattern, err); + } + } + return attr_copy_file (src_path, dst_path, + copy_attr_filter, ©_attr_ctx.ctx); +#else /* USE_XATTR */ + return 0; +#endif /* USE_XATTR */ +} + /* Read the contents of the directory SRC_PATH_IN, and recursively copy the contents to DST_PATH_IN. NEW_DST is nonzero if DST_PATH_IN is a directory that was created previously in the @@ -1519,6 +1624,9 @@ } #endif + if (copy_extended_attributes (src_path, dst_path, x)) + delayed_fail = 1; + if (x->preserve_mode || x->move_mode) { if (copy_acl (src_path, dst_path, src_mode) && x->require_preserve) --- coreutils-4.5.8/src/copy.h +++ coreutils-4.5.8/src/copy.h @@ -102,6 +102,10 @@ int preserve_mode; int preserve_timestamps; + /* Regular expression pattern that specifies which extended attributes to + copy. NULL stands for copying no extended attributes. */ + const char *attr_pattern; + /* Enabled for mv, and for cp by the --preserve=links option. If nonzero, attempt to preserve in the destination files any logical hard links between the source files. If used with cp's --- coreutils-4.5.8/src/cp.c +++ coreutils-4.5.8/src/cp.c @@ -81,7 +81,8 @@ SPARSE_OPTION, STRIP_TRAILING_SLASHES_OPTION, TARGET_DIRECTORY_OPTION, - UNLINK_DEST_BEFORE_OPENING + UNLINK_DEST_BEFORE_OPENING, + PRESERVE_XATTRS_OPTION }; /* Initial number of entries in each hash table entry's table of inodes. */ @@ -137,6 +138,7 @@ {"parents", no_argument, NULL, PARENTS_OPTION}, {"path", no_argument, NULL, PARENTS_OPTION}, /* Deprecated. */ {"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION}, + {"attributes", required_argument, NULL, PRESERVE_XATTRS_OPTION}, {"recursive", no_argument, NULL, 'R'}, {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING}, {"reply", required_argument, NULL, REPLY_OPTION}, @@ -225,6 +227,13 @@ -v, --verbose explain what is being done\n\ -x, --one-file-system stay on this file system\n\ "), stdout); + fputs(_("\n\ + --attributes=regex preserve extended attributes whose name\n\ + matches the specified regular expression\n\ + (defaults to preserving all extended\n\ + attributes except file permissions;\n\ + regex=`-' preserves no extended attributes).\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\ @@ -792,6 +801,8 @@ x->verbose = 0; x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = ""; /* all extended attributes */ } /* Given a string, ARG, containing a comma-separated list of arguments @@ -1004,6 +1015,13 @@ x.require_preserve = 1; break; + case PRESERVE_XATTRS_OPTION: + if (strcmp (optarg, "-") == 0) + x.attr_pattern = NULL; + else + x.attr_pattern = optarg; + break; + case PARENTS_OPTION: flag_path = 1; break; --- coreutils-4.5.8/src/install.c +++ coreutils-4.5.8/src/install.c @@ -170,6 +170,8 @@ x->xstat = stat; x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = NULL; /* no extended attributes */ } int --- coreutils-4.5.8/src/mv.c +++ coreutils-4.5.8/src/mv.c @@ -142,6 +142,8 @@ x->xstat = lstat; x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = ""; /* all extended attributes */ } /* If PATH is an existing directory, return nonzero, else 0. */