From: Andreas Gruenbacher <agruen@suse.de>

NFSv3 has no concept of a umask on the server side: The client applies the
umask locally, and sends the effective permissions to the server.  This
behavior is wrong when files are created in a directory that has a default
ACL.  In this case, the umask is supposed to be ignored, and only the default
ACL determines the file's effective permissions.

Usually its the server's task to conditionally apply the umask.  But since the
server knows nothing about the umask, we have to do it on the client side. 
This patch tries to fetch the parent directory's default ACL before creating a
new file, computes the appropriate create mode to send to the server, and
finally sets the new file's access and default acl appropriately.

Many thanks to Buck Huppmann <buchk@pobox.com> for sending the initial version
of this patch, as well as for arguing why we need this change.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Acked-by: Olaf Kirch <okir@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/nfs/dir.c   |   45 +++++++++++++++++++++++++++++++++++++++++----
 25-akpm/fs/nfs/inode.c |    2 ++
 2 files changed, 43 insertions(+), 4 deletions(-)

diff -puN fs/nfs/dir.c~nfsacl-acl-umask-handling-workaround-in-nfs-client fs/nfs/dir.c
--- 25/fs/nfs/dir.c~nfsacl-acl-umask-handling-workaround-in-nfs-client	2005-01-23 01:27:59.116319280 -0800
+++ 25-akpm/fs/nfs/dir.c	2005-01-23 01:27:59.122318368 -0800
@@ -31,6 +31,7 @@
 #include <linux/pagemap.h>
 #include <linux/smp_lock.h>
 #include <linux/namei.h>
+#include <linux/posix_acl.h>
 
 #include "delegation.h"
 
@@ -976,6 +977,38 @@ out_err:
 	return error;
 }
 
+static int nfs_set_default_acl(struct inode *dir, struct inode *inode,
+			       mode_t mode)
+{
+#ifdef CONFIG_NFS_ACL
+	struct posix_acl *dfacl, *acl;
+	int error = 0;
+
+	dfacl = NFS_PROTO(dir)->getacl(dir, ACL_TYPE_DEFAULT);
+	if (IS_ERR(dfacl)) {
+		error = PTR_ERR(dfacl);
+		return (error == -EOPNOTSUPP) ? 0 : error;
+	}
+	if (!dfacl)
+		return 0;
+	acl = posix_acl_clone(dfacl, GFP_KERNEL);
+	error = -ENOMEM;
+	if (!acl)
+		goto out;
+	error = posix_acl_create_masq(acl, &mode);
+	if (error < 0)
+		goto out;
+	error = NFS_PROTO(inode)->setacls(inode, acl, S_ISDIR(inode->i_mode) ?
+						      dfacl : NULL);
+out:
+	posix_acl_release(acl);
+	posix_acl_release(dfacl);
+	return error;
+#else
+	return 0;
+#endif
+}
+
 /*
  * Following a failed create operation, we drop the dentry rather
  * than retain a negative dentry. This avoids a problem in the event
@@ -993,7 +1026,7 @@ static int nfs_create(struct inode *dir,
 	dfprintk(VFS, "NFS: create(%s/%ld, %s\n", dir->i_sb->s_id, 
 		dir->i_ino, dentry->d_name.name);
 
-	attr.ia_mode = mode;
+	attr.ia_mode = mode & ~current->fs->umask;
 	attr.ia_valid = ATTR_MODE;
 
 	if (nd && (nd->flags & LOOKUP_CREATE))
@@ -1007,7 +1040,7 @@ static int nfs_create(struct inode *dir,
 		d_instantiate(dentry, inode);
 		nfs_renew_times(dentry);
 		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-		error = 0;
+		error = nfs_set_default_acl(dir, inode, mode);
 	} else {
 		error = PTR_ERR(inode);
 		d_drop(dentry);
@@ -1033,7 +1066,7 @@ nfs_mknod(struct inode *dir, struct dent
 	if (!new_valid_dev(rdev))
 		return -EINVAL;
 
-	attr.ia_mode = mode;
+	attr.ia_mode = mode & ~current->fs->umask;
 	attr.ia_valid = ATTR_MODE;
 
 	lock_kernel();
@@ -1045,6 +1078,8 @@ nfs_mknod(struct inode *dir, struct dent
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	else
 		d_drop(dentry);
+	if (!error)
+		error = nfs_set_default_acl(dir, dentry->d_inode, mode);
 	unlock_kernel();
 	return error;
 }
@@ -1063,7 +1098,7 @@ static int nfs_mkdir(struct inode *dir, 
 		dir->i_ino, dentry->d_name.name);
 
 	attr.ia_valid = ATTR_MODE;
-	attr.ia_mode = mode | S_IFDIR;
+	attr.ia_mode = (mode & ~current->fs->umask) | S_IFDIR;
 
 	lock_kernel();
 #if 0
@@ -1083,6 +1118,8 @@ static int nfs_mkdir(struct inode *dir, 
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	else
 		d_drop(dentry);
+	if (!error)
+		error = nfs_set_default_acl(dir, dentry->d_inode, mode);
 	unlock_kernel();
 	return error;
 }
diff -puN fs/nfs/inode.c~nfsacl-acl-umask-handling-workaround-in-nfs-client fs/nfs/inode.c
--- 25/fs/nfs/inode.c~nfsacl-acl-umask-handling-workaround-in-nfs-client	2005-01-23 01:27:59.117319128 -0800
+++ 25-akpm/fs/nfs/inode.c	2005-01-23 01:27:59.124318064 -0800
@@ -1494,6 +1494,8 @@ static struct super_block *nfs_get_sb(st
 		return ERR_PTR(error);
 	}
 	s->s_flags |= MS_ACTIVE;
+	/* The nfs client applies the umask itself when needed. */
+	s->s_flags |= MS_POSIXACL;
 	return s;
 }
 
_