Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Dec 2009 19:39:10 +0000 (UTC)
From:      Edward Tomasz Napierala <trasz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r200796 - in head: sbin/dumpfs sbin/mount sbin/tunefs sys/sys sys/ufs/ffs sys/ufs/ufs
Message-ID:  <200912211939.nBLJdAl5038307@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trasz
Date: Mon Dec 21 19:39:10 2009
New Revision: 200796
URL: http://svn.freebsd.org/changeset/base/200796

Log:
  Implement NFSv4 ACL support for UFS.
  
  Reviewed by:	rwatson

Modified:
  head/sbin/dumpfs/dumpfs.c
  head/sbin/mount/mntopts.h
  head/sbin/mount/mount.8
  head/sbin/mount/mount.c
  head/sbin/tunefs/tunefs.8
  head/sbin/tunefs/tunefs.c
  head/sys/sys/mount.h
  head/sys/ufs/ffs/ffs_vfsops.c
  head/sys/ufs/ffs/fs.h
  head/sys/ufs/ufs/acl.h
  head/sys/ufs/ufs/ufs_acl.c
  head/sys/ufs/ufs/ufs_lookup.c
  head/sys/ufs/ufs/ufs_vnops.c

Modified: head/sbin/dumpfs/dumpfs.c
==============================================================================
--- head/sbin/dumpfs/dumpfs.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/dumpfs/dumpfs.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -251,8 +251,11 @@ dumpfs(const char *name)
 		printf("gjournal ");
 	if (fsflags & FS_FLAGS_UPDATED)
 		printf("fs_flags expanded ");
+	if (fsflags & FS_NFS4ACLS)
+		printf("nfsv4acls ");
 	fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_INDEXDIRS |
-		     FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED);
+		     FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED |
+		     FS_NFS4ACLS);
 	if (fsflags != 0)
 		printf("unknown flags (%#x)", fsflags);
 	putchar('\n');

Modified: head/sbin/mount/mntopts.h
==============================================================================
--- head/sbin/mount/mntopts.h	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/mount/mntopts.h	Mon Dec 21 19:39:10 2009	(r200796)
@@ -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		{ "nfsv4acls",	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 *);

Modified: head/sbin/mount/mount.8
==============================================================================
--- head/sbin/mount/mount.8	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/mount/mount.8	Mon Dec 21 19:39:10 2009	(r200796)
@@ -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 nfsv4acls
+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 nfsv4acls
+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.

Modified: head/sbin/mount/mount.c
==============================================================================
--- head/sbin/mount/mount.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/mount/mount.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -111,6 +111,7 @@ static struct opt {
 	{ MNT_SOFTDEP,		"soft-updates" },
 	{ MNT_MULTILABEL,	"multilabel" },
 	{ MNT_ACLS,		"acls" },
+	{ MNT_NFS4ACLS,		"nfsv4acls" },
 	{ MNT_GJOURNAL,		"gjournal" },
 	{ 0, NULL }
 };
@@ -918,6 +919,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, "nfsv4acls");
 
 	return (res);
 }

Modified: head/sbin/tunefs/tunefs.8
==============================================================================
--- head/sbin/tunefs/tunefs.8	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/tunefs/tunefs.8	Mon Dec 21 19:39:10 2009	(r200796)
@@ -44,6 +44,7 @@
 .Op Fl L Ar volname
 .Op Fl l Cm enable | disable
 .Op Fl m Ar minfree
+.Op Fl N Cm enable | disable
 .Op Fl n Cm enable | disable
 .Op Fl o Cm space | time
 .Op Fl p
@@ -70,7 +71,7 @@ this option will cause all backups to be
 primary super-block.
 This is potentially dangerous - use with caution.
 .It Fl a Cm enable | disable
-Turn on/off the administrative ACL enable flag.
+Turn on/off the administrative POSIX.1e ACL enable flag.
 .It Fl e Ar maxbpg
 Indicate the maximum number of blocks any single file can
 allocate out of a cylinder group before it is forced to begin
@@ -114,6 +115,8 @@ factor of three over the performance obt
 If the value is raised above the current usage level,
 users will be unable to allocate files until enough files have
 been deleted to get under the higher threshold.
+.It Fl N Cm enable | disable
+Turn on/off the administrative NFSv4 ACL enable flag.
 .It Fl n Cm enable | disable
 Turn on/off soft updates.
 .It Fl o Cm space | time

Modified: head/sbin/tunefs/tunefs.c
==============================================================================
--- head/sbin/tunefs/tunefs.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sbin/tunefs/tunefs.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -76,12 +76,12 @@ void printfs(void);
 int
 main(int argc, char *argv[])
 {
-	char *avalue, *Jvalue, *Lvalue, *lvalue, *nvalue;
+	char *avalue, *Jvalue, *Lvalue, *lvalue, *Nvalue, *nvalue;
 	const char *special, *on;
 	const char *name;
 	int active;
 	int Aflag, aflag, eflag, evalue, fflag, fvalue, Jflag, Lflag, lflag;
-	int mflag, mvalue, nflag, oflag, ovalue, pflag, sflag, svalue;
+	int mflag, mvalue, Nflag, nflag, oflag, ovalue, pflag, sflag, svalue;
 	int ch, found_arg, i;
 	const char *chg[2];
 	struct ufs_args args;
@@ -90,12 +90,12 @@ main(int argc, char *argv[])
 	if (argc < 3)
 		usage();
 	Aflag = aflag = eflag = fflag = Jflag = Lflag = lflag = mflag = 0;
-	nflag = oflag = pflag = sflag = 0;
-	avalue = Jvalue = Lvalue = lvalue = nvalue = NULL;
+	Nflag = nflag = oflag = pflag = sflag = 0;
+	avalue = Jvalue = Lvalue = lvalue = Nvalue = nvalue = NULL;
 	evalue = fvalue = mvalue = ovalue = svalue = 0;
 	active = 0;
 	found_arg = 0;		/* At least one arg is required. */
-	while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:n:o:ps:")) != -1)
+	while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:N:n:o:ps:")) != -1)
 		switch (ch) {
 
 		case 'A':
@@ -105,7 +105,7 @@ main(int argc, char *argv[])
 
 		case 'a':
 			found_arg = 1;
-			name = "ACLs";
+			name = "POSIX.1e ACLs";
 			avalue = optarg;
 			if (strcmp(avalue, "enable") &&
 			    strcmp(avalue, "disable")) {
@@ -187,6 +187,18 @@ main(int argc, char *argv[])
 			mflag = 1;
 			break;
 
+		case 'N':
+			found_arg = 1;
+			name = "NFSv4 ACLs";
+			Nvalue = optarg;
+			if (strcmp(Nvalue, "enable") &&
+			    strcmp(Nvalue, "disable")) {
+				errx(10, "bad %s (options are %s)",
+				    name, "`enable' or `disable'");
+			}
+			Nflag = 1;
+			break;
+
 		case 'n':
 			found_arg = 1;
 			name = "soft updates";
@@ -255,10 +267,13 @@ main(int argc, char *argv[])
 		strlcpy(sblock.fs_volname, Lvalue, MAXVOLLEN);
 	}
 	if (aflag) {
-		name = "ACLs";
+		name = "POSIX.1e ACLs";
 		if (strcmp(avalue, "enable") == 0) {
 			if (sblock.fs_flags & FS_ACLS) {
 				warnx("%s remains unchanged as enabled", name);
+			} else if (sblock.fs_flags & FS_NFS4ACLS) {
+				warnx("%s and NFSv4 ACLs are mutually "
+				    "exclusive", name);
 			} else {
 				sblock.fs_flags |= FS_ACLS;
 				warnx("%s set", name);
@@ -349,6 +364,29 @@ main(int argc, char *argv[])
 				warnx(OPTWARN, "space", "<", MINFREE);
 		}
 	}
+	if (Nflag) {
+		name = "NFSv4 ACLs";
+		if (strcmp(Nvalue, "enable") == 0) {
+			if (sblock.fs_flags & FS_NFS4ACLS) {
+				warnx("%s remains unchanged as enabled", name);
+			} else if (sblock.fs_flags & FS_ACLS) {
+				warnx("%s and POSIX.1e ACLs are mutually "
+				    "exclusive", name);
+			} else {
+				sblock.fs_flags |= FS_NFS4ACLS;
+				warnx("%s set", name);
+			}
+		} else if (strcmp(Nvalue, "disable") == 0) {
+			if ((~sblock.fs_flags & FS_NFS4ACLS) ==
+			    FS_NFS4ACLS) {
+				warnx("%s remains unchanged as disabled",
+				    name);
+			} else {
+				sblock.fs_flags &= ~FS_NFS4ACLS;
+				warnx("%s cleared", name);
+			}
+		}
+	}
 	if (nflag) {
  		name = "soft updates";
  		if (strcmp(nvalue, "enable") == 0) {
@@ -423,16 +461,18 @@ usage(void)
 	fprintf(stderr, "%s\n%s\n%s\n%s\n",
 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
 "              [-J enable | disable ] [-L volname] [-l enable | disable]",
-"              [-m minfree] [-n enable | disable] [-o space | time] [-p]",
-"              [-s avgfpdir] special | filesystem");
+"              [-m minfree] [-N enable | disable] [-n enable | disable]",
+"              [-o space | time] [-p] [-s avgfpdir] special | filesystem");
 	exit(2);
 }
 
 void
 printfs(void)
 {
-	warnx("ACLs: (-a)                                         %s",
+	warnx("POSIX.1e ACLs: (-a)                                %s",
 		(sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
+	warnx("NFSv4 ACLs: (-N)                                   %s",
+		(sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled");
 	warnx("MAC multilabel: (-l)                               %s",
 		(sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
 	warnx("soft updates: (-n)                                 %s", 

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/sys/mount.h	Mon Dec 21 19:39:10 2009	(r200796)
@@ -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

Modified: head/sys/ufs/ffs/ffs_vfsops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vfsops.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ffs/ffs_vfsops.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -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", "nfsv4acls", NULL };
 
 static int
 ffs_mount(struct mount *mp)
@@ -177,6 +177,15 @@ ffs_mount(struct mount *mp)
 		vfs_deleteopt(mp->mnt_opt, "snapshot");
 	}
 
+	if (vfs_getopt(mp->mnt_optnew, "nfsv4acls", NULL, NULL) == 0) {
+		if (mntorflags & MNT_ACLS) {
+			printf("WARNING: \"acls\" and \"nfsv4acls\" "
+			    "options are mutually exclusive\n");
+			return (EINVAL);
+		}
+		mntorflags |= MNT_NFS4ACLS;
+	}
+
 	MNT_ILOCK(mp);
 	mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags;
 	MNT_IUNLOCK(mp);
@@ -360,6 +369,13 @@ ffs_mount(struct mount *mp)
 			MNT_IUNLOCK(mp);
 		}
 
+		if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
+			/* XXX: Set too late ? */
+			MNT_ILOCK(mp);
+			mp->mnt_flag |= MNT_NFS4ACLS;
+			MNT_IUNLOCK(mp);
+		}
+	
 		/*
 		 * If this is a snapshot request, take the snapshot.
 		 */
@@ -834,7 +850,13 @@ ffs_mountfs(devvp, mp, td)
 	if ((fs->fs_flags & FS_ACLS) != 0) {
 #ifdef UFS_ACL
 		MNT_ILOCK(mp);
+
+		if (mp->mnt_flag & MNT_NFS4ACLS)
+			printf("WARNING: ACLs flag on fs conflicts with "
+			    "\"nfsv4acls\" mount option; option ignored\n");
+		mp->mnt_flag &= ~MNT_NFS4ACLS;
 		mp->mnt_flag |= MNT_ACLS;
+
 		MNT_IUNLOCK(mp);
 #else
 		printf(
@@ -842,6 +864,24 @@ ffs_mountfs(devvp, mp, td)
 		    mp->mnt_stat.f_mntonname);
 #endif
 	}
+	if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
+#ifdef UFS_ACL
+		MNT_ILOCK(mp);
+
+		if (mp->mnt_flag & MNT_ACLS)
+			printf("WARNING: NFSv4 ACLs flag on fs conflicts with "
+			    "\"acls\" mount option; option ignored\n");
+		mp->mnt_flag &= ~MNT_ACLS;
+		mp->mnt_flag |= MNT_NFS4ACLS;
+
+		MNT_IUNLOCK(mp);
+#else
+		printf(
+"WARNING: %s: NFSv4 ACLs flag on fs but no ACLs support\n",
+		    mp->mnt_stat.f_mntonname);
+#endif
+	}
+
 	ump->um_mountp = mp;
 	ump->um_dev = dev;
 	ump->um_devvp = devvp;

Modified: head/sys/ufs/ffs/fs.h
==============================================================================
--- head/sys/ufs/ffs/fs.h	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ffs/fs.h	Mon Dec 21 19:39:10 2009	(r200796)
@@ -393,22 +393,24 @@ CTASSERT(sizeof(struct fs) == 1376);
  * flag to indicate that the indicies need to be rebuilt (by fsck) before
  * they can be used.
  *
- * FS_ACLS indicates that ACLs are administratively enabled for the
- * file system, so they should be loaded from extended attributes,
+ * FS_ACLS indicates that POSIX.1e ACLs are administratively enabled
+ * for the file system, so they should be loaded from extended attributes,
  * observed for access control purposes, and be administered by object
- * owners.  FS_MULTILABEL indicates that the TrustedBSD MAC Framework
- * should attempt to back MAC labels into extended attributes on the
- * file system rather than maintain a single mount label for all
- * objects.
- */
-#define FS_UNCLEAN    0x01	/* filesystem not clean at mount */
-#define FS_DOSOFTDEP  0x02	/* filesystem using soft dependencies */
-#define FS_NEEDSFSCK  0x04	/* filesystem needs sync fsck before mount */
-#define FS_INDEXDIRS  0x08	/* kernel supports indexed directories */
-#define FS_ACLS       0x10	/* file system has ACLs enabled */
-#define FS_MULTILABEL 0x20	/* file system is MAC multi-label */
-#define FS_GJOURNAL   0x40	/* gjournaled file system */
-#define FS_FLAGS_UPDATED 0x80	/* flags have been moved to new location */
+ * owners.  FS_NFS4ACLS indicates that NFSv4 ACLs are administratively
+ * enabled.  This flag is mutually exclusive with FS_ACLS.  FS_MULTILABEL
+ * indicates that the TrustedBSD MAC Framework should attempt to back MAC
+ * labels into extended attributes on the file system rather than maintain
+ * a single mount label for all objects.
+ */
+#define FS_UNCLEAN	0x0001	/* filesystem not clean at mount */
+#define FS_DOSOFTDEP	0x0002	/* filesystem using soft dependencies */
+#define FS_NEEDSFSCK	0x0004	/* filesystem needs sync fsck before mount */
+#define FS_INDEXDIRS	0x0008	/* kernel supports indexed directories */
+#define FS_ACLS		0x0010	/* file system has POSIX.1e ACLs enabled */
+#define FS_MULTILABEL	0x0020	/* file system is MAC multi-label */
+#define FS_GJOURNAL	0x0040	/* gjournaled file system */
+#define FS_FLAGS_UPDATED 0x0080	/* flags have been moved to new location */
+#define FS_NFS4ACLS	0x0100	/* file system has NFSv4 ACLs enabled */
 
 /*
  * Macros to access bits in the fs_active array.

Modified: head/sys/ufs/ufs/acl.h
==============================================================================
--- head/sys/ufs/ufs/acl.h	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ufs/acl.h	Mon Dec 21 19:39:10 2009	(r200796)
@@ -37,6 +37,8 @@
 
 #ifdef _KERNEL
 
+int	ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
+int	ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
 void	ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl);
 void	ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip);
 

Modified: head/sys/ufs/ufs/ufs_acl.c
==============================================================================
--- head/sys/ufs/ufs/ufs_acl.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ufs/ufs_acl.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -141,6 +141,81 @@ ufs_sync_inode_from_acl(struct acl *acl,
 }
 
 /*
+ * Retrieve NFSv4 ACL, skipping access checks.  Must be used in UFS code
+ * instead of VOP_GETACL() when we don't want to be restricted by the user
+ * not having ACL_READ_ACL permission, e.g. when calculating inherited ACL
+ * or in ufs_vnops.c:ufs_accessx().
+ */
+int
+ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
+{
+	int error, len;
+	struct inode *ip = VTOI(vp);
+
+	len = sizeof(*aclp);
+	bzero(aclp, len);
+
+	error = vn_extattr_get(vp, IO_NODELOCKED,
+	    NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
+	    &len, (char *) aclp, td);
+	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(aclp, ip->i_mode, ip->i_uid);
+
+		return (0);
+	}
+
+	if (error)
+		return (error);
+
+	if (len != sizeof(*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(aclp, 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);
+}
+
+static int
+ufs_getacl_nfs4(struct vop_getacl_args *ap)
+{
+	int error;
+
+	if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
+		return (EINVAL);
+
+	error = VOP_ACCESSX(ap->a_vp, VREAD_ACL, ap->a_td->td_ucred, ap->a_td);
+	if (error)
+		return (error);
+
+	error = ufs_getacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
+
+	return (error);
+}
+
+/*
  * Read POSIX.1e ACL from an EA.  Return error if its not found
  * or if any other error has occured.
  */
@@ -209,7 +284,7 @@ ufs_getacl_posix1e(struct vop_getacl_arg
 	 * ACLs, remove this check.
 	 */
 	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
-		return (EOPNOTSUPP);
+		return (EINVAL);
 
 	old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
 
@@ -282,10 +357,118 @@ ufs_getacl(ap)
 	} */ *ap;
 {
 
+	if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+		return (EOPNOTSUPP);
+
+	if (ap->a_type == ACL_TYPE_NFS4)
+		return (ufs_getacl_nfs4(ap));
+
 	return (ufs_getacl_posix1e(ap));
 }
 
 /*
+ * Set NFSv4 ACL without doing any access checking.  This is required
+ * e.g. by the UFS code that implements ACL inheritance, or from
+ * ufs_vnops.c:ufs_chmod(), as some of the checks have to be skipped
+ * in that case, and others are redundant.
+ */
+int
+ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
+{
+	int error;
+	mode_t mode;
+	struct inode *ip = VTOI(vp);
+
+	KASSERT(acl_nfs4_check(aclp, vp->v_type == VDIR) == 0,
+	    ("invalid ACL passed to ufs_setacl_nfs4_internal"));
+
+	if (acl_nfs4_is_trivial(aclp, ip->i_uid)) {
+		error = vn_extattr_rm(vp, IO_NODELOCKED,
+		    NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, 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(vp, IO_NODELOCKED,
+		    NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
+		    sizeof(*aclp), (char *) aclp, 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, 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(vp, NOTE_ATTRIB);
+
+	return (0);
+}
+
+static int
+ufs_setacl_nfs4(struct vop_setacl_args *ap)
+{
+	int error;
+	struct inode *ip = VTOI(ap->a_vp);
+
+	if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
+		return (EINVAL);
+
+	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);
+
+	/*
+	 * 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 (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
+		return (ENOSPC);
+
+	error = ufs_setacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
+
+	return (0);
+}
+
+/*
  * Set the ACL on a file.
  *
  * As part of the ACL is stored in the inode, and the rest in an EA,
@@ -302,7 +485,7 @@ ufs_setacl_posix1e(struct vop_setacl_arg
 	struct oldacl *old;
 
 	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
-		return (EOPNOTSUPP);
+		return (EINVAL);
 
 	/*
 	 * If this is a set operation rather than a delete operation,
@@ -422,16 +605,43 @@ ufs_setacl(ap)
 		struct thread *td;
 	} */ *ap;
 {
+	if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+		return (EOPNOTSUPP);
+
+	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 (EINVAL);
+
+	/*
+	 * 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 (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
+		return (ENOSPC);
+
+	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)
 {
 
 	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
-		return (EOPNOTSUPP);
+		return (EINVAL);
 
 	/*
 	 * Verify we understand this type of ACL, and that it applies
@@ -471,6 +681,12 @@ ufs_aclcheck(ap)
 	} */ *ap;
 {
 
+	if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+		return (EOPNOTSUPP);
+
+	if (ap->a_type == ACL_TYPE_NFS4)
+		return (ufs_aclcheck_nfs4(ap));
+
 	return (ufs_aclcheck_posix1e(ap));
 }
 

Modified: head/sys/ufs/ufs/ufs_lookup.c
==============================================================================
--- head/sys/ufs/ufs/ufs_lookup.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ufs/ufs_lookup.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -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, VWRITE | 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, VWRITE | 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, VWRITE | 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);

Modified: head/sys/ufs/ufs/ufs_vnops.c
==============================================================================
--- head/sys/ufs/ufs/ufs_vnops.c	Mon Dec 21 19:27:52 2009	(r200795)
+++ head/sys/ufs/ufs/ufs_vnops.c	Mon Dec 21 19:39:10 2009	(r200796)
@@ -88,7 +88,7 @@ __FBSDID("$FreeBSD$");
 
 #include <ufs/ffs/ffs_extern.h>
 
-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;
@@ -298,8 +298,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;
@@ -315,6 +315,7 @@ ufs_access(ap)
 #endif
 #ifdef UFS_ACL
 	struct acl *acl;
+	acl_type_t type;
 #endif
 
 	/*
@@ -322,7 +323,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:
@@ -367,41 +368,63 @@ 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 for the owner
+	 * 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,
-		    ap->a_td);
+		if (type == ACL_TYPE_NFS4)
+			error = ufs_getacl_nfs4_internal(vp, acl, ap->a_td);
+		else
+			error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td);
 		switch (error) {
-		case EOPNOTSUPP:
-			error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
-			    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, accmode, ap->a_cred, NULL);
+			} else {
+				error = vfs_unixify_accmode(&accmode);
+				if (error == 0)
+					error = vaccess_acl_posix1e(vp->v_type, ip->i_uid,
+					    ip->i_gid, acl, accmode, ap->a_cred, NULL);
+			}
 			break;
 		default:
-			printf(
-"ufs_access(): Error retrieving ACL on object (%d).\n",
-			    error);
+			if (error != EOPNOTSUPP)
+				printf(
+"ufs_accessx(): Error retrieving ACL on object (%d).\n",
+				    error);
 			/*
 			 * XXX: Fall back until debugged.  Should
 			 * eventually possibly log an error, and return
 			 * EPERM for safety.
 			 */
-			error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
-			    ip->i_gid, ap->a_accmode, ap->a_cred, NULL);
+			error = vfs_unixify_accmode(&accmode);
+			if (error == 0)
+				error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
+				    ip->i_gid, accmode, ap->a_cred, NULL);
 		}
 		acl_free(acl);
-	} else
+
+		return (error);
+	}
 #endif /* !UFS_ACL */
+	error = vfs_unixify_accmode(&accmode);
+	if (error == 0)
 		error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
-		    ap->a_accmode, ap->a_cred, NULL);
+		    accmode, ap->a_cred, NULL);
 	return (error);
 }
 
@@ -608,11 +631,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

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912211939.nBLJdAl5038307>