Skip to content

Commit a257cdd

Browse files
Andreas GruenbacherTrond Myklebust
Andreas Gruenbacher
authored and
Trond Myklebust
committed
[PATCH] NFSD: Add server support for NFSv3 ACLs.
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 <[email protected]> Acked-by: Olaf Kirch <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Trond Myklebust <[email protected]>
1 parent 9ba0263 commit a257cdd

File tree

16 files changed

+1166
-1
lines changed

16 files changed

+1166
-1
lines changed

fs/Kconfig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,7 @@ config NFSD
13531353
select LOCKD
13541354
select SUNRPC
13551355
select EXPORTFS
1356+
select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL
13561357
help
13571358
If you want your Linux box to act as an NFS *server*, so that other
13581359
computers on your local network which support NFS can access certain
@@ -1376,13 +1377,27 @@ config NFSD
13761377
To compile the NFS server support as a module, choose M here: the
13771378
module will be called nfsd. If unsure, say N.
13781379

1380+
config NFSD_V2_ACL
1381+
bool
1382+
depends on NFSD
1383+
13791384
config NFSD_V3
13801385
bool "Provide NFSv3 server support"
13811386
depends on NFSD
13821387
help
13831388
If you would like to include the NFSv3 server as well as the NFSv2
13841389
server, say Y here. If unsure, say Y.
13851390

1391+
config NFSD_V3_ACL
1392+
bool "Provide server support for the NFSv3 ACL protocol extension"
1393+
depends on NFSD_V3
1394+
select NFSD_V2_ACL
1395+
help
1396+
Implement the NFSv3 ACL protocol extension for manipulating POSIX
1397+
Access Control Lists on exported file systems. NFS clients should
1398+
be compiled with the NFSv3 ACL protocol extension; see the
1399+
CONFIG_NFS_V3_ACL option. If unsure, say N.
1400+
13861401
config NFSD_V4
13871402
bool "Provide NFSv4 server support (EXPERIMENTAL)"
13881403
depends on NFSD_V3 && EXPERIMENTAL
@@ -1427,6 +1442,15 @@ config LOCKD_V4
14271442
config EXPORTFS
14281443
tristate
14291444

1445+
config NFS_ACL_SUPPORT
1446+
tristate
1447+
select FS_POSIX_ACL
1448+
1449+
config NFS_COMMON
1450+
bool
1451+
depends on NFSD || NFS_FS
1452+
default y
1453+
14301454
config SUNRPC
14311455
tristate
14321456

fs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o
3131

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

3536
obj-$(CONFIG_QUOTA) += dquot.o
3637
obj-$(CONFIG_QFMT_V1) += quota_v1.o

fs/nfs_common/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#
2+
# Makefile for Linux filesystem routines that are shared by client and server.
3+
#
4+
5+
obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
6+
7+
nfs_acl-objs := nfsacl.o

fs/nfs_common/nfsacl.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
* fs/nfs_common/nfsacl.c
3+
*
4+
* Copyright (C) 2002-2003 Andreas Gruenbacher <[email protected]>
5+
*/
6+
7+
/*
8+
* The Solaris nfsacl protocol represents some ACLs slightly differently
9+
* than POSIX 1003.1e draft 17 does (and we do):
10+
*
11+
* - Minimal ACLs always have an ACL_MASK entry, so they have
12+
* four instead of three entries.
13+
* - The ACL_MASK entry in such minimal ACLs always has the same
14+
* permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
15+
* the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
16+
* - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
17+
* entries contain the identifiers of the owner and owning group.
18+
* (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
19+
* - ACL entries in the kernel are kept sorted in ascending order
20+
* of (e_tag, e_id). Solaris ACLs are unsorted.
21+
*/
22+
23+
#include <linux/module.h>
24+
#include <linux/fs.h>
25+
#include <linux/sunrpc/xdr.h>
26+
#include <linux/nfsacl.h>
27+
#include <linux/nfs3.h>
28+
#include <linux/sort.h>
29+
30+
MODULE_LICENSE("GPL");
31+
32+
EXPORT_SYMBOL(nfsacl_encode);
33+
EXPORT_SYMBOL(nfsacl_decode);
34+
35+
struct nfsacl_encode_desc {
36+
struct xdr_array2_desc desc;
37+
unsigned int count;
38+
struct posix_acl *acl;
39+
int typeflag;
40+
uid_t uid;
41+
gid_t gid;
42+
};
43+
44+
static int
45+
xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
46+
{
47+
struct nfsacl_encode_desc *nfsacl_desc =
48+
(struct nfsacl_encode_desc *) desc;
49+
u32 *p = (u32 *) elem;
50+
51+
if (nfsacl_desc->count < nfsacl_desc->acl->a_count) {
52+
struct posix_acl_entry *entry =
53+
&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
54+
55+
*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
56+
switch(entry->e_tag) {
57+
case ACL_USER_OBJ:
58+
*p++ = htonl(nfsacl_desc->uid);
59+
break;
60+
case ACL_GROUP_OBJ:
61+
*p++ = htonl(nfsacl_desc->gid);
62+
break;
63+
case ACL_USER:
64+
case ACL_GROUP:
65+
*p++ = htonl(entry->e_id);
66+
break;
67+
default: /* Solaris depends on that! */
68+
*p++ = 0;
69+
break;
70+
}
71+
*p++ = htonl(entry->e_perm & S_IRWXO);
72+
} else {
73+
const struct posix_acl_entry *pa, *pe;
74+
int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE;
75+
76+
FOREACH_ACL_ENTRY(pa, nfsacl_desc->acl, pe) {
77+
if (pa->e_tag == ACL_GROUP_OBJ) {
78+
group_obj_perm = pa->e_perm & S_IRWXO;
79+
break;
80+
}
81+
}
82+
/* fake up ACL_MASK entry */
83+
*p++ = htonl(ACL_MASK | nfsacl_desc->typeflag);
84+
*p++ = htonl(0);
85+
*p++ = htonl(group_obj_perm);
86+
}
87+
88+
return 0;
89+
}
90+
91+
unsigned int
92+
nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
93+
struct posix_acl *acl, int encode_entries, int typeflag)
94+
{
95+
int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
96+
struct nfsacl_encode_desc nfsacl_desc = {
97+
.desc = {
98+
.elem_size = 12,
99+
.array_len = encode_entries ? entries : 0,
100+
.xcode = xdr_nfsace_encode,
101+
},
102+
.acl = acl,
103+
.typeflag = typeflag,
104+
.uid = inode->i_uid,
105+
.gid = inode->i_gid,
106+
};
107+
int err;
108+
109+
if (entries > NFS_ACL_MAX_ENTRIES ||
110+
xdr_encode_word(buf, base, entries))
111+
return -EINVAL;
112+
err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
113+
if (!err)
114+
err = 8 + nfsacl_desc.desc.elem_size *
115+
nfsacl_desc.desc.array_len;
116+
return err;
117+
}
118+
119+
struct nfsacl_decode_desc {
120+
struct xdr_array2_desc desc;
121+
unsigned int count;
122+
struct posix_acl *acl;
123+
};
124+
125+
static int
126+
xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
127+
{
128+
struct nfsacl_decode_desc *nfsacl_desc =
129+
(struct nfsacl_decode_desc *) desc;
130+
u32 *p = (u32 *) elem;
131+
struct posix_acl_entry *entry;
132+
133+
if (!nfsacl_desc->acl) {
134+
if (desc->array_len > NFS_ACL_MAX_ENTRIES)
135+
return -EINVAL;
136+
nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
137+
if (!nfsacl_desc->acl)
138+
return -ENOMEM;
139+
nfsacl_desc->count = 0;
140+
}
141+
142+
entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
143+
entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
144+
entry->e_id = ntohl(*p++);
145+
entry->e_perm = ntohl(*p++);
146+
147+
switch(entry->e_tag) {
148+
case ACL_USER_OBJ:
149+
case ACL_USER:
150+
case ACL_GROUP_OBJ:
151+
case ACL_GROUP:
152+
case ACL_OTHER:
153+
if (entry->e_perm & ~S_IRWXO)
154+
return -EINVAL;
155+
break;
156+
case ACL_MASK:
157+
/* Solaris sometimes sets additonal bits in the mask */
158+
entry->e_perm &= S_IRWXO;
159+
break;
160+
default:
161+
return -EINVAL;
162+
}
163+
164+
return 0;
165+
}
166+
167+
static int
168+
cmp_acl_entry(const void *x, const void *y)
169+
{
170+
const struct posix_acl_entry *a = x, *b = y;
171+
172+
if (a->e_tag != b->e_tag)
173+
return a->e_tag - b->e_tag;
174+
else if (a->e_id > b->e_id)
175+
return 1;
176+
else if (a->e_id < b->e_id)
177+
return -1;
178+
else
179+
return 0;
180+
}
181+
182+
/*
183+
* Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
184+
*/
185+
static int
186+
posix_acl_from_nfsacl(struct posix_acl *acl)
187+
{
188+
struct posix_acl_entry *pa, *pe,
189+
*group_obj = NULL, *mask = NULL;
190+
191+
if (!acl)
192+
return 0;
193+
194+
sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
195+
cmp_acl_entry, NULL);
196+
197+
/* Clear undefined identifier fields and find the ACL_GROUP_OBJ
198+
and ACL_MASK entries. */
199+
FOREACH_ACL_ENTRY(pa, acl, pe) {
200+
switch(pa->e_tag) {
201+
case ACL_USER_OBJ:
202+
pa->e_id = ACL_UNDEFINED_ID;
203+
break;
204+
case ACL_GROUP_OBJ:
205+
pa->e_id = ACL_UNDEFINED_ID;
206+
group_obj = pa;
207+
break;
208+
case ACL_MASK:
209+
mask = pa;
210+
/* fall through */
211+
case ACL_OTHER:
212+
pa->e_id = ACL_UNDEFINED_ID;
213+
break;
214+
}
215+
}
216+
if (acl->a_count == 4 && group_obj && mask &&
217+
mask->e_perm == group_obj->e_perm) {
218+
/* remove bogus ACL_MASK entry */
219+
memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
220+
sizeof(struct posix_acl_entry));
221+
acl->a_count = 3;
222+
}
223+
return 0;
224+
}
225+
226+
unsigned int
227+
nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
228+
struct posix_acl **pacl)
229+
{
230+
struct nfsacl_decode_desc nfsacl_desc = {
231+
.desc = {
232+
.elem_size = 12,
233+
.xcode = pacl ? xdr_nfsace_decode : NULL,
234+
},
235+
};
236+
u32 entries;
237+
int err;
238+
239+
if (xdr_decode_word(buf, base, &entries) ||
240+
entries > NFS_ACL_MAX_ENTRIES)
241+
return -EINVAL;
242+
err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
243+
if (err)
244+
return err;
245+
if (pacl) {
246+
if (entries != nfsacl_desc.desc.array_len ||
247+
posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
248+
posix_acl_release(nfsacl_desc.acl);
249+
return -EINVAL;
250+
}
251+
*pacl = nfsacl_desc.acl;
252+
}
253+
if (aclcnt)
254+
*aclcnt = entries;
255+
return 8 + nfsacl_desc.desc.elem_size *
256+
nfsacl_desc.desc.array_len;
257+
}

fs/nfsd/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ obj-$(CONFIG_NFSD) += nfsd.o
66

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

0 commit comments

Comments
 (0)