[go: nahoru, domu]

1/*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 *
24 * GPL HEADER END
25 */
26/*
27 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
29 *
30 * Copyright (c) 2011, 2012, Intel Corporation.
31 */
32/*
33 * This file is part of Lustre, http://www.lustre.org/
34 * Lustre is a trademark of Sun Microsystems, Inc.
35 *
36 * lustre/llite/remote_perm.c
37 *
38 * Lustre Permission Cache for Remote Client
39 *
40 * Author: Lai Siyao <lsy@clusterfs.com>
41 * Author: Fan Yong <fanyong@clusterfs.com>
42 */
43
44#define DEBUG_SUBSYSTEM S_LLITE
45
46#include <linux/module.h>
47#include <linux/types.h>
48
49#include "../include/lustre_lite.h"
50#include "../include/lustre_ha.h"
51#include "../include/lustre_dlm.h"
52#include "../include/lprocfs_status.h"
53#include "../include/lustre_disk.h"
54#include "../include/lustre_param.h"
55#include "llite_internal.h"
56
57struct kmem_cache *ll_remote_perm_cachep = NULL;
58struct kmem_cache *ll_rmtperm_hash_cachep = NULL;
59
60static inline struct ll_remote_perm *alloc_ll_remote_perm(void)
61{
62	struct ll_remote_perm *lrp;
63
64	OBD_SLAB_ALLOC_PTR_GFP(lrp, ll_remote_perm_cachep, GFP_KERNEL);
65	if (lrp)
66		INIT_HLIST_NODE(&lrp->lrp_list);
67	return lrp;
68}
69
70static inline void free_ll_remote_perm(struct ll_remote_perm *lrp)
71{
72	if (!lrp)
73		return;
74
75	if (!hlist_unhashed(&lrp->lrp_list))
76		hlist_del(&lrp->lrp_list);
77	OBD_SLAB_FREE(lrp, ll_remote_perm_cachep, sizeof(*lrp));
78}
79
80static struct hlist_head *alloc_rmtperm_hash(void)
81{
82	struct hlist_head *hash;
83	int i;
84
85	OBD_SLAB_ALLOC_GFP(hash, ll_rmtperm_hash_cachep,
86			   REMOTE_PERM_HASHSIZE * sizeof(*hash),
87			   GFP_IOFS);
88	if (!hash)
89		return NULL;
90
91	for (i = 0; i < REMOTE_PERM_HASHSIZE; i++)
92		INIT_HLIST_HEAD(hash + i);
93
94	return hash;
95}
96
97void free_rmtperm_hash(struct hlist_head *hash)
98{
99	int i;
100	struct ll_remote_perm *lrp;
101	struct hlist_node *next;
102
103	if (!hash)
104		return;
105
106	for (i = 0; i < REMOTE_PERM_HASHSIZE; i++)
107		hlist_for_each_entry_safe(lrp, next, hash + i,
108					      lrp_list)
109			free_ll_remote_perm(lrp);
110	OBD_SLAB_FREE(hash, ll_rmtperm_hash_cachep,
111		      REMOTE_PERM_HASHSIZE * sizeof(*hash));
112}
113
114static inline int remote_perm_hashfunc(uid_t uid)
115{
116	return uid & (REMOTE_PERM_HASHSIZE - 1);
117}
118
119/* NB: setxid permission is not checked here, instead it's done on
120 * MDT when client get remote permission. */
121static int do_check_remote_perm(struct ll_inode_info *lli, int mask)
122{
123	struct hlist_head *head;
124	struct ll_remote_perm *lrp;
125	int found = 0, rc;
126
127	if (!lli->lli_remote_perms)
128		return -ENOENT;
129
130	head = lli->lli_remote_perms +
131		remote_perm_hashfunc(from_kuid(&init_user_ns, current_uid()));
132
133	spin_lock(&lli->lli_lock);
134	hlist_for_each_entry(lrp, head, lrp_list) {
135		if (lrp->lrp_uid != from_kuid(&init_user_ns, current_uid()))
136			continue;
137		if (lrp->lrp_gid != from_kgid(&init_user_ns, current_gid()))
138			continue;
139		if (lrp->lrp_fsuid != from_kuid(&init_user_ns, current_fsuid()))
140			continue;
141		if (lrp->lrp_fsgid != from_kgid(&init_user_ns, current_fsgid()))
142			continue;
143		found = 1;
144		break;
145	}
146
147	if (!found) {
148		rc = -ENOENT;
149		goto out;
150	}
151
152	CDEBUG(D_SEC, "found remote perm: %u/%u/%u/%u - %#x\n",
153	       lrp->lrp_uid, lrp->lrp_gid, lrp->lrp_fsuid, lrp->lrp_fsgid,
154	       lrp->lrp_access_perm);
155	rc = ((lrp->lrp_access_perm & mask) == mask) ? 0 : -EACCES;
156
157out:
158	spin_unlock(&lli->lli_lock);
159	return rc;
160}
161
162int ll_update_remote_perm(struct inode *inode, struct mdt_remote_perm *perm)
163{
164	struct ll_inode_info *lli = ll_i2info(inode);
165	struct ll_remote_perm *lrp = NULL, *tmp = NULL;
166	struct hlist_head *head, *perm_hash = NULL;
167
168	LASSERT(ll_i2sbi(inode)->ll_flags & LL_SBI_RMT_CLIENT);
169
170#if 0
171	if (perm->rp_uid != current->uid ||
172	    perm->rp_gid != current->gid ||
173	    perm->rp_fsuid != current->fsuid ||
174	    perm->rp_fsgid != current->fsgid) {
175		/* user might setxid in this small period */
176		CDEBUG(D_SEC,
177		       "remote perm user %u/%u/%u/%u != current %u/%u/%u/%u\n",
178		       perm->rp_uid, perm->rp_gid, perm->rp_fsuid,
179		       perm->rp_fsgid, current->uid, current->gid,
180		       current->fsuid, current->fsgid);
181		return -EAGAIN;
182	}
183#endif
184
185	if (!lli->lli_remote_perms) {
186		perm_hash = alloc_rmtperm_hash();
187		if (perm_hash == NULL) {
188			CERROR("alloc lli_remote_perms failed!\n");
189			return -ENOMEM;
190		}
191	}
192
193	spin_lock(&lli->lli_lock);
194
195	if (!lli->lli_remote_perms)
196		lli->lli_remote_perms = perm_hash;
197	else if (perm_hash)
198		free_rmtperm_hash(perm_hash);
199
200	head = lli->lli_remote_perms + remote_perm_hashfunc(perm->rp_uid);
201
202again:
203	hlist_for_each_entry(tmp, head, lrp_list) {
204		if (tmp->lrp_uid != perm->rp_uid)
205			continue;
206		if (tmp->lrp_gid != perm->rp_gid)
207			continue;
208		if (tmp->lrp_fsuid != perm->rp_fsuid)
209			continue;
210		if (tmp->lrp_fsgid != perm->rp_fsgid)
211			continue;
212		if (lrp)
213			free_ll_remote_perm(lrp);
214		lrp = tmp;
215		break;
216	}
217
218	if (!lrp) {
219		spin_unlock(&lli->lli_lock);
220		lrp = alloc_ll_remote_perm();
221		if (!lrp) {
222			CERROR("alloc memory for ll_remote_perm failed!\n");
223			return -ENOMEM;
224		}
225		spin_lock(&lli->lli_lock);
226		goto again;
227	}
228
229	lrp->lrp_access_perm = perm->rp_access_perm;
230	if (lrp != tmp) {
231		lrp->lrp_uid	 = perm->rp_uid;
232		lrp->lrp_gid	 = perm->rp_gid;
233		lrp->lrp_fsuid       = perm->rp_fsuid;
234		lrp->lrp_fsgid       = perm->rp_fsgid;
235		hlist_add_head(&lrp->lrp_list, head);
236	}
237	lli->lli_rmtperm_time = cfs_time_current();
238	spin_unlock(&lli->lli_lock);
239
240	CDEBUG(D_SEC, "new remote perm@%p: %u/%u/%u/%u - %#x\n",
241	       lrp, lrp->lrp_uid, lrp->lrp_gid, lrp->lrp_fsuid, lrp->lrp_fsgid,
242	       lrp->lrp_access_perm);
243
244	return 0;
245}
246
247int lustre_check_remote_perm(struct inode *inode, int mask)
248{
249	struct ll_inode_info *lli = ll_i2info(inode);
250	struct ll_sb_info *sbi = ll_i2sbi(inode);
251	struct ptlrpc_request *req = NULL;
252	struct mdt_remote_perm *perm;
253	struct obd_capa *oc;
254	unsigned long save;
255	int i = 0, rc;
256
257	do {
258		save = lli->lli_rmtperm_time;
259		rc = do_check_remote_perm(lli, mask);
260		if (!rc || (rc != -ENOENT && i))
261			break;
262
263		might_sleep();
264
265		mutex_lock(&lli->lli_rmtperm_mutex);
266		/* check again */
267		if (save != lli->lli_rmtperm_time) {
268			rc = do_check_remote_perm(lli, mask);
269			if (!rc || (rc != -ENOENT && i)) {
270				mutex_unlock(&lli->lli_rmtperm_mutex);
271				break;
272			}
273		}
274
275		if (i++ > 5) {
276			CERROR("check remote perm falls in dead loop!\n");
277			LBUG();
278		}
279
280		oc = ll_mdscapa_get(inode);
281		rc = md_get_remote_perm(sbi->ll_md_exp, ll_inode2fid(inode), oc,
282					ll_i2suppgid(inode), &req);
283		capa_put(oc);
284		if (rc) {
285			mutex_unlock(&lli->lli_rmtperm_mutex);
286			break;
287		}
288
289		perm = req_capsule_server_swab_get(&req->rq_pill, &RMF_ACL,
290						   lustre_swab_mdt_remote_perm);
291		if (unlikely(perm == NULL)) {
292			mutex_unlock(&lli->lli_rmtperm_mutex);
293			rc = -EPROTO;
294			break;
295		}
296
297		rc = ll_update_remote_perm(inode, perm);
298		mutex_unlock(&lli->lli_rmtperm_mutex);
299		if (rc == -ENOMEM)
300			break;
301
302		ptlrpc_req_finished(req);
303		req = NULL;
304	} while (1);
305	ptlrpc_req_finished(req);
306	return rc;
307}
308
309#if 0  /* NB: remote perms can't be freed in ll_mdc_blocking_ast of UPDATE lock,
310	* because it will fail sanity test 48.
311	*/
312void ll_free_remote_perms(struct inode *inode)
313{
314	struct ll_inode_info *lli = ll_i2info(inode);
315	struct hlist_head *hash = lli->lli_remote_perms;
316	struct ll_remote_perm *lrp;
317	struct hlist_node *node, *next;
318	int i;
319
320	LASSERT(hash);
321
322	spin_lock(&lli->lli_lock);
323
324	for (i = 0; i < REMOTE_PERM_HASHSIZE; i++) {
325		hlist_for_each_entry_safe(lrp, node, next, hash + i,
326					      lrp_list)
327			free_ll_remote_perm(lrp);
328	}
329
330	spin_unlock(&lli->lli_lock);
331}
332#endif
333