Whamcloud - gitweb
1195593afc785b829f2eaf4805d9cdfdc06905b8
[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_identity ||
101                     uc->mu_valid == UCRED_OLD)
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 lu_buf *buf;
215         int rc;
216         ENTRY;
217
218         if (ma->ma_valid & MA_ACL_DEF)
219                 RETURN(0);
220         
221         buf = mdd_buf_get(env, ma->ma_acl, ma->ma_acl_size);
222         rc = mdo_xattr_get(env, mdd_obj, buf, XATTR_NAME_ACL_DEFAULT,
223                            BYPASS_CAPA);
224         if (rc > 0) {
225                 ma->ma_acl_size = rc;
226                 ma->ma_valid |= MA_ACL_DEF;
227                 rc = 0;
228         } else if ((rc == -EOPNOTSUPP) || (rc == -ENODATA)) {
229                 rc = 0;
230         }
231         RETURN(rc);
232 }
233
234 /*
235  * Modify the ACL for the chmod.
236  */
237 static int mdd_posix_acl_chmod_masq(posix_acl_xattr_entry *entry,
238                                     __u32 mode, int count)
239 {
240         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
241
242         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
243                 mdd_acl_le_to_cpu(pa);
244                 switch(pa->e_tag) {
245                         case ACL_USER_OBJ:
246                                 pa->e_perm = (mode & S_IRWXU) >> 6;
247                                 break;
248
249                         case ACL_USER:
250                         case ACL_GROUP:
251                                 break;
252
253                         case ACL_GROUP_OBJ:
254                                 group_obj = pa;
255                                 break;
256
257                         case ACL_MASK:
258                                 mask_obj = pa;
259                                 break;
260
261                         case ACL_OTHER:
262                                 pa->e_perm = (mode & S_IRWXO);
263                                 break;
264
265                         default:
266                                 return -EIO;
267                 }
268                 mdd_acl_cpu_to_le(pa);
269         }
270
271         if (mask_obj) {
272                 mask_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
273         } else {
274                 if (!group_obj)
275                         return -EIO;
276                 group_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
277         }
278
279         return 0;
280 }
281
282 /*
283  * Hold write_lock for o.
284  */
285 int mdd_acl_chmod(const struct lu_env *env, struct mdd_object *o, __u32 mode, 
286                   struct thandle *handle)
287 {
288         struct lu_buf           *buf;
289         posix_acl_xattr_header  *head;
290         posix_acl_xattr_entry   *entry;
291         int                      entry_count;
292         int                      rc;
293
294         ENTRY;
295
296         buf = mdd_buf_get(env, mdd_env_info(env)->mti_xattr_buf, 
297                           sizeof(mdd_env_info(env)->mti_xattr_buf));
298         
299         rc = mdo_xattr_get(env, o, buf, XATTR_NAME_ACL_ACCESS, BYPASS_CAPA);
300         if ((rc == -EOPNOTSUPP) || (rc == -ENODATA))
301                 RETURN(0);
302         else if (rc <= 0)
303                 RETURN(rc);
304
305         buf->lb_len = rc;
306         head = (posix_acl_xattr_header *)(buf->lb_buf);
307         entry = head->a_entries;
308         entry_count = (buf->lb_len - sizeof(head->a_version)) /
309                       sizeof(posix_acl_xattr_entry);
310         if (entry_count <= 0)
311                 RETURN(0);
312        
313         rc = mdd_posix_acl_chmod_masq(entry, mode, entry_count);
314         if (rc)
315                 RETURN(rc);
316
317         rc = mdo_xattr_set(env, o, buf, XATTR_NAME_ACL_ACCESS,
318                            0, handle, BYPASS_CAPA);
319         RETURN(rc);
320 }
321
322 /*
323  * Modify acl when creating a new obj.
324  */
325 static int mdd_posix_acl_create_masq(posix_acl_xattr_entry *entry,
326                                      __u32 *mode_p, int count)
327 {
328         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
329         __u32 mode = *mode_p;
330         int not_equiv = 0;
331
332         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
333                 mdd_acl_le_to_cpu(pa);
334                 switch(pa->e_tag) {
335                         case ACL_USER_OBJ:
336                                 pa->e_perm &= (mode >> 6) | ~S_IRWXO;
337                                 mode &= (pa->e_perm << 6) | ~S_IRWXU;
338                                 break;
339
340                         case ACL_USER:
341                         case ACL_GROUP:
342                                 not_equiv = 1;
343                                 break;
344
345                         case ACL_GROUP_OBJ:
346                                 group_obj = pa;
347                                 break;
348
349                         case ACL_OTHER:
350                                 pa->e_perm &= mode | ~S_IRWXO;
351                                 mode &= pa->e_perm | ~S_IRWXO;
352                                 break;
353
354                         case ACL_MASK:
355                                 mask_obj = pa;
356                                 not_equiv = 1;
357                                 break;
358
359                         default:
360                                 return -EIO;
361                 }
362                 mdd_acl_cpu_to_le(pa);
363         }
364
365         if (mask_obj) {
366                 mask_obj->e_perm = le16_to_cpu(mask_obj->e_perm) &
367                                    ((mode >> 3) | ~S_IRWXO);
368                 mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
369                 mask_obj->e_perm = cpu_to_le16(mask_obj->e_perm);
370         } else {
371                 if (!group_obj)
372                         return -EIO;
373                 group_obj->e_perm = le16_to_cpu(group_obj->e_perm) &
374                                     ((mode >> 3) | ~S_IRWXO);
375                 mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
376                 group_obj->e_perm = cpu_to_le16(group_obj->e_perm);
377         }
378
379         *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
380         return not_equiv;
381 }
382
383 /*
384  * Hold write_lock for obj.
385  */
386 int __mdd_acl_init(const struct lu_env *env, struct mdd_object *obj,
387                    struct lu_buf *buf, __u32 *mode, struct thandle *handle)
388 {
389         posix_acl_xattr_header  *head;
390         posix_acl_xattr_entry   *entry;
391         int                      entry_count;
392         int                      rc;
393
394         ENTRY;
395
396         head = (posix_acl_xattr_header *)(buf->lb_buf);
397         entry = head->a_entries;
398         entry_count = (buf->lb_len - sizeof(head->a_version)) /
399                       sizeof(posix_acl_xattr_entry);
400         if (entry_count <= 0)
401                 RETURN(0);
402        
403         if (S_ISDIR(*mode)) {
404                 rc = mdo_xattr_set(env, obj, buf, XATTR_NAME_ACL_DEFAULT, 0, 
405                                    handle, BYPASS_CAPA);
406                 if (rc)
407                         RETURN(rc);
408         }
409
410         rc = mdd_posix_acl_create_masq(entry, mode, entry_count);
411         if (rc <= 0)
412                 RETURN(rc);
413
414         rc = mdo_xattr_set(env, obj, buf, XATTR_NAME_ACL_ACCESS, 0, handle,
415                            BYPASS_CAPA);
416         RETURN(rc);
417 }
418
419 /*
420  * Hold read_lock for pobj.
421  * Hold write_lock for cobj.
422  */
423 int mdd_acl_init(const struct lu_env *env, struct mdd_object *pobj,
424                  struct mdd_object *cobj, __u32 *mode, struct thandle *handle)
425 {
426         struct lu_buf   *buf;
427         int             rc;
428         ENTRY;
429
430         if (S_ISLNK(*mode))
431                 RETURN(0);
432
433         buf = mdd_buf_get(env, mdd_env_info(env)->mti_xattr_buf, 
434                           sizeof(mdd_env_info(env)->mti_xattr_buf));
435         rc = mdo_xattr_get(env, pobj, buf, XATTR_NAME_ACL_DEFAULT, BYPASS_CAPA);
436         if ((rc == -EOPNOTSUPP) || (rc == -ENODATA))
437                 RETURN(0);
438         else if (rc <= 0)
439                 RETURN(rc);
440
441         buf->lb_len = rc;
442         rc = __mdd_acl_init(env, cobj, buf, mode, handle);
443         RETURN(rc);
444 }
445 #endif
446
447 /*
448  * Hold read_lock for obj.
449  */
450 static int mdd_check_acl(const struct lu_env *env, struct mdd_object *obj,
451                          struct lu_attr *la, int mask)
452 {
453 #ifdef CONFIG_FS_POSIX_ACL
454         struct md_ucred  *uc  = md_ucred(env);
455         posix_acl_xattr_header *head;
456         posix_acl_xattr_entry *entry;
457         struct lu_buf   *buf;
458         int entry_count;
459         int rc;
460         ENTRY;
461
462         buf = mdd_buf_get(env, mdd_env_info(env)->mti_xattr_buf, 
463                           sizeof(mdd_env_info(env)->mti_xattr_buf));
464         rc = mdo_xattr_get(env, obj, buf, XATTR_NAME_ACL_ACCESS,
465                            mdd_object_capa(env, obj));
466         if (rc <= 0)
467                 RETURN(rc ? : -EACCES);
468
469         buf->lb_len = rc;
470         head = (posix_acl_xattr_header *)(buf->lb_buf);
471         entry = head->a_entries;
472         entry_count = (buf->lb_len - sizeof(head->a_version)) /
473                       sizeof(posix_acl_xattr_entry);
474
475         rc = mdd_posix_acl_permission(uc, la, mask, entry, entry_count);
476         RETURN(rc);
477 #else
478         ENTRY;
479         RETURN(-EAGAIN);
480 #endif
481 }
482
483 int __mdd_permission_internal(const struct lu_env *env, struct mdd_object *obj,
484                               struct lu_attr *la, int mask, int needlock)
485 {
486         struct md_ucred *uc = md_ucred(env);
487         __u32 mode;
488         int rc;
489         ENTRY;
490
491         if (mask == 0)
492                 RETURN(0);
493
494         /* These means unnecessary for permission check */
495         if ((uc == NULL) || (uc->mu_valid == UCRED_INIT))
496                 RETURN(0);
497
498         /* Invalid user credit */
499         if (uc->mu_valid == UCRED_INVALID)
500                 RETURN(-EACCES);
501
502         /*
503          * Nobody gets write access to an immutable file.
504          */
505         if ((mask & MAY_WRITE) && mdd_is_immutable(obj))
506                 RETURN(-EACCES);
507
508         if (la == NULL) {
509                 la = &mdd_env_info(env)->mti_la;
510                 rc = mdd_la_get(env, obj, la, BYPASS_CAPA);
511                 if (rc)
512                         RETURN(rc);
513         }
514
515         mode = la->la_mode;
516         if (uc->mu_fsuid == la->la_uid) {
517                 mode >>= 6;
518         } else {
519                 if (mode & S_IRWXG) {
520                         if (needlock)
521                                 mdd_read_lock(env, obj);
522                         rc = mdd_check_acl(env, obj, la, mask);
523                         if (needlock)
524                                 mdd_read_unlock(env, obj);
525                         if (rc == -EACCES)
526                                 goto check_capabilities;
527                         else if ((rc != -EAGAIN) && (rc != -EOPNOTSUPP) &&
528                                  (rc != -ENODATA))
529                                 RETURN(rc);
530                 }
531                 if (mdd_in_group_p(uc, la->la_gid))
532                         mode >>= 3;
533         }
534
535         if (((mode & mask & S_IRWXO) == mask))
536                 RETURN(0);
537
538 check_capabilities:
539         if (!(mask & MAY_EXEC) ||
540             (la->la_mode & S_IXUGO) || S_ISDIR(la->la_mode))
541                 if (mdd_capable(uc, CAP_DAC_OVERRIDE))
542                         RETURN(0);
543
544         if ((mask == MAY_READ) ||
545             (S_ISDIR(la->la_mode) && !(mask & MAY_WRITE)))
546                 if (mdd_capable(uc, CAP_DAC_READ_SEARCH))
547                         RETURN(0);
548
549         RETURN(-EACCES);
550 }
551
552 int mdd_permission(const struct lu_env *env, 
553                    struct md_object *pobj, struct md_object *cobj,
554                    struct md_attr *ma, int mask)
555 {
556         struct mdd_object *mdd_pobj, *mdd_cobj;
557         struct lu_attr *la = NULL;
558         int check_create, check_link;
559         int check_unlink;
560         int check_rename_src, check_rename_tar;
561         int check_vtx_part, check_vtx_full;
562         int rc = 0;
563         ENTRY;
564
565         LASSERT(cobj);
566         mdd_cobj = md2mdd_obj(cobj);
567
568         /* For cross_open case, the "mask" is open flags,
569          * so convert it to permission mask first.
570          * XXX: MDS_OPEN_CROSS must be NOT equal to permission mask MAY_*. */
571         if (unlikely(mask & MDS_OPEN_CROSS)) {
572                 la = &mdd_env_info(env)->mti_la;
573                 rc = mdd_la_get(env, mdd_cobj, la, BYPASS_CAPA);
574                 if (rc)
575                         RETURN(rc);
576
577                 mask = accmode(env, la, mask & ~MDS_OPEN_CROSS);
578         }
579
580         check_create = mask & MAY_CREATE;
581         check_link = mask & MAY_LINK;
582         check_unlink = mask & MAY_UNLINK;
583         check_rename_src = mask & MAY_RENAME_SRC;
584         check_rename_tar = mask & MAY_RENAME_TAR;
585         check_vtx_part = mask & MAY_VTX_PART;
586         check_vtx_full = mask & MAY_VTX_FULL;
587
588         mask &= ~(MAY_CREATE | MAY_LINK |
589                 MAY_UNLINK |
590                 MAY_RENAME_SRC | MAY_RENAME_TAR |
591                 MAY_VTX_PART | MAY_VTX_FULL);
592
593         rc = mdd_permission_internal_locked(env, mdd_cobj, NULL, mask);
594
595         if (!rc && (check_create || check_link))
596                 rc = mdd_may_create(env, mdd_cobj, NULL, 1, check_link);
597
598         if (!rc && check_unlink) {
599                 LASSERT(ma);
600                 rc = mdd_may_unlink(env, mdd_cobj, ma);
601         }
602
603         if (!rc && (check_rename_src || check_rename_tar)) {
604                 LASSERT(pobj);
605                 LASSERT(ma);
606                 mdd_pobj = md2mdd_obj(pobj);
607                 rc = mdd_may_delete(env, mdd_pobj, mdd_cobj, ma, 1,
608                                     check_rename_tar);
609         }
610
611         if (!rc && (check_vtx_part || check_vtx_full)) {
612                 struct md_ucred *uc = md_ucred(env);
613
614                 LASSERT(ma);
615                 if (likely(!la)) {
616                         la = &mdd_env_info(env)->mti_la;
617                         rc = mdd_la_get(env, mdd_cobj, la, BYPASS_CAPA);
618                         if (rc)
619                                 RETURN(rc);
620                 }
621
622                 if (!(la->la_mode & S_ISVTX) || (la->la_uid == uc->mu_fsuid) ||
623                     (check_vtx_full && (ma->ma_attr.la_valid & LA_UID) &&
624                     (ma->ma_attr.la_uid == uc->mu_fsuid))) {
625                         ma->ma_attr_flags |= MDS_VTX_BYPASS;
626                 } else {
627                         ma->ma_attr_flags &= ~MDS_VTX_BYPASS;
628                         if (check_vtx_full)
629                                 rc = -EPERM;
630                 }
631         }
632
633         RETURN(rc);
634 }
635
636 int mdd_capa_get(const struct lu_env *env, struct md_object *obj,
637                  struct lustre_capa *capa, int renewal)
638 {
639         struct mdd_object *mdd_obj = md2mdd_obj(obj);
640         struct obd_capa *oc;
641         int rc = 0;
642         ENTRY;
643
644         oc = mdo_capa_get(env, mdd_obj, renewal ? capa : NULL, capa->lc_uid,
645                           capa->lc_opc);
646         if (IS_ERR(oc)) {
647                 rc = PTR_ERR(oc);
648         } else {
649                 capa_cpy(capa, oc);
650                 capa_put(oc);
651         }
652
653         RETURN(rc);
654 }