diff options
author | J. Bruce Fields <bfields@snoopy.citi.umich.edu> | 2007-02-16 04:28:36 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:14:01 -0500 |
commit | bec50c47aaf6f1f9247f1860547ab394a0802a4c (patch) | |
tree | c988f486976a75ae0694a28f127c58b738debe21 /fs | |
parent | f43daf67871d9da5c638994416b4144eac63c992 (diff) |
[PATCH] knfsd: nfsd4: acls: avoid unnecessary denies
We're inserting deny's between some ACEs in order to enforce posix draft acl
semantics which prevent permissions from accumulating across entries in an
acl.
That's fine, but we're doing that by inserting a deny after *every* allow,
which is overkill. We shouldn't be adding them in places where they actually
make no difference.
Also replaced some helper functions for creating acl entries; I prefer just
assigning directly to the struct fields--it takes a few more lines, but the
field names provide some documentation that I think makes the result easier
understand.
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/nfs4acl.c | 190 |
1 files changed, 145 insertions, 45 deletions
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 8daa3f8bb30..0a69cce33ef 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c | |||
@@ -89,12 +89,19 @@ mask_from_posix(unsigned short perm, unsigned int flags) | |||
89 | } | 89 | } |
90 | 90 | ||
91 | static u32 | 91 | static u32 |
92 | deny_mask(u32 allow_mask, unsigned int flags) | 92 | deny_mask_from_posix(unsigned short perm, u32 flags) |
93 | { | 93 | { |
94 | u32 ret = ~allow_mask & ~NFS4_MASK_UNSUPP; | 94 | u32 mask = 0; |
95 | if (!(flags & NFS4_ACL_DIR)) | 95 | |
96 | ret &= ~NFS4_ACE_DELETE_CHILD; | 96 | if (perm & ACL_READ) |
97 | return ret; | 97 | mask |= NFS4_READ_MODE; |
98 | if (perm & ACL_WRITE) | ||
99 | mask |= NFS4_WRITE_MODE; | ||
100 | if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR)) | ||
101 | mask |= NFS4_ACE_DELETE_CHILD; | ||
102 | if (perm & ACL_EXECUTE) | ||
103 | mask |= NFS4_EXECUTE_MODE; | ||
104 | return mask; | ||
98 | } | 105 | } |
99 | 106 | ||
100 | /* XXX: modify functions to return NFS errors; they're only ever | 107 | /* XXX: modify functions to return NFS errors; they're only ever |
@@ -164,14 +171,51 @@ nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, | |||
164 | return acl; | 171 | return acl; |
165 | } | 172 | } |
166 | 173 | ||
174 | struct posix_acl_summary { | ||
175 | unsigned short owner; | ||
176 | unsigned short users; | ||
177 | unsigned short group; | ||
178 | unsigned short groups; | ||
179 | unsigned short other; | ||
180 | unsigned short mask; | ||
181 | }; | ||
182 | |||
167 | static void | 183 | static void |
168 | nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype, | 184 | summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas) |
169 | uid_t owner, unsigned int flags) | ||
170 | { | 185 | { |
171 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | 186 | struct posix_acl_entry *pa, *pe; |
172 | eflag, mask, whotype, owner); | 187 | pas->users = 0; |
173 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | 188 | pas->groups = 0; |
174 | eflag, deny_mask(mask, flags), whotype, owner); | 189 | pas->mask = 07; |
190 | |||
191 | pe = acl->a_entries + acl->a_count; | ||
192 | |||
193 | FOREACH_ACL_ENTRY(pa, acl, pe) { | ||
194 | switch (pa->e_tag) { | ||
195 | case ACL_USER_OBJ: | ||
196 | pas->owner = pa->e_perm; | ||
197 | break; | ||
198 | case ACL_GROUP_OBJ: | ||
199 | pas->group = pa->e_perm; | ||
200 | break; | ||
201 | case ACL_USER: | ||
202 | pas->users |= pa->e_perm; | ||
203 | break; | ||
204 | case ACL_GROUP: | ||
205 | pas->groups |= pa->e_perm; | ||
206 | break; | ||
207 | case ACL_OTHER: | ||
208 | pas->other = pa->e_perm; | ||
209 | break; | ||
210 | case ACL_MASK: | ||
211 | pas->mask = pa->e_perm; | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | /* We'll only care about effective permissions: */ | ||
216 | pas->users &= pas->mask; | ||
217 | pas->group &= pas->mask; | ||
218 | pas->groups &= pas->mask; | ||
175 | } | 219 | } |
176 | 220 | ||
177 | /* We assume the acl has been verified with posix_acl_valid. */ | 221 | /* We assume the acl has been verified with posix_acl_valid. */ |
@@ -179,30 +223,63 @@ static void | |||
179 | _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, | 223 | _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, |
180 | unsigned int flags) | 224 | unsigned int flags) |
181 | { | 225 | { |
182 | struct posix_acl_entry *pa, *pe, *group_owner_entry; | 226 | struct posix_acl_entry *pa, *group_owner_entry; |
183 | u32 mask; | 227 | struct nfs4_ace *ace; |
184 | unsigned short mask_mask; | 228 | struct posix_acl_summary pas; |
229 | unsigned short deny; | ||
185 | int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ? | 230 | int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ? |
186 | NFS4_INHERITANCE_FLAGS : 0); | 231 | NFS4_INHERITANCE_FLAGS : 0); |
187 | 232 | ||
188 | BUG_ON(pacl->a_count < 3); | 233 | BUG_ON(pacl->a_count < 3); |
189 | pe = pacl->a_entries + pacl->a_count; | 234 | summarize_posix_acl(pacl, &pas); |
190 | pa = pe - 2; /* if mask entry exists, it's second from the last. */ | ||
191 | if (pa->e_tag == ACL_MASK) | ||
192 | mask_mask = pa->e_perm; | ||
193 | else | ||
194 | mask_mask = S_IRWXO; | ||
195 | 235 | ||
196 | pa = pacl->a_entries; | 236 | pa = pacl->a_entries; |
197 | BUG_ON(pa->e_tag != ACL_USER_OBJ); | 237 | ace = acl->aces + acl->naces; |
198 | mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER); | 238 | |
199 | nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_OWNER, 0, flags); | 239 | /* We could deny everything not granted by the owner: */ |
240 | deny = ~pas.owner; | ||
241 | /* | ||
242 | * but it is equivalent (and simpler) to deny only what is not | ||
243 | * granted by later entries: | ||
244 | */ | ||
245 | deny &= pas.users | pas.group | pas.groups | pas.other; | ||
246 | if (deny) { | ||
247 | ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE; | ||
248 | ace->flag = eflag; | ||
249 | ace->access_mask = deny_mask_from_posix(deny, flags); | ||
250 | ace->whotype = NFS4_ACL_WHO_OWNER; | ||
251 | ace++; | ||
252 | acl->naces++; | ||
253 | } | ||
254 | |||
255 | ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE; | ||
256 | ace->flag = eflag; | ||
257 | ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER); | ||
258 | ace->whotype = NFS4_ACL_WHO_OWNER; | ||
259 | ace++; | ||
260 | acl->naces++; | ||
200 | pa++; | 261 | pa++; |
201 | 262 | ||
202 | while (pa->e_tag == ACL_USER) { | 263 | while (pa->e_tag == ACL_USER) { |
203 | mask = mask_from_posix(pa->e_perm & mask_mask, flags); | 264 | deny = ~(pa->e_perm & pas.mask); |
204 | nfs4_acl_add_pair(acl, eflag, mask, | 265 | deny &= pas.groups | pas.group | pas.other; |
205 | NFS4_ACL_WHO_NAMED, pa->e_id, flags); | 266 | if (deny) { |
267 | ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE; | ||
268 | ace->flag = eflag; | ||
269 | ace->access_mask = deny_mask_from_posix(deny, flags); | ||
270 | ace->whotype = NFS4_ACL_WHO_NAMED; | ||
271 | ace->who = pa->e_id; | ||
272 | ace++; | ||
273 | acl->naces++; | ||
274 | } | ||
275 | ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE; | ||
276 | ace->flag = eflag; | ||
277 | ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, | ||
278 | flags); | ||
279 | ace->whotype = NFS4_ACL_WHO_NAMED; | ||
280 | ace->who = pa->e_id; | ||
281 | ace++; | ||
282 | acl->naces++; | ||
206 | pa++; | 283 | pa++; |
207 | } | 284 | } |
208 | 285 | ||
@@ -212,41 +289,64 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, | |||
212 | /* allow ACEs */ | 289 | /* allow ACEs */ |
213 | 290 | ||
214 | group_owner_entry = pa; | 291 | group_owner_entry = pa; |
215 | mask = mask_from_posix(pa->e_perm & mask_mask, flags); | 292 | |
216 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | 293 | ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE; |
217 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | 294 | ace->flag = eflag; |
218 | NFS4_ACL_WHO_GROUP, 0); | 295 | ace->access_mask = mask_from_posix(pas.group, flags); |
296 | ace->whotype = NFS4_ACL_WHO_GROUP; | ||
297 | ace++; | ||
298 | acl->naces++; | ||
219 | pa++; | 299 | pa++; |
220 | 300 | ||
221 | while (pa->e_tag == ACL_GROUP) { | 301 | while (pa->e_tag == ACL_GROUP) { |
222 | mask = mask_from_posix(pa->e_perm & mask_mask, flags); | 302 | ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE; |
223 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | 303 | ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP; |
224 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | 304 | ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, |
225 | NFS4_ACL_WHO_NAMED, pa->e_id); | 305 | flags); |
306 | ace->whotype = NFS4_ACL_WHO_NAMED; | ||
307 | ace->who = pa->e_id; | ||
308 | ace++; | ||
309 | acl->naces++; | ||
226 | pa++; | 310 | pa++; |
227 | } | 311 | } |
228 | 312 | ||
229 | /* deny ACEs */ | 313 | /* deny ACEs */ |
230 | 314 | ||
231 | pa = group_owner_entry; | 315 | pa = group_owner_entry; |
232 | mask = mask_from_posix(pa->e_perm, flags); | 316 | |
233 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | 317 | deny = ~pas.group & pas.other; |
234 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | 318 | if (deny) { |
235 | deny_mask(mask, flags), NFS4_ACL_WHO_GROUP, 0); | 319 | ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE; |
320 | ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP; | ||
321 | ace->access_mask = deny_mask_from_posix(deny, flags); | ||
322 | ace->whotype = NFS4_ACL_WHO_GROUP; | ||
323 | ace++; | ||
324 | acl->naces++; | ||
325 | } | ||
236 | pa++; | 326 | pa++; |
327 | |||
237 | while (pa->e_tag == ACL_GROUP) { | 328 | while (pa->e_tag == ACL_GROUP) { |
238 | mask = mask_from_posix(pa->e_perm, flags); | 329 | deny = ~(pa->e_perm & pas.mask); |
239 | nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | 330 | deny &= pas.other; |
240 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | 331 | if (deny) { |
241 | deny_mask(mask, flags), NFS4_ACL_WHO_NAMED, pa->e_id); | 332 | ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE; |
333 | ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP; | ||
334 | ace->access_mask = mask_from_posix(deny, flags); | ||
335 | ace->whotype = NFS4_ACL_WHO_NAMED; | ||
336 | ace->who = pa->e_id; | ||
337 | ace++; | ||
338 | acl->naces++; | ||
339 | } | ||
242 | pa++; | 340 | pa++; |
243 | } | 341 | } |
244 | 342 | ||
245 | if (pa->e_tag == ACL_MASK) | 343 | if (pa->e_tag == ACL_MASK) |
246 | pa++; | 344 | pa++; |
247 | BUG_ON(pa->e_tag != ACL_OTHER); | 345 | ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE; |
248 | mask = mask_from_posix(pa->e_perm, flags); | 346 | ace->flag = eflag; |
249 | nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_EVERYONE, 0, flags); | 347 | ace->access_mask = mask_from_posix(pa->e_perm, flags); |
348 | ace->whotype = NFS4_ACL_WHO_EVERYONE; | ||
349 | acl->naces++; | ||
250 | } | 350 | } |
251 | 351 | ||
252 | static void | 352 | static void |