]>
Commit | Line | Data |
---|---|---|
a257cdd0 AG |
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/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 | } |