Whamcloud - gitweb
(1) provide locked and unlocked version for mdd_permission.
[fs/lustre-release.git] / lustre / mdd / mdd_permission.c
1 /* -*- MODE: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  mdd/mdd_handler.c
5  *  Lustre Metadata Server (mdd) routines
6  *
7  *  Copyright (C) 2006 Cluster File Systems, Inc.
8  *   Author: fangyong@clusterfs.com 
9  *           lsy@clusterfs.com
10  *   This file is part of the Lustre file system, http://www.lustre.org
11  *   Lustre is a trademark of Cluster File Systems, Inc.
12  *
13  *   You may have signed or agreed to another license before downloading
14  *   this software.  If so, you are bound by the terms and conditions
15  *   of that agreement, and the following does not apply to you.  See the
16  *   LICENSE file included with this distribution for more information.
17  *
18  *   If you did not agree to a different license, then this copy of Lustre
19  *   is open source software; you can redistribute it and/or modify it
20  *   under the terms of version 2 of the GNU General Public License as
21  *   published by the Free Software Foundation.
22  *
23  *   In either case, Lustre is distributed in the hope that it will be
24  *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
25  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *   license text for more details.
27  */
28 #ifndef EXPORT_SYMTAB
29 # define EXPORT_SYMTAB
30 #endif
31 #define DEBUG_SUBSYSTEM S_MDS
32
33 #include <linux/module.h>
34 #include <linux/jbd.h>
35 #include <obd.h>
36 #include <obd_class.h>
37 #include <lustre_ver.h>
38 #include <obd_support.h>
39 #include <lprocfs_status.h>
40
41 #include <linux/ldiskfs_fs.h>
42 #include <lustre_mds.h>
43 #include <lustre/lustre_idl.h>
44
45 #ifdef CONFIG_FS_POSIX_ACL
46 # include <linux/posix_acl_xattr.h>
47 # include <linux/posix_acl.h>
48 #endif
49
50 #include "mdd_internal.h"
51
52 #define mdd_get_group_info(group_info) do {             \
53         atomic_inc(&(group_info)->usage);               \
54 } while (0)
55
56 #define mdd_put_group_info(group_info) do {             \
57         if (atomic_dec_and_test(&(group_info)->usage))  \
58                 groups_free(group_info);                \
59 } while (0)
60
61 #define MDD_NGROUPS_PER_BLOCK       ((int)(CFS_PAGE_SIZE / sizeof(gid_t)))
62
63 #define MDD_GROUP_AT(gi, i) \
64     ((gi)->blocks[(i) / MDD_NGROUPS_PER_BLOCK][(i) % MDD_NGROUPS_PER_BLOCK])
65
66 /*
67  * groups_search() is copied from linux kernel!
68  * A simple bsearch.
69  */
70 static int mdd_groups_search(struct group_info *group_info, gid_t grp)
71 {
72         int left, right;
73
74         if (!group_info)
75                 return 0;
76
77         left = 0;
78         right = group_info->ngroups;
79         while (left < right) {
80                 int mid = (left + right) / 2;
81                 int cmp = grp - MDD_GROUP_AT(group_info, mid);
82
83                 if (cmp > 0)
84                         left = mid + 1;
85                 else if (cmp < 0)
86                         right = mid;
87                 else
88                         return 1;
89         }
90         return 0;
91 }
92
93 int mdd_in_group_p(struct md_ucred *uc, gid_t grp)
94 {
95         int rc = 1;
96
97         if (grp != uc->mu_fsgid) {
98                 struct group_info *group_info = NULL;
99
100                 if (uc->mu_ginfo || (uc->mu_valid == UCRED_OLD) ||
101                     (!uc->mu_ginfo && !uc->mu_identity))
102                         if ((grp == uc->mu_suppgids[0]) ||
103                             (grp == uc->mu_suppgids[1]))
104                                 return 1;
105
106                 if (uc->mu_ginfo)
107                         group_info = uc->mu_ginfo;
108                 else if (uc->mu_identity)
109                         group_info = uc->mu_identity->mi_ginfo;
110
111                 if (!group_info)
112                         return 0;
113
114                 mdd_get_group_info(group_info);
115                 rc = mdd_groups_search(group_info, grp);
116                 mdd_put_group_info(group_info);
117         }
118         return rc;
119 }
120
121 #ifdef CONFIG_FS_POSIX_ACL
122 static inline void mdd_acl_le_to_cpu(posix_acl_xattr_entry *p)
123 {
124         p->e_tag = le16_to_cpu(p->e_tag);
125         p->e_perm = le16_to_cpu(p->e_perm);
126         p->e_id = le32_to_cpu(p->e_id);
127 }
128
129 static inline void mdd_acl_cpu_to_le(posix_acl_xattr_entry *p)
130 {
131         p->e_tag = cpu_to_le16(p->e_tag);
132         p->e_perm = cpu_to_le16(p->e_perm);
133         p->e_id = cpu_to_le32(p->e_id);
134 }
135
136 /*
137  * Check permission based on POSIX ACL.
138  */
139 static int mdd_posix_acl_permission(struct md_ucred *uc, struct lu_attr *la,
140                                     int want, posix_acl_xattr_entry *entry,
141                                     int count)
142 {
143         posix_acl_xattr_entry *pa, *pe, *mask_obj;
144         int found = 0;
145         ENTRY;
146
147         if (count <= 0)
148                 RETURN(-EACCES);
149
150         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
151                 mdd_acl_le_to_cpu(pa);
152                 switch(pa->e_tag) {
153                         case ACL_USER_OBJ:
154                                 /* (May have been checked already) */
155                                 if (la->la_uid == uc->mu_fsuid)
156                                         goto check_perm;
157                                 break;
158                         case ACL_USER:
159                                 if (pa->e_id == uc->mu_fsuid)
160                                         goto mask;
161                                 break;
162                         case ACL_GROUP_OBJ:
163                                 if (mdd_in_group_p(uc, la->la_gid)) {
164                                         found = 1;
165                                         if ((pa->e_perm & want) == want)
166                                                 goto mask;
167                                 }
168                                 break;
169                         case ACL_GROUP:
170                                 if (mdd_in_group_p(uc, pa->e_id)) {
171                                         found = 1;
172                                         if ((pa->e_perm & want) == want)
173                                                 goto mask;
174                                 }
175                                 break;
176                         case ACL_MASK:
177                                 break;
178                         case ACL_OTHER:
179                                 if (found)
180                                         RETURN(-EACCES);
181                                 else
182                                         goto check_perm;
183                         default:
184                                 RETURN(-EIO);
185                 }
186         }
187         RETURN(-EIO);
188
189 mask:
190         for (mask_obj = pa + 1; mask_obj <= pe; mask_obj++) {
191                 mdd_acl_le_to_cpu(mask_obj);
192                 if (mask_obj->e_tag == ACL_MASK) {
193                         if ((pa->e_perm & mask_obj->e_perm & want) == want)
194                                 RETURN(0);
195
196                         RETURN(-EACCES);
197                 }
198         }
199
200 check_perm:
201         if ((pa->e_perm & want) == want)
202                 RETURN(0);
203
204         RETURN(-EACCES);
205 }
206
207 /*
208  * Get default acl EA only.
209  * Hold read_lock for mdd_obj.
210  */
211 int mdd_acl_def_get(const struct lu_env *env, struct mdd_object *mdd_obj, 
212                     struct md_attr *ma)
213 {
214         struct dt_object *next = mdd_object_child(mdd_obj);
215         int rc;
216         ENTRY;
217
218         if (ma->ma_valid & MA_ACL_DEF)
219                 RETURN(0);
220
221         rc = next->do_ops->do_xattr_get(env, next,
222                                         mdd_buf_get(env, ma->ma_acl,
223                                                     ma->ma_acl_size),
224                                         XATTR_NAME_ACL_DEFAULT, BYPASS_CAPA);
225         if (rc > 0) {
226                 ma->ma_acl_size = rc;
227                 ma->ma_valid |= MA_ACL_DEF;
228                 rc = 0;
229         } else if ((rc == -EOPNOTSUPP) || (rc == -ENODATA)) {
230                 rc = 0;
231         }
232         RETURN(rc);
233 }
234
235 /*
236  * Modify the ACL for the chmod.
237  */
238 static int mdd_posix_acl_chmod_masq(posix_acl_xattr_entry *entry,
239                                     __u32 mode, int count)
240 {
241         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
242
243         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
244                 mdd_acl_le_to_cpu(pa);
245                 switch(pa->e_tag) {
246                         case ACL_USER_OBJ:
247                                 pa->e_perm = (mode & S_IRWXU) >> 6;
248                                 break;
249
250                         case ACL_USER:
251                         case ACL_GROUP:
252                                 break;
253
254                         case ACL_GROUP_OBJ:
255                                 group_obj = pa;
256                                 break;
257
258                         case ACL_MASK:
259                                 mask_obj = pa;
260                                 break;
261
262                         case ACL_OTHER:
263                                 pa->e_perm = (mode & S_IRWXO);
264                                 break;
265
266                         default:
267                                 return -EIO;
268                 }
269                 mdd_acl_cpu_to_le(pa);
270         }
271
272         if (mask_obj) {
273                 mask_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
274         } else {
275                 if (!group_obj)
276                         return -EIO;
277                 group_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
278         }
279
280         return 0;
281 }
282
283 /*
284  * Hold write_lock for o.
285  */
286 int mdd_acl_chmod(const struct lu_env *env, struct mdd_object *o, __u32 mode, 
287                   struct thandle *handle)
288 {
289         struct dt_object        *next;
290         struct lu_buf           *buf;
291         posix_acl_xattr_header  *head;
292         posix_acl_xattr_entry   *entry;
293         int                      entry_count;
294         int                      rc;
295
296         ENTRY;
297
298         next = mdd_object_child(o);
299         buf = &mdd_env_info(env)->mti_buf;
300         buf->lb_buf = mdd_env_info(env)->mti_xattr_buf;
301         buf->lb_len = sizeof(mdd_env_info(env)->mti_xattr_buf);
302         rc = next->do_ops->do_xattr_get(env, next, buf,
303                                         XATTR_NAME_ACL_ACCESS, BYPASS_CAPA);
304         if ((rc == -EOPNOTSUPP) || (rc == -ENODATA))
305                 RETURN(0);
306         else if (rc <= 0)
307                 RETURN(rc);
308
309         buf->lb_len = rc;
310         head = (posix_acl_xattr_header *)(buf->lb_buf);
311         entry = head->a_entries;
312         entry_count = (buf->lb_len - sizeof(head->a_version)) /
313                       sizeof(posix_acl_xattr_entry);
314         if (entry_count <= 0)
315                 RETURN(0);
316        
317         rc = mdd_posix_acl_chmod_masq(entry, mode, entry_count);
318         if (rc)
319                 RETURN(rc);
320
321         rc = next->do_ops->do_xattr_set(env, next, buf, XATTR_NAME_ACL_ACCESS,
322                                         0, handle, BYPASS_CAPA);
323         RETURN(rc);
324 }
325
326 /*
327  * Modify acl when creating a new obj.
328  */
329 static int mdd_posix_acl_create_masq(posix_acl_xattr_entry *entry,
330                                      __u32 *mode_p, int count)
331 {
332         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
333         __u32 mode = *mode_p;
334         int not_equiv = 0;
335
336         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
337                 mdd_acl_le_to_cpu(pa);
338                 switch(pa->e_tag) {
339                         case ACL_USER_OBJ:
340                                 pa->e_perm &= (mode >> 6) | ~S_IRWXO;
341                                 mode &= (pa->e_perm << 6) | ~S_IRWXU;
342                                 break;
343
344                         case ACL_USER:
345                         case ACL_GROUP:
346                                 not_equiv = 1;
347                                 break;
348
349                         case ACL_GROUP_OBJ:
350                                 group_obj = pa;
351                                 break;
352
353                         case ACL_OTHER:
354                                 pa->e_perm &= mode | ~S_IRWXO;
355                                 mode &= pa->e_perm | ~S_IRWXO;
356                                 break;
357
358                         case ACL_MASK:
359                                 mask_obj = pa;
360                                 not_equiv = 1;
361                                 break;
362
363                         default:
364                                 return -EIO;
365                 }
366                 mdd_acl_cpu_to_le(pa);
367         }
368
369         if (mask_obj) {
370                 mask_obj->e_perm = le16_to_cpu(mask_obj->e_perm) &
371                                    ((mode >> 3) | ~S_IRWXO);
372                 mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
373                 mask_obj->e_perm = cpu_to_le16(mask_obj->e_perm);
374         } else {
375                 if (!group_obj)
376                         return -EIO;
377                 group_obj->e_perm = le16_to_cpu(group_obj->e_perm) &
378                                     ((mode >> 3) | ~S_IRWXO);
379                 mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
380                 group_obj->e_perm = cpu_to_le16(group_obj->e_perm);
381         }
382
383         *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
384         return not_equiv;
385 }
386
387 /*
388  * Hold write_lock for obj.
389  */
390 int __mdd_acl_init(const struct lu_env *env, struct mdd_object *obj,
391                    struct lu_buf *buf, __u32 *mode, struct thandle *handle)
392 {
393         struct dt_object        *next;
394         posix_acl_xattr_header  *head;
395         posix_acl_xattr_entry   *entry;
396         int                      entry_count;
397         int                      rc;
398
399         ENTRY;
400
401         head = (posix_acl_xattr_header *)(buf->lb_buf);
402         entry = head->a_entries;
403         entry_count = (buf->lb_len - sizeof(head->a_version)) /
404                       sizeof(posix_acl_xattr_entry);
405         if (entry_count <= 0)
406                 RETURN(0);
407        
408         next = mdd_object_child(obj);
409         if (S_ISDIR(*mode)) {
410                 rc = next->do_ops->do_xattr_set(env, next, buf,
411                                                 XATTR_NAME_ACL_DEFAULT,
412                                                 0, handle, BYPASS_CAPA);
413                 if (rc)
414                         RETURN(rc);
415         }
416
417         rc = mdd_posix_acl_create_masq(entry, mode, entry_count);
418         if (rc < 0)
419                 RETURN(rc);
420         else if (rc > 0)
421                 rc = next->do_ops->do_xattr_set(env, next, buf,
422                                                 XATTR_NAME_ACL_ACCESS,
423                                                 0, handle, BYPASS_CAPA);
424         RETURN(rc);
425 }
426
427 /*
428  * Hold read_lock for pobj.
429  * Hold write_lock for cobj.
430  */
431 int mdd_acl_init(const struct lu_env *env, struct mdd_object *pobj,
432                  struct mdd_object *cobj, __u32 *mode, struct thandle *handle)
433 {
434         struct dt_object        *next = mdd_object_child(pobj);
435         struct lu_buf           *buf = &mdd_env_info(env)->mti_buf;
436         int                      rc;
437
438         ENTRY;
439
440         if (S_ISLNK(*mode))
441                 RETURN(0);
442
443         buf->lb_buf = mdd_env_info(env)->mti_xattr_buf;
444         buf->lb_len = sizeof(mdd_env_info(env)->mti_xattr_buf);
445         rc = next->do_ops->do_xattr_get(env, next, buf,
446                                         XATTR_NAME_ACL_DEFAULT, BYPASS_CAPA);
447         if ((rc == -EOPNOTSUPP) || (rc == -ENODATA))
448                 RETURN(0);
449         else if (rc <= 0)
450                 RETURN(rc);
451
452         buf->lb_len = rc;
453         rc = __mdd_acl_init(env, cobj, buf, mode, handle);
454         RETURN(rc);
455 }
456 #endif
457
458 /*
459  * Hold read_lock for obj.
460  */
461 static int mdd_check_acl(const struct lu_env *env, struct mdd_object *obj,
462                          struct lu_attr *la, int mask)
463 {
464 #ifdef CONFIG_FS_POSIX_ACL
465         struct dt_object *next;
466         struct lu_buf    *buf = &mdd_env_info(env)->mti_buf;
467         struct md_ucred  *uc  = md_ucred(env);
468         posix_acl_xattr_header *head;
469         posix_acl_xattr_entry *entry;
470         int entry_count;
471         int rc;
472         ENTRY;
473
474         next = mdd_object_child(obj);
475
476         buf->lb_buf = mdd_env_info(env)->mti_xattr_buf;
477         buf->lb_len = sizeof(mdd_env_info(env)->mti_xattr_buf);
478         rc = next->do_ops->do_xattr_get(env, next, buf,
479                                         XATTR_NAME_ACL_ACCESS,
480                                         mdd_object_capa(env, obj));
481         if (rc <= 0)
482                 RETURN(rc ? : -EACCES);
483
484         buf->lb_len = rc;
485         head = (posix_acl_xattr_header *)(buf->lb_buf);
486         entry = head->a_entries;
487         entry_count = (buf->lb_len - sizeof(head->a_version)) /
488                       sizeof(posix_acl_xattr_entry);
489
490         rc = mdd_posix_acl_permission(uc, la, mask, entry, entry_count);
491         RETURN(rc);
492 #else
493         ENTRY;
494         RETURN(-EAGAIN);
495 #endif
496 }
497
498 int __mdd_permission_internal(const struct lu_env *env, struct mdd_object *obj,
499                               struct lu_attr *la, int mask, int needlock)
500 {
501         struct md_ucred *uc = md_ucred(env);
502         __u32 mode;
503         int rc;
504         ENTRY;
505
506         if (mask == 0)
507                 RETURN(0);
508
509         /* These means unnecessary for permission check */
510         if ((uc == NULL) || (uc->mu_valid == UCRED_INIT))
511                 RETURN(0);
512
513         /* Invalid user credit */
514         if (uc->mu_valid == UCRED_INVALID)
515                 RETURN(-EACCES);
516
517         /*
518          * Nobody gets write access to an immutable file.
519          */
520         if ((mask & MAY_WRITE) && mdd_is_immutable(obj))
521                 RETURN(-EACCES);
522
523         if (la == NULL) {
524                 la = &mdd_env_info(env)->mti_la;
525                 rc = mdd_la_get(env, obj, la, BYPASS_CAPA);
526                 if (rc)
527                         RETURN(rc);
528         }
529
530         mode = la->la_mode;
531         if (uc->mu_fsuid == la->la_uid) {
532                 mode >>= 6;
533         } else {
534                 if (mode & S_IRWXG) {
535                         if (needlock)
536                                 mdd_read_lock(env, obj);
537                         rc = mdd_check_acl(env, obj, la, mask);
538                         if (needlock)
539                                 mdd_read_unlock(env, obj);
540                         if (rc == -EACCES)
541                                 goto check_capabilities;
542                         else if ((rc != -EAGAIN) && (rc != -EOPNOTSUPP) &&
543                                  (rc != -ENODATA))
544                                 RETURN(rc);
545                 }
546                 if (mdd_in_group_p(uc, la->la_gid))
547                         mode >>= 3;
548         }
549
550         if (((mode & mask & S_IRWXO) == mask))
551                 RETURN(0);
552
553 check_capabilities:
554         if (!(mask & MAY_EXEC) ||
555             (la->la_mode & S_IXUGO) || S_ISDIR(la->la_mode))
556                 if (mdd_capable(uc, CAP_DAC_OVERRIDE))
557                         RETURN(0);
558
559         if ((mask == MAY_READ) ||
560             (S_ISDIR(la->la_mode) && !(mask & MAY_WRITE)))
561                 if (mdd_capable(uc, CAP_DAC_READ_SEARCH))
562                         RETURN(0);
563
564         RETURN(-EACCES);
565 }
566
567 int mdd_permission(const struct lu_env *env, struct md_object *obj, int mask)
568 {
569         struct mdd_object *mdd_obj = md2mdd_obj(obj);
570         int rc;
571         ENTRY;
572
573         rc = mdd_permission_internal_locked(env, mdd_obj, NULL, mask);
574
575         RETURN(rc);
576 }
577
578 int mdd_capa_get(const struct lu_env *env, struct md_object *obj,
579                  struct lustre_capa *capa, int renewal)
580 {
581         struct dt_object *next;
582         struct mdd_object *mdd_obj = md2mdd_obj(obj);
583         struct obd_capa *oc;
584         int rc = 0;
585         ENTRY;
586
587         LASSERT(lu_object_exists(mdd2lu_obj(mdd_obj)));
588         next = mdd_object_child(mdd_obj);
589
590         oc = next->do_ops->do_capa_get(env, next, renewal ? capa : NULL,
591                                        capa->lc_opc);
592         if (IS_ERR(oc)) {
593                 rc = PTR_ERR(oc);
594         } else {
595                 capa_cpy(capa, oc);
596                 capa_put(oc);
597         }
598
599         RETURN(rc);
600 }