Index: coreutils/lib/acl.c =================================================================== --- coreutils.orig/lib/acl.c +++ coreutils/lib/acl.c @@ -23,12 +23,17 @@ #endif #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" @@ -37,6 +42,9 @@ #ifndef ENOSYS # define ENOSYS (-1) #endif +#ifndef ENOTSUP +# define ENOTSUP (-1) +#endif #if ENABLE_NLS # include @@ -45,8 +53,38 @@ # define _(Text) Text #endif -#ifndef MIN_ACL_ENTRIES -# define MIN_ACL_ENTRIES 4 +/* If DESC is a valid file handle (not -1), fchown it to MODE. + Otherwise, chown NAME to MODE. System call return value + semantics. */ + +static int +chmod_or_fchmod (const char *name, int desc, mode_t mode) +{ +#if HAVE_FCHMOD + if (desc != -1) + return fchmod (desc, mode); + else +#endif + return chmod (name, mode); +} + +/* Return the number of entries of ACL. On error, return -1. */ + +#if USE_ACL && HAVE_ACL_TO_TEXT && !HAVE_ACL_ENTRIES +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++) { + if ((t = strchr (t, '\n')) == NULL) + break; + } + acl_free (text); + return entries; +} #endif /* Return 1 if NAME has a nontrivial access control list, 0 if @@ -56,20 +94,59 @@ int 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. */ + +# ifndef MIN_ACL_ENTRIES +# define MIN_ACL_ENTRIES 4 +# endif -#if HAVE_ACL && defined GETACLCNT if (! S_ISLNK (sb->st_mode)) { int n = acl (name, GETACLCNT, 0, NULL); return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n); } +#elif USE_ACL + if (! S_ISLNK (sb->st_mode)) + { + int ret; +# if HAVE_ACL_EXTENDED_FILE + /* Linux-specific version. */ + + ret = acl_extended_file (name); +# else + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + + 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; +# endif + 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; } @@ -86,16 +163,105 @@ copy_acl (const char *src_name, int sour int dest_desc, mode_t mode) { int ret; -#ifdef HAVE_FCHMOD + +#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && \ + HAVE_ACL_FREE && HAVE_ACL_ENTRIES + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + + acl_t acl; +#if HAVE_ACL_GET_FD + if (source_desc != -1) + acl = acl_get_fd (source_desc); + else +#endif + 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 if (dest_desc != -1) - ret = fchmod (dest_desc, mode); + ret = acl_set_fd (dest_desc, acl); else -#else - ret = chmod (dst_name, mode); #endif - if (ret) + 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 list of (DESC or NAME) to the equivalent of MODE. @@ -107,13 +273,90 @@ copy_acl (const char *src_name, int sour int set_acl (char const *name, int desc, mode_t mode) { +#if USE_ACL && HAVE_ACL_FROM_TEXT && HAVE_ACL_SET_FILE && HAVE_ACL_FREE && \ + HAVE_ACL_DELETE_DEF_FILE + acl_t acl; int ret; -#ifdef HAVE_FCHMOD - ret = fchmod (desc, mode); +# if HAVE_ACL_FROM_MODE + /* Linux-specific version. */ + + acl = acl_from_mode (mode); + if (!acl) + { + error (0, errno, "%s", quote (name)); + return -1; + } + +# elif HAVE_ACL_FROM_TEXT + /* POSIX 1003.1e draft 17 (abandoned) specific version. */ + + 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; + } #else - ret = chmod (name, mode); +# error Need either acl_from_mode or acl_from_test +#endif +#if HAVE_ACL_SET_FD + if (desc != -1) + ret = acl_set_fd (desc, acl); + else +#endif + 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 - if (ret) - error (0, errno, _("setting permissions for %s"), quote (name)); - return ret; } 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,3 +1,18 @@ +2005-12-04 Andreas Gruenbacher + + * lib/acl.h: Add copy_acl() and set_acl(). Fix for POSIX/Linux ACLs. + * lib/acl.c: Add copy_acl() and set_acl() implementations in versions + for Linux and other POSIX 1003.1e draft 17. Fix file_has_acl for + those platforms. + * 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. + 2005-12-04 Jim Meyering * Version 6.0-cvs.