aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4acl.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@snoopy.citi.umich.edu>2007-02-16 04:28:36 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-16 11:14:01 -0500
commitbec50c47aaf6f1f9247f1860547ab394a0802a4c (patch)
treec988f486976a75ae0694a28f127c58b738debe21 /fs/nfsd/nfs4acl.c
parentf43daf67871d9da5c638994416b4144eac63c992 (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/nfsd/nfs4acl.c')
-rw-r--r--fs/nfsd/nfs4acl.c190
1 files changed, 145 insertions, 45 deletions
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 8daa3f8bb303..0a69cce33efe 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
91static u32 91static u32
92deny_mask(u32 allow_mask, unsigned int flags) 92deny_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
174struct 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
167static void 183static void
168nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype, 184summarize_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
252static void 352static void