Skip to content

Commit

Permalink
[PATCH] NFSD: Add server support for NFSv3 ACLs.
Browse files Browse the repository at this point in the history
 This adds functions for encoding and decoding POSIX ACLs for the NFSACL
 protocol extension, and the GETACL and SETACL RPCs.  The implementation is
 compatible with NFSACL in Solaris.

 Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
 Acked-by: Olaf Kirch <okir@suse.de>
 Signed-off-by: Andrew Morton <akpm@osdl.org>
 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
  • Loading branch information
Andreas Gruenbacher authored and Trond Myklebust committed Jun 22, 2005
1 parent 9ba0263 commit a257cdd
Show file tree
Hide file tree
Showing 16 changed files with 1,166 additions and 1 deletion.
24 changes: 24 additions & 0 deletions fs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,7 @@ config NFSD
select LOCKD
select SUNRPC
select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL
help
If you want your Linux box to act as an NFS *server*, so that other
computers on your local network which support NFS can access certain
Expand All @@ -1376,13 +1377,27 @@ config NFSD
To compile the NFS server support as a module, choose M here: the
module will be called nfsd. If unsure, say N.

config NFSD_V2_ACL
bool
depends on NFSD

config NFSD_V3
bool "Provide NFSv3 server support"
depends on NFSD
help
If you would like to include the NFSv3 server as well as the NFSv2
server, say Y here. If unsure, say Y.

config NFSD_V3_ACL
bool "Provide server support for the NFSv3 ACL protocol extension"
depends on NFSD_V3
select NFSD_V2_ACL
help
Implement the NFSv3 ACL protocol extension for manipulating POSIX
Access Control Lists on exported file systems. NFS clients should
be compiled with the NFSv3 ACL protocol extension; see the
CONFIG_NFS_V3_ACL option. If unsure, say N.

config NFSD_V4
bool "Provide NFSv4 server support (EXPERIMENTAL)"
depends on NFSD_V3 && EXPERIMENTAL
Expand Down Expand Up @@ -1427,6 +1442,15 @@ config LOCKD_V4
config EXPORTFS
tristate

config NFS_ACL_SUPPORT
tristate
select FS_POSIX_ACL

config NFS_COMMON
bool
depends on NFSD || NFS_FS
default y

config SUNRPC
tristate

Expand Down
1 change: 1 addition & 0 deletions fs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o

obj-$(CONFIG_FS_MBCACHE) += mbcache.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/

obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
Expand Down
7 changes: 7 additions & 0 deletions fs/nfs_common/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Makefile for Linux filesystem routines that are shared by client and server.
#

obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o

nfs_acl-objs := nfsacl.o
257 changes: 257 additions & 0 deletions fs/nfs_common/nfsacl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
* fs/nfs_common/nfsacl.c
*
* Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
*/

/*
* The Solaris nfsacl protocol represents some ACLs slightly differently
* than POSIX 1003.1e draft 17 does (and we do):
*
* - Minimal ACLs always have an ACL_MASK entry, so they have
* four instead of three entries.
* - The ACL_MASK entry in such minimal ACLs always has the same
* permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
* the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
* - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
* entries contain the identifiers of the owner and owning group.
* (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
* - ACL entries in the kernel are kept sorted in ascending order
* of (e_tag, e_id). Solaris ACLs are unsorted.
*/

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sunrpc/xdr.h>
#include <linux/nfsacl.h>
#include <linux/nfs3.h>
#include <linux/sort.h>

MODULE_LICENSE("GPL");

EXPORT_SYMBOL(nfsacl_encode);
EXPORT_SYMBOL(nfsacl_decode);

struct nfsacl_encode_desc {
struct xdr_array2_desc desc;
unsigned int count;
struct posix_acl *acl;
int typeflag;
uid_t uid;
gid_t gid;
};

static int
xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
{
struct nfsacl_encode_desc *nfsacl_desc =
(struct nfsacl_encode_desc *) desc;
u32 *p = (u32 *) elem;

if (nfsacl_desc->count < nfsacl_desc->acl->a_count) {
struct posix_acl_entry *entry =
&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];

*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
switch(entry->e_tag) {
case ACL_USER_OBJ:
*p++ = htonl(nfsacl_desc->uid);
break;
case ACL_GROUP_OBJ:
*p++ = htonl(nfsacl_desc->gid);
break;
case ACL_USER:
case ACL_GROUP:
*p++ = htonl(entry->e_id);
break;
default: /* Solaris depends on that! */
*p++ = 0;
break;
}
*p++ = htonl(entry->e_perm & S_IRWXO);
} else {
const struct posix_acl_entry *pa, *pe;
int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE;

FOREACH_ACL_ENTRY(pa, nfsacl_desc->acl, pe) {
if (pa->e_tag == ACL_GROUP_OBJ) {
group_obj_perm = pa->e_perm & S_IRWXO;
break;
}
}
/* fake up ACL_MASK entry */
*p++ = htonl(ACL_MASK | nfsacl_desc->typeflag);
*p++ = htonl(0);
*p++ = htonl(group_obj_perm);
}

return 0;
}

unsigned int
nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
struct posix_acl *acl, int encode_entries, int typeflag)
{
int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
struct nfsacl_encode_desc nfsacl_desc = {
.desc = {
.elem_size = 12,
.array_len = encode_entries ? entries : 0,
.xcode = xdr_nfsace_encode,
},
.acl = acl,
.typeflag = typeflag,
.uid = inode->i_uid,
.gid = inode->i_gid,
};
int err;

if (entries > NFS_ACL_MAX_ENTRIES ||
xdr_encode_word(buf, base, entries))
return -EINVAL;
err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
if (!err)
err = 8 + nfsacl_desc.desc.elem_size *
nfsacl_desc.desc.array_len;
return err;
}

struct nfsacl_decode_desc {
struct xdr_array2_desc desc;
unsigned int count;
struct posix_acl *acl;
};

static int
xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
{
struct nfsacl_decode_desc *nfsacl_desc =
(struct nfsacl_decode_desc *) desc;
u32 *p = (u32 *) elem;
struct posix_acl_entry *entry;

if (!nfsacl_desc->acl) {
if (desc->array_len > NFS_ACL_MAX_ENTRIES)
return -EINVAL;
nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
if (!nfsacl_desc->acl)
return -ENOMEM;
nfsacl_desc->count = 0;
}

entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
entry->e_id = ntohl(*p++);
entry->e_perm = ntohl(*p++);

switch(entry->e_tag) {
case ACL_USER_OBJ:
case ACL_USER:
case ACL_GROUP_OBJ:
case ACL_GROUP:
case ACL_OTHER:
if (entry->e_perm & ~S_IRWXO)
return -EINVAL;
break;
case ACL_MASK:
/* Solaris sometimes sets additonal bits in the mask */
entry->e_perm &= S_IRWXO;
break;
default:
return -EINVAL;
}

return 0;
}

static int
cmp_acl_entry(const void *x, const void *y)
{
const struct posix_acl_entry *a = x, *b = y;

if (a->e_tag != b->e_tag)
return a->e_tag - b->e_tag;
else if (a->e_id > b->e_id)
return 1;
else if (a->e_id < b->e_id)
return -1;
else
return 0;
}

/*
* Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
*/
static int
posix_acl_from_nfsacl(struct posix_acl *acl)
{
struct posix_acl_entry *pa, *pe,
*group_obj = NULL, *mask = NULL;

if (!acl)
return 0;

sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
cmp_acl_entry, NULL);

/* Clear undefined identifier fields and find the ACL_GROUP_OBJ
and ACL_MASK entries. */
FOREACH_ACL_ENTRY(pa, acl, pe) {
switch(pa->e_tag) {
case ACL_USER_OBJ:
pa->e_id = ACL_UNDEFINED_ID;
break;
case ACL_GROUP_OBJ:
pa->e_id = ACL_UNDEFINED_ID;
group_obj = pa;
break;
case ACL_MASK:
mask = pa;
/* fall through */
case ACL_OTHER:
pa->e_id = ACL_UNDEFINED_ID;
break;
}
}
if (acl->a_count == 4 && group_obj && mask &&
mask->e_perm == group_obj->e_perm) {
/* remove bogus ACL_MASK entry */
memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
sizeof(struct posix_acl_entry));
acl->a_count = 3;
}
return 0;
}

unsigned int
nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
struct posix_acl **pacl)
{
struct nfsacl_decode_desc nfsacl_desc = {
.desc = {
.elem_size = 12,
.xcode = pacl ? xdr_nfsace_decode : NULL,
},
};
u32 entries;
int err;

if (xdr_decode_word(buf, base, &entries) ||
entries > NFS_ACL_MAX_ENTRIES)
return -EINVAL;
err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
if (err)
return err;
if (pacl) {
if (entries != nfsacl_desc.desc.array_len ||
posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
posix_acl_release(nfsacl_desc.acl);
return -EINVAL;
}
*pacl = nfsacl_desc.acl;
}
if (aclcnt)
*aclcnt = entries;
return 8 + nfsacl_desc.desc.elem_size *
nfsacl_desc.desc.array_len;
}
2 changes: 2 additions & 0 deletions fs/nfsd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ obj-$(CONFIG_NFSD) += nfsd.o

nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o
nfsd-objs := $(nfsd-y)
Loading

0 comments on commit a257cdd

Please sign in to comment.