/* Copy the permissions of src_path to dst_path. This includes the file mode permission bits and ACLs. File ownership is not copied. */ int perm_copy_fd (const char *src_path, int src_fd, const char *dst_path, int dst_fd, struct error_context *ctx) { #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) acl_t acl; #endif struct stat st; int ret = 0; ret = fstat(src_fd, &st); if (ret != 0) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl = acl_get_fd (src_fd); if (acl == NULL) { ret = -1; if (errno == ENOSYS || errno == ENOTSUP) ret = set_acl_fd (dst_path, dst_fd, st.st_mode, ctx); else { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); } return ret; } if (acl_set_fd (dst_fd, acl) != 0) { int saved_errno = errno; __apply_mask_to_mode(&st.st_mode, acl); ret = fchmod (dst_fd, st.st_mode); if ((errno != ENOSYS && errno != ENOTSUP) || acl_entries (acl) != 3) { const char *qpath = quote (ctx, dst_path); errno = saved_errno; error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); ret = -1; } } (void) acl_free (acl); return ret; #else /* POSIX.1 version. */ ret = fchmod (dst_fd, st.st_mode); if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; #endif }
int getacl(const char *pathp, aclent_t *aclpbuf) { acl_t acl = NULL, default_acl = NULL; struct stat st; if (stat(pathp, &st) != 0) { return -1; } acl = acl_get_file(pathp, ACL_TYPE_ACCESS); if(acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) { acl = acl_from_mode(st.st_mode); if (acl == NULL) { return -1; } } if (S_ISDIR(st.st_mode)) { default_acl = acl_get_file(pathp, ACL_TYPE_DEFAULT); if ((default_acl != NULL) && (acl_entries(default_acl) == 0)) { acl_free(default_acl); default_acl = NULL; } } acl_entry_t acl_entry; int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); int i = 0; while(ret > 0) { aclpbuf[i++] = getentry(acl_entry, st, 0); ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); } acl_free(acl); if((default_acl != NULL) && (ret != -1)) { ret = acl_get_entry(default_acl, ACL_FIRST_ENTRY, &acl_entry); while(ret > 0) { aclpbuf[i++] = getentry(acl_entry, st, ACL_DEFAULT); ret = acl_get_entry(default_acl, ACL_NEXT_ENTRY, &acl_entry); } acl_free(default_acl); } return i; }
/* * lists the acl for a file/dir in short text form * return 0 on failure * return 1 on success */ static int list_acl(char *file) { acl_t acl = NULL; acl_t dacl = NULL; char *acl_text, *dacl_text = NULL; if ((acl = acl_get_file(file, ACL_TYPE_ACCESS)) == NULL) { fprintf(stderr, _("%s: cannot get access ACL on '%s': %s\n"), program, file, strerror(errno)); return 0; } if ((dacl = acl_get_file(file, ACL_TYPE_DEFAULT)) == NULL && (errno != EACCES)) { /* EACCES given if not a directory */ fprintf(stderr, _("%s: cannot get default ACL on '%s': %s\n"), program, file, strerror(errno)); return 0; } acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE); if (acl_text == NULL) { fprintf(stderr, _("%s: cannot get access ACL text on " "'%s': %s\n"), program, file, strerror(errno)); return 0; } if (acl_entries(dacl) > 0) { dacl_text = acl_to_any_text(dacl, NULL, ',', TEXT_ABBREVIATE); if (dacl_text == NULL) { fprintf(stderr, _("%s: cannot get default ACL text on " "'%s': %s\n"), program, file, strerror(errno)); return 0; } } if (dacl_text) { printf("%s [%s/%s]\n", file, acl_text, dacl_text); acl_free(dacl_text); } else printf("%s [%s]\n", file, acl_text); acl_free(acl_text); acl_free(acl); acl_free(dacl); return 1; }
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. Return 1 if the given ACL is non-trivial. Return 0 if it is trivial. */ int acl_extended_nontrivial (acl_t acl) { /* acl is non-trivial if it is non-empty. */ return (acl_entries (acl) > 0); }
int acl_default_nontrivial (acl_t acl) { /* acl is non-trivial if it is non-empty. */ return (acl_entries (acl) > 0); }
/* Copy the permissions of src_path to dst_path. This includes the file mode permission bits and ACLs. File ownership is not copied. */ int perm_copy_file (const char *src_path, const char *dst_path, struct error_context *ctx) { #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) acl_t acl; #endif struct stat st; int ret = 0; ret = stat(src_path, &st); if (ret != 0) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl = acl_get_file (src_path, ACL_TYPE_ACCESS); if (acl == NULL) { ret = -1; if (errno == ENOSYS || errno == ENOTSUP) ret = set_acl (dst_path, st.st_mode, ctx); else { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); } return ret; } if (acl_set_file (dst_path, ACL_TYPE_ACCESS, acl) != 0) { int saved_errno = errno; __apply_mask_to_mode(&st.st_mode, acl); ret = chmod (dst_path, st.st_mode); if ((errno != ENOSYS && errno != ENOTSUP) || acl_entries (acl) != 3) { const char *qpath = quote (ctx, dst_path); errno = saved_errno; error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); ret = -1; } } (void) acl_free (acl); if (ret == 0 && S_ISDIR (st.st_mode)) { acl = acl_get_file (src_path, ACL_TYPE_DEFAULT); if (acl == NULL) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } # if defined(HAVE_ACL_DELETE_DEF_FILE) if (acl_entries(acl) == 0) ret = acl_delete_def_file(dst_path); else ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl); # else ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl); # endif if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); } (void) acl_free(acl); } return ret; #else /* POSIX.1 version. */ ret = chmod (dst_path, st.st_mode); if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; #endif }
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 (ACL_NOT_WELL_SUPPORTED (errno)) 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 (ACL_NOT_WELL_SUPPORTED (errno)) { 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 }
int file_has_acl (char const *name, struct stat const *sb) { #if USE_ACL if (! S_ISLNK (sb->st_mode)) { # if HAVE_ACL_GET_FILE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ int ret; if (HAVE_ACL_EXTENDED_FILE) /* Linux */ { /* On Linux, acl_extended_file is an optimized function: It only makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for ACL_TYPE_DEFAULT. */ ret = acl_extended_file (name); } else /* FreeBSD, MacOS X, IRIX, Tru64 */ { # if HAVE_ACL_TYPE_EXTENDED /* MacOS X */ /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS) and acl_get_file (name, ACL_TYPE_DEFAULT) always return NULL / EINVAL. There is no point in making these two useless calls. The real ACL is retrieved through acl_get_file (name, ACL_TYPE_EXTENDED). */ acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED); if (acl) { ret = acl_extended_nontrivial (acl); acl_free (acl); } else ret = -1; # else /* FreeBSD, IRIX, Tru64 */ acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); if (acl) { int saved_errno; ret = acl_access_nontrivial (acl); saved_errno = errno; acl_free (acl); errno = saved_errno; # if HAVE_ACL_FREE_TEXT /* Tru64 */ /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always returns NULL with errno not set. There is no point in making this call. */ # else /* FreeBSD, IRIX */ /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory either both succeed or both fail; it depends on the file system. Therefore there is no point in making the second call if the first one already failed. */ 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; } # endif } else ret = -1; # endif } if (ret < 0) return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1; return ret; # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ # if defined ACL_NO_TRIVIAL /* Solaris 10 (newer version), which has additional API declared in <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial, acl_fromtext, ...). */ return acl_trivial (name); # else /* Solaris, Cygwin, general case */ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions of Unixware. The acl() call returns the access and default ACL both at once. */ int count; { aclent_t *entries; for (;;) { count = acl (name, GETACLCNT, 0, NULL); if (count < 0) { if (errno == ENOSYS || errno == ENOTSUP) break; else return -1; } if (count == 0) break; /* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cygwin returns only 3 entries for files with no ACL. But this is safe: If there are more than 4 entries, there cannot be only the "user::", "group::", "other:", and "mask:" entries. */ if (count > 4) return 1; entries = (aclent_t *) malloc (count * sizeof (aclent_t)); if (entries == NULL) { errno = ENOMEM; return -1; } if (acl (name, GETACL, count, entries) == count) { if (acl_nontrivial (count, entries)) { free (entries); return 1; } free (entries); break; } /* Huh? The number of ACL entries changed since the last call. Repeat. */ free (entries); } } # ifdef ACE_GETACL /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 file systems (whereas the other ones are used in UFS file systems). */ { ace_t *entries; for (;;) { count = acl (name, ACE_GETACLCNT, 0, NULL); if (count < 0) { if (errno == ENOSYS || errno == EINVAL) break; else return -1; } if (count == 0) break; /* In the old (original Solaris 10) convention: If there are more than 3 entries, there cannot be only the ACE_OWNER, ACE_GROUP, ACE_OTHER entries. In the newer Solaris 10 and Solaris 11 convention: If there are more than 6 entries, there cannot be only the ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with NEW_ACE_ACCESS_DENIED_ACE_TYPE. */ if (count > 6) return 1; entries = (ace_t *) malloc (count * sizeof (ace_t)); if (entries == NULL) { errno = ENOMEM; return -1; } if (acl (name, ACE_GETACL, count, entries) == count) { if (acl_ace_nontrivial (count, entries)) { free (entries); return 1; } free (entries); break; } /* Huh? The number of ACL entries changed since the last call. Repeat. */ free (entries); } } # endif return 0; # endif # elif HAVE_GETACL /* HP-UX */ for (;;) { int count; struct acl_entry entries[NACLENTRIES]; count = getacl (name, 0, NULL); if (count < 0) { /* ENOSYS is seen on newer HP-UX versions. EOPNOTSUPP is typically seen on NFS mounts. ENOTSUP was seen on Quantum StorNext file systems (cvfs). */ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) break; else return -1; } if (count == 0) return 0; if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); /* If there are more than 3 entries, there cannot be only the (uid,%), (%,gid), (%,%) entries. */ if (count > 3) return 1; if (getacl (name, count, entries) == count) { struct stat statbuf; if (stat (name, &statbuf) < 0) return -1; return acl_nontrivial (count, entries, &statbuf); } /* Huh? The number of ACL entries changed since the last call. Repeat. */ } # if HAVE_ACLV_H /* HP-UX >= 11.11 */ for (;;) { int count; struct acl entries[NACLVENTRIES]; count = acl ((char *) name, ACL_CNT, NACLVENTRIES, entries); if (count < 0) { /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23. EINVAL is seen on NFS in HP-UX 11.31. */ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) break; else return -1; } if (count == 0) return 0; if (count > NACLVENTRIES) /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); /* If there are more than 4 entries, there cannot be only the four base ACL entries. */ if (count > 4) return 1; if (acl ((char *) name, ACL_GET, count, entries) == count) return aclv_nontrivial (count, entries); /* Huh? The number of ACL entries changed since the last call. Repeat. */ } # endif # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ acl_type_t type; char aclbuf[1024]; void *acl = aclbuf; size_t aclsize = sizeof (aclbuf); mode_t mode; for (;;) { /* The docs say that type being 0 is equivalent to ACL_ANY, but it is not true, in AIX 5.3. */ type.u64 = ACL_ANY; if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0) break; if (errno == ENOSYS) return 0; if (errno != ENOSPC) { if (acl != aclbuf) { int saved_errno = errno; free (acl); errno = saved_errno; } return -1; } aclsize = 2 * aclsize; if (acl != aclbuf) free (acl); acl = malloc (aclsize); if (acl == NULL) { errno = ENOMEM; return -1; } } if (type.u64 == ACL_AIXC) { int result = acl_nontrivial ((struct acl *) acl); if (acl != aclbuf) free (acl); return result; } else if (type.u64 == ACL_NFS4) { int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl); if (acl != aclbuf) free (acl); return result; } else { /* A newer type of ACL has been introduced in the system. We should better support it. */ if (acl != aclbuf) free (acl); errno = EINVAL; return -1; } # elif HAVE_STATACL /* older AIX */ union { struct acl a; char room[4096]; } u; if (statacl (name, STX_NORMAL, &u.a, sizeof (u)) < 0) return -1; return acl_nontrivial (&u.a); # elif HAVE_ACLSORT /* NonStop Kernel */ int count; struct acl entries[NACLENTRIES]; for (;;) { count = acl ((char *) name, ACL_CNT, NACLENTRIES, NULL); if (count < 0) { if (errno == ENOSYS || errno == ENOTSUP) break; else return -1; } if (count == 0) return 0; if (count > NACLENTRIES) /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ abort (); /* If there are more than 4 entries, there cannot be only the four base ACL entries. */ if (count > 4) return 1; if (acl ((char *) name, ACL_GET, count, entries) == count) return acl_nontrivial (count, entries); /* Huh? The number of ACL entries changed since the last call. Repeat. */ } # endif } #endif return 0; }
int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused) { const char *default_prefix = NULL; acl_t acl = NULL, default_acl = NULL; int error = 0; if (walk_flags & WALK_TREE_FAILED) { fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"), strerror(errno)); return 1; } /* * Symlinks can never have ACLs, so when doing a physical walk, we * skip symlinks altogether, and when doing a half-logical walk, we * skip all non-toplevel symlinks. */ if ((walk_flags & WALK_TREE_SYMLINK) && ((walk_flags & WALK_TREE_PHYSICAL) || !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL)))) return 0; if (opt_print_acl) { acl = acl_get_file(path_p, ACL_TYPE_ACCESS); if (acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) acl = acl_get_file_mode(path_p); if (acl == NULL) goto fail; } if (opt_print_default_acl && S_ISDIR(st->st_mode)) { default_acl = acl_get_file(path_p, ACL_TYPE_DEFAULT); if (default_acl == NULL) { if (errno != ENOSYS && errno != ENOTSUP) goto fail; } else if (acl_entries(default_acl) == 0) { acl_free(default_acl); default_acl = NULL; } } if (opt_skip_base && (!acl || acl_equiv_mode(acl, NULL) == 0) && !default_acl) return 0; if (opt_print_acl && opt_print_default_acl) default_prefix = "default:"; if (opt_strip_leading_slash) { if (*path_p == '/') { if (!absolute_warning) { fprintf(stderr, _("%s: Removing leading " "'/' from absolute path names\n"), progname); absolute_warning = 1; } while (*path_p == '/') path_p++; } else if (*path_p == '.' && *(path_p+1) == '/') while (*++path_p == '/') /* nothing */ ; if (*path_p == '\0') path_p = "."; } if (opt_tabular) { if (do_show(stdout, path_p, st, acl, default_acl) != 0) goto fail; } else { if (opt_comments) { printf("# file: %s\n", xquote(path_p, "\n\r")); printf("# owner: %s\n", xquote(user_name(st->st_uid, opt_numeric), " \t\n\r")); printf("# group: %s\n", xquote(group_name(st->st_gid, opt_numeric), " \t\n\r")); if ((st->st_mode & (S_ISVTX | S_ISUID | S_ISGID)) && !posixly_correct) printf("# flags: %s\n", flagstr(st->st_mode)); } if (acl != NULL) { char *acl_text = acl_to_any_text(acl, NULL, '\n', print_options); if (!acl_text) goto fail; if (puts(acl_text) < 0) { acl_free(acl_text); goto fail; } acl_free(acl_text); } if (default_acl != NULL) { char *acl_text = acl_to_any_text(default_acl, default_prefix, '\n', print_options); if (!acl_text) goto fail; if (puts(acl_text) < 0) { acl_free(acl_text); goto fail; } acl_free(acl_text); } } if (acl || default_acl || opt_comments) printf("\n"); cleanup: if (acl) acl_free(acl); if (default_acl) acl_free(default_acl); return error; fail: fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"), strerror(errno)); error = -1; goto cleanup; }
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. */ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ 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 (ACL_NOT_WELL_SUPPORTED (errno)) 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 (ACL_NOT_WELL_SUPPORTED (errno)) { int n = acl_entries (acl); acl_free (acl); /* On most hosts with MODE_INSIDE_ACL an ACL is trivial if n == 3, and it cannot be less than 3. On IRIX 6.5 it is also trivial if n == -1. For simplicity and safety, assume the ACL is trivial if n <= 3. Also see file-has-acl.c for some of the other possibilities; it's not clear whether that complexity is needed here. */ if (n <= 3 * MODE_INSIDE_ACL) { 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_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ 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 # if USE_ACL && defined ACL_NO_TRIVIAL /* Solaris 10 NFSv4 ACLs. */ acl_t *aclp = NULL; ret = (source_desc < 0 ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp) : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp)); if (ret != 0 && errno != ENOSYS) { error (0, errno, "%s", quote (src_name)); return ret; } # endif ret = qset_acl (dst_name, dest_desc, mode); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); # if USE_ACL && defined ACL_NO_TRIVIAL if (ret == 0 && aclp) { ret = (dest_desc < 0 ? acl_set (dst_name, aclp) : facl_set (dest_desc, aclp)); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); acl_free (aclp); } # endif return ret; #endif }
int do_set( const char *path_p, const struct stat *st, const seq_t seq) { acl_t old_acl = NULL, old_default_acl = NULL; acl_t acl = NULL, default_acl = NULL; acl_t *xacl, *old_xacl; acl_entry_t ent; cmd_t cmd; int which_entry; int errors = 0, error; char *acl_text; int acl_modified = 0, default_acl_modified = 0; int acl_mask_provided = 0, default_acl_mask_provided = 0; /* Execute the commands in seq (read ACLs on demand) */ error = seq_get_cmd(seq, SEQ_FIRST_CMD, &cmd); if (error == 0) return 0; while (error == 1) { if (cmd->c_type == ACL_TYPE_ACCESS) { xacl = &acl; old_xacl = &old_acl; acl_modified = 1; if (cmd->c_tag == ACL_MASK) acl_mask_provided = 1; } else { xacl = &default_acl; old_xacl = &old_default_acl; default_acl_modified = 1; if (cmd->c_tag == ACL_MASK) default_acl_mask_provided = 1; } RETRIEVE_ACL(cmd->c_type); /* Check for `X', and replace with `x' as appropriate. */ if (cmd->c_perm & CMD_PERM_COND_EXECUTE) { cmd->c_perm &= ~CMD_PERM_COND_EXECUTE; if (S_ISDIR(st->st_mode) || has_execute_perms(*xacl)) cmd->c_perm |= CMD_PERM_EXECUTE; } switch(cmd->c_cmd) { case CMD_ENTRY_REPLACE: ent = find_entry(*xacl, cmd->c_tag, cmd->c_id); if (!ent) { if (acl_create_entry(xacl, &ent) != 0) goto fail; acl_set_tag_type(ent, cmd->c_tag); if (cmd->c_id != ACL_UNDEFINED_ID) acl_set_qualifier(ent, &cmd->c_id); } set_perm(ent, cmd->c_perm, ~cmd->c_perm); break; case CMD_ENTRY_ADD: ent = find_entry(*xacl, cmd->c_tag, cmd->c_id); if (ent) set_perm(ent, cmd->c_perm, 0); break; case CMD_ENTRY_SUBTRACT: ent = find_entry(*xacl, cmd->c_tag, cmd->c_id); if (ent) set_perm(ent, 0, cmd->c_perm); break; case CMD_REMOVE_ENTRY: ent = find_entry(*xacl, cmd->c_tag, cmd->c_id); if (ent) acl_delete_entry(*xacl, ent); else /* ignore */; break; case CMD_REMOVE_EXTENDED_ACL: remove_extended_entries(acl); break; case CMD_REMOVE_ACL: acl_free(*xacl); *xacl = acl_init(5); if (!*xacl) goto fail; break; default: errno = EINVAL; goto fail; } error = seq_get_cmd(seq, SEQ_NEXT_CMD, &cmd); } if (error < 0) goto fail; /* Try to fill in missing entries */ if (default_acl && acl_entries(default_acl) != 0) { xacl = &acl; old_xacl = &old_acl; if (!find_entry(default_acl, ACL_USER_OBJ, ACL_UNDEFINED_ID)) { if (!acl) RETRIEVE_ACL(ACL_TYPE_ACCESS); clone_entry(acl, ACL_USER_OBJ, &default_acl, ACL_USER_OBJ); } if (!find_entry(default_acl, ACL_GROUP_OBJ, ACL_UNDEFINED_ID)) { if (!acl) RETRIEVE_ACL(ACL_TYPE_ACCESS); clone_entry(acl, ACL_GROUP_OBJ, &default_acl, ACL_GROUP_OBJ); } if (!find_entry(default_acl, ACL_OTHER, ACL_UNDEFINED_ID)) { if (!acl) RETRIEVE_ACL(ACL_TYPE_ACCESS); clone_entry(acl, ACL_OTHER, &default_acl, ACL_OTHER); } } /* update mask entries and check if ACLs are valid */ if (acl && acl_modified) { if (acl_equiv_mode(acl, NULL) != 0) { if (!acl_mask_provided && !find_entry(acl, ACL_MASK, ACL_UNDEFINED_ID)) clone_entry(acl, ACL_GROUP_OBJ, &acl, ACL_MASK); if (opt_recalculate != -1 && (!acl_mask_provided || opt_recalculate == 1)) acl_calc_mask(&acl); } error = acl_check(acl, &which_entry); if (error < 0) goto fail; if (error > 0) { acl_text = acl_to_any_text(acl, NULL, ',', 0); fprintf(stderr, gettext("%s: %s: Malformed access ACL " "`%s': %s at entry %d\n"), progname, path_p, acl_text, acl_error(error), which_entry+1); acl_free(acl_text); errors++; goto cleanup; } } if (default_acl && acl_entries(default_acl) != 0 && default_acl_modified) { if (acl_equiv_mode(default_acl, NULL) != 0) { if (!default_acl_mask_provided && !find_entry(default_acl,ACL_MASK,ACL_UNDEFINED_ID)) clone_entry(default_acl, ACL_GROUP_OBJ, &default_acl, ACL_MASK); if (opt_recalculate != -1 && (!default_acl_mask_provided || opt_recalculate == 1)) acl_calc_mask(&default_acl); } error = acl_check(default_acl, &which_entry); if (error < 0) goto fail; if (error > 0) { acl_text = acl_to_any_text(default_acl, NULL, ',', 0); fprintf(stderr, gettext("%s: %s: Malformed default ACL " "`%s': %s at entry %d\n"), progname, path_p, acl_text, acl_error(error), which_entry+1); acl_free(acl_text); errors++; goto cleanup; } } /* Only directores can have default ACLs */ if (default_acl && !S_ISDIR(st->st_mode) && opt_recursive) { /* In recursive mode, ignore default ACLs for files */ acl_free(default_acl); default_acl = NULL; } /* check which ACLs have changed */ if (acl && old_acl && acl_cmp(old_acl, acl) == 0) { acl_free(acl); acl = NULL; } if ((default_acl && old_default_acl && acl_cmp(old_default_acl, default_acl) == 0)) { acl_free(default_acl); default_acl = NULL; } /* update the file system */ if (opt_test) { print_test(stdout, path_p, st, acl, default_acl); goto cleanup; } if (acl) { if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) { if (errno == ENOSYS || errno == ENOTSUP) { int saved_errno = errno; mode_t mode; if (acl_equiv_mode(acl, &mode) != 0) { errno = saved_errno; goto fail; } else if (chmod(path_p, mode) != 0) goto fail; } else goto fail; } } if (default_acl) { if (S_ISDIR(st->st_mode)) { if (acl_entries(default_acl) == 0) { if (acl_delete_def_file(path_p) != 0 && errno != ENOSYS && errno != ENOTSUP) goto fail; } else { if (acl_set_file(path_p, ACL_TYPE_DEFAULT, default_acl) != 0) goto fail; } } else { if (acl_entries(default_acl) != 0) { fprintf(stderr, gettext( "%s: %s: Only directories " "can have default ACLs\n"), progname, path_p); errors++; goto cleanup; } } } error = 0; cleanup: if (acl) acl_free(acl); if (old_acl) acl_free(old_acl); if (default_acl) acl_free(default_acl); if (old_default_acl) acl_free(old_default_acl); return errors; fail: fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno)); errors++; goto cleanup; }