diff -urNp current/TODO nfs4acl/TODO --- current/TODO 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/TODO 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,35 @@ +Things that need to be done before this goes into -CURRENT: + +- Don't use fpathconf(..., _PC_EXTENDED_SECURITY_NP); instead just call + acl_set_file(3) or acl_get_file(3) with ACL_TYPE_NFS4 and handle EOPNOTSUPP. + +- Decide how VAPPEND is supposed to work - always OR-ed with VWRITE, + or used alone. Fix stuff accordingly. + +- MFP4 of the above. + +- Decide what to do with MNT_RDONLY et a. Fix stuff. + +- MFP4 of the above. + +- Talk about semantics. Do whatever is needed. + +- Figure out whether this thing should be called 'nfs4' or 'nfsv4'. + Use this name consistently. + +- Review. + +Things to do, in no particular order: + +- Add support for NFSv4 ACLs to tar(1). + +- Make 'struct acl' variable size. + +- Benchmark things. + +- (maybe) Add a flag to inode to mark whether the file has ACL; + don't try to read ACL extatrr if the flag is not set. + +- Port Samba vfs_zfsacl module using libsunacl + (http://freebsd.org/~trasz/libsunacl.shar). + diff -urNp current/bin/chmod/chmod.c nfs4acl/bin/chmod/chmod.c --- current/bin/chmod/chmod.c 2009-06-23 22:30:00.000000000 +0200 +++ nfs4acl/bin/chmod/chmod.c 2009-06-26 15:39:56.000000000 +0200 @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD: src/bin/chmod/chmod. #include static void usage(void); +static int may_have_nfs4acl(const FTSENT *ent); int main(int argc, char *argv[]) @@ -180,8 +181,15 @@ done: argv += optind; break; } newmode = getmode(set, p->fts_statp->st_mode); - if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) - continue; + /* + * With NFSv4 ACLs, it is possible that applying a mode + * identical to the one computed from an ACL will change + * that ACL. + */ + if (may_have_nfs4acl(p) == 0) { + if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) + continue; + } if ((*change_mode)(p->fts_accpath, newmode) && !fflag) { warn("%s", p->fts_path); rval = 1; @@ -219,3 +227,25 @@ usage(void) "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); exit(1); } + +static int +may_have_nfs4acl(const FTSENT *ent) +{ + int ret; + static dev_t previous_dev = (dev_t)-1; + static int supports_acls = -1; + + if (previous_dev != ent->fts_statp->st_dev) { + previous_dev = ent->fts_statp->st_dev; + supports_acls = 0; + + ret = pathconf(ent->fts_accpath, _PC_EXTENDED_SECURITY_NP); + if (ret > 0) + supports_acls = 1; + else if (ret < 0 && errno != EINVAL) + warn("%s", ent->fts_path); + } + + return (supports_acls); +} + diff -urNp current/bin/cp/utils.c nfs4acl/bin/cp/utils.c --- current/bin/cp/utils.c 2009-05-30 20:05:56.000000000 +0200 +++ nfs4acl/bin/cp/utils.c 2009-05-30 20:11:28.000000000 +0200 @@ -377,21 +377,57 @@ setfile(struct stat *fs, int fd) int preserve_fd_acls(int source_fd, int dest_fd) { - struct acl *aclp; acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0, trivial; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } - if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || - fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + if (fpathconf(source_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } + + if (fpathconf(dest_fd, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(dest_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) return (0); - acl = acl_get_fd(source_fd); + + acl = acl_get_fd_np(source_fd, source_type); if (acl == NULL) { warn("failed to get acl entries while setting %s", to.p_path); return (1); } - aclp = &acl->ats_acl; - if (aclp->acl_cnt == 3) + + if (acl_is_trivial_np(acl, &trivial)) { + warn("acl_is_trivial() failed"); + return (1); + } + + if (trivial) + return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + return (0); - if (acl_set_fd(dest_fd, acl) < 0) { + } + + if (acl_set_fd_np(dest_fd, acl, dest_type) < 0) { warn("failed to set acl entries for %s", to.p_path); return (1); } @@ -405,10 +441,41 @@ preserve_dir_acls(struct stat *fs, char int (*aclsetf)(const char *, acl_type_t, acl_t); struct acl *aclp; acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0; + + if (pathconf(source_dir, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } + + if (pathconf(source_dir, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } - if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || - pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + if (pathconf(dest_dir, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (pathconf(dest_dir, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + + return (0); + } + /* * If the file is a link we will not follow it */ @@ -419,31 +486,35 @@ preserve_dir_acls(struct stat *fs, char aclgetf = acl_get_file; aclsetf = acl_set_file; } - /* - * Even if there is no ACL_TYPE_DEFAULT entry here, a zero - * size ACL will be returned. So it is not safe to simply - * check the pointer to see if the default ACL is present. - */ - acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); - if (acl == NULL) { - warn("failed to get default acl entries on %s", - source_dir); - return (1); - } - aclp = &acl->ats_acl; - if (aclp->acl_cnt != 0 && aclsetf(dest_dir, - ACL_TYPE_DEFAULT, acl) < 0) { - warn("failed to set default acl entries on %s", - dest_dir); - return (1); + + if (source_type != ACL_TYPE_NFS4) { + /* + * Even if there is no ACL_TYPE_DEFAULT entry here, a zero + * size ACL will be returned. So it is not safe to simply + * check the pointer to see if the default ACL is present. + */ + acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); + if (acl == NULL) { + warn("failed to get default acl entries on %s", + source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt != 0 && aclsetf(dest_dir, + ACL_TYPE_DEFAULT, acl) < 0) { + warn("failed to set default acl entries on %s", + dest_dir); + return (1); + } } - acl = aclgetf(source_dir, ACL_TYPE_ACCESS); + + acl = aclgetf(source_dir, source_type); if (acl == NULL) { warn("failed to get acl entries on %s", source_dir); return (1); } aclp = &acl->ats_acl; - if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { + if (aclsetf(dest_dir, dest_type, acl) < 0) { warn("failed to set acl entries on %s", dest_dir); return (1); } diff -urNp current/bin/getfacl/getfacl.1 nfs4acl/bin/getfacl/getfacl.1 --- current/bin/getfacl/getfacl.1 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/getfacl/getfacl.1 2009-05-26 17:15:43.000000000 +0200 @@ -38,7 +38,7 @@ .Nd get ACL information .Sh SYNOPSIS .Nm -.Op Fl dhq +.Op Fl dhinqv .Op Ar .Sh DESCRIPTION The @@ -61,13 +61,24 @@ The operation applies to the default ACL access ACL. An error is generated if a default ACL cannot be associated with .Ar file . +This option is not valid for NFSv4 ACLs. .It Fl h If the target of the operation is a symbolic link, return the ACL from the symbolic link itself rather than following the link. +.It Fl i +For NFSv4 ACLs, append numerical ID at the end of each entry containing +user or group name. +.It Fl n +Display user and group IDs numerically rather than converting to +a user or group name. +For POSIX.1e ACLs, this option does nothing. .It Fl q Do not write commented information about file name and ownership. This is useful when dealing with filenames with unprintable characters. +.It Fl v +For NFSv4 ACLs, display access mask and flags in a verbose form. +For POSIX.1e ACLs, this option does nothing. .El .Pp The following operand is available: diff -urNp current/bin/getfacl/getfacl.c nfs4acl/bin/getfacl/getfacl.c --- current/bin/getfacl/getfacl.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/getfacl/getfacl.c 2009-05-26 17:15:43.000000000 +0200 @@ -54,7 +54,7 @@ static void usage(void) { - fprintf(stderr, "getfacl [-dhq] [file ...]\n"); + fprintf(stderr, "getfacl [-dhnqv] [file ...]\n"); } static char * @@ -175,19 +175,20 @@ acl_from_stat(struct stat sb) } static int -print_acl(char *path, acl_type_t type, int hflag, int qflag) +print_acl(char *path, acl_type_t type, int hflag, int iflag, int nflag, + int qflag, int vflag) { struct stat sb; acl_t acl; char *acl_text; - int error; + int error, flags = 0; if (hflag) error = lstat(path, &sb); else error = stat(path, &sb); if (error == -1) { - warn("%s", path); + warn("%s: stat() failed", path); return(-1); } @@ -196,6 +197,16 @@ print_acl(char *path, acl_type_t type, i else more_than_one++; + if (pathconf(path, _PC_EXTENDED_SECURITY_NP)) { + if (type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries in NFSv4 ACLs", + path); + return (-1); + } + + type = ACL_TYPE_NFS4; + } + if (!qflag) printf("# file: %s\n# owner: %s\n# group: %s\n", path, getuname(sb.st_uid), getgname(sb.st_gid)); @@ -210,18 +221,27 @@ print_acl(char *path, acl_type_t type, i return(-1); } errno = 0; - if (type != ACL_TYPE_ACCESS) + if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_NFS4) return(0); acl = acl_from_stat(sb); if (!acl) { - warn("acl_from_stat()"); + warn("%s: acl_from_stat() failed", path); return(-1); } } - acl_text = acl_to_text(acl, 0); + if (iflag) + flags |= ACL_TEXT_APPEND_ID; + + if (nflag) + flags |= ACL_TEXT_NUMERIC_IDS; + + if (vflag) + flags |= ACL_TEXT_VERBOSE; + + acl_text = acl_to_text_np(acl, 0, flags); if (!acl_text) { - warn("%s", path); + warn("%s: acl_to_text_np() failed", path); return(-1); } @@ -234,7 +254,8 @@ print_acl(char *path, acl_type_t type, i } static int -print_acl_from_stdin(acl_type_t type, int hflag, int qflag) +print_acl_from_stdin(acl_type_t type, int hflag, int iflag, int nflag, + int qflag, int vflag) { char *p, pathname[PATH_MAX]; int carried_error = 0; @@ -242,7 +263,8 @@ print_acl_from_stdin(acl_type_t type, in while (fgets(pathname, (int)sizeof(pathname), stdin)) { if ((p = strchr(pathname, '\n')) != NULL) *p = '\0'; - if (print_acl(pathname, type, hflag, qflag) == -1) { + if (print_acl(pathname, type, hflag, iflag, nflag, + qflag, vflag) == -1) { carried_error = -1; } } @@ -256,11 +278,14 @@ main(int argc, char *argv[]) acl_type_t type = ACL_TYPE_ACCESS; int carried_error = 0; int ch, error, i; - int hflag, qflag; + int hflag, iflag, qflag, nflag, vflag; hflag = 0; + iflag = 0; qflag = 0; - while ((ch = getopt(argc, argv, "dhq")) != -1) + nflag = 0; + vflag = 0; + while ((ch = getopt(argc, argv, "dhinqv")) != -1) switch(ch) { case 'd': type = ACL_TYPE_DEFAULT; @@ -268,9 +293,18 @@ main(int argc, char *argv[]) case 'h': hflag = 1; break; + case 'i': + iflag = 1; + break; + case 'n': + nflag = 1; + break; case 'q': qflag = 1; break; + case 'v': + vflag = 1; + break; default: usage(); return(-1); @@ -279,17 +313,20 @@ main(int argc, char *argv[]) argv += optind; if (argc == 0) { - error = print_acl_from_stdin(type, hflag, qflag); + error = print_acl_from_stdin(type, hflag, iflag, nflag, + qflag, vflag); return(error ? 1 : 0); } for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-")) { - error = print_acl_from_stdin(type, hflag, qflag); + error = print_acl_from_stdin(type, hflag, iflag, nflag, + qflag, vflag); if (error == -1) carried_error = -1; } else { - error = print_acl(argv[i], type, hflag, qflag); + error = print_acl(argv[i], type, hflag, iflag, nflag, + qflag, vflag); if (error == -1) carried_error = -1; } diff -urNp current/bin/ls/print.c nfs4acl/bin/ls/print.c --- current/bin/ls/print.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/ls/print.c 2009-04-29 09:30:28.000000000 +0200 @@ -616,9 +616,8 @@ static void aclmode(char *buf, const FTSENT *p, int *haveacls) { char name[MAXPATHLEN + 1]; - int entries, ret; + int type = ACL_TYPE_ACCESS, ret, trivial; acl_t facl; - acl_entry_t ae; /* * Add a + after the standard rwxrwxrwx mode if the file has an @@ -638,30 +637,48 @@ aclmode(char *buf, const FTSENT *p, int *haveacls = 1; return; } - if ((ret = pathconf(name, _PC_ACL_EXTENDED)) <= 0) { - if (ret < 0 && errno != EINVAL) - warn("%s", name); - else - *haveacls = 0; + + *haveacls = 0; + + ret = pathconf(name, _PC_ACL_EXTENDED); + if (ret > 0) { + type = ACL_TYPE_ACCESS; + *haveacls = 1; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", name); + return; + } + + ret = pathconf(name, _PC_EXTENDED_SECURITY_NP); + if (ret > 0) { + type = ACL_TYPE_NFS4; + *haveacls = 1; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", name); return; } - *haveacls = 1; - if ((facl = acl_get_file(name, ACL_TYPE_ACCESS)) != NULL) { - if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) { - entries = 1; - while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1) - if (++entries > 3) - break; - /* - * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS - * must have at least three entries (owner, group, - * and other). So anything with more than 3 ACLs looks - * interesting to us. - */ - if (entries > 3) - buf[10] = '+'; + + if (*haveacls == 0) + return; + + if ((facl = acl_get_file(name, type)) != NULL) { + if (acl_is_trivial_np(facl, &trivial)) { + warn("%s", name); + return; } + + if (!trivial) + buf[10] = '+'; + acl_free(facl); - } else + } else { + /* + * We were denied access. So, obviously, there + * is some ACL denying READ_ACL there. + */ + if (errno == EPERM || errno == EACCES) + buf[10] = '+'; + warn("%s", name); + } } diff -urNp current/bin/mv/mv.c nfs4acl/bin/mv/mv.c --- current/bin/mv/mv.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/mv/mv.c 2009-05-26 17:15:43.000000000 +0200 @@ -74,6 +74,8 @@ static int copy(const char *, const char static int do_move(const char *, const char *); static int fastcopy(const char *, const char *, struct stat *); static void usage(void); +static int preserve_fd_acls(int source_fd, int dest_fd, const char *source_path, + const char *dest_path); int main(int argc, char *argv[]) @@ -260,7 +262,6 @@ fastcopy(const char *from, const char *t struct timeval tval[2]; static u_int blen; static char *bp; - acl_t acl; mode_t oldmode; int nread, from_fd, to_fd; @@ -311,23 +312,18 @@ err: if (unlink(to)) sbp->st_mode &= ~(S_ISUID | S_ISGID); } } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode (was: 0%03o)", to, oldmode); + /* * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect * for dest_file, then its ACLs shall reflect the ACLs of the * source_file. */ - if (fpathconf(to_fd, _PC_ACL_EXTENDED) == 1 && - fpathconf(from_fd, _PC_ACL_EXTENDED) == 1) { - acl = acl_get_fd(from_fd); - if (acl == NULL) - warn("failed to get acl entries while setting %s", - from); - else if (acl_set_fd(to_fd, acl) < 0) - warn("failed to set acl entries for %s", to); - } + preserve_fd_acls(from_fd, to_fd, from, to); + (void)close(from_fd); - if (fchmod(to_fd, sbp->st_mode)) - warn("%s: set mode (was: 0%03o)", to, oldmode); + /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason @@ -438,6 +434,67 @@ copy(const char *from, const char *to) return (0); } +static int +preserve_fd_acls(int source_fd, int dest_fd, const char *source_path, + const char *dest_path) +{ + acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0, trivial; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(source_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } + + if (fpathconf(dest_fd, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(dest_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) + return (0); + + acl = acl_get_fd_np(source_fd, source_type); + if (acl == NULL) { + warn("failed to get acl entries while setting %s", source_path); + return (1); + } + + if (acl_is_trivial_np(acl, &trivial)) { + warn("acl_is_trivial() failed"); + return (1); + } + + if (trivial) + return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + + return (0); + } + + if (acl_set_fd_np(dest_fd, acl, dest_type) < 0) { + warn("failed to set acl entries for %s", dest_path); + return (1); + } + return (0); +} + static void usage(void) { diff -urNp current/bin/setfacl/Makefile nfs4acl/bin/setfacl/Makefile --- current/bin/setfacl/Makefile 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/Makefile 2009-05-23 08:37:49.000000000 +0200 @@ -1,6 +1,7 @@ # $FreeBSD: src/bin/setfacl/Makefile,v 1.8 2004/06/13 19:22:53 obrien Exp $ PROG= setfacl +CFLAGS+=-D_ACL_PRIVATE SRCS= file.c mask.c merge.c remove.c setfacl.c util.c .include diff -urNp current/bin/setfacl/mask.c nfs4acl/bin/setfacl/mask.c --- current/bin/setfacl/mask.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/mask.c 2009-04-29 09:30:28.000000000 +0200 @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD: src/bin/setfacl/mask /* set the appropriate mask the given ACL's */ int -set_acl_mask(acl_t *prev_acl) +set_acl_mask(acl_t *prev_acl, const char *filename) { acl_entry_t entry; acl_t acl; @@ -59,7 +59,7 @@ set_acl_mask(acl_t *prev_acl) acl = acl_dup(*prev_acl); if (acl == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); if (n_flag == 0) { /* @@ -70,7 +70,7 @@ set_acl_mask(acl_t *prev_acl) * class in the resulting ACL */ if (acl_calc_mask(&acl)) { - warn("acl_calc_mask() failed"); + warn("%s: acl_calc_mask() failed", filename); acl_free(acl); return (-1); } @@ -86,7 +86,8 @@ set_acl_mask(acl_t *prev_acl) while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (tag == ACL_MASK) { acl_free(acl); @@ -100,7 +101,7 @@ set_acl_mask(acl_t *prev_acl) * file, then write an error message to standard error and * continue with the next file. */ - warnx("warning: no mask entry"); + warnx("%s: warning: no mask entry", filename); acl_free(acl); return (0); } diff -urNp current/bin/setfacl/merge.c nfs4acl/bin/setfacl/merge.c --- current/bin/setfacl/merge.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/merge.c 2009-05-26 17:15:43.000000000 +0200 @@ -36,12 +36,15 @@ __FBSDID("$FreeBSD: src/bin/setfacl/merg #include "setfacl.h" -static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new); +static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, + int acl_brand); static int -merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new) +merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) { acl_permset_t permset; + acl_entry_type_t entry_type; + acl_flagset_t flagset; int have_entry; uid_t *id, *id_new; @@ -59,6 +62,18 @@ merge_user_group(acl_entry_t *entry, acl err(1, "acl_get_permset() failed"); if (acl_set_permset(*entry_new, permset) == -1) err(1, "acl_set_permset() failed"); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(*entry, &entry_type)) + err(1, "acl_get_entry_type_np() failed"); + if (acl_set_entry_type_np(*entry_new, entry_type)) + err(1, "acl_set_entry_type_np() failed"); + if (acl_get_flagset_np(*entry, &flagset)) + err(1, "acl_get_flagset_np() failed"); + if (acl_set_flagset_np(*entry_new, flagset)) + err(1, "acl_set_flagset_np() failed"); + } + have_entry = 1; } acl_free(id); @@ -71,20 +86,31 @@ merge_user_group(acl_entry_t *entry, acl * merge an ACL into existing file's ACL */ int -merge_acl(acl_t acl, acl_t *prev_acl) +merge_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry, entry_new; acl_permset_t permset; acl_t acl_new; acl_tag_t tag, tag_new; - int entry_id, entry_id_new, have_entry; + acl_entry_type_t entry_type, entry_type_new; + acl_flagset_t flagset; + int entry_id, entry_id_new, have_entry, entry_number = 0; + int acl_brand, prev_acl_brand; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (acl_brand != prev_acl_brand) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be merged is %s", filename, + prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e", + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } - if (acl_type == ACL_TYPE_ACCESS) - acl_new = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_new = acl_dup(prev_acl[DEFAULT_ACL]); + acl_new = acl_dup(*prev_acl); if (acl_new == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); entry_id = ACL_FIRST_ENTRY; @@ -94,28 +120,45 @@ merge_acl(acl_t acl, acl_t *prev_acl) /* keep track of existing ACL_MASK entries */ if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed - invalid ACL entry"); + err(1, "%s: acl_get_tag_type() failed - " + "invalid ACL entry", filename); if (tag == ACL_MASK) have_mask = 1; /* check against the existing ACL entries */ entry_id_new = ACL_FIRST_ENTRY; - while (have_entry == 0 && - acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { + while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { entry_id_new = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (acl_get_tag_type(entry_new, &tag_new) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (tag != tag_new) continue; + /* + * For NFSv4, in addition to "tag" and "id" we also + * compare "entry_type". + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (acl_get_entry_type_np(entry_new, &entry_type_new)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (entry_type != entry_type_new) + continue; + } + switch(tag) { case ACL_USER: case ACL_GROUP: have_entry = merge_user_group(&entry, - &entry_new); + &entry_new, acl_brand); if (have_entry == 0) break; /* FALLTHROUGH */ @@ -123,37 +166,131 @@ merge_acl(acl_t acl, acl_t *prev_acl) case ACL_GROUP_OBJ: case ACL_OTHER: case ACL_MASK: + case ACL_EVERYONE: if (acl_get_permset(entry, &permset) == -1) - err(1, "acl_get_permset() failed"); + err(1, "%s: acl_get_permset() failed", + filename); if (acl_set_permset(entry_new, permset) == -1) - err(1, "acl_set_permset() failed"); + err(1, "%s: acl_set_permset() failed", + filename); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() failed", + filename); + if (acl_set_entry_type_np(entry_new, entry_type)) + err(1, "%s: acl_set_entry_type_np() failed", + filename); + if (acl_get_flagset_np(entry, &flagset)) + err(1, "%s: acl_get_flagset_np() failed", + filename); + if (acl_set_flagset_np(entry_new, flagset)) + err(1, "%s: acl_set_flagset_np() failed", + filename); + } have_entry = 1; break; default: /* should never be here */ - errx(1, "Invalid tag type: %i", tag); + errx(1, "%s: invalid tag type: %i", filename, tag); break; } } /* if this entry has not been found, it must be new */ if (have_entry == 0) { - if (acl_create_entry(&acl_new, &entry_new) == -1) { - acl_free(acl_new); - return (-1); + + /* + * NFSv4 ACL entries must be prepended to the ACL. + * Appending them at the end makes no sense, since + * in most cases they wouldn't even get evaluated. + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { + warn("%s: acl_create_entry_np() failed", filename); + acl_free(acl_new); + return (-1); + } + /* + * Without this increment, adding several + * entries at once, for example + * "setfacl -m user:1:r:allow,user:2:r:allow", + * would make them appear in reverse order. + */ + entry_number++; + } else { + if (acl_create_entry(&acl_new, &entry_new) == -1) { + warn("%s: acl_create_entry() failed", filename); + acl_free(acl_new); + return (-1); + } } if (acl_copy_entry(entry_new, entry) == -1) - err(1, "acl_copy_entry() failed"); + err(1, "%s: acl_copy_entry() failed", filename); } } - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; + acl_free(*prev_acl); + *prev_acl = acl_new; + + return (0); +} + +int +add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry, entry_new; + acl_t acl_new; + int entry_id, acl_brand, prev_acl_brand; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (prev_acl_brand != ACL_BRAND_NFS4) { + warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", + filename); + return (-1); + } + + if (acl_brand != ACL_BRAND_NFS4) { + warnx("%s: branding mismatch; existing ACL is NFSv4, " + "entry to be added is %s", filename, + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } + + acl_new = acl_dup(*prev_acl); + if (acl_new == NULL) + err(1, "%s: acl_dup() failed", filename); + + entry_id = ACL_FIRST_ENTRY; + + while (acl_get_entry(acl, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { + if (entry_number >= acl_new->ats_acl.acl_cnt) + warnx("%s: invalid entry number", filename); + else + warn("%s: acl_create_entry_np() failed", filename); + acl_free(acl_new); + return (-1); + } + + /* + * Without this increment, adding several + * entries at once, for example + * "setfacl -m user:1:r:allow,user:2:r:allow", + * would make them appear in reverse order. + */ + entry_number++; + + if (acl_copy_entry(entry_new, entry) == -1) + err(1, "%s: acl_copy_entry() failed", filename); } + acl_free(*prev_acl); + *prev_acl = acl_new; + return (0); } diff -urNp current/bin/setfacl/remove.c nfs4acl/bin/setfacl/remove.c --- current/bin/setfacl/remove.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/remove.c 2009-05-26 17:15:43.000000000 +0200 @@ -41,21 +41,31 @@ __FBSDID("$FreeBSD: src/bin/setfacl/remo * remove ACL entries from an ACL */ int -remove_acl(acl_t acl, acl_t *prev_acl) +remove_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry; acl_t acl_new; acl_tag_t tag; - int carried_error, entry_id; + int carried_error, entry_id, acl_brand, prev_acl_brand; + + carried_error = 0; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (acl_brand != prev_acl_brand) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be removed is %s", filename, + prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e", + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } carried_error = 0; - if (acl_type == ACL_TYPE_ACCESS) - acl_new = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_new = acl_dup(prev_acl[DEFAULT_ACL]); + acl_new = acl_dup(*prev_acl); if (acl_new == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); tag = ACL_UNDEFINED_TAG; @@ -64,23 +74,72 @@ remove_acl(acl_t acl, acl_t *prev_acl) while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", filename); if (tag == ACL_MASK) have_mask++; if (acl_delete_entry(acl_new, entry) == -1) { carried_error++; - warnx("cannot remove non-existent acl entry"); + warnx("%s: cannot remove non-existent ACL entry", + filename); } } - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; + acl_free(*prev_acl); + *prev_acl = acl_new; + + if (carried_error) + return (-1); + + return (0); +} + +int +remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry; + acl_t acl_new; + acl_tag_t tag; + int carried_error, entry_id; + uint i; + + carried_error = 0; + + acl_new = acl_dup(*prev_acl); + if (acl_new == NULL) + err(1, "%s: acl_dup() failed", filename); + + tag = ACL_UNDEFINED_TAG; + + /* + * Find out whether we're removing the mask entry, + * to behave the same as the routine above. + * + * XXX: Is this loop actually needed? + */ + entry_id = ACL_FIRST_ENTRY; + i = 0; + while (acl_get_entry(acl_new, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + if (i != entry_number) + continue; + if (acl_get_tag_type(entry, &tag) == -1) + err(1, "%s: acl_get_tag_type() failed", filename); + if (tag == ACL_MASK) + have_mask++; + } + + if (acl_delete_entry_np(acl_new, entry_number) == -1) { + carried_error++; + + if (entry_number >= (uint)acl_new->ats_acl.acl_cnt) + warnx("%s: invalid entry number", filename); + else + warn("%s: acl_delete_entry_np() failed", filename); } + acl_free(*prev_acl); + *prev_acl = acl_new; + if (carried_error) return (-1); @@ -91,18 +150,13 @@ remove_acl(acl_t acl, acl_t *prev_acl) * remove default entries */ int -remove_default(acl_t *prev_acl) +remove_default(acl_t *prev_acl, const char *filename) { + acl_free(*prev_acl); + *prev_acl = acl_init(ACL_MAX_ENTRIES); + if (*prev_acl == NULL) + err(1, "%s: acl_init() failed", filename); - if (prev_acl[1]) { - acl_free(prev_acl[1]); - prev_acl[1] = acl_init(ACL_MAX_ENTRIES); - if (prev_acl[1] == NULL) - err(1, "acl_init() failed"); - } else { - warn("cannot remove default ACL"); - return (-1); - } return (0); } @@ -110,71 +164,15 @@ remove_default(acl_t *prev_acl) * remove extended entries */ void -remove_ext(acl_t *prev_acl) +remove_ext(acl_t *prev_acl, const char *filename) { - acl_t acl_new, acl_old; - acl_entry_t entry, entry_new; - acl_permset_t perm; - acl_tag_t tag; - int entry_id, have_mask_entry; - - if (acl_type == ACL_TYPE_ACCESS) - acl_old = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_old = acl_dup(prev_acl[DEFAULT_ACL]); - if (acl_old == NULL) - err(1, "acl_dup() failed"); + acl_t acl_new; - have_mask_entry = 0; - acl_new = acl_init(ACL_MAX_ENTRIES); + acl_new = acl_strip_np(*prev_acl, !n_flag); if (acl_new == NULL) - err(1, "acl_init() failed"); - tag = ACL_UNDEFINED_TAG; - - /* only save the default user/group/other entries */ - entry_id = ACL_FIRST_ENTRY; - while (acl_get_entry(acl_old, entry_id, &entry) == 1) { - entry_id = ACL_NEXT_ENTRY; - - if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_strip_np() failed", filename); - switch(tag) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_OTHER: - if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); - if (acl_get_permset(entry, &perm) == -1) - err(1, "acl_get_permset() failed"); - if (acl_create_entry(&acl_new, &entry_new) == -1) - err(1, "acl_create_entry() failed"); - if (acl_set_tag_type(entry_new, tag) == -1) - err(1, "acl_set_tag_type() failed"); - if (acl_set_permset(entry_new, perm) == -1) - err(1, "acl_get_permset() failed"); - if (acl_copy_entry(entry_new, entry) == -1) - err(1, "acl_copy_entry() failed"); - break; - case ACL_MASK: - have_mask_entry = 1; - break; - default: - break; - } - } - if (have_mask_entry && n_flag == 0) { - if (acl_calc_mask(&acl_new) == -1) - err(1, "acl_calc_mask() failed"); - } else { - have_mask = 1; - } - - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; - } + acl_free(*prev_acl); + *prev_acl = acl_new; } + diff -urNp current/bin/setfacl/setfacl.1 nfs4acl/bin/setfacl/setfacl.1 --- current/bin/setfacl/setfacl.1 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.1 2009-05-26 17:15:43.000000000 +0200 @@ -34,9 +34,10 @@ .Sh SYNOPSIS .Nm .Op Fl bdhkn +.Op Fl a Ar position entries .Op Fl m Ar entries .Op Fl M Ar file -.Op Fl x Ar entries +.Op Fl x Ar entries | position .Op Fl X Ar file .Op Ar .Sh DESCRIPTION @@ -50,9 +51,19 @@ the file names are taken from the standa .Pp The following options are available: .Bl -tag -width indent +.It Fl a Ar position entries +Modify the ACL on the specified files by inserting new +ACL entries +specified in +.Ar entries , +starting at position +.Ar position , +counting from zero. +This option is only applicable to NFSv4 ACLs. .It Fl b -Remove all ACL entries except for the three required entries. -If the ACL contains a +Remove all ACL entries except for the three required entries +(POSIX.1e ACLs) or six "canonical" entries (NFSv4 ACLs). +If the POSIX.1e ACL contains a .Dq Li mask entry, the permissions of the .Dq Li group @@ -66,7 +77,7 @@ entries of the current ACL. The operations apply to the default ACL entries instead of access ACL entries. Currently only directories may have -default ACL's. +default ACL's. This option is not applicable to NFSv4 ACLs. .It Fl h If the target of the operation is a symbolic link, perform the operation on the symbolic link itself, rather than following the link. @@ -77,7 +88,7 @@ is not considered an error if the specif any default ACL entries. An error will be reported if any of the specified files cannot have a default entry (i.e.\& -non-directories). +non-directories). This option is not applicable to NFSv4 ACLs. .It Fl m Ar entries Modify the ACL entries on the specified files by adding new entries and modifying existing ACL entries with the ACL entries @@ -95,11 +106,15 @@ is the input is taken from stdin. .It Fl n Do not recalculate the permissions associated with the ACL -mask entry. -.It Fl x Ar entries -Remove the ACL entries specified in +mask entry. This option is not applicable to NFSv4 ACLs. +.It Fl x Ar entries | position +If .Ar entries +is specified, remove the ACL entries specified there from the access or default ACL of the specified files. +Otherwise, remove entry at index +.Ar position , +counting from zero. .It Fl X Ar file Remove the ACL entries specified in the file .Ar file @@ -108,8 +123,8 @@ from the access or default ACL of the sp .Pp The above options are evaluated in the order specified on the command-line. -.Sh ACL ENTRIES -An ACL entry contains three colon-separated fields: +.Sh POSIX.1e ACL ENTRIES +A POSIX.1E ACL entry contains three colon-separated fields: an ACL tag, an ACL qualifier, and discretionary access permissions: .Bl -tag -width indent @@ -223,7 +238,7 @@ previously specified; whitespace is igno .Ql # is ignored (comments). .Pp -When ACL entries are evaluated, the access check algorithm checks +When POSIX.1e ACL entries are evaluated, the access check algorithm checks the ACL entries in the following order: file owner, .Dq Li user ACL entries, file owning group, @@ -243,13 +258,110 @@ ACL entries for user, group, other and m For more details see the examples below. Default ACLs can be created by using .Fl d . +.Sh NFSv4 ACL ENTRIES +An NFSv4 ACL entry contains four or five colon-separated fields: an ACL tag, +an ACL qualifier (only for +.Dq Li user +and +.Dq Li group +tags), discretionary access permissions, ACL inheritance flags, and ACL type: +.Bl -tag -width indent +.It Ar "ACL tag" +The ACL tag specifies the ACL entry type and consists of +one of the following: +.Dq Li user +or +.Ql u +specifying the access +granted to the specified user; +.Dq Li group +or +.Ql g +specifying the access granted to the specified group; +.Dq Li owner@ +specifying the access granted to the owner of the file; +.Dq Li group@ +specifying the access granted to the file owning group; +.Dq Li everyone@ +specifying everyone. Note that +.Dq Li everyone@ +is not the same as traditional Unix +.Dq Li other +- it means, +literally, everyone, including file owner and owning group. +.It Ar "ACL qualifier" +The ACL qualifier field describes the user or group associated with +the ACL entry. +It may consist of one of the following: uid or +user name, or gid or group name. In entries whose tag type is +one of +.Dq Li owner@ , +.Dq Li group@ , +or +.Dq Li everyone@ , +this field is ommited altogether, including the trailing comma. +.It Ar "access permissions" +The access permissions field contains up to one of each of +the following: +.Ql r , +.Ql w , +.Ql x , +.Ql p , +.Ql d , +.Ql D , +.Ql a , +.Ql A , +.Ql R , +.Ql W , +.Ql c , +.Ql C , +.Ql o , +and +.Ql S +to set read_data, write_data, execute, append_data, delete_child, delete, +read_attributes, write_attributes, read_xattr, write_xattr, read_acl, write_acl, +write_owner, and synchronize permissions, respectively. This section obviously +needs to be rewritten. +Each of these may be excluded +or replaced with a +.Ql - +character to indicate no access. +.It Ar "ACL inheritance flags" +The ACL inheritance flags field contains up to one of each of +the following: +.Ql f , +.Ql d , +.Ql i , +.Ql n , +to set file_inherit, dir_inherit, inherit_only, and no_propagate flags, +respectively. Inheritance flags may be only set on directories. +.It Ar "ACL type" +The ACL type field is either +.Dq Li allow +or +.Dq Li deny . +.El +.Pp +ACL entries applied from a file using the +.Fl M +or +.Fl X +options shall be of the following form: one ACL entry per line, as +previously specified; whitespace is ignored; any text after a +.Ql # +is ignored (comments). +.Pp +NFSv4 ACL entries are evaluated in their visible order. +.Pp +Multiple ACL entries specified on the command line are +separated by commas. .Sh EXIT STATUS .Ex -std .Sh EXAMPLES .Dl setfacl -d -m u::rwx,g::rx,o::rx,mask::rwx dir .Dl setfacl -d -m g:admins:rwx dir .Pp -The first command sets the mandatory elements of the default ACL. +The first command sets the mandatory elements of the POSIX.1e default ACL. The second command specifies that users in group admins can have read, write, and execute permissions for directory named "dir". It should be noted that any files or directories created underneath "dir" will @@ -259,9 +371,13 @@ inherit these default ACLs upon creation .Pp Sets read, write, and execute permissions for the .Pa file -owner's ACL entry and read and write permissions for group mail on +owner's POSIX.1e ACL entry and read and write permissions for group mail on .Pa file . .Pp +.Dl setfacl -m owner@:rwxp::allow,g:mail:rwp::allow file +.Pp +Semantically equal to the example above, but for NFSv4 ACL. +.Pp .Dl setfacl -M file1 file2 .Pp Sets/updates the ACL entries contained in @@ -271,10 +387,15 @@ on .Pp .Dl setfacl -x g:mail:rw file .Pp -Remove the group mail ACL entry containing read/write permissions +Remove the group mail POSIX.1e ACL entry containing read/write permissions from .Pa file . .Pp +.Dl setfacl -x0 file +.Pp +Remove the first entry from the NFSv4 ACL from +.Pa file . +.Pp .Dl setfacl -bn file .Pp Remove all diff -urNp current/bin/setfacl/setfacl.c nfs4acl/bin/setfacl/setfacl.c --- current/bin/setfacl/setfacl.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.c 2009-05-26 17:15:43.000000000 +0200 @@ -41,9 +41,9 @@ __FBSDID("$FreeBSD: src/bin/setfacl/setf #include "setfacl.h" -static void add_filename(const char *filename); -static acl_t *get_file_acls(const char *filename); -static void usage(void); +static void add_filename(const char *filename); +static acl_t get_file_acl(const char *filename, acl_type_t type, int h_flag); +static void usage(void); static void add_filename(const char *filename) @@ -59,35 +59,48 @@ add_filename(const char *filename) TAILQ_INSERT_TAIL(&filelist, file, next); } -static acl_t * -get_file_acls(const char *filename) +static acl_t +get_file_acl(const char *filename, acl_type_t type, int h_flag) { - acl_t *acl; + acl_t acl = NULL; struct stat sb; - if (stat(filename, &sb) == -1) { - warn("stat() of %s failed", filename); - return (NULL); + switch (type) { + case ACL_TYPE_ACCESS: + case ACL_TYPE_NFS4: + if (h_flag) + acl = acl_get_link_np(filename, type); + else + acl = acl_get_file(filename, type); + break; + + case ACL_TYPE_DEFAULT: + if (stat(filename, &sb) == -1) { + warn("%s: stat() failed", filename); + return (NULL); + } + + if (S_ISDIR(sb.st_mode) == 0) { + warnx("%s: default ACL may only be set on a directory", + filename); + return (NULL); + } + + if (h_flag) + acl = acl_get_link_np(filename, ACL_TYPE_DEFAULT); + else + acl = acl_get_file(filename, ACL_TYPE_DEFAULT); + break; } - acl = zmalloc(sizeof(acl_t) * 2); - if (h_flag) - acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS); - else - acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS); - if (acl[ACCESS_ACL] == NULL) - err(1, "acl_get_file() failed"); - if (S_ISDIR(sb.st_mode)) { + if (acl == NULL) { if (h_flag) - acl[DEFAULT_ACL] = acl_get_link_np(filename, - ACL_TYPE_DEFAULT); + warn("%s: acl_get_link_np() failed", filename); else - acl[DEFAULT_ACL] = acl_get_file(filename, - ACL_TYPE_DEFAULT); - if (acl[DEFAULT_ACL] == NULL) - err(1, "acl_get_file() failed"); - } else - acl[DEFAULT_ACL] = NULL; + warn("%s: acl_get_file() failed", filename); + + return (NULL); + } return (acl); } @@ -96,20 +109,23 @@ static void usage(void) { - fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] " - "[-x entries] [-X file] [file ...]\n"); + fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " + "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); exit(1); } int main(int argc, char *argv[]) { - acl_t *acl, final_acl; + acl_t acl; + acl_type_t acl_type; char filename[PATH_MAX]; - int local_error, carried_error, ch, i; + int local_error, carried_error, ch, i, entry_number; + int h_flag; struct sf_file *file; struct sf_entry *entry; const char *fn_dup; + char *end; acl_type = ACL_TYPE_ACCESS; carried_error = local_error = 0; @@ -118,13 +134,13 @@ main(int argc, char *argv[]) TAILQ_INIT(&entrylist); TAILQ_INIT(&filelist); - while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1) + while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) switch(ch) { case 'M': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); if (entry->acl == NULL) - err(1, "get_acl_from_file() failed"); + err(1, "%s: get_acl_from_file() failed", optarg); entry->op = OP_MERGE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; @@ -134,6 +150,25 @@ main(int argc, char *argv[]) entry->op = OP_REMOVE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; + case 'a': + entry = zmalloc(sizeof(struct sf_entry)); + + entry_number = strtol(optarg, &end, 10); + if (end - optarg != (int)strlen(optarg)) + errx(1, "%s: invalid entry number", optarg); + if (entry_number < 0) + errx(1, "%s: entry number cannot be less than zero", optarg); + entry->entry_number = entry_number; + + if (argv[optind] == NULL) + errx(1, "missing ACL"); + entry->acl = acl_from_text(argv[optind]); + if (entry->acl == NULL) + err(1, "%s", argv[optind]); + optind++; + entry->op = OP_ADD_ACL; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; case 'b': entry = zmalloc(sizeof(struct sf_entry)); entry->op = OP_REMOVE_EXT; @@ -163,10 +198,18 @@ main(int argc, char *argv[]) break; case 'x': entry = zmalloc(sizeof(struct sf_entry)); - entry->acl = acl_from_text(optarg); - if (entry->acl == NULL) - err(1, "%s", optarg); - entry->op = OP_REMOVE_ACL; + entry_number = strtol(optarg, &end, 10); + if (end - optarg == (int)strlen(optarg)) { + if (entry_number < 0) + errx(1, "%s: entry number cannot be less than zero", optarg); + entry->entry_number = entry_number; + entry->op = OP_REMOVE_BY_NUMBER; + } else { + entry->acl = acl_from_text(optarg); + if (entry->acl == NULL) + err(1, "%s", optarg); + entry->op = OP_REMOVE_ACL; + } TAILQ_INSERT_TAIL(&entrylist, entry, next); break; default: @@ -199,16 +242,23 @@ main(int argc, char *argv[]) /* cycle through each file */ TAILQ_FOREACH(file, &filelist, next) { - /* get our initial access and default ACL's */ - acl = get_file_acls(file->filename); + local_error = 0; + + if (pathconf(file->filename, _PC_EXTENDED_SECURITY_NP)) { + if (acl_type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries " + "in NFSv4 ACLs", file->filename); + continue; + } + + acl_type = ACL_TYPE_NFS4; + + } else if (acl_type == ACL_TYPE_NFS4) + acl_type = ACL_TYPE_ACCESS; + + acl = get_file_acl(file->filename, acl_type, h_flag); if (acl == NULL) continue; - if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) { - warnx("Default ACL not valid for %s", file->filename); - continue; - } - - local_error = 0; /* cycle through each option */ TAILQ_FOREACH(entry, &entrylist, next) { @@ -216,24 +266,44 @@ main(int argc, char *argv[]) continue; switch(entry->op) { + case OP_ADD_ACL: + local_error += add_acl(entry->acl, + entry->entry_number, &acl, file->filename); + break; case OP_MERGE_ACL: - local_error += merge_acl(entry->acl, acl); + local_error += merge_acl(entry->acl, &acl, + file->filename); need_mask = 1; break; case OP_REMOVE_EXT: - remove_ext(acl); + remove_ext(&acl, file->filename); need_mask = 0; break; case OP_REMOVE_DEF: + if (acl_type == ACL_TYPE_NFS4) { + warnx("%s: there are no default entries in NFSv4 ACLs; " + "cannot remove", file->filename); + local_error++; + break; + } if (acl_delete_def_file(file->filename) == -1) { - warn("acl_delete_def_file() failed"); + warn("%s: acl_delete_def_file() failed", + file->filename); local_error++; } - local_error += remove_default(acl); + if (acl_type == ACL_TYPE_DEFAULT) + local_error += remove_default(&acl, + file->filename); need_mask = 0; break; case OP_REMOVE_ACL: - local_error += remove_acl(entry->acl, acl); + local_error += remove_acl(entry->acl, &acl, + file->filename); + need_mask = 1; + break; + case OP_REMOVE_BY_NUMBER: + local_error += remove_by_number(entry->entry_number, + &acl, file->filename); need_mask = 1; break; } @@ -245,36 +315,32 @@ main(int argc, char *argv[]) continue; } - if (acl_type == ACL_TYPE_ACCESS) { - final_acl = acl[ACCESS_ACL]; - acl_free(acl[DEFAULT_ACL]); - } else { - final_acl = acl[DEFAULT_ACL]; - acl_free(acl[ACCESS_ACL]); - } + if (acl_type == ACL_TYPE_NFS4) + need_mask = 0; - if (need_mask && (set_acl_mask(&final_acl) == -1)) { - warnx("failed to set ACL mask on %s", file->filename); + if (need_mask && (set_acl_mask(&acl, + file->filename) == -1)) { + warnx("%s: failed to set ACL mask", file->filename); carried_error++; } else if (h_flag) { if (acl_set_link_np(file->filename, acl_type, - final_acl) == -1) { + acl) == -1) { carried_error++; - warn("acl_set_link_np() failed for %s", + warn("%s: acl_set_link_np() failed", file->filename); } } else { if (acl_set_file(file->filename, acl_type, - final_acl) == -1) { + acl) == -1) { carried_error++; - warn("acl_set_file() failed for %s", + warn("%s: acl_set_file() failed", file->filename); } } - acl_free(final_acl); - free(acl); + acl_free(acl); } return (carried_error); } + diff -urNp current/bin/setfacl/setfacl.h nfs4acl/bin/setfacl/setfacl.h --- current/bin/setfacl/setfacl.h 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.h 2009-04-29 09:30:29.000000000 +0200 @@ -38,15 +38,14 @@ #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */ #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */ #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */ - -/* ACL types for the acl array */ -#define ACCESS_ACL 0 -#define DEFAULT_ACL 1 +#define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */ +#define OP_ADD_ACL 0x05 /* add acls entries at a given position */ /* TAILQ entry for acl operations */ struct sf_entry { uint op; acl_t acl; + uint entry_number; TAILQ_ENTRY(sf_entry) next; }; TAILQ_HEAD(, sf_entry) entrylist; @@ -61,21 +60,21 @@ TAILQ_HEAD(, sf_file) filelist; /* files.c */ acl_t get_acl_from_file(const char *filename); /* merge.c */ -int merge_acl(acl_t acl, acl_t *prev_acl); +int merge_acl(acl_t acl, acl_t *prev_acl, const char *filename); +int add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename); /* remove.c */ -int remove_acl(acl_t acl, acl_t *prev_acl); -int remove_default(acl_t *prev_acl); -void remove_ext(acl_t *prev_acl); +int remove_acl(acl_t acl, acl_t *prev_acl, const char *filename); +int remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename); +int remove_default(acl_t *prev_acl, const char *filename); +void remove_ext(acl_t *prev_acl, const char *filename); /* mask.c */ -int set_acl_mask(acl_t *prev_acl); +int set_acl_mask(acl_t *prev_acl, const char *filename); /* util.c */ void *zmalloc(size_t size); -acl_type_t acl_type; uint have_mask; uint need_mask; uint have_stdin; -uint h_flag; uint n_flag; #endif /* _SETFACL_H */ diff -urNp current/sbin/mount/mntopts.h nfs4acl/sbin/mount/mntopts.h --- current/sbin/mount/mntopts.h 2009-04-29 09:27:28.000000000 +0200 +++ nfs4acl/sbin/mount/mntopts.h 2009-04-29 09:35:43.000000000 +0200 @@ -54,6 +54,7 @@ struct mntopt { #define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 } #define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 } #define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 } +#define MOPT_NFS4ACLS { "nfs4acls", 0, MNT_NFS4ACLS, 0 } /* Control flags. */ #define MOPT_FORCE { "force", 0, MNT_FORCE, 0 } @@ -87,7 +88,8 @@ struct mntopt { MOPT_NOCLUSTERR, \ MOPT_NOCLUSTERW, \ MOPT_MULTILABEL, \ - MOPT_ACLS + MOPT_ACLS, \ + MOPT_NFS4ACLS void getmntopts(const char *, const struct mntopt *, int *, int *); void rmslashes(char *, char *); diff -urNp current/sbin/mount/mount.8 nfs4acl/sbin/mount/mount.8 --- current/sbin/mount/mount.8 2009-04-29 09:27:28.000000000 +0200 +++ nfs4acl/sbin/mount/mount.8 2009-05-26 17:15:44.000000000 +0200 @@ -120,11 +120,14 @@ takes effect. The following options are available: .Bl -tag -width indent .It Cm acls -Enable Access Control Lists, or ACLS, which can be customized via the +Enable POSIX.1e Access Control Lists, or ACLS, which can be customized via the .Xr setfacl 1 and .Xr getfacl 1 commands. +This flag is mutually exclusive with +.Cm nfs4acls +flag. .It Cm async All .Tn I/O @@ -186,6 +189,15 @@ See .Xr mac 4 for more information, which cause the multilabel mount flag to be set automatically at mount-time. +.It Cm nfs4acls +Enable NFSv4 ACLS, which can be customized via the +.Xr setfacl 1 +and +.Xr getfacl 1 +commands. +This flag is mutually exclusive with +.Cm acls +flag. .It Cm noasync Metadata I/O should be done synchronously, while data I/O should be done asynchronously. diff -urNp current/sbin/mount/mount.c nfs4acl/sbin/mount/mount.c --- current/sbin/mount/mount.c 2009-05-29 19:54:13.000000000 +0200 +++ nfs4acl/sbin/mount/mount.c 2009-05-29 20:25:33.000000000 +0200 @@ -111,6 +111,7 @@ static struct opt { { MNT_SOFTDEP, "soft-updates" }, { MNT_MULTILABEL, "multilabel" }, { MNT_ACLS, "acls" }, + { MNT_NFS4ACLS, "nfs4acls" }, { MNT_GJOURNAL, "gjournal" }, { 0, NULL } }; @@ -915,6 +916,7 @@ flags2opts(int flags) if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir"); if (flags & MNT_MULTILABEL) res = catopt(res, "multilabel"); if (flags & MNT_ACLS) res = catopt(res, "acls"); + if (flags & MNT_NFS4ACLS) res = catopt(res, "nfs4acls"); return (res); } diff -urNp current/share/man/man9/Makefile nfs4acl/share/man/man9/Makefile --- current/share/man/man9/Makefile 2009-06-23 22:33:17.000000000 +0200 +++ nfs4acl/share/man/man9/Makefile 2009-06-26 15:28:03.000000000 +0200 @@ -247,6 +247,7 @@ MAN= accept_filter.9 \ usbdi.9 \ utopia.9 \ vaccess.9 \ + vaccess_acl_nfs4.9 \ vaccess_acl_posix1e.9 \ vcount.9 \ vflush.9 \ diff -urNp current/share/man/man9/VOP_ACCESS.9 nfs4acl/share/man/man9/VOP_ACCESS.9 --- current/share/man/man9/VOP_ACCESS.9 2009-06-04 22:03:14.000000000 +0200 +++ nfs4acl/share/man/man9/VOP_ACCESS.9 2009-06-04 22:31:02.000000000 +0200 @@ -95,6 +95,7 @@ requested access. .El .Sh SEE ALSO .Xr vaccess 9 , +.Xr vaccess_acl_nfs4 9 , .Xr vaccess_acl_posix1e 9 , .Xr vnode 9 .Sh AUTHORS diff -urNp current/share/man/man9/acl.9 nfs4acl/share/man/man9/acl.9 --- current/share/man/man9/acl.9 2009-05-25 16:31:55.000000000 +0200 +++ nfs4acl/share/man/man9/acl.9 2009-05-25 17:32:06.000000000 +0200 @@ -208,6 +208,7 @@ The following values are valid: .Sh SEE ALSO .Xr acl 3 , .Xr vaccess_acl_posix1e 9 , +.Xr vaccess_acl_nfs4 9 , .Xr VFS 9 , .Xr vnaccess 9 , .Xr VOP_ACLCHECK 9 , diff -urNp current/share/man/man9/vaccess_acl_nfs4.9 nfs4acl/share/man/man9/vaccess_acl_nfs4.9 --- current/share/man/man9/vaccess_acl_nfs4.9 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/share/man/man9/vaccess_acl_nfs4.9 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,129 @@ +.\"- +.\" Copyright (c) 2001 Robert N. M. Watson +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 22, 2001 +.Os +.Dt VACCESS_ACL_NFS4 9 +.Sh NAME +.Nm vaccess_acl_nfs4 +.Nd generate a NFSv4 ACL access control decision using vnode parameters +.Sh SYNOPSIS +.In sys/param.h +.In sys/vnode.h +.In sys/acl.h +.Ft int +.Fo vaccess_acl_nfs4 +.Fa "enum vtype type" +.Fa "uid_t file_uid" +.Fa "gid_t file_gid" +.Fa "struct acl *acl" +.Fa "accmode_t accmode" +.Fa "struct ucred *cred" +.Fa "int *privused" +.Fc +.Sh DESCRIPTION +This call implements the logic for the +.Ux +discretionary file security model +with NFSv4 ACL extensions. +It accepts the vnodes type +.Fa type , +owning UID +.Fa file_uid , +owning GID +.Fa file_gid , +access ACL for the file +.Fa acl , +desired access mode +.Fa accmode , +requesting credential +.Fa cred , +and an optional call-by-reference +.Vt int +pointer returning whether or not +privilege was required for successful evaluation of the call; the +.Fa privused +pointer may be set to +.Dv NULL +by the caller in order not to be informed of +privilege information, or it may point to an integer that will be set to +1 if privilege is used, and 0 otherwise. +.Pp +This call is intended to support implementations of +.Xr VOP_ACCESS 9 , +which will use their own access methods to retrieve the vnode properties, +and then invoke +.Fn vaccess_acl_nfs4 +in order to perform the actual check. +Implementations of +.Xr VOP_ACCESS 9 +may choose to implement additional security mechanisms whose results will +be composed with the return value. +.Pp +The algorithm used by +.Fn vaccess_acl_nfs4 +is based on the NFSv4 ACL evaluation algorithm, as described in +NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-21.txt. +The algorithm selects a +.Em matching +entry from the access ACL, which may +then be composed with an available ACL mask entry, providing +.Ux +security compatibility. +.Pp +Once appropriate protections are selected for the current credential, +the requested access mode, in combination with the vnode type, will be +compared with the discretionary rights available for the credential. +If the rights granted by discretionary protections are insufficient, +then super-user privilege, if available for the credential, will also be +considered. +.Sh RETURN VALUES +.Fn vaccess_acl_nfs4 +will return 0 on success, or a non-zero error value on failure. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EACCES +Permission denied. +An attempt was made to access a file in a way forbidden by its file access +permissions. +.It Bq Er EPERM +Operation not permitted. +An attempt was made to perform an operation limited to processes with +appropriate privileges or to the owner of a file or other resource. +.El +.Sh SEE ALSO +.Xr vaccess 9 , +.Xr vnode 9 , +.Xr VOP_ACCESS 9 +.Sh AUTHORS +Current implementation of +.Fn vaccess_acl_nfs4 +was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . +.Sh BUGS +This manual page should include a full description of the NFSv4 ACL +evaluation algorithm, or cross reference another page that does. diff -urNp current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c 2009-06-23 22:34:16.000000000 +0200 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c 2009-06-26 15:28:31.000000000 +0200 @@ -3841,6 +3841,10 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong *valp = (int)SPA_MINBLOCKSIZE; return (0); + case _PC_EXTENDED_SECURITY_NP: + *valp = 1; + return (0); + default: return (EOPNOTSUPP); } @@ -3964,7 +3968,6 @@ zfs_freebsd_access(ap) struct thread *a_td; } */ *ap; { - /* * ZFS itself only knowns about VREAD, VWRITE and VEXEC, the rest * we have to handle by calling vaccess(). @@ -4822,7 +4825,7 @@ zfs_freebsd_setacl(ap) return (EINVAL); /* - * With NFS4 ACLs, chmod(2) may need to add additional entries, + * With NFSv4 ACLs, chmod(2) may need to add additional entries, * splitting every entry into two and appending "canonical six" * entries at the end. Don't allow for setting an ACL that would * cause chmod(2) to run out of ACL entries. @@ -4896,11 +4899,9 @@ struct vop_vector zfs_vnodeops = { .vop_deleteextattr = zfs_deleteextattr, .vop_setextattr = zfs_setextattr, .vop_listextattr = zfs_listextattr, -#ifdef notyet .vop_getacl = zfs_freebsd_getacl, .vop_setacl = zfs_freebsd_setacl, .vop_aclcheck = zfs_freebsd_aclcheck, -#endif }; struct vop_vector zfs_fifoops = { @@ -4914,9 +4915,7 @@ struct vop_vector zfs_fifoops = { .vop_setattr = zfs_freebsd_setattr, .vop_write = VOP_PANIC, .vop_fid = zfs_freebsd_fid, -#ifdef notyet .vop_getacl = zfs_freebsd_getacl, .vop_setacl = zfs_freebsd_setacl, .vop_aclcheck = zfs_freebsd_aclcheck, -#endif }; diff -urNp current/sys/conf/files nfs4acl/sys/conf/files --- current/sys/conf/files 2009-06-23 22:34:58.000000000 +0200 +++ nfs4acl/sys/conf/files 2009-06-26 15:28:59.000000000 +0200 @@ -1997,6 +1997,7 @@ kern/serdev_if.m standard kern/stack_protector.c standard \ compile-with "${NORMAL_C:N-fstack-protector*}" kern/subr_acl_posix1e.c standard +kern/subr_acl_nfs4.c standard kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_bus.c standard diff -urNp current/sys/fs/nfs/nfs_commonacl.c nfs4acl/sys/fs/nfs/nfs_commonacl.c --- current/sys/fs/nfs/nfs_commonacl.c 2009-06-17 21:47:34.000000000 +0200 +++ nfs4acl/sys/fs/nfs/nfs_commonacl.c 2009-06-26 15:29:06.000000000 +0200 @@ -695,7 +695,7 @@ nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, if (nfsrv_useacl == 0 || !NFSHASNFS4ACL(vnode_mount(vp))) return (NFSERR_ATTRNOTSUPP); /* - * With NFS4 ACLs, chmod(2) may need to add additional entries. + * With NFSv4 ACLs, chmod(2) may need to add additional entries. * Make sure it has enough room for that - splitting every entry * into two and appending "canonical six" entries at the end. * Cribbed out of kern/vfs_acl.c - Rick M. diff -urNp current/sys/kern/subr_acl_nfs4.c nfs4acl/sys/kern/subr_acl_nfs4.c --- current/sys/kern/subr_acl_nfs4.c 2009-06-17 21:47:54.000000000 +0200 +++ nfs4acl/sys/kern/subr_acl_nfs4.c 2009-06-15 23:22:03.000000000 +0200 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008 Edward Tomasz Napierała + * Copyright (c) 2009 Edward Tomasz Napierała * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ #ifdef _KERNEL #include -__FBSDID("$FreeBSD: src/sys/kern/subr_acl_nfs4.c,v 1.1 2009/06/09 19:51:22 trasz Exp $"); +__FBSDID("$FreeBSD$"); #include #include @@ -51,6 +51,210 @@ __FBSDID("$FreeBSD: src/sys/kern/subr_ac #define CTASSERT(a) #endif +#ifdef _KERNEL + +static struct { + accmode_t accmode; + int mask; +} accmode2mask[] = {{VREAD, ACL_READ_DATA}, + {VWRITE, ACL_WRITE_DATA}, + {VAPPEND, ACL_APPEND_DATA}, + {VEXEC, ACL_EXECUTE}, + {VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {VDELETE_CHILD, ACL_DELETE_CHILD}, + {VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {VDELETE, ACL_DELETE}, + {VREAD_ACL, ACL_READ_ACL}, + {VWRITE_ACL, ACL_WRITE_ACL}, + {VWRITE_OWNER, ACL_WRITE_OWNER}, + {VSYNCHRONIZE, ACL_SYNCHRONIZE}, + {0, 0}}; + +static int +_access_mask_from_accmode(accmode_t accmode) +{ + int access_mask = 0, i; + + for (i = 0; accmode2mask[i].accmode != 0; i++) { + if (accmode & accmode2mask[i].accmode) + access_mask |= accmode2mask[i].mask; + } + + return (access_mask); +} + +/* + * Return 0, iff access is allowed, 1 otherwise. + */ +static int +_acl_denies(const struct acl *aclp, int access_mask, struct ucred *cred, + int file_uid, int file_gid, int *denied_explicitly) +{ + int i; + const struct acl_entry *entry; + + if (denied_explicitly != NULL) + *denied_explicitly = 0; + + KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); + KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) + continue; + switch (entry->ae_tag) { + case ACL_USER_OBJ: + if (file_uid != cred->cr_uid) + continue; + break; + case ACL_USER: + if (entry->ae_id != cred->cr_uid) + continue; + break; + case ACL_GROUP_OBJ: + if (!groupmember(file_gid, cred)) + continue; + break; + case ACL_GROUP: + if (!groupmember(entry->ae_id, cred)) + continue; + break; + default: + KASSERT(entry->ae_tag == ACL_EVERYONE, + ("entry->ae_tag == ACL_EVERYONE")); + } + + if (entry->ae_entry_type == ACL_ENTRY_TYPE_DENY) { + if (entry->ae_perm & access_mask) { + if (denied_explicitly != NULL) + *denied_explicitly = 1; + return (1); + } + } + + access_mask &= ~(entry->ae_perm); + if (access_mask == 0) + return (0); + } + + return (1); +} + +int +vaccess_acl_nfs4(enum vtype type, uid_t file_uid, gid_t file_gid, + struct acl *aclp, accmode_t accmode, struct ucred *cred, int *privused) +{ + accmode_t priv_granted = 0; + int denied, explicitly_denied, access_mask, is_directory, + must_be_owner = 0; + + if (privused != NULL) + *privused = 0; + + if (accmode & VADMIN) + must_be_owner = 1; + + access_mask = _access_mask_from_accmode(accmode); + + if (type == VDIR) + is_directory = 1; + else + is_directory = 0; + + /* + * File owner is always allowed to read and write the ACL + * and basic attributes. This is to prevent a situation + * where user would change ACL in a way that prevents him + * from undoing the change. + */ + if (file_uid == cred->cr_uid) + access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | + ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); + + /* + * Ignore append permission for regular files; use write + * permission instead. + */ + if (!is_directory && (access_mask & ACL_APPEND_DATA)) { + access_mask &= ~ACL_APPEND_DATA; + access_mask |= ACL_WRITE_DATA; + } + + denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, + &explicitly_denied); + + if (must_be_owner) { + if (file_uid != cred->cr_uid) + denied = EPERM; + } + + if (!denied) + return (0); + + /* + * Access failed. Iff it was not denied explicitly and + * VEXPLICIT_DENY flag was specified, allow access. + */ + if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) + return (0); + + accmode &= ~VEXPLICIT_DENY; + + /* + * No match. Try to use privileges, if there are any. + */ + if (is_directory) { + if ((accmode & VEXEC) && !priv_check_cred(cred, + PRIV_VFS_LOOKUP, 0)) + priv_granted |= VEXEC; + } else { + if ((accmode & VEXEC) && !priv_check_cred(cred, + PRIV_VFS_EXEC, 0)) + priv_granted |= VEXEC; + } + + if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) + priv_granted |= VREAD; + + if ((accmode & (VWRITE | VAPPEND | VDELETE_CHILD)) && + !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) + priv_granted |= (VWRITE | VAPPEND | VDELETE_CHILD); + + if ((accmode & VADMIN_PERMS) && + !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) + priv_granted |= VADMIN_PERMS; + + if ((accmode & (VSTAT_PERMS | VSYNCHRONIZE)) && + !priv_check_cred(cred, PRIV_VFS_STAT, 0)) + priv_granted |= (VSTAT_PERMS | VSYNCHRONIZE); + + if ((accmode & priv_granted) == accmode) { + if (privused != NULL) + *privused = 1; + + return (0); + } + + if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) + denied = EPERM; + else + denied = EACCES; + + /* + * Nie wydostaniecie się. Drzwi zamknięte. + */ + return (denied); +} +#endif + static int _acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, acl_entry_type_t entry_type) @@ -577,3 +781,290 @@ acl_nfs4_sync_mode_from_acl(mode_t *_mod *_mode = mode | (old_mode & ACL_PRESERVE_MASK); } + +void +acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, + struct acl *child_aclp, mode_t mode, int file_owner_id, + int is_directory) +{ + int i, flags; + const struct acl_entry *parent_entry; + struct acl_entry *entry, *copy; + + KASSERT(child_aclp->acl_cnt == 0, ("child_aclp->acl_cnt == 0")); + KASSERT(parent_aclp->acl_cnt > 0, ("parent_aclp->acl_cnt > 0")); + KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.6.2. Applying the mode given to CREATE or OPEN + * to an inherited ACL + */ + + /* + * 1. Form an ACL that is the concatenation of all inheritable ACEs. + */ + for (i = 0; i < parent_aclp->acl_cnt; i++) { + parent_entry = &(parent_aclp->acl_entry[i]); + flags = parent_entry->ae_flags; + + /* + * Entry is not inheritable at all. + */ + if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_FILE_INHERIT)) == 0) + continue; + + /* + * We're creating a file, but entry is not inheritable + * by files. + */ + if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) + continue; + + /* + * Entry is inheritable only by files, but has NO_PROPAGATE + * flag set, and we're creating a directory, so it wouldn't + * propagate to any file in that directory anyway. + */ + if (is_directory && + (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && + (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) + continue; + + KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; + child_aclp->acl_cnt++; + } + + /* + * 2. For each entry in the new ACL, adjust its flags, possibly + * creating two entries in place of one. + */ + for (i = 0; i < child_aclp->acl_cnt; i++) { + entry = &(child_aclp->acl_entry[i]); + + /* + * This is not in the specification, but SunOS + * apparently does that. + */ + if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || + !is_directory) && + entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object + * being created is not a directory, then clear the + * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, + * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_INHERIT_ONLY. + */ + if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || + !is_directory) { + entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * Continue on to the next ACE. + */ + continue; + } + + /* + * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT + * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure + * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the + * next ACE. Otherwise... + */ + /* + * XXX: Read it again and make sure what does the "otherwise" + * apply to. + */ + if (is_directory && + (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && + ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + continue; + } + + /* + * 2.C. If the type of the ACE is neither ALLOW nor deny, + * then continue. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + /* + * 2.D. Copy the original ACE into a second, adjacent ACE. + */ + copy = _acl_duplicate_entry(child_aclp, i); + + /* + * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY + * is set. + */ + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + + /* + * 2.F. On the second ACE, clear the following flags: + * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. + */ + copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * 2.G. On the second ACE, if the type is ALLOW, + * an implementation MAY clear the following + * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. + */ + if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * Increment the counter to skip the copied entry. + */ + i++; + } + + /* + * 3. To ensure that the mode is honored, apply the algorithm describe + * in Section 2.16.6.3, using the mode that is to be used for file + * creation. + */ + acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); +} + +#ifdef _KERNEL +static int +_acls_are_equal(const struct acl *a, const struct acl *b) +{ + int i; + const struct acl_entry *entrya, *entryb; + + if (a->acl_cnt != b->acl_cnt) + return (0); + + for (i = 0; i < b->acl_cnt; i++) { + entrya = &(a->acl_entry[i]); + entryb = &(b->acl_entry[i]); + + if (entrya->ae_tag != entryb->ae_tag || + entrya->ae_id != entryb->ae_id || + entrya->ae_perm != entryb->ae_perm || + entrya->ae_entry_type != entryb->ae_entry_type || + entrya->ae_flags != entryb->ae_flags) + return (0); + } + + return (1); +} + +/* + * This routine is used to determine whether to remove entry_type attribute + * that stores ACL contents. + */ +int +acl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) +{ + int trivial; + mode_t tmpmode = 0; + struct acl *tmpaclp; + + if (aclp->acl_cnt != 6) + return (0); + + /* + * Compute the mode from the ACL, then compute new ACL from that mode. + * If the ACLs are identical, then the ACL is trivial. + * + * XXX: I guess there is a faster way to do this. However, even + * this slow implementation significantly speeds things up + * for files that don't have any entry_type ACL entries - it's + * critical for performance to not use EA when they are not + * needed. + */ + tmpaclp = acl_alloc(M_WAITOK | M_ZERO); + acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); + acl_nfs4_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); + trivial = _acls_are_equal(aclp, tmpaclp); + acl_free(tmpaclp); + + return (trivial); +} +#endif + +int +acl_nfs4_check(const struct acl *aclp, int is_directory) +{ + int i; + const struct acl_entry *entry; + + /* + * The spec doesn't seem to say anything about ACL validity. + * It seems there is not much to do here. There is even no need + * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) + * entries, as there can be several of them and that's perfectly + * valid. There can be none of them too. Really. + */ + + if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) + return (EINVAL); + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + switch (entry->ae_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_EVERYONE: + if (entry->ae_id != ACL_UNDEFINED_ID) + return (EINVAL); + break; + + case ACL_USER: + case ACL_GROUP: + if (entry->ae_id == ACL_UNDEFINED_ID) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) + return (EINVAL); + + /* + * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + return (EINVAL); + + if ((entry->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) + return (EINVAL); + + /* Disallow unimplemented flags. */ + if (entry->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | + ACL_ENTRY_FAILED_ACCESS)) + return (EINVAL); + + /* Disallow flags not allowed for ordinary files. */ + if (!is_directory) { + if (entry->ae_flags & (ACL_ENTRY_FILE_INHERIT | + ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) + return (EINVAL); + } + } + + return (0); +} diff -urNp current/sys/kern/vfs_acl.c nfs4acl/sys/kern/vfs_acl.c --- current/sys/kern/vfs_acl.c 2009-06-17 21:47:59.000000000 +0200 +++ nfs4acl/sys/kern/vfs_acl.c 2009-06-26 15:29:14.000000000 +0200 @@ -173,7 +173,7 @@ acl_copyout(struct acl *kernel_acl, void /* * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" - * counterpart. It's required for old (pre-NFS4 ACLs) libc to work + * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work * with new kernel. Fixing 'type' for old binaries with new libc * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). */ @@ -213,8 +213,20 @@ vacl_set_acl(struct thread *td, struct v inkernelacl = acl_alloc(M_WAITOK); error = acl_copyin(aclp, inkernelacl, type); - if (error) + if (error != 0) + goto out; + + /* + * With NFSv4 ACLs, chmod(2) may need to add additional entries. + * Make sure it has enough room for that - splitting every entry + * into two and appending "canonical six" entries at the end. + */ + if (type == ACL_TYPE_NFS4 && + inkernelacl->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) { + error = ENOSPC; goto out; + } + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) goto out; @@ -233,7 +245,7 @@ out_unlock: vn_finished_write(mp); out: acl_free(inkernelacl); - return(error); + return (error); } /* @@ -253,12 +265,14 @@ vacl_get_acl(struct thread *td, struct v if (error != 0) goto out; #endif + error = VOP_ACCESSX(vp, VREAD_ACL, td->td_ucred, td); + if (error != 0) + goto out; + error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, td->td_ucred, td); -#ifdef MAC out: -#endif VOP_UNLOCK(vp, 0); if (error == 0) error = acl_copyout(inkernelacl, aclp, type); @@ -276,12 +290,12 @@ vacl_delete(struct thread *td, struct vn int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); - if (error) + if (error != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef MAC error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); - if (error) + if (error != 0) goto out; #endif error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); @@ -305,9 +319,22 @@ vacl_aclcheck(struct thread *td, struct inkernelacl = acl_alloc(M_WAITOK); error = acl_copyin(aclp, inkernelacl, type); - if (error) + if (error != 0) goto out; - error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td); + + /* + * With NFSv4 ACLs, chmod(2) may need to add additional entries. + * Make sure it has enough room for that - splitting every entry + * into two and appending "canonical six" entries at the end. + */ + if (type == ACL_TYPE_NFS4 && + inkernelacl->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) { + error = ENOSPC; + goto out; + } + + error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl, + td->td_ucred, td); out: acl_free(inkernelacl); return (error); diff -urNp current/sys/kern/vfs_subr.c nfs4acl/sys/kern/vfs_subr.c --- current/sys/kern/vfs_subr.c 2009-06-17 21:48:06.000000000 +0200 +++ nfs4acl/sys/kern/vfs_subr.c 2009-06-26 15:29:31.000000000 +0200 @@ -3641,6 +3641,13 @@ extattr_check_cred(struct vnode *vp, int /* Potentially should be: return (EPERM); */ return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM, 0)); case EXTATTR_NAMESPACE_USER: +#ifdef SunOS_doesnt_do_that + if (accmode == VREAD) + return (VOP_ACCESSX(vp, VREAD_NAMED_ATTRS, cred, td)); + if (accmode == VWRITE) + return (VOP_ACCESSX(vp, VWRITE_NAMED_ATTRS, cred, td)); +#endif + /* XXX: Is this possible for "accmode" to not be any of the two above? */ return (VOP_ACCESS(vp, accmode, cred, td)); default: return (EPERM); diff -urNp current/sys/kern/vfs_vnops.c nfs4acl/sys/kern/vfs_vnops.c --- current/sys/kern/vfs_vnops.c 2009-06-23 22:46:27.000000000 +0200 +++ nfs4acl/sys/kern/vfs_vnops.c 2009-06-26 15:29:35.000000000 +0200 @@ -711,6 +711,9 @@ vn_stat(vp, sb, active_cred, file_cred, if (error) return (error); #endif + error = VOP_ACCESSX(vp, VREAD_ATTRIBUTES, active_cred, td); + if (error) + return (error); vap = &vattr; diff -urNp current/sys/security/mac_lomac/mac_lomac.c nfs4acl/sys/security/mac_lomac/mac_lomac.c --- current/sys/security/mac_lomac/mac_lomac.c 2009-06-23 22:50:13.000000000 +0200 +++ nfs4acl/sys/security/mac_lomac/mac_lomac.c 2009-06-26 15:29:45.000000000 +0200 @@ -2470,7 +2470,7 @@ lomac_vnode_check_open(struct ucred *cre obj = SLOT(vplabel); /* XXX privilege override for admin? */ - if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (accmode & VMODIFY_PERMS) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } diff -urNp current/sys/sys/mount.h nfs4acl/sys/sys/mount.h --- current/sys/sys/mount.h 2009-06-17 21:48:57.000000000 +0200 +++ nfs4acl/sys/sys/mount.h 2009-06-26 15:29:52.000000000 +0200 @@ -239,6 +239,7 @@ void __mnt_vnode_markerfree(str #define MNT_NOATIME 0x10000000 /* disable update of file access time */ #define MNT_NOCLUSTERR 0x40000000 /* disable cluster read */ #define MNT_NOCLUSTERW 0x80000000 /* disable cluster write */ +#define MNT_NFS4ACLS 0x00000010 /* * NFS export related mount flags. @@ -274,7 +275,7 @@ void __mnt_vnode_markerfree(str MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \ MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \ MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | \ - MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS) + MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | MNT_NFS4ACLS) /* Mask of flags that can be updated. */ #define MNT_UPDATEMASK (MNT_NOSUID | MNT_NOEXEC | \ @@ -282,7 +283,7 @@ void __mnt_vnode_markerfree(str MNT_NOATIME | \ MNT_NOSYMFOLLOW | MNT_IGNORE | \ MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | \ - MNT_ACLS | MNT_USER) + MNT_ACLS | MNT_USER | MNT_NFS4ACLS) /* * External filesystem command modifier flags. @@ -300,10 +301,6 @@ void __mnt_vnode_markerfree(str #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID) /* - * Still available. - */ -#define MNT_SPARE_0x00000010 0x00000010 -/* * Internal filesystem control flags stored in mnt_kern_flag. * * MNTK_UNMOUNT locks the mount entry so that name lookup cannot proceed diff -urNp current/sys/ufs/ffs/ffs_vfsops.c nfs4acl/sys/ufs/ffs/ffs_vfsops.c --- current/sys/ufs/ffs/ffs_vfsops.c 2009-06-17 21:48:59.000000000 +0200 +++ nfs4acl/sys/ufs/ffs/ffs_vfsops.c 2009-06-26 15:30:12.000000000 +0200 @@ -128,7 +128,7 @@ static struct buf_ops ffs_ops = { static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr", "noclusterw", "noexec", "export", "force", "from", "multilabel", "snapshot", "nosuid", "suiddir", "nosymfollow", "sync", - "union", NULL }; + "union", "nfs4acls", NULL }; static int ffs_mount(struct mount *mp) @@ -177,6 +177,14 @@ ffs_mount(struct mount *mp) vfs_deleteopt(mp->mnt_opt, "snapshot"); } + if (vfs_getopt(mp->mnt_optnew, "nfs4acls", NULL, NULL) == 0) { + if (mntorflags & MNT_ACLS) { + printf("WARNING: both acls and nfs4acls specified\n"); + return (EINVAL); + } + mntorflags |= MNT_NFS4ACLS; + } + MNT_ILOCK(mp); mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags; MNT_IUNLOCK(mp); @@ -834,7 +842,12 @@ ffs_mountfs(devvp, mp, td) if ((fs->fs_flags & FS_ACLS) != 0) { #ifdef UFS_ACL MNT_ILOCK(mp); - mp->mnt_flag |= MNT_ACLS; + + if (mp->mnt_flag & MNT_NFS4ACLS) + printf("WARNING: ACLs flag on fs conflicts with nfs4acls mount option; flag ignored\n"); + else + mp->mnt_flag |= MNT_ACLS; + MNT_IUNLOCK(mp); #else printf( diff -urNp current/sys/ufs/ufs/ufs_acl.c nfs4acl/sys/ufs/ufs/ufs_acl.c --- current/sys/ufs/ufs/ufs_acl.c 2009-05-23 07:50:07.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_acl.c 2009-05-30 20:40:35.000000000 +0200 @@ -140,6 +140,62 @@ ufs_sync_inode_from_acl(struct acl *acl, DIP_SET(ip, i_mode, ip->i_mode); } +static int +ufs_getacl_nfs4(struct vop_getacl_args *ap) +{ + int error, len; + struct inode *ip = VTOI(ap->a_vp); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + len = sizeof(*ap->a_aclp); + bzero(ap->a_aclp, len); + + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, &len, (char *) ap->a_aclp, + ap->a_td); + ap->a_aclp->acl_maxcnt = ACL_MAX_ENTRIES; + if (error == ENOATTR) { + /* + * Legitimately no ACL set on object, purely + * emulate it through the inode. + */ + acl_nfs4_sync_acl_from_mode(ap->a_aclp, ip->i_mode, ip->i_uid); + + return (0); + } + + if (error) + return (error); + + if (len != sizeof(*ap->a_aclp)) { + /* + * A short (or long) read, meaning that for + * some reason the ACL is corrupted. Return + * EPERM since the object DAC protections + * are unsafe. + */ + printf("ufs_getacl_nfs4(): Loaded invalid ACL (" + "%d bytes), inumber %d on %s\n", len, + ip->i_number, ip->i_fs->fs_fsmnt); + + return (EPERM); + } + + error = acl_nfs4_check(ap->a_aclp, ap->a_vp->v_type == VDIR); + if (error) { + printf("ufs_getacl_nfs4(): Loaded invalid ACL " + "(failed acl_nfs4_check), inumber %d on %s\n", + ip->i_number, ip->i_fs->fs_fsmnt); + + return (EPERM); + } + + return (0); +} + /* * Read POSIX.1e ACL from an EA. Return error if its not found * or if any other error has occured. @@ -285,9 +341,89 @@ ufs_getacl(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_getacl_nfs4(ap)); + return (ufs_getacl_posix1e(ap)); } +static int +ufs_setacl_nfs4(struct vop_setacl_args *ap) +{ + int error; + mode_t mode; + struct inode *ip = VTOI(ap->a_vp); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + if (ap->a_aclp == NULL) + return (EINVAL); + + error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, ap->a_cred, + ap->a_td); + if (error) + return (error); + + /* + * Authorize the ACL operation. + */ + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + /* + * Must hold VWRITE_ACL or have appropriate privilege. + */ + if ((error = VOP_ACCESSX(ap->a_vp, VWRITE_ACL, ap->a_cred, ap->a_td))) + return (error); + + if (acl_nfs4_is_trivial(ap->a_aclp, ip->i_uid)) { + error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, ap->a_td); + + /* + * An attempt to remove ACL from a file that didn't have + * any extended entries is not an error. + */ + if (error == ENOATTR) + error = 0; + + } else { + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, + sizeof(*ap->a_aclp), + (char *) ap->a_aclp, ap->a_td); + } + + /* + * Map lack of attribute definition in UFS_EXTATTR into lack of + * support for ACLs on the filesystem. + */ + if (error == ENOATTR) + return (EOPNOTSUPP); + + if (error) + return (error); + + mode = ip->i_mode; + + acl_nfs4_sync_mode_from_acl(&mode, ap->a_aclp); + + ip->i_mode &= ACL_PRESERVE_MASK; + ip->i_mode |= mode; + DIP_SET(ip, i_mode, ip->i_mode); + ip->i_flag |= IN_CHANGE; + + VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); + + return (0); +} + /* * Set the ACL on a file. * @@ -426,10 +562,27 @@ ufs_setacl(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_setacl_nfs4(ap)); + return (ufs_setacl_posix1e(ap)); } static int +ufs_aclcheck_nfs4(struct vop_aclcheck_args *ap) +{ + int is_directory = 0; + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type == VDIR) + is_directory = 1; + + return (acl_nfs4_check(ap->a_aclp, is_directory)); +} + +static int ufs_aclcheck_posix1e(struct vop_aclcheck_args *ap) { @@ -474,6 +627,9 @@ ufs_aclcheck(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_aclcheck_nfs4(ap)); + return (ufs_aclcheck_posix1e(ap)); } diff -urNp current/sys/ufs/ufs/ufs_lookup.c nfs4acl/sys/ufs/ufs/ufs_lookup.c --- current/sys/ufs/ufs/ufs_lookup.c 2009-06-17 21:48:59.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_lookup.c 2009-06-26 15:30:21.000000000 +0200 @@ -80,6 +80,61 @@ SYSCTL_INT(_debug, OID_AUTO, dircheck, C static int ufs_lookup_(struct vnode *, struct vnode **, struct componentname *, ino_t *); +static int +ufs_delete_denied(struct vnode *vdp, struct vnode *tdp, struct ucred *cred, + struct thread *td) +{ + int error; + +#ifdef UFS_ACL + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.2.1. ACE4_DELETE vs. ACE4_DELETE_CHILD + */ + + /* + * XXX: Is this check required? + */ + error = VOP_ACCESS(vdp, VEXEC, cred, td); + if (error) + return (error); + + error = VOP_ACCESSX(tdp, VDELETE, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESSX(vdp, VDELETE_CHILD, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESSX(vdp, VEXPLICIT_DENY | VDELETE_CHILD, cred, td); + if (error) + return (error); + +#endif /* !UFS_ACL */ + + /* + * Standard Unix access control - delete access requires VWRITE. + */ + error = VOP_ACCESS(vdp, VWRITE, cred, td); + if (error) + return (error); + + /* + * If directory is "sticky", then user must own + * the directory, or the file in it, else she + * may not delete it (unless she's root). This + * implements append-only directories. + */ + if ((VTOI(vdp)->i_mode & ISVTX) && + VOP_ACCESS(vdp, VADMIN, cred, td) && + VOP_ACCESS(tdp, VADMIN, cred, td)) + return (EPERM); + + return (0); +} + /* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. @@ -410,8 +465,13 @@ notfound: /* * Access for write is interpreted as allowing * creation of files in the directory. + * + * XXX: Fix the comment above. */ - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (flags & WILLBEDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); if (error) return (error); /* @@ -498,12 +558,17 @@ found: if (nameiop == DELETE && (flags & ISLASTCN)) { if (flags & LOCKPARENT) ASSERT_VOP_ELOCKED(vdp, __FUNCTION__); - /* - * Write access to directory required to delete files. - */ - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); - if (error) + if ((error = VFS_VGET(vdp->v_mount, ino, + LK_EXCLUSIVE, &tdp)) != 0) return (error); + + error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } + + /* * Return pointer to current entry in dp->i_offset, * and distance past previous entry (if there @@ -523,23 +588,10 @@ found: if (dp->i_number == ino) { VREF(vdp); *vpp = vdp; - return (0); - } - if ((error = VFS_VGET(vdp->v_mount, ino, - LK_EXCLUSIVE, &tdp)) != 0) - return (error); - /* - * If directory is "sticky", then user must own - * the directory, or the file in it, else she - * may not delete it (unless she's root). This - * implements append-only directories. - */ - if ((dp->i_mode & ISVTX) && - VOP_ACCESS(vdp, VADMIN, cred, cnp->cn_thread) && - VOP_ACCESS(tdp, VADMIN, cred, cnp->cn_thread)) { vput(tdp); - return (EPERM); + return (0); } + *vpp = tdp; return (0); } @@ -551,7 +603,11 @@ found: * regular file, or empty directory. */ if (nameiop == RENAME && (flags & ISLASTCN)) { - if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread))) + if (flags & WILLBEDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (error) return (error); /* * Careful about locking second inode. @@ -563,6 +619,33 @@ found: if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, &tdp)) != 0) return (error); + + error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } + +#ifdef SunOS_doesnt_do_that + /* + * The only purpose of this check is to return the correct + * error. Assume that we want to rename directory "a" + * to a file "b", and that we have no ACL_WRITE_DATA on + * a containing directory, but we _do_ have ACL_APPEND_DATA. + * In that case, the VOP_ACCESS check above will return 0, + * and the operation will fail with ENOTDIR instead + * of EACCESS. + */ + if (tdp->v_type == VDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } +#endif + *vpp = tdp; cnp->cn_flags |= SAVENAME; return (0); diff -urNp current/sys/ufs/ufs/ufs_vnops.c nfs4acl/sys/ufs/ufs/ufs_vnops.c --- current/sys/ufs/ufs/ufs_vnops.c 2009-06-23 22:50:52.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_vnops.c 2009-06-26 15:30:29.000000000 +0200 @@ -88,7 +88,7 @@ __FBSDID("$FreeBSD: src/sys/ufs/ufs/ufs_ #include -static vop_access_t ufs_access; +static vop_accessx_t ufs_accessx; static int ufs_chmod(struct vnode *, int, struct ucred *, struct thread *); static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct thread *); static vop_close_t ufs_close; @@ -297,8 +297,8 @@ ufs_close(ap) } static int -ufs_access(ap) - struct vop_access_args /* { +ufs_accessx(ap) + struct vop_accessx_args /* { struct vnode *a_vp; accmode_t a_accmode; struct ucred *a_cred; @@ -314,6 +314,7 @@ ufs_access(ap) #endif #ifdef UFS_ACL struct acl *acl; + acl_type_t type; #endif /* @@ -321,7 +322,7 @@ ufs_access(ap) * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ - if (accmode & VWRITE) { + if (accmode & VMODIFY_PERMS) { switch (vp->v_type) { case VDIR: case VLNK: @@ -366,14 +367,24 @@ relock: } } - /* If immutable bit set, nobody gets to write it. */ - if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) + /* + * If immutable bit set, nobody gets to write it. + * "& ~VADMIN_PERMS" is here, because without it, + * it would be impossible to remove the IMMUTABLE flag. + */ + if ((accmode & (VMODIFY_PERMS & ~VADMIN_PERMS)) && + (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) return (EPERM); #ifdef UFS_ACL - if ((vp->v_mount->mnt_flag & MNT_ACLS) != 0) { + if ((vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) != 0) { + if (vp->v_mount->mnt_flag & MNT_NFS4ACLS) + type = ACL_TYPE_NFS4; + else + type = ACL_TYPE_ACCESS; + acl = acl_alloc(M_WAITOK); - error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred, + error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td); switch (error) { case EOPNOTSUPP: @@ -381,12 +392,17 @@ relock: ip->i_gid, ap->a_accmode, ap->a_cred, NULL); break; case 0: - error = vaccess_acl_posix1e(vp->v_type, ip->i_uid, - ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + if (type == ACL_TYPE_NFS4) { + error = vaccess_acl_nfs4(vp->v_type, ip->i_uid, + ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + } else { + error = vaccess_acl_posix1e(vp->v_type, ip->i_uid, + ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + } break; default: printf( -"ufs_access(): Error retrieving ACL on object (%d).\n", +"ufs_accessx(): Error retrieving ACL on object (%d).\n", error); /* * XXX: Fall back until debugged. Should @@ -607,11 +623,20 @@ ufs_setattr(ap) * check succeeds. */ if (vap->va_vaflags & VA_UTIMES_NULL) { - error = VOP_ACCESS(vp, VADMIN, cred, td); + /* + * NFSv4.1, draft 21, 6.2.1.3.1, Discussion of Mask Attributes + * + * "A user having ACL_WRITE_DATA or ACL_WRITE_ATTRIBUTES + * will be allowed to set the times [..] to the current + * server time." + * + * XXX: Calling it four times seems a little excessive. + */ + error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td); if (error) error = VOP_ACCESS(vp, VWRITE, cred, td); } else - error = VOP_ACCESS(vp, VADMIN, cred, td); + error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td); if (error) return (error); if (vap->va_atime.tv_sec != VNOVAL) @@ -651,6 +676,35 @@ ufs_setattr(ap) return (error); } +#ifdef UFS_ACL +static int +ufs_update_nfs4_acl_after_mode_change(struct vnode *vp, int mode, + int file_owner_id, struct ucred *cred, struct thread *td) +{ + int error; + struct acl *aclp; + + aclp = acl_alloc(M_WAITOK); + + error = VOP_GETACL(vp, ACL_TYPE_NFS4, aclp, cred, td); + /* + * We cannot get EOPNOTSUPP here, as the filesystem claims + * to support ACLs. + */ + if (error) + goto out; + + acl_nfs4_sync_acl_from_mode(aclp, mode, file_owner_id); + + error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, td); + +out: + acl_free(aclp); + + return (error); +} +#endif /* UFS_ACL */ + /* * Mark this file's access time for update for vfs_mark_atime(). This * is called from execve() and mmap(). @@ -688,7 +742,7 @@ ufs_chmod(vp, mode, cred, td) * To modify the permissions on a file, must possess VADMIN * for that file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESSX(vp, VWRITE_ACL, cred, td))) return (error); /* * Privileged processes may set the sticky bit on non-directories, @@ -705,11 +759,25 @@ ufs_chmod(vp, mode, cred, td) if (error) return (error); } + + /* + * Deny setting setuid if we are not the file owner. + */ + if ((mode & ISUID) && ip->i_uid != cred->cr_uid) { + error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); + if (error) + return (error); + } + ip->i_mode &= ~ALLPERMS; ip->i_mode |= (mode & ALLPERMS); DIP_SET(ip, i_mode, ip->i_mode); ip->i_flag |= IN_CHANGE; - return (0); +#ifdef UFS_ACL + if ((vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0) + error = ufs_update_nfs4_acl_after_mode_change(vp, mode, ip->i_uid, cred, td); +#endif + return (error); } /* @@ -741,14 +809,14 @@ ufs_chown(vp, uid, gid, cred, td) * To modify the ownership of a file, must possess VADMIN for that * file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td))) return (error); /* * To change the owner of a file, or change the group of a file to a * group of which we are not a member, the caller must have * privilege. */ - if ((uid != ip->i_uid || + if (((uid != ip->i_uid && uid != cred->cr_uid) || (gid != ip->i_gid && !groupmember(gid, cred))) && (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) return (error); @@ -1396,6 +1464,36 @@ out: return (error); } +#ifdef UFS_ACL +static int +ufs_do_nfs4_acl_inheritance(struct vnode *dvp, struct vnode *tvp, + mode_t child_mode, struct ucred *cred, struct thread *td) +{ + int error; + struct acl *parent_aclp, *child_aclp; + + parent_aclp = acl_alloc(M_WAITOK); + child_aclp = acl_alloc(M_WAITOK | M_ZERO); + + error = VOP_GETACL(dvp, ACL_TYPE_NFS4, parent_aclp, cred, td); + if (error) + goto out; + + acl_nfs4_compute_inherited_acl(parent_aclp, child_aclp, + child_mode, VTOI(tvp)->i_uid, tvp->v_type == VDIR); + + error = VOP_SETACL(tvp, ACL_TYPE_NFS4, child_aclp, cred, td); + if (error) + goto out; + +out: + acl_free(parent_aclp); + acl_free(child_aclp); + + return (error); +} +#endif + /* * Mkdir system call */ @@ -1627,6 +1725,13 @@ ufs_mkdir(ap) acl_free(dacl); dacl = acl = NULL; } + + if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) { + error = ufs_do_nfs4_acl_inheritance(dvp, tvp, dmode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } #endif /* !UFS_ACL */ /* @@ -2143,6 +2248,18 @@ ufs_pathconf(ap) *ap->a_retval = 0; #endif break; + + case _PC_EXTENDED_SECURITY_NP: +#ifdef UFS_ACL + if (ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) + *ap->a_retval = 1; + else + *ap->a_retval = 0; +#else + *ap->a_retval = 0; +#endif + break; + case _PC_ACL_PATH_MAX: #ifdef UFS_ACL if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS) @@ -2440,6 +2557,13 @@ ufs_makeinode(mode, dvp, vpp, cnp) } acl_free(acl); } + + if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) { + error = ufs_do_nfs4_acl_inheritance(dvp, tvp, mode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } #endif /* !UFS_ACL */ ufs_makedirentry(ip, cnp, &newdir); error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL); @@ -2470,7 +2594,7 @@ struct vop_vector ufs_vnodeops = { .vop_read = VOP_PANIC, .vop_reallocblks = VOP_PANIC, .vop_write = VOP_PANIC, - .vop_access = ufs_access, + .vop_accessx = ufs_accessx, .vop_bmap = ufs_bmap, .vop_cachedlookup = ufs_lookup, .vop_close = ufs_close, @@ -2514,7 +2638,7 @@ struct vop_vector ufs_vnodeops = { struct vop_vector ufs_fifoops = { .vop_default = &fifo_specops, .vop_fsync = VOP_PANIC, - .vop_access = ufs_access, + .vop_accessx = ufs_accessx, .vop_close = ufsfifo_close, .vop_getattr = ufs_getattr, .vop_inactive = ufs_inactive, diff -urNp current/tools/regression/acltools/01.t nfs4acl/tools/regression/acltools/01.t --- current/tools/regression/acltools/01.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/01.t 2009-04-29 15:07:25.000000000 +0200 @@ -0,0 +1,60 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-nfs4.test on ZFS filesystem. +# +# WARNING: It uses hardcoded ZFS pool name "acltools" +# +# If any of the tests fails, here is how to debug it: go to +# the directory with problematic filesystem mounted on it, +# and do /path/to/test run /path/to/test tools-nfs4.test, e.g. +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# Output should be obvious. + +echo "1..4" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` + +# Set up the test filesystem. +MD=`mdconfig -at swap -s 64m` +MNT=`mktemp -dt acltools` +zpool create -R $MNT acltools /dev/$MD +if [ $? -ne 0 ]; then + echo "not ok 1 - 'zpool create' failed." + exit 1 +fi + +echo "ok 1" + +cd $MNT + +# First, check whether we can crash the kernel by creating too many +# entries. For some reason this won't work in the test file. +touch xxx +setfacl -x5 xxx +while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done +chmod 600 xxx +rm xxx +echo "ok 2" + +perl $TESTDIR/run $TESTDIR/tools-nfs4.test + +if [ $? -eq 0 ]; then + echo "ok 3" +else + echo "not ok 3" +fi + +cd / +zpool destroy -f acltools +rmdir $MNT +mdconfig -du $MD + +echo "ok 4" + diff -urNp current/tools/regression/acltools/02.t nfs4acl/tools/regression/acltools/02.t --- current/tools/regression/acltools/02.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/02.t 2009-04-29 15:07:37.000000000 +0200 @@ -0,0 +1,59 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-nfs4.test. +# +# If any of the tests fails, here is how to debug it: go to +# the directory with problematic filesystem mounted on it, +# and do /path/to/test run /path/to/test tools-nfs4.test, e.g. +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# Output should be obvious. + +echo "1..4" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` + +# Set up the test filesystem. +MD=`mdconfig -at swap -s 10m` +MNT=`mktemp -dt acltools` +newfs /dev/$MD > /dev/null +mount -o nfs4acls /dev/$MD $MNT +if [ $? -ne 0 ]; then + echo "not ok 1 - mount failed." + exit 1 +fi + +echo "ok 1" + +cd $MNT + +# First, check whether we can crash the kernel by creating too many +# entries. For some reason this won't work in the test file. +touch xxx +setfacl -x5 xxx +while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done +chmod 600 xxx +rm xxx +echo "ok 2" + +perl $TESTDIR/run $TESTDIR/tools-nfs4.test + +if [ $? -eq 0 ]; then + echo "ok 3" +else + echo "not ok 3" +fi + +cd / +umount -f $MNT +rmdir $MNT +mdconfig -du $MD + +echo "ok 4" + diff -urNp current/tools/regression/acltools/03.t nfs4acl/tools/regression/acltools/03.t --- current/tools/regression/acltools/03.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/03.t 2009-04-29 15:07:48.000000000 +0200 @@ -0,0 +1,82 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-crossfs.test, intended to verify +# whether cp(1) and mv(1) do the correct thing wrt ACLs. +# +# Output should be obvious. + +echo "1..5" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` +MNTROOT=`mktemp -dt acltools` + +# Set up the test filesystems. +MD1=`mdconfig -at swap -s 10m` +MNT1=$MNTROOT/nfs4 +mkdir $MNT1 +newfs /dev/$MD1 > /dev/null +mount -o nfs4acls /dev/$MD1 $MNT1 +if [ $? -ne 0 ]; then + echo "not ok 1 - mount failed." + exit 1 +fi + +echo "ok 1" + +MD2=`mdconfig -at swap -s 10m` +MNT2=$MNTROOT/posix +mkdir $MNT2 +newfs /dev/$MD2 > /dev/null +mount -o acls /dev/$MD2 $MNT2 +if [ $? -ne 0 ]; then + echo "not ok 2 - mount failed." + exit 1 +fi + +echo "ok 2" + +MD3=`mdconfig -at swap -s 10m` +MNT3=$MNTROOT/none +mkdir $MNT3 +newfs /dev/$MD3 > /dev/null +mount /dev/$MD3 $MNT3 +if [ $? -ne 0 ]; then + echo "not ok 3 - mount failed." + exit 1 +fi + +echo "ok 3" + +cd $MNTROOT + +perl $TESTDIR/run $TESTDIR/tools-crossfs.test + +if [ $? -eq 0 ]; then + echo "ok 4" +else + echo "not ok 4" +fi + +cd / + +umount -f $MNT3 +rmdir $MNT3 +mdconfig -du $MD3 + +umount -f $MNT2 +rmdir $MNT2 +mdconfig -du $MD2 + +umount -f $MNT1 +rmdir $MNT1 +mdconfig -du $MD1 + +rmdir $MNTROOT + +echo "ok 5" + diff -urNp current/tools/regression/acltools/fuzzer.sh nfs4acl/tools/regression/acltools/fuzzer.sh --- current/tools/regression/acltools/fuzzer.sh 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/fuzzer.sh 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,198 @@ +#!/bin/sh +# This is an NFSv4 ACL fuzzer. It expects to be run by non-root +# in a scratch directory on a filesystem with NFSv4 ACLs support. +# It generates the output that is expected to be fed to +# /usr/src/tools/regression/acltools/run script. + +NUMBER_OF_COMMANDS=300 + +run_command() +{ + echo "\$ $1" + eval $1 2>&1 | sed 's/^/> /' +} + +rnd_from_0_to() +{ + max=`expr $1 + 1` + rnd=`jot -r 1` + rnd=`expr $rnd % $max` + + echo $rnd +} + +rnd_path() +{ + rnd=`rnd_from_0_to 3` + case $rnd in + 0) echo "$TMP/aaa" ;; + 1) echo "$TMP/bbb" ;; + 2) echo "$TMP/aaa/ccc" ;; + 3) echo "$TMP/bbb/ddd" ;; + esac +} + +f_prepend_random_acl_on() +{ + rnd=`rnd_from_0_to 4` + case $rnd in + 0) u="owner@" ;; + 1) u="group@" ;; + 2) u="everyone@" ;; + 3) u="u:1138" ;; + 4) u="g:1138" ;; + esac + + p="" + while :; do + rnd=`rnd_from_0_to 30` + if [ -n "$p" -a $rnd -ge 14 ]; then + break; + fi + + case $rnd in + 0) p="${p}r" ;; + 1) p="${p}w" ;; + 2) p="${p}x" ;; + 3) p="${p}p" ;; + 4) p="${p}d" ;; + 5) p="${p}D" ;; + 6) p="${p}a" ;; + 7) p="${p}A" ;; + 8) p="${p}R" ;; + 9) p="${p}W" ;; + 10) p="${p}R" ;; + 11) p="${p}c" ;; + 12) p="${p}C" ;; + 13) p="${p}o" ;; + 14) p="${p}s" ;; + esac + done + + f="" + while :; do + rnd=`rnd_from_0_to 10` + if [ $rnd -ge 6 ]; then + break; + fi + + case $rnd in + 0) f="${f}f" ;; + 1) f="${f}d" ;; + 2) f="${f}n" ;; + 3) f="${f}i" ;; + esac + done + + rnd=`rnd_from_0_to 1` + case $rnd in + 0) x="allow" ;; + 1) x="deny" ;; + esac + + acl="$u:$p:$f:$x" + + file=`rnd_path` + run_command "setfacl -a0 $acl $file" +} + +f_getfacl() +{ + file=`rnd_path` + run_command "getfacl -qn $file" +} + +f_ls_mode() +{ + file=`rnd_path` + run_command "ls -al $file | sed -n '2p' | cut -d' ' -f1" +} + +f_chmod() +{ + b1=`rnd_from_0_to 7` + b2=`rnd_from_0_to 7` + b3=`rnd_from_0_to 7` + b4=`rnd_from_0_to 7` + file=`rnd_path` + + run_command "chmod $b1$b2$b3$b4 $file $2" +} + +f_touch() +{ + file=`rnd_path` + run_command "touch $file" +} + +f_rm() +{ + file=`rnd_path` + run_command "rm -f $file" +} + +f_mkdir() +{ + file=`rnd_path` + run_command "mkdir $file" +} + +f_rmdir() +{ + file=`rnd_path` + run_command "rmdir $file" +} + +f_mv() +{ + from=`rnd_path` + to=`rnd_path` + run_command "mv -f $from $to" +} + +# XXX: chown(8), setting times with touch(1). + +switch_to_random_user() +{ +# echo "switch" +} + +execute_random_command() +{ + rnd=`rnd_from_0_to 20` + + case $rnd in + 0|10|11|12|13|15) cmd=f_prepend_random_acl_on ;; + 1) cmd=f_getfacl ;; + 2) cmd=f_ls_mode ;; + 3) cmd=f_chmod ;; + 4|18|19) cmd=f_touch ;; + 5) cmd=f_rm ;; + 6|16|17) cmd=f_mkdir ;; + 7) cmd=f_rmdir ;; + 8) cmd=f_mv ;; + esac + + $cmd "XXX" +} + +echo "# Fuzzing; will stop after $NUMBER_OF_COMMANDS commands." +TMP="aclfuzzer_`dd if=/dev/random bs=1k count=1 2>/dev/null | openssl md5`" + +run_command "whoami" +umask 022 +run_command "umask 022" +run_command "mkdir $TMP" + +i=0; +while [ "$i" -lt "$NUMBER_OF_COMMANDS" ]; do + switch_to_random_user + execute_random_command + i=`expr $i + 1` +done + +run_command "find $TMP -exec setfacl -a0 everyone@:rxd:allow {} \;" +run_command "rm -rfv $TMP" + +echo "# Fuzzed, thank you." + diff -urNp current/tools/regression/acltools/tools-crossfs.test nfs4acl/tools/regression/acltools/tools-crossfs.test --- current/tools/regression/acltools/tools-crossfs.test 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/tools-crossfs.test 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,148 @@ +# This is a tools-level test whether cp(1) and mv(1) do the correct +# thing wrt ACLs. Run it as root using ACL-enabled kernel: +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# You need to have three subdirectories, named nfs4, posix and none, +# with filesystems with NFSv4 ACLs, POSIX.1e ACLs and no ACLs enabled, +# respectively, mounted on them, in your current directory. +# +# WARNING: Creates files in unsafe way. + +$ whoami +> root +$ umask 022 + +$ touch nfs4/xxx +$ getfacl -nq nfs4/xxx +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ touch posix/xxx +$ getfacl -nq posix/xxx +> user::rw- +> group::r-- +> other::r-- + +$ rm posix/xxx + +# mv without any ACLs. +$ chmod 456 nfs4/xxx +$ mv nfs4/xxx posix/ +$ ls -l posix/xxx | cut -d' ' -f1 +> -r--r-xrw- + +# mv with POSIX.1e ACLs. +$ setfacl -m u:42:x,g:43:w posix/xxx +$ rm -f posix/yyy +$ mv posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::r-- +> user:42:--x +> group::r-x +> group:43:-w- +> mask::rwx +> other::rw- + +# mv from POSIX.1e to NFSv4. +$ rm -f nfs4/xxx +$ mv posix/yyy nfs4/xxx +> mv: ACL brand of source is POSIX.1e, but destination is NFSv4; ACL not copied +$ getfacl -nq nfs4/xxx +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +# mv with NFSv4 ACLs. +$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx +$ rm -f nfs4/yyy +$ mv nfs4/xxx nfs4/yyy +$ getfacl -nq nfs4/yyy +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +# mv from NFSv4 to POSIX.1e. +$ rm -f posix/xxx +$ mv nfs4/yyy posix/xxx +> mv: ACL brand of source is NFSv4, but destination is POSIX.1e; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -r--rwxrw- + +# mv from POSIX.1e to none. +$ setfacl -m u:42:x,g:43:w posix/xxx +$ mv posix/xxx none/xxx +$ ls -l none/xxx | cut -d' ' -f1 +> -r--rwxrw- + +# cp with POSIX.1e ACLs. +$ rm -f posix/xxx +$ touch posix/xxx +$ setfacl -m u:42:x,g:43:w posix/xxx +$ getfacl -nq posix/xxx +> user::rw- +> user:42:--x +> group::r-- +> group:43:-w- +> mask::rwx +> other::r-- + +$ rm -f posix/yyy +$ cp posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::rw- +> group::r-x +> other::r-- + +$ rm -f posix/yyy +$ cp -p posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::rw- +> user:42:--x +> group::r-- +> group:43:-w- +> mask::rwx +> other::r-- + +# mv from POSIX.1e to NFSv4. +$ rm -f nfs4/xxx +$ cp -p posix/xxx nfs4/xxx +> cp: ACL brand of source is POSIX.1e, but destination is NFSv4; ACL not copied +$ ls -l nfs4/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +# cp with NFSv4 ACLs. +$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx +$ rm -f nfs4/yyy +$ cp -p nfs4/xxx nfs4/yyy +$ getfacl -nq nfs4/yyy +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# mv from NFSv4 to POSIX.1e. +$ rm -f posix/xxx +$ cp -p nfs4/xxx posix/xxx +> cp: ACL brand of source is NFSv4, but destination is POSIX.1e; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +$ cp -p nfs4/yyy none/xxx + diff -urNp current/tools/regression/acltools/tools-nfs4.test nfs4acl/tools/regression/acltools/tools-nfs4.test --- current/tools/regression/acltools/tools-nfs4.test 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/tools-nfs4.test 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,802 @@ +# This is a tools-level test for NFSv4 ACL functionality. Run it as root +# using ACL-enabled kernel: +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# WARNING: Creates files in unsafe way. + +$ whoami +> root +$ umask 022 + +# Smoke test for getfacl(1). +$ touch xxx +$ getfacl xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ getfacl -q xxx +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Check verbose mode formatting. +$ getfacl -v xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:execute::deny +> owner@:read_data/write_data/append_data/write_attributes/write_xattr/write_acl/write_owner::allow +> group@:write_data/execute/append_data::deny +> group@:read_data::allow +> everyone@:write_data/execute/append_data/write_attributes/write_xattr/write_acl/write_owner::deny +> everyone@:read_data/read_attributes/read_xattr/read_acl/synchronize::allow + +# Test setfacl -a. +$ setfacl -a2 u:0:write_acl:allow,g:1:read_acl:deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test user and group name resolving. +$ rm xxx +$ touch xxx +$ setfacl -a2 u:root:write_acl:allow,g:daemon:read_acl:deny xxx +$ getfacl xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:root:-----------C--:------:allow +> group:daemon:----------c---:------:deny +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Check whether ls correctly marks files with "+". +$ ls -l xxx | cut -d' ' -f1 +> -rw-r--r--+ + +# Test removing entries by number. +$ setfacl -x 4 xxx +$ setfacl -x 4 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test setfacl -m. +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -m everyone@::deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test getfacl -i. +$ getfacl -i xxx +> # file: xxx +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:root:-----------C--:------:allow:0 +> group:daemon:----------c---:------:deny:1 +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Make sure cp without any flags does not copy copy the ACL. +$ cp xxx yyy +$ ls -l yyy | cut -d' ' -f1 +> -rw-r--r-- + +# Make sure it does with the "-p" flag. +$ rm yyy +$ cp -p xxx yyy +$ getfacl -n yyy +> # file: yyy +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rm yyy + +# Test removing entries by... by example? +$ setfacl -x everyone@::deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test setfacl -b. +$ setfacl -b xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ ls -l xxx | cut -d' ' -f1 +> -rw-r--r-- + +# Check setfacl(1) and getfacl(1) with multiple files. +$ touch xxx yyy zzz + +$ ls -l xxx yyy zzz | cut -d' ' -f1 +> -rw-r--r-- +> -rw-r--r-- +> -rw-r--r-- + +$ setfacl -m u:42:x:allow,g:43:w:allow nnn xxx yyy zzz +> setfacl: nnn: acl_get_file() failed: No such file or directory + +$ ls -l nnn xxx yyy zzz | cut -d' ' -f1 +> ls: nnn: No such file or directory +> -rw-r--r--+ +> -rw-r--r--+ +> -rw-r--r--+ + +$ getfacl -nq nnn xxx yyy zzz +> getfacl: nnn: stat() failed: No such file or directory +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +> +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +> +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ setfacl -b nnn xxx yyy zzz +> setfacl: nnn: acl_get_file() failed: No such file or directory + +$ ls -l nnn xxx yyy zzz | cut -d' ' -f1 +> ls: nnn: No such file or directory +> -rw-r--r-- +> -rw-r--r-- +> -rw-r--r-- + +$ rm xxx yyy zzz + +# Test applying mode to an ACL. +$ touch xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow -x everyone@::allow xxx +$ chmod 600 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -rw-------+ + +$ rm xxx +$ touch xxx +$ chown 42 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 600 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 42 +> # group: wheel +> user:42:--------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -rw-------+ + +$ rm xxx +$ touch xxx +$ chown 43 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 124 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 43 +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:rw-p----------:------:deny +> owner@:--x----A-W-Co-:------:allow +> group@:r-x-----------:------:deny +> group@:-w-p----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> ---x-w-r--+ + +$ rm xxx +$ touch xxx +$ chown 43 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 412 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 43 +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--------------:------:deny +> user:44:--x-----------:------:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -r----x-w-+ + +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-d----:allow +> group:43:-w--D---------:-d----:deny +> group@:-----da-------:------:allow +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +$ chmod 777 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:--------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:--------------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:rwxp--a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chmod 124 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:--------------:------:deny +> group:42:----D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:r-------------:------:deny +> group:44:r----da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:rw-p----------:------:deny +> owner@:--x----A-W-Co-:------:allow +> group@:r-x-----------:------:deny +> group@:-w-p----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chmod 412 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-x-----------:------:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:-w------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:rw-p----------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chown 42 ddd +$ chmod 412 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: 42 +> # group: wheel +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:-w------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:rw-p----------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow + +# Test applying ACL to mode. +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 u:42:rwx:fi:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> drwxr-xr-x+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group@:w:deny,group@:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr----x---+ + +# XXX: This one is fishy. Shouldn't it be "dr---wx---+"? +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group@:w:fi:deny,group@:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr---wx---+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group:43:w:deny,group:43:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr--------+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,user:43:w:deny,user:43:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr--------+ + +# Test inheritance. +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:43:write_data/write_acl:fin:deny,u:43:rwxp:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:dn:deny ddd +$ setfacl -a0 user:42:write_acl/write_owner:fi:allow ddd +$ setfacl -a0 group:41:read_data/read_attributes:dni:allow ddd +$ setfacl -a0 user:41:write_data/write_attributes:fn:allow ddd +$ getfacl -qn ddd +> user:41:-w-----A------:f--n--:allow +> group:41:r-----a-------:-din--:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-d-n--:deny +> group:43:-w---------C--:f-in--:deny +> user:43:rwxp----------:------:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ cd ddd +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rm xxx +$ umask 077 +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:r-x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ rm xxx +$ umask 770 +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:r-x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +$ rm xxx +$ umask 707 +$ touch xxx +$ getfacl -qn xxx +> user:41:--------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--x-----------:------:deny +> group@:rw-p----------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ umask 077 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:r-------------:------:deny +> group:41:r-----a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ rmdir yyy +$ umask 770 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:r-------------:------:deny +> group:41:r-----a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:rwxp--a-R-c--s:------:allow + +$ rmdir yyy +$ umask 707 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:--------------:------:deny +> group:41:------a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +# There is some complication regarding how write_acl and write_owner flags +# get inherited. Make sure we got it right. +$ setfacl -b . +$ setfacl -a0 u:42:Co:f:allow . +$ setfacl -a0 u:43:Co:d:allow . +$ setfacl -a0 u:44:Co:fd:allow . +$ setfacl -a0 u:45:Co:fi:allow . +$ setfacl -a0 u:46:Co:di:allow . +$ setfacl -a0 u:47:Co:fdi:allow . +$ setfacl -a0 u:48:Co:fn:allow . +$ setfacl -a0 u:49:Co:dn:allow . +$ setfacl -a0 u:50:Co:fdn:allow . +$ setfacl -a0 u:51:Co:fni:allow . +$ setfacl -a0 u:52:Co:dni:allow . +$ setfacl -a0 u:53:Co:fdni:allow . +$ umask 022 +$ rm xxx +$ touch xxx +$ getfacl -nq xxx +> user:53:--------------:------:deny +> user:53:--------------:------:allow +> user:51:--------------:------:deny +> user:51:--------------:------:allow +> user:50:--------------:------:deny +> user:50:--------------:------:allow +> user:48:--------------:------:deny +> user:48:--------------:------:allow +> user:47:--------------:------:deny +> user:47:--------------:------:allow +> user:45:--------------:------:deny +> user:45:--------------:------:allow +> user:44:--------------:------:deny +> user:44:--------------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir yyy +$ mkdir yyy +$ getfacl -nq yyy +> user:53:--------------:------:deny +> user:53:--------------:------:allow +> user:52:--------------:------:deny +> user:52:--------------:------:allow +> user:50:--------------:------:deny +> user:50:--------------:------:allow +> user:49:--------------:------:deny +> user:49:--------------:------:allow +> user:47:-----------Co-:fdi---:allow +> user:47:--------------:------:deny +> user:47:--------------:------:allow +> user:46:-----------Co-:-di---:allow +> user:46:--------------:------:deny +> user:46:--------------:------:allow +> user:45:-----------Co-:f-i---:allow +> user:44:-----------Co-:fdi---:allow +> user:44:--------------:------:deny +> user:44:--------------:------:allow +> user:43:-----------Co-:-di---:allow +> user:43:--------------:------:deny +> user:43:--------------:------:allow +> user:42:-----------Co-:f-i---:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ setfacl -b . +$ setfacl -a0 u:42:Co:f:deny . +$ setfacl -a0 u:43:Co:d:deny . +$ setfacl -a0 u:44:Co:fd:deny . +$ setfacl -a0 u:45:Co:fi:deny . +$ setfacl -a0 u:46:Co:di:deny . +$ setfacl -a0 u:47:Co:fdi:deny . +$ setfacl -a0 u:48:Co:fn:deny . +$ setfacl -a0 u:49:Co:dn:deny . +$ setfacl -a0 u:50:Co:fdn:deny . +$ setfacl -a0 u:51:Co:fni:deny . +$ setfacl -a0 u:52:Co:dni:deny . +$ setfacl -a0 u:53:Co:fdni:deny . +$ umask 022 +$ rm xxx +$ touch xxx +$ getfacl -nq xxx +> user:53:-----------Co-:------:deny +> user:51:-----------Co-:------:deny +> user:50:-----------Co-:------:deny +> user:48:-----------Co-:------:deny +> user:47:-----------Co-:------:deny +> user:45:-----------Co-:------:deny +> user:44:-----------Co-:------:deny +> user:42:-----------Co-:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir yyy +$ mkdir yyy +$ getfacl -nq yyy +> user:53:-----------Co-:------:deny +> user:52:-----------Co-:------:deny +> user:50:-----------Co-:------:deny +> user:49:-----------Co-:------:deny +> user:47:-----------Co-:fdi---:deny +> user:47:-----------Co-:------:deny +> user:46:-----------Co-:-di---:deny +> user:46:-----------Co-:------:deny +> user:45:-----------Co-:f-i---:deny +> user:44:-----------Co-:fdi---:deny +> user:44:-----------Co-:------:deny +> user:43:-----------Co-:-di---:deny +> user:43:-----------Co-:------:deny +> user:42:-----------Co-:f-i---:deny +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ rmdir yyy +$ rm xxx +$ cd .. +$ rmdir ddd + +$ rm xxx + diff -urNp current/tools/regression/acltools/tools-posix.test nfs4acl/tools/regression/acltools/tools-posix.test --- current/tools/regression/acltools/tools-posix.test 2009-04-29 09:29:31.000000000 +0200 +++ nfs4acl/tools/regression/acltools/tools-posix.test 2009-04-29 16:29:29.000000000 +0200 @@ -13,7 +13,7 @@ $ umask 022 # Smoke test for getfacl(1). $ touch xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -27,7 +27,7 @@ $ getfacl -q xxx > other::r-- $ setfacl -m u:42:r,g:43:w xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -99,8 +99,32 @@ $ getfacl xxx > mask::rw- > other::r-- +$ setfacl -m u:42:r xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user::rw- +> user:42:r-- +> group::r-- +> group:43:-w- +> mask::rw- +> other::r-- + +# Test removing entries by number. +$ setfacl -x 1 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user::rw- +> group::r-- +> group:43:-w- +> mask::rw- +> other::r-- + $ setfacl -m g:43:r xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -118,7 +142,7 @@ $ ls -l yyy | cut -d' ' -f1 # Make sure it does with the "-p" flag. $ rm yyy $ cp -p xxx yyy -$ getfacl yyy +$ getfacl -n yyy > # file: yyy > # owner: root > # group: wheel @@ -133,7 +157,7 @@ $ rm yyy # Test removing entries by... by example? $ setfacl -m u:42:r,g:43:w xxx $ setfacl -x u:42: xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -145,7 +169,7 @@ $ getfacl xxx # Test setfacl -b. $ setfacl -b xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -158,7 +182,7 @@ $ ls -l xxx | cut -d' ' -f1 > -rw-r--r--+ $ setfacl -nb xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -178,7 +202,7 @@ $ ls -l xxx yyy zzz | cut -d' ' -f1 > -rw-r--r-- $ setfacl -m u:42:x,g:43:w nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -186,8 +210,8 @@ $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > -rw-rwxr--+ > -rw-rwxr--+ -$ getfacl -q nnn xxx yyy zzz -> getfacl: nnn: No such file or directory +$ getfacl -nq nnn xxx yyy zzz +> getfacl: nnn: stat() failed: No such file or directory > user::rw- > user:42:--x > group::r-- @@ -210,7 +234,7 @@ $ getfacl -q nnn xxx yyy zzz > other::r-- $ setfacl -b nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -219,7 +243,7 @@ $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > -rw-r--r--+ $ setfacl -bn nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -233,7 +257,7 @@ $ rm xxx yyy zzz $ touch xxx $ setfacl -m u:42:rwx,g:43:rwx xxx $ chmod 600 xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -245,7 +269,7 @@ $ getfacl xxx > other::--- $ chmod 060 xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -259,7 +283,7 @@ $ getfacl xxx # Test default ACLs. $ umask 022 $ mkdir ddd -$ getfacl -q ddd +$ getfacl -qn ddd > user::rwx > group::r-x > other::r-x @@ -269,7 +293,7 @@ $ ls -l | grep ddd | cut -d' ' -f1 $ getfacl -dq ddd $ setfacl -dm u::rwx,g::rx,o::rx,mask::rwx ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > group::r-x > mask::rwx @@ -281,7 +305,7 @@ $ ls -l | grep ddd | cut -d' ' -f1 $ setfacl -dm g:42:rwx,u:42:r ddd $ setfacl -dm g::w ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > user:42:r-- > group::-w- @@ -290,7 +314,7 @@ $ getfacl -dq ddd > other::r-x $ setfacl -dx group:42: ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > user:42:r-- > group::-w- diff -urNp current/tools/regression/fstest/Makefile nfs4acl/tools/regression/fstest/Makefile --- current/tools/regression/fstest/Makefile 2009-04-29 09:29:33.000000000 +0200 +++ nfs4acl/tools/regression/fstest/Makefile 2009-04-29 09:37:31.000000000 +0200 @@ -4,7 +4,7 @@ OSTYPE=$(shell uname) ifeq "${OSTYPE}" "FreeBSD" CFLAGS += -D__OS_FreeBSD__ -CFLAGS += -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_LCHFLAGS +CFLAGS += -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL endif ifeq "${OSTYPE}" "SunOS" diff -urNp current/tools/regression/fstest/fstest.c nfs4acl/tools/regression/fstest/fstest.c --- current/tools/regression/fstest/fstest.c 2009-06-04 22:03:37.000000000 +0200 +++ nfs4acl/tools/regression/fstest/fstest.c 2009-06-04 22:31:30.000000000 +0200 @@ -45,6 +45,9 @@ #define stat64 stat #define lstat64 lstat #endif +#ifdef HAS_FREEBSD_ACL +#include +#endif #ifndef ALLPERMS #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) @@ -75,7 +78,12 @@ enum action { ACTION_TRUNCATE, ACTION_STAT, ACTION_LSTAT, - ACTION_PATHCONF + ACTION_PATHCONF, +#ifdef HAS_FREEBSD_ACL + ACTION_PREPENDACL, + ACTION_READACL, +#endif + ACTION_WRITE, }; #define TYPE_NONE 0x0000 @@ -118,6 +126,11 @@ static struct syscall_desc syscalls[] = { "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, { "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, { "pathconf", ACTION_PATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, +#ifdef HAS_FREEBSD_ACL + { "prependacl", ACTION_PREPENDACL, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, + { "readacl", ACTION_READACL, { TYPE_STRING, TYPE_NONE } }, +#endif + { "write", ACTION_WRITE, { TYPE_STRING, TYPE_NONE } }, { NULL, -1, { TYPE_NONE } } }; @@ -397,6 +410,11 @@ call_syscall(struct syscall_desc *scall, char *str; long long num; } args[MAX_ARGS]; +#ifdef HAS_FREEBSD_ACL + int entry_id = ACL_FIRST_ENTRY; + acl_t acl, newacl; + acl_entry_t entry, newentry; +#endif /* * Verify correctness of the arguments. @@ -540,6 +558,48 @@ call_syscall(struct syscall_desc *scall, rval = -1; break; } +#ifdef HAS_FREEBSD_ACL + case ACTION_PREPENDACL: + rval = -1; + + acl = acl_get_file(STR(0), ACL_TYPE_NFS4); + if (acl == NULL) + break; + + newacl = acl_from_text(STR(1)); + if (acl == NULL) + break; + + while (acl_get_entry(newacl, entry_id, &newentry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + if (acl_create_entry_np(&acl, &entry, 0)) + break; + + if (acl_copy_entry(entry, newentry)) + break; + } + + rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl); + break; + + case ACTION_READACL: + acl = acl_get_file(STR(0), ACL_TYPE_NFS4); + if (acl == NULL) + rval = -1; + else + rval = 0; + break; +#endif + + case ACTION_WRITE: + rval = open(STR(0), O_WRONLY); + if (rval < 0) + break; + + rval = write(rval, "x", 1); + break; + default: fprintf(stderr, "unsupported syscall\n"); exit(1); diff -urNp current/tools/regression/fstest/tests/chmod/12.t nfs4acl/tools/regression/fstest/tests/chmod/12.t --- current/tools/regression/fstest/tests/chmod/12.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/chmod/12.t 2009-04-29 09:37:32.000000000 +0200 @@ -0,0 +1,31 @@ +#!/bin/sh + +desc="verify SUID/SGID bit behaviour" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..10" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Check whether writing to the file by non-owner clears the SUID. +expect 0 create ${n0} 04777 +expect 0 -u 65534 -g 65534 write ${n0} +expect 0777 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether writing to the file by non-owner clears the SGID. +expect 0 create ${n0} 02777 +expect 0 -u 65534 -g 65534 write ${n0} +expect 0777 stat ${n0} mode +expect 0 unlink ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/00.t nfs4acl/tools/regression/fstest/tests/granular/00.t --- current/tools/regression/fstest/tests/granular/00.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/00.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,109 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - WRITE_DATA vs APPEND_DATA on directories" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..49" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Tests 2..7 - check out whether root user can do stuff. +# Can create files? +expect 0 create ${n0} 0644 + +# Can create symlinks? +expect 0 link ${n0} ${n1} +expect 0 unlink ${n1} +expect 0 unlink ${n0} + +# Can create directories? +expect 0 mkdir ${n0} 0755 +expect 0 rmdir ${n0} + +# Check whether user 65534 is permitted to create and remove +# files, but not subdirectories. +expect 0 prependacl . user:65534:write_data::allow,user:65534:append_data::deny + +# Can create files? +expect 0 -u 65534 -g 65534 create ${n0} 0644 + +# Can create symlinks? +expect 0 -u 65534 -g 65534 link ${n0} ${n1} +expect 0 -u 65534 -g 65534 unlink ${n1} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Can create directories? +expect EACCES -u 65534 -g 65534 mkdir ${n0} 0755 +expect ENOENT -u 65534 -g 65534 rmdir ${n0} +expect 0 mkdir ${n0} 0755 +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Can move files from other directory? +expect 0 create ../${n3}/${n1} 0644 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move files from other directory overwriting existing files? +expect 0 create ../${n3}/${n1} 0644 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Can move directories from other directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move directories from other directory overwriting existing directory? +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 rmdir ../${n3}/${n1} + +# Check whether user 65534 is permitted to create +# subdirectories, but not files - and to remove neither of them. +expect 0 prependacl . user:65534:write_data::deny,user:65534:append_data::allow + +# Can create files? +expect EACCES -u 65534 -g 65534 create ${n0} 0644 + +# Can create symlinks? +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 link ${n0} ${n1} +expect ENOENT -u 65534 -g 65534 unlink ${n1} +expect EACCES -u 65534 -g 65534 unlink ${n0} +expect 0 unlink ${n0} + +# Can create directories? +expect 0 -u 65534 -g 65534 mkdir ${n0} 0755 +expect EACCES -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# Can move files from other directory? +expect 0 create ../${n3}/${n1} 0644 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move files from other directory overwriting existing files? +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 unlink ../${n3}/${n1} + +# Can move directories from other directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move directories from other directory overwriting existing directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} +expect 0 rmdir ${n3} diff -urNp current/tools/regression/fstest/tests/granular/01.t nfs4acl/tools/regression/fstest/tests/granular/01.t --- current/tools/regression/fstest/tests/granular/01.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/01.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,34 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_READ_ATTRIBUTES and ACL_WRITE_ATTRIBUTES" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..12" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Tests 1..12 - check out whether user 65534 is permitted to read attributes. +expect 0 create ${n0} 0644 +expect 0 lstat ${n0} size +expect 0 -u 65534 -g 65534 stat ${n0} size +expect 0 prependacl ${n0} user:65534:read_attributes::deny +expect 0 lstat ${n0} size +expect EACCES -u 65534 -g 65534 stat ${n0} size +expect 0 prependacl ${n0} user:65534:read_attributes::allow +expect 0 -u 65534 -g 65534 stat ${n0} size +expect 0 lstat ${n0} size +expect 0 unlink ${n0} + +# Tests 12..12 - check out whether user 65534 is permitted to write attributes. +# XXX: Check if ACL_WRITE_ATTRIBUTES allows for modifying access times. + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/02.t nfs4acl/tools/regression/fstest/tests/granular/02.t --- current/tools/regression/fstest/tests/granular/02.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/02.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,141 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_READ_ACL and ACL_WRITE_ACL" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..83" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Check whether user 65534 is permitted to read ACL. +expect 0 create ${n0} 0644 +expect 0 readacl ${n0} +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0 prependacl ${n0} user:65534:read_acl::deny +expect 0 readacl ${n0} +expect EACCES -u 65534 -g 65534 readacl ${n0} +expect 0 prependacl ${n0} user:65534:read_acl::allow +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0 readacl ${n0} +expect 0 unlink ${n0} + +# Check whether user 65534 is permitted to write ACL. +expect 0 create ${n0} 0644 +expect EPERM -u 65534 -g 65534 prependacl ${n0} user:65534:read_data::allow +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_data::allow +expect 0 unlink ${n0} + +# Check whether user 65534 is permitted to write mode. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 unlink ${n0} + +# There is an interesting problem with interaction between ACL_WRITE_ACL +# and SUID/SGID bits. In case user does have ACL_WRITE_ACL, but is not +# a file owner, Solaris does the following: +# 1. Setting SUID fails with EPERM. +# 2. Setting SGID succeeds, but mode is not changed. +# 3. Modifying ACL does not clear SUID nor SGID bits. +# 4. Writing the file does clear both SUID and SGID bits. +# +# What we are doing is the following: +# 1. Setting SUID or SGID fails with EPERM. +# 2. Modifying ACL does not clear SUID nor SGID bits. +# 3. Writing the file does clear both SUID and SGID bits. +# +# Check whether user 65534 is denied to write mode with SUID bit. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 04777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect EPERM -u 65534 -g 65534 chmod ${n0} 04777 +expect 0 unlink ${n0} + +# Check whether user 65534 is denied to write mode with SGID bit. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 02777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect EPERM -u 65534 -g 65534 chmod ${n0} 02777 +expect 0 unlink ${n0} + +# Check whether user 65534 is allowed to write mode with sticky bit. +expect 0 mkdir ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 01777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 chmod ${n0} 01777 +expect 0 rmdir ${n0} + +# Check whether modifying the ACL by not-owner preserves the SUID. +expect 0 create ${n0} 04755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 04755 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether modifying the ACL by not-owner preserves the SGID. +expect 0 create ${n0} 02755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 02755 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether modifying the ACL by not-owner preserves the sticky bit. +expect 0 mkdir ${n0} 0755 +expect 0 chmod ${n0} 01755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 01755 stat ${n0} mode +expect 0 rmdir ${n0} + +# Clearing the SUID and SGID bits when being written to by non-owner +# is checked in chmod/12.t. + +# Check whether the file owner is always permitted to get and set +# ACL and file mode, even if ACL_{READ,WRITE}_ACL would deny it. +expect 0 chmod . 0777 +expect 0 -u 65534 -g 65534 create ${n0} 0600 +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_acl::deny +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_acl::deny +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0600 -u 65534 -g 65534 stat ${n0} mode +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 unlink ${n0} + +expect 0 -u 65534 -g 65534 mkdir ${n0} 0600 +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_acl::deny +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_acl::deny +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0600 -u 65534 -g 65534 stat ${n0} mode +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 rmdir ${n0} + +# Check whether the root is allowed for these as well. +expect 0 -u 65534 -g 65534 create ${n0} 0600 +expect 0 prependacl ${n0} everyone@:write_acl::deny +expect 0 prependacl ${n0} everyone@:read_acl::deny +expect 0 readacl ${n0} +expect 0600 stat ${n0} mode +expect 0 chmod ${n0} 0777 +expect 0 unlink ${n0} + +expect 0 -u 65534 -g 65534 mkdir ${n0} 0600 +expect 0 prependacl ${n0} everyone@:write_acl::deny +expect 0 prependacl ${n0} everyone@:read_acl::deny +expect 0600 stat ${n0} mode +expect 0 readacl ${n0} +expect 0600 stat ${n0} mode +expect 0 chmod ${n0} 0777 +expect 0 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/03.t nfs4acl/tools/regression/fstest/tests/granular/03.t --- current/tools/regression/fstest/tests/granular/03.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/03.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,131 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - DELETE and DELETE_CHILD" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..65" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Unlink allowed on writable directory. +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 unlink ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving file elsewhere allowed on writable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Moving file from elsewhere allowed on writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving file from elsewhere overwriting local file allowed +# on writable directory. +expect 0 create ${n0} 0644 +expect 0 create ../${n3}/${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Denied DELETE changes nothing wrt moving elsewhere or from elsewhere. +expect 0 create ${n0} 0644 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# DELETE_CHILD denies unlink on writable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 unlink ${n0} +expect 0 unlink ${n0} + +# DELETE_CHILD denies moving file elsewhere. +expect 0 create ${n0} 0644 +expect EPERM -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 rename ${n0} ../${n3}/${n0} + +# DELETE_CHILD does not deny moving file from elsewhere +# to a writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE_CHILD denies moving file from elsewhere +# to a writable directory overwriting local file. +expect 0 create ../${n3}/${n0} 0644 +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE allowed on file allows for unlinking, no matter +# what permissions on containing directory are. +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Same for moving the file elsewhere. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Same for moving the file from elsewhere into a writable +# directory with DELETE_CHILD denied. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 unlink ${n0} + +# DELETE does not allow for overwriting a file in a unwritable +# directory with DELETE_CHILD denied. +expect 0 create ${n0} 0644 +expect 0 create ../${n3}/${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl ${n0} user:65534:delete::allow +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# But it allows for plain deletion. +expect 0 -u 65534 -g 65534 unlink ${n0} + +# DELETE_CHILD allowed on unwritable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving things elsewhere is allowed. +expect 0 create ${n0} 0644 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Moving things back is not. +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we're overwriting. +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we have DELETE on the existing file. +expect 0 prependacl ${n0} user:65534:delete::allow +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 unlink ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/04.t nfs4acl/tools/regression/fstest/tests/granular/04.t --- current/tools/regression/fstest/tests/granular/04.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/04.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,77 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_WRITE_OWNER" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..52" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# ACL_WRITE_OWNER permits to set gid to our own only. +expect 0 create ${n0} 0644 +expect 0,0 lstat ${n0} uid,gid +expect EPERM -u 65534 -g 65532,65531 chown ${n0} -1 65532 +expect 0,0 lstat ${n0} uid,gid +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect EPERM -u 65534 -g 65532,65531 chown ${n0} -1 65530 +expect 0,0 lstat ${n0} uid,gid +expect 0 -u 65534 -g 65532,65531 chown ${n0} -1 65532 +expect 0,65532 lstat ${n0} uid,gid +expect 0 unlink ${n0} + +# ACL_WRITE_OWNER permits to set uid to our own only. +expect 0 create ${n0} 0644 +expect 0,0 lstat ${n0} uid,gid +expect EPERM -u 65534 -g 65532,65531 chown ${n0} 65534 65531 +expect 0,0 lstat ${n0} uid,gid +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect EPERM -u 65534 -g 65532,65531 chown ${n0} 65530 65531 +expect 0,0 lstat ${n0} uid,gid +expect 0 -u 65534 -g 65532,65531 chown ${n0} 65534 65531 +expect 65534,65531 lstat ${n0} uid,gid +expect 0 unlink ${n0} + +# When non-owner calls chown(2) successfully, set-uid and set-gid bits are +# removed, except when both uid and gid are equal to -1. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} 65534 65532 +expect 0555,65534,65532 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 65533 +expect 0555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 -1 +expect 06555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 unlink ${n0} + +expect 0 mkdir ${n0} 0755 +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} 65534 65532 +expect 0555,65534,65532 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 65533 +expect 0555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 -1 +expect 06555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/05.t nfs4acl/tools/regression/fstest/tests/granular/05.t --- current/tools/regression/fstest/tests/granular/05.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/05.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,146 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - DELETE and DELETE_CHILD with directories" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..68" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Unlink allowed on writable directory. +expect 0 mkdir ${n0} 0755 +expect EACCES -u 65534 -g 65534 rmdir ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving directory elsewhere allowed on writable directory. +expect 0 mkdir ${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 12 +# Moving directory from elsewhere allowed on writable directory. +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:append_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving directory from elsewhere overwriting local directory allowed +# on writable directory. +expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# 23 +# Denied DELETE changes nothing wrt removing. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Denied DELETE changes nothing wrt moving elsewhere or from elsewhere. +expect 0 mkdir ${n0} 0777 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# DELETE_CHILD denies unlink on writable directory. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# 35 +# DELETE_CHILD denies moving directory elsewhere. +expect 0 mkdir ${n0} 0777 +expect EPERM -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 rename ${n0} ../${n3}/${n0} + +# DELETE_CHILD does not deny moving directory from elsewhere +# to a writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE_CHILD denies moving directory from elsewhere +# to a writable directory overwriting local directory. +expect 0 mkdir ../${n3}/${n0} 0755 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE allowed on directory allows for unlinking, no matter +# what permissions on containing directory are. +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Same for moving the directory elsewhere. +expect 0 mkdir ${n0} 0777 +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 46 +# Same for moving the directory from elsewhere into a writable +# directory with DELETE_CHILD denied. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 rmdir ${n0} + +# DELETE does not allow for overwriting a directory in a unwritable +# directory with DELETE_CHILD denied. +expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl ${n0} user:65534:delete::allow +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# 54 +# But it allows for plain deletion. +# XXX: expect 0 -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# DELETE_CHILD allowed on unwritable directory. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving things elsewhere is allowed. +expect 0 mkdir ${n0} 0777 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 60 +# Moving things back is not. +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we're overwriting. +# XXX: expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 mkdir ../${n3}/${n0} 0777 + +# Even if we have DELETE on the existing directory. +expect 0 prependacl ${n0} user:65534:delete::allow +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2}