xref: /OK3568_Linux_fs/kernel/fs/9p/fid.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * V9FS FID Management
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
6*4882a593Smuzhiyun  *  Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/errno.h>
11*4882a593Smuzhiyun #include <linux/fs.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <linux/sched.h>
14*4882a593Smuzhiyun #include <linux/idr.h>
15*4882a593Smuzhiyun #include <net/9p/9p.h>
16*4882a593Smuzhiyun #include <net/9p/client.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include "v9fs.h"
19*4882a593Smuzhiyun #include "v9fs_vfs.h"
20*4882a593Smuzhiyun #include "fid.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /**
23*4882a593Smuzhiyun  * v9fs_fid_add - add a fid to a dentry
24*4882a593Smuzhiyun  * @dentry: dentry that the fid is being added to
25*4882a593Smuzhiyun  * @fid: fid to add
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  */
28*4882a593Smuzhiyun 
__add_fid(struct dentry * dentry,struct p9_fid * fid)29*4882a593Smuzhiyun static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun 	hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun 
v9fs_fid_add(struct dentry * dentry,struct p9_fid * fid)34*4882a593Smuzhiyun void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	spin_lock(&dentry->d_lock);
37*4882a593Smuzhiyun 	__add_fid(dentry, fid);
38*4882a593Smuzhiyun 	spin_unlock(&dentry->d_lock);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun /**
42*4882a593Smuzhiyun  * v9fs_fid_find - retrieve a fid that belongs to the specified uid
43*4882a593Smuzhiyun  * @dentry: dentry to look for fid in
44*4882a593Smuzhiyun  * @uid: return fid that belongs to the specified user
45*4882a593Smuzhiyun  * @any: if non-zero, return any fid associated with the dentry
46*4882a593Smuzhiyun  *
47*4882a593Smuzhiyun  */
48*4882a593Smuzhiyun 
v9fs_fid_find(struct dentry * dentry,kuid_t uid,int any)49*4882a593Smuzhiyun static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	struct p9_fid *fid, *ret;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
54*4882a593Smuzhiyun 		 dentry, dentry, from_kuid(&init_user_ns, uid),
55*4882a593Smuzhiyun 		 any);
56*4882a593Smuzhiyun 	ret = NULL;
57*4882a593Smuzhiyun 	/* we'll recheck under lock if there's anything to look in */
58*4882a593Smuzhiyun 	if (dentry->d_fsdata) {
59*4882a593Smuzhiyun 		struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
60*4882a593Smuzhiyun 		spin_lock(&dentry->d_lock);
61*4882a593Smuzhiyun 		hlist_for_each_entry(fid, h, dlist) {
62*4882a593Smuzhiyun 			if (any || uid_eq(fid->uid, uid)) {
63*4882a593Smuzhiyun 				ret = fid;
64*4882a593Smuzhiyun 				break;
65*4882a593Smuzhiyun 			}
66*4882a593Smuzhiyun 		}
67*4882a593Smuzhiyun 		spin_unlock(&dentry->d_lock);
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	return ret;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun  * We need to hold v9ses->rename_sem as long as we hold references
75*4882a593Smuzhiyun  * to returned path array. Array element contain pointers to
76*4882a593Smuzhiyun  * dentry names.
77*4882a593Smuzhiyun  */
build_path_from_dentry(struct v9fs_session_info * v9ses,struct dentry * dentry,const unsigned char *** names)78*4882a593Smuzhiyun static int build_path_from_dentry(struct v9fs_session_info *v9ses,
79*4882a593Smuzhiyun 				  struct dentry *dentry, const unsigned char ***names)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	int n = 0, i;
82*4882a593Smuzhiyun 	const unsigned char **wnames;
83*4882a593Smuzhiyun 	struct dentry *ds;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
86*4882a593Smuzhiyun 		n++;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	wnames = kmalloc_array(n, sizeof(char *), GFP_KERNEL);
89*4882a593Smuzhiyun 	if (!wnames)
90*4882a593Smuzhiyun 		goto err_out;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
93*4882a593Smuzhiyun 		wnames[i] = ds->d_name.name;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	*names = wnames;
96*4882a593Smuzhiyun 	return n;
97*4882a593Smuzhiyun err_out:
98*4882a593Smuzhiyun 	return -ENOMEM;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
v9fs_fid_lookup_with_uid(struct dentry * dentry,kuid_t uid,int any)101*4882a593Smuzhiyun static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
102*4882a593Smuzhiyun 					       kuid_t uid, int any)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	struct dentry *ds;
105*4882a593Smuzhiyun 	const unsigned char **wnames, *uname;
106*4882a593Smuzhiyun 	int i, n, l, clone, access;
107*4882a593Smuzhiyun 	struct v9fs_session_info *v9ses;
108*4882a593Smuzhiyun 	struct p9_fid *fid, *old_fid = NULL;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	v9ses = v9fs_dentry2v9ses(dentry);
111*4882a593Smuzhiyun 	access = v9ses->flags & V9FS_ACCESS_MASK;
112*4882a593Smuzhiyun 	fid = v9fs_fid_find(dentry, uid, any);
113*4882a593Smuzhiyun 	if (fid)
114*4882a593Smuzhiyun 		return fid;
115*4882a593Smuzhiyun 	/*
116*4882a593Smuzhiyun 	 * we don't have a matching fid. To do a TWALK we need
117*4882a593Smuzhiyun 	 * parent fid. We need to prevent rename when we want to
118*4882a593Smuzhiyun 	 * look at the parent.
119*4882a593Smuzhiyun 	 */
120*4882a593Smuzhiyun 	down_read(&v9ses->rename_sem);
121*4882a593Smuzhiyun 	ds = dentry->d_parent;
122*4882a593Smuzhiyun 	fid = v9fs_fid_find(ds, uid, any);
123*4882a593Smuzhiyun 	if (fid) {
124*4882a593Smuzhiyun 		/* Found the parent fid do a lookup with that */
125*4882a593Smuzhiyun 		fid = p9_client_walk(fid, 1, &dentry->d_name.name, 1);
126*4882a593Smuzhiyun 		goto fid_out;
127*4882a593Smuzhiyun 	}
128*4882a593Smuzhiyun 	up_read(&v9ses->rename_sem);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	/* start from the root and try to do a lookup */
131*4882a593Smuzhiyun 	fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
132*4882a593Smuzhiyun 	if (!fid) {
133*4882a593Smuzhiyun 		/* the user is not attached to the fs yet */
134*4882a593Smuzhiyun 		if (access == V9FS_ACCESS_SINGLE)
135*4882a593Smuzhiyun 			return ERR_PTR(-EPERM);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
138*4882a593Smuzhiyun 				uname = NULL;
139*4882a593Smuzhiyun 		else
140*4882a593Smuzhiyun 			uname = v9ses->uname;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 		fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
143*4882a593Smuzhiyun 				       v9ses->aname);
144*4882a593Smuzhiyun 		if (IS_ERR(fid))
145*4882a593Smuzhiyun 			return fid;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 		v9fs_fid_add(dentry->d_sb->s_root, fid);
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 	/* If we are root ourself just return that */
150*4882a593Smuzhiyun 	if (dentry->d_sb->s_root == dentry)
151*4882a593Smuzhiyun 		return fid;
152*4882a593Smuzhiyun 	/*
153*4882a593Smuzhiyun 	 * Do a multipath walk with attached root.
154*4882a593Smuzhiyun 	 * When walking parent we need to make sure we
155*4882a593Smuzhiyun 	 * don't have a parallel rename happening
156*4882a593Smuzhiyun 	 */
157*4882a593Smuzhiyun 	down_read(&v9ses->rename_sem);
158*4882a593Smuzhiyun 	n  = build_path_from_dentry(v9ses, dentry, &wnames);
159*4882a593Smuzhiyun 	if (n < 0) {
160*4882a593Smuzhiyun 		fid = ERR_PTR(n);
161*4882a593Smuzhiyun 		goto err_out;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 	clone = 1;
164*4882a593Smuzhiyun 	i = 0;
165*4882a593Smuzhiyun 	while (i < n) {
166*4882a593Smuzhiyun 		l = min(n - i, P9_MAXWELEM);
167*4882a593Smuzhiyun 		/*
168*4882a593Smuzhiyun 		 * We need to hold rename lock when doing a multipath
169*4882a593Smuzhiyun 		 * walk to ensure none of the patch component change
170*4882a593Smuzhiyun 		 */
171*4882a593Smuzhiyun 		fid = p9_client_walk(fid, l, &wnames[i], clone);
172*4882a593Smuzhiyun 		if (IS_ERR(fid)) {
173*4882a593Smuzhiyun 			if (old_fid) {
174*4882a593Smuzhiyun 				/*
175*4882a593Smuzhiyun 				 * If we fail, clunk fid which are mapping
176*4882a593Smuzhiyun 				 * to path component and not the last component
177*4882a593Smuzhiyun 				 * of the path.
178*4882a593Smuzhiyun 				 */
179*4882a593Smuzhiyun 				p9_client_clunk(old_fid);
180*4882a593Smuzhiyun 			}
181*4882a593Smuzhiyun 			kfree(wnames);
182*4882a593Smuzhiyun 			goto err_out;
183*4882a593Smuzhiyun 		}
184*4882a593Smuzhiyun 		old_fid = fid;
185*4882a593Smuzhiyun 		i += l;
186*4882a593Smuzhiyun 		clone = 0;
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 	kfree(wnames);
189*4882a593Smuzhiyun fid_out:
190*4882a593Smuzhiyun 	if (!IS_ERR(fid)) {
191*4882a593Smuzhiyun 		spin_lock(&dentry->d_lock);
192*4882a593Smuzhiyun 		if (d_unhashed(dentry)) {
193*4882a593Smuzhiyun 			spin_unlock(&dentry->d_lock);
194*4882a593Smuzhiyun 			p9_client_clunk(fid);
195*4882a593Smuzhiyun 			fid = ERR_PTR(-ENOENT);
196*4882a593Smuzhiyun 		} else {
197*4882a593Smuzhiyun 			__add_fid(dentry, fid);
198*4882a593Smuzhiyun 			spin_unlock(&dentry->d_lock);
199*4882a593Smuzhiyun 		}
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun err_out:
202*4882a593Smuzhiyun 	up_read(&v9ses->rename_sem);
203*4882a593Smuzhiyun 	return fid;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun /**
207*4882a593Smuzhiyun  * v9fs_fid_lookup - lookup for a fid, try to walk if not found
208*4882a593Smuzhiyun  * @dentry: dentry to look for fid in
209*4882a593Smuzhiyun  *
210*4882a593Smuzhiyun  * Look for a fid in the specified dentry for the current user.
211*4882a593Smuzhiyun  * If no fid is found, try to create one walking from a fid from the parent
212*4882a593Smuzhiyun  * dentry (if it has one), or the root dentry. If the user haven't accessed
213*4882a593Smuzhiyun  * the fs yet, attach now and walk from the root.
214*4882a593Smuzhiyun  */
215*4882a593Smuzhiyun 
v9fs_fid_lookup(struct dentry * dentry)216*4882a593Smuzhiyun struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	kuid_t uid;
219*4882a593Smuzhiyun 	int  any, access;
220*4882a593Smuzhiyun 	struct v9fs_session_info *v9ses;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	v9ses = v9fs_dentry2v9ses(dentry);
223*4882a593Smuzhiyun 	access = v9ses->flags & V9FS_ACCESS_MASK;
224*4882a593Smuzhiyun 	switch (access) {
225*4882a593Smuzhiyun 	case V9FS_ACCESS_SINGLE:
226*4882a593Smuzhiyun 	case V9FS_ACCESS_USER:
227*4882a593Smuzhiyun 	case V9FS_ACCESS_CLIENT:
228*4882a593Smuzhiyun 		uid = current_fsuid();
229*4882a593Smuzhiyun 		any = 0;
230*4882a593Smuzhiyun 		break;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	case V9FS_ACCESS_ANY:
233*4882a593Smuzhiyun 		uid = v9ses->uid;
234*4882a593Smuzhiyun 		any = 1;
235*4882a593Smuzhiyun 		break;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	default:
238*4882a593Smuzhiyun 		uid = INVALID_UID;
239*4882a593Smuzhiyun 		any = 0;
240*4882a593Smuzhiyun 		break;
241*4882a593Smuzhiyun 	}
242*4882a593Smuzhiyun 	return v9fs_fid_lookup_with_uid(dentry, uid, any);
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
v9fs_writeback_fid(struct dentry * dentry)245*4882a593Smuzhiyun struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	int err;
248*4882a593Smuzhiyun 	struct p9_fid *fid;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	fid = clone_fid(v9fs_fid_lookup_with_uid(dentry, GLOBAL_ROOT_UID, 0));
251*4882a593Smuzhiyun 	if (IS_ERR(fid))
252*4882a593Smuzhiyun 		goto error_out;
253*4882a593Smuzhiyun 	/*
254*4882a593Smuzhiyun 	 * writeback fid will only be used to write back the
255*4882a593Smuzhiyun 	 * dirty pages. We always request for the open fid in read-write
256*4882a593Smuzhiyun 	 * mode so that a partial page write which result in page
257*4882a593Smuzhiyun 	 * read can work.
258*4882a593Smuzhiyun 	 */
259*4882a593Smuzhiyun 	err = p9_client_open(fid, O_RDWR);
260*4882a593Smuzhiyun 	if (err < 0) {
261*4882a593Smuzhiyun 		p9_client_clunk(fid);
262*4882a593Smuzhiyun 		fid = ERR_PTR(err);
263*4882a593Smuzhiyun 		goto error_out;
264*4882a593Smuzhiyun 	}
265*4882a593Smuzhiyun error_out:
266*4882a593Smuzhiyun 	return fid;
267*4882a593Smuzhiyun }
268