Root/
1 | /* |
2 | * fs/nfs_common/nfsacl.c |
3 | * |
4 | * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de> |
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/gfp.h> |
26 | #include <linux/sunrpc/xdr.h> |
27 | #include <linux/nfsacl.h> |
28 | #include <linux/nfs3.h> |
29 | #include <linux/sort.h> |
30 | |
31 | MODULE_LICENSE("GPL"); |
32 | |
33 | EXPORT_SYMBOL_GPL(nfsacl_encode); |
34 | EXPORT_SYMBOL_GPL(nfsacl_decode); |
35 | |
36 | struct nfsacl_encode_desc { |
37 | struct xdr_array2_desc desc; |
38 | unsigned int count; |
39 | struct posix_acl *acl; |
40 | int typeflag; |
41 | uid_t uid; |
42 | gid_t gid; |
43 | }; |
44 | |
45 | struct nfsacl_simple_acl { |
46 | struct posix_acl acl; |
47 | struct posix_acl_entry ace[4]; |
48 | }; |
49 | |
50 | static int |
51 | xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem) |
52 | { |
53 | struct nfsacl_encode_desc *nfsacl_desc = |
54 | (struct nfsacl_encode_desc *) desc; |
55 | __be32 *p = elem; |
56 | |
57 | struct posix_acl_entry *entry = |
58 | &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; |
59 | |
60 | *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag); |
61 | switch(entry->e_tag) { |
62 | case ACL_USER_OBJ: |
63 | *p++ = htonl(nfsacl_desc->uid); |
64 | break; |
65 | case ACL_GROUP_OBJ: |
66 | *p++ = htonl(nfsacl_desc->gid); |
67 | break; |
68 | case ACL_USER: |
69 | case ACL_GROUP: |
70 | *p++ = htonl(entry->e_id); |
71 | break; |
72 | default: /* Solaris depends on that! */ |
73 | *p++ = 0; |
74 | break; |
75 | } |
76 | *p++ = htonl(entry->e_perm & S_IRWXO); |
77 | return 0; |
78 | } |
79 | |
80 | /** |
81 | * nfsacl_encode - Encode an NFSv3 ACL |
82 | * |
83 | * @buf: destination xdr_buf to contain XDR encoded ACL |
84 | * @base: byte offset in xdr_buf where XDR'd ACL begins |
85 | * @inode: inode of file whose ACL this is |
86 | * @acl: posix_acl to encode |
87 | * @encode_entries: whether to encode ACEs as well |
88 | * @typeflag: ACL type: NFS_ACL_DEFAULT or zero |
89 | * |
90 | * Returns size of encoded ACL in bytes or a negative errno value. |
91 | */ |
92 | int 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 | struct nfsacl_simple_acl aclbuf; |
108 | int err; |
109 | |
110 | if (entries > NFS_ACL_MAX_ENTRIES || |
111 | xdr_encode_word(buf, base, entries)) |
112 | return -EINVAL; |
113 | if (encode_entries && acl && acl->a_count == 3) { |
114 | struct posix_acl *acl2 = &aclbuf.acl; |
115 | |
116 | /* Avoid the use of posix_acl_alloc(). nfsacl_encode() is |
117 | * invoked in contexts where a memory allocation failure is |
118 | * fatal. Fortunately this fake ACL is small enough to |
119 | * construct on the stack. */ |
120 | posix_acl_init(acl2, 4); |
121 | |
122 | /* Insert entries in canonical order: other orders seem |
123 | to confuse Solaris VxFS. */ |
124 | acl2->a_entries[0] = acl->a_entries[0]; /* ACL_USER_OBJ */ |
125 | acl2->a_entries[1] = acl->a_entries[1]; /* ACL_GROUP_OBJ */ |
126 | acl2->a_entries[2] = acl->a_entries[1]; /* ACL_MASK */ |
127 | acl2->a_entries[2].e_tag = ACL_MASK; |
128 | acl2->a_entries[3] = acl->a_entries[2]; /* ACL_OTHER */ |
129 | nfsacl_desc.acl = acl2; |
130 | } |
131 | err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc); |
132 | if (!err) |
133 | err = 8 + nfsacl_desc.desc.elem_size * |
134 | nfsacl_desc.desc.array_len; |
135 | return err; |
136 | } |
137 | |
138 | struct nfsacl_decode_desc { |
139 | struct xdr_array2_desc desc; |
140 | unsigned int count; |
141 | struct posix_acl *acl; |
142 | }; |
143 | |
144 | static int |
145 | xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) |
146 | { |
147 | struct nfsacl_decode_desc *nfsacl_desc = |
148 | (struct nfsacl_decode_desc *) desc; |
149 | __be32 *p = elem; |
150 | struct posix_acl_entry *entry; |
151 | |
152 | if (!nfsacl_desc->acl) { |
153 | if (desc->array_len > NFS_ACL_MAX_ENTRIES) |
154 | return -EINVAL; |
155 | nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL); |
156 | if (!nfsacl_desc->acl) |
157 | return -ENOMEM; |
158 | nfsacl_desc->count = 0; |
159 | } |
160 | |
161 | entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; |
162 | entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT; |
163 | entry->e_id = ntohl(*p++); |
164 | entry->e_perm = ntohl(*p++); |
165 | |
166 | switch(entry->e_tag) { |
167 | case ACL_USER_OBJ: |
168 | case ACL_USER: |
169 | case ACL_GROUP_OBJ: |
170 | case ACL_GROUP: |
171 | case ACL_OTHER: |
172 | if (entry->e_perm & ~S_IRWXO) |
173 | return -EINVAL; |
174 | break; |
175 | case ACL_MASK: |
176 | /* Solaris sometimes sets additional bits in the mask */ |
177 | entry->e_perm &= S_IRWXO; |
178 | break; |
179 | default: |
180 | return -EINVAL; |
181 | } |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static int |
187 | cmp_acl_entry(const void *x, const void *y) |
188 | { |
189 | const struct posix_acl_entry *a = x, *b = y; |
190 | |
191 | if (a->e_tag != b->e_tag) |
192 | return a->e_tag - b->e_tag; |
193 | else if (a->e_id > b->e_id) |
194 | return 1; |
195 | else if (a->e_id < b->e_id) |
196 | return -1; |
197 | else |
198 | return 0; |
199 | } |
200 | |
201 | /* |
202 | * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL. |
203 | */ |
204 | static int |
205 | posix_acl_from_nfsacl(struct posix_acl *acl) |
206 | { |
207 | struct posix_acl_entry *pa, *pe, |
208 | *group_obj = NULL, *mask = NULL; |
209 | |
210 | if (!acl) |
211 | return 0; |
212 | |
213 | sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry), |
214 | cmp_acl_entry, NULL); |
215 | |
216 | /* Clear undefined identifier fields and find the ACL_GROUP_OBJ |
217 | and ACL_MASK entries. */ |
218 | FOREACH_ACL_ENTRY(pa, acl, pe) { |
219 | switch(pa->e_tag) { |
220 | case ACL_USER_OBJ: |
221 | pa->e_id = ACL_UNDEFINED_ID; |
222 | break; |
223 | case ACL_GROUP_OBJ: |
224 | pa->e_id = ACL_UNDEFINED_ID; |
225 | group_obj = pa; |
226 | break; |
227 | case ACL_MASK: |
228 | mask = pa; |
229 | /* fall through */ |
230 | case ACL_OTHER: |
231 | pa->e_id = ACL_UNDEFINED_ID; |
232 | break; |
233 | } |
234 | } |
235 | if (acl->a_count == 4 && group_obj && mask && |
236 | mask->e_perm == group_obj->e_perm) { |
237 | /* remove bogus ACL_MASK entry */ |
238 | memmove(mask, mask+1, (3 - (mask - acl->a_entries)) * |
239 | sizeof(struct posix_acl_entry)); |
240 | acl->a_count = 3; |
241 | } |
242 | return 0; |
243 | } |
244 | |
245 | /** |
246 | * nfsacl_decode - Decode an NFSv3 ACL |
247 | * |
248 | * @buf: xdr_buf containing XDR'd ACL data to decode |
249 | * @base: byte offset in xdr_buf where XDR'd ACL begins |
250 | * @aclcnt: count of ACEs in decoded posix_acl |
251 | * @pacl: buffer in which to place decoded posix_acl |
252 | * |
253 | * Returns the length of the decoded ACL in bytes, or a negative errno value. |
254 | */ |
255 | int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt, |
256 | struct posix_acl **pacl) |
257 | { |
258 | struct nfsacl_decode_desc nfsacl_desc = { |
259 | .desc = { |
260 | .elem_size = 12, |
261 | .xcode = pacl ? xdr_nfsace_decode : NULL, |
262 | }, |
263 | }; |
264 | u32 entries; |
265 | int err; |
266 | |
267 | if (xdr_decode_word(buf, base, &entries) || |
268 | entries > NFS_ACL_MAX_ENTRIES) |
269 | return -EINVAL; |
270 | nfsacl_desc.desc.array_maxlen = entries; |
271 | err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc); |
272 | if (err) |
273 | return err; |
274 | if (pacl) { |
275 | if (entries != nfsacl_desc.desc.array_len || |
276 | posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) { |
277 | posix_acl_release(nfsacl_desc.acl); |
278 | return -EINVAL; |
279 | } |
280 | *pacl = nfsacl_desc.acl; |
281 | } |
282 | if (aclcnt) |
283 | *aclcnt = entries; |
284 | return 8 + nfsacl_desc.desc.elem_size * |
285 | nfsacl_desc.desc.array_len; |
286 | } |
287 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9