diff options
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 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 | ||
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 |