xref: /openbmc/linux/fs/9p/fid.c (revision 9464bf9762a8b16f4fbd05b115dcde51b339ac58)
11f327613SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23ed8491cSEric Van Hensbergen /*
33ed8491cSEric Van Hensbergen  * V9FS FID Management
43ed8491cSEric Van Hensbergen  *
5ba17674fSLatchesar Ionkov  *  Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
646f6dac2SEric Van Hensbergen  *  Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
73ed8491cSEric Van Hensbergen  */
83ed8491cSEric Van Hensbergen 
93ed8491cSEric Van Hensbergen #include <linux/module.h>
103ed8491cSEric Van Hensbergen #include <linux/errno.h>
113ed8491cSEric Van Hensbergen #include <linux/fs.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
13914e2637SAl Viro #include <linux/sched.h>
14bd238fb4SLatchesar Ionkov #include <net/9p/9p.h>
15bd238fb4SLatchesar Ionkov #include <net/9p/client.h>
163ed8491cSEric Van Hensbergen 
173ed8491cSEric Van Hensbergen #include "v9fs.h"
183ed8491cSEric Van Hensbergen #include "v9fs_vfs.h"
193ed8491cSEric Van Hensbergen #include "fid.h"
203ed8491cSEric Van Hensbergen 
__add_fid(struct dentry * dentry,struct p9_fid * fid)21bc868036SDavid Howells static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
22bc868036SDavid Howells {
23bc868036SDavid Howells 	hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
24bc868036SDavid Howells }
25bc868036SDavid Howells 
26bc868036SDavid Howells 
273ed8491cSEric Van Hensbergen /**
28ba17674fSLatchesar Ionkov  * v9fs_fid_add - add a fid to a dentry
29ba17674fSLatchesar Ionkov  * @dentry: dentry that the fid is being added to
30e3baced0SYang Li  * @pfid: fid to add, NULLed out
313ed8491cSEric Van Hensbergen  *
323ed8491cSEric Van Hensbergen  */
v9fs_fid_add(struct dentry * dentry,struct p9_fid ** pfid)33dafbe689SDominique Martinet void v9fs_fid_add(struct dentry *dentry, struct p9_fid **pfid)
343ed8491cSEric Van Hensbergen {
35dafbe689SDominique Martinet 	struct p9_fid *fid = *pfid;
36dafbe689SDominique Martinet 
37634095daSAl Viro 	spin_lock(&dentry->d_lock);
385e608671SAl Viro 	__add_fid(dentry, fid);
39634095daSAl Viro 	spin_unlock(&dentry->d_lock);
40dafbe689SDominique Martinet 
41dafbe689SDominique Martinet 	*pfid = NULL;
423ed8491cSEric Van Hensbergen }
433ed8491cSEric Van Hensbergen 
v9fs_is_writeable(int mode)441543b4c5SEric Van Hensbergen static bool v9fs_is_writeable(int mode)
451543b4c5SEric Van Hensbergen {
461543b4c5SEric Van Hensbergen 	if (mode & (P9_OWRITE|P9_ORDWR))
471543b4c5SEric Van Hensbergen 		return true;
481543b4c5SEric Van Hensbergen 	else
491543b4c5SEric Van Hensbergen 		return false;
501543b4c5SEric Van Hensbergen }
511543b4c5SEric Van Hensbergen 
523ed8491cSEric Van Hensbergen /**
53987a6485SGreg Kurz  * v9fs_fid_find_inode - search for an open fid off of the inode list
54154372e6SEric Van Hensbergen  * @inode: return a fid pointing to a specific inode
551543b4c5SEric Van Hensbergen  * @want_writeable: only consider fids which are writeable
56154372e6SEric Van Hensbergen  * @uid: return a fid belonging to the specified user
571543b4c5SEric Van Hensbergen  * @any: ignore uid as a selection criteria
58154372e6SEric Van Hensbergen  *
59154372e6SEric Van Hensbergen  */
v9fs_fid_find_inode(struct inode * inode,bool want_writeable,kuid_t uid,bool any)601543b4c5SEric Van Hensbergen struct p9_fid *v9fs_fid_find_inode(struct inode *inode, bool want_writeable,
611543b4c5SEric Van Hensbergen 	kuid_t uid, bool any)
62154372e6SEric Van Hensbergen {
63987a6485SGreg Kurz 	struct hlist_head *h;
64987a6485SGreg Kurz 	struct p9_fid *fid, *ret = NULL;
65154372e6SEric Van Hensbergen 
66154372e6SEric Van Hensbergen 	p9_debug(P9_DEBUG_VFS, " inode: %p\n", inode);
67154372e6SEric Van Hensbergen 
68987a6485SGreg Kurz 	spin_lock(&inode->i_lock);
69987a6485SGreg Kurz 	h = (struct hlist_head *)&inode->i_private;
70987a6485SGreg Kurz 	hlist_for_each_entry(fid, h, ilist) {
711543b4c5SEric Van Hensbergen 		if (any || uid_eq(fid->uid, uid)) {
721543b4c5SEric Van Hensbergen 			if (want_writeable && !v9fs_is_writeable(fid->mode)) {
731543b4c5SEric Van Hensbergen 				p9_debug(P9_DEBUG_VFS, " mode: %x not writeable?\n",
741543b4c5SEric Van Hensbergen 							fid->mode);
751543b4c5SEric Van Hensbergen 				continue;
761543b4c5SEric Van Hensbergen 			}
77b48dbb99SDominique Martinet 			p9_fid_get(fid);
78154372e6SEric Van Hensbergen 			ret = fid;
79154372e6SEric Van Hensbergen 			break;
80154372e6SEric Van Hensbergen 		}
81154372e6SEric Van Hensbergen 	}
82987a6485SGreg Kurz 	spin_unlock(&inode->i_lock);
83154372e6SEric Van Hensbergen 	return ret;
84154372e6SEric Van Hensbergen }
85154372e6SEric Van Hensbergen 
86154372e6SEric Van Hensbergen /**
87987a6485SGreg Kurz  * v9fs_open_fid_add - add an open fid to an inode
88bc868036SDavid Howells  * @inode: inode that the fid is being added to
89e3baced0SYang Li  * @pfid: fid to add, NULLed out
90987a6485SGreg Kurz  *
91987a6485SGreg Kurz  */
92987a6485SGreg Kurz 
v9fs_open_fid_add(struct inode * inode,struct p9_fid ** pfid)93dafbe689SDominique Martinet void v9fs_open_fid_add(struct inode *inode, struct p9_fid **pfid)
94987a6485SGreg Kurz {
95dafbe689SDominique Martinet 	struct p9_fid *fid = *pfid;
96dafbe689SDominique Martinet 
97987a6485SGreg Kurz 	spin_lock(&inode->i_lock);
98987a6485SGreg Kurz 	hlist_add_head(&fid->ilist, (struct hlist_head *)&inode->i_private);
99987a6485SGreg Kurz 	spin_unlock(&inode->i_lock);
100dafbe689SDominique Martinet 
101dafbe689SDominique Martinet 	*pfid = NULL;
102987a6485SGreg Kurz }
103987a6485SGreg Kurz 
104987a6485SGreg Kurz 
105987a6485SGreg Kurz /**
106ba17674fSLatchesar Ionkov  * v9fs_fid_find - retrieve a fid that belongs to the specified uid
107ba17674fSLatchesar Ionkov  * @dentry: dentry to look for fid in
108ba17674fSLatchesar Ionkov  * @uid: return fid that belongs to the specified user
109ba17674fSLatchesar Ionkov  * @any: if non-zero, return any fid associated with the dentry
110ba17674fSLatchesar Ionkov  *
111ba17674fSLatchesar Ionkov  */
112ba17674fSLatchesar Ionkov 
v9fs_fid_find(struct dentry * dentry,kuid_t uid,int any)113b4642556SEric W. Biederman static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
114ba17674fSLatchesar Ionkov {
115ba17674fSLatchesar Ionkov 	struct p9_fid *fid, *ret;
116ba17674fSLatchesar Ionkov 
1174b8e9923SAl Viro 	p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
1184b8e9923SAl Viro 		 dentry, dentry, from_kuid(&init_user_ns, uid),
119b4642556SEric W. Biederman 		 any);
120ba17674fSLatchesar Ionkov 	ret = NULL;
121aaeb7ecfSAl Viro 	/* we'll recheck under lock if there's anything to look in */
12222e424feSDominique Martinet 	if (dentry->d_fsdata) {
123aaeb7ecfSAl Viro 		struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
1249a268faaSSohaib Mohamed 
125634095daSAl Viro 		spin_lock(&dentry->d_lock);
12656a79b7bSLinus Torvalds 		hlist_for_each_entry(fid, h, dlist) {
127b4642556SEric W. Biederman 			if (any || uid_eq(fid->uid, uid)) {
128ba17674fSLatchesar Ionkov 				ret = fid;
129b48dbb99SDominique Martinet 				p9_fid_get(ret);
130ba17674fSLatchesar Ionkov 				break;
131ba17674fSLatchesar Ionkov 			}
132ba17674fSLatchesar Ionkov 		}
133634095daSAl Viro 		spin_unlock(&dentry->d_lock);
134ba17674fSLatchesar Ionkov 	}
135*a63c78c3SDominique Martinet 	if (!ret && dentry->d_inode)
136*a63c78c3SDominique Martinet 		ret = v9fs_fid_find_inode(dentry->d_inode, false, uid, any);
137ba17674fSLatchesar Ionkov 
138ba17674fSLatchesar Ionkov 	return ret;
139ba17674fSLatchesar Ionkov }
140ba17674fSLatchesar Ionkov 
141a534c8d1SAneesh Kumar K.V /*
142a534c8d1SAneesh Kumar K.V  * We need to hold v9ses->rename_sem as long as we hold references
143a534c8d1SAneesh Kumar K.V  * to returned path array. Array element contain pointers to
144a534c8d1SAneesh Kumar K.V  * dentry names.
145a534c8d1SAneesh Kumar K.V  */
build_path_from_dentry(struct v9fs_session_info * v9ses,struct dentry * dentry,const unsigned char *** names)146a534c8d1SAneesh Kumar K.V static int build_path_from_dentry(struct v9fs_session_info *v9ses,
1477880b43bSAl Viro 				  struct dentry *dentry, const unsigned char ***names)
148a534c8d1SAneesh Kumar K.V {
149a534c8d1SAneesh Kumar K.V 	int n = 0, i;
1507880b43bSAl Viro 	const unsigned char **wnames;
151a534c8d1SAneesh Kumar K.V 	struct dentry *ds;
152a534c8d1SAneesh Kumar K.V 
153a534c8d1SAneesh Kumar K.V 	for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
154a534c8d1SAneesh Kumar K.V 		n++;
155a534c8d1SAneesh Kumar K.V 
1566da2ec56SKees Cook 	wnames = kmalloc_array(n, sizeof(char *), GFP_KERNEL);
157a534c8d1SAneesh Kumar K.V 	if (!wnames)
158a534c8d1SAneesh Kumar K.V 		goto err_out;
159a534c8d1SAneesh Kumar K.V 
160a534c8d1SAneesh Kumar K.V 	for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
1617880b43bSAl Viro 		wnames[i] = ds->d_name.name;
162a534c8d1SAneesh Kumar K.V 
163a534c8d1SAneesh Kumar K.V 	*names = wnames;
164a534c8d1SAneesh Kumar K.V 	return n;
165a534c8d1SAneesh Kumar K.V err_out:
166a534c8d1SAneesh Kumar K.V 	return -ENOMEM;
167a534c8d1SAneesh Kumar K.V }
168a534c8d1SAneesh Kumar K.V 
v9fs_fid_lookup_with_uid(struct dentry * dentry,kuid_t uid,int any)1697c9e592eSAneesh Kumar K.V static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
170b4642556SEric W. Biederman 					       kuid_t uid, int any)
1713ed8491cSEric Van Hensbergen {
172a534c8d1SAneesh Kumar K.V 	struct dentry *ds;
1737880b43bSAl Viro 	const unsigned char **wnames, *uname;
174c58c72d3STyler Hicks 	int i, n, l, access;
1757c9e592eSAneesh Kumar K.V 	struct v9fs_session_info *v9ses;
176cba83f47STyler Hicks 	struct p9_fid *fid, *root_fid, *old_fid;
1773ed8491cSEric Van Hensbergen 
17842869c8aSAneesh Kumar K.V 	v9ses = v9fs_dentry2v9ses(dentry);
179ba17674fSLatchesar Ionkov 	access = v9ses->flags & V9FS_ACCESS_MASK;
180ba17674fSLatchesar Ionkov 	fid = v9fs_fid_find(dentry, uid, any);
181ba17674fSLatchesar Ionkov 	if (fid)
182ba17674fSLatchesar Ionkov 		return fid;
183a534c8d1SAneesh Kumar K.V 	/*
184a534c8d1SAneesh Kumar K.V 	 * we don't have a matching fid. To do a TWALK we need
185a534c8d1SAneesh Kumar K.V 	 * parent fid. We need to prevent rename when we want to
186a534c8d1SAneesh Kumar K.V 	 * look at the parent.
187a534c8d1SAneesh Kumar K.V 	 */
188a534c8d1SAneesh Kumar K.V 	down_read(&v9ses->rename_sem);
189ba17674fSLatchesar Ionkov 	ds = dentry->d_parent;
190ba17674fSLatchesar Ionkov 	fid = v9fs_fid_find(ds, uid, any);
191a534c8d1SAneesh Kumar K.V 	if (fid) {
192a534c8d1SAneesh Kumar K.V 		/* Found the parent fid do a lookup with that */
19347b1e343STyler Hicks 		old_fid = fid;
1946636b6dcSJianyong Wu 
19547b1e343STyler Hicks 		fid = p9_client_walk(old_fid, 1, &dentry->d_name.name, 1);
196b48dbb99SDominique Martinet 		p9_fid_put(old_fid);
197a534c8d1SAneesh Kumar K.V 		goto fid_out;
198a534c8d1SAneesh Kumar K.V 	}
199a534c8d1SAneesh Kumar K.V 	up_read(&v9ses->rename_sem);
200ba17674fSLatchesar Ionkov 
201a534c8d1SAneesh Kumar K.V 	/* start from the root and try to do a lookup */
202cba83f47STyler Hicks 	root_fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
203cba83f47STyler Hicks 	if (!root_fid) {
204a534c8d1SAneesh Kumar K.V 		/* the user is not attached to the fs yet */
205ba17674fSLatchesar Ionkov 		if (access == V9FS_ACCESS_SINGLE)
206ba17674fSLatchesar Ionkov 			return ERR_PTR(-EPERM);
207ba17674fSLatchesar Ionkov 
208a534c8d1SAneesh Kumar K.V 		if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
209ba17674fSLatchesar Ionkov 			uname = NULL;
210bd238fb4SLatchesar Ionkov 		else
211ba17674fSLatchesar Ionkov 			uname = v9ses->uname;
2123ed8491cSEric Van Hensbergen 
213dafbe689SDominique Martinet 		fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
214ba17674fSLatchesar Ionkov 				       v9ses->aname);
215dafbe689SDominique Martinet 		if (IS_ERR(fid))
216dafbe689SDominique Martinet 			return fid;
217ba17674fSLatchesar Ionkov 
218dafbe689SDominique Martinet 		root_fid = p9_fid_get(fid);
219dafbe689SDominique Martinet 		v9fs_fid_add(dentry->d_sb->s_root, &fid);
220ba17674fSLatchesar Ionkov 	}
221a534c8d1SAneesh Kumar K.V 	/* If we are root ourself just return that */
2222a3dcbccSTyler Hicks 	if (dentry->d_sb->s_root == dentry)
223cba83f47STyler Hicks 		return root_fid;
224cba83f47STyler Hicks 
225a534c8d1SAneesh Kumar K.V 	/*
226a534c8d1SAneesh Kumar K.V 	 * Do a multipath walk with attached root.
227a534c8d1SAneesh Kumar K.V 	 * When walking parent we need to make sure we
228a534c8d1SAneesh Kumar K.V 	 * don't have a parallel rename happening
229a534c8d1SAneesh Kumar K.V 	 */
230a534c8d1SAneesh Kumar K.V 	down_read(&v9ses->rename_sem);
231a534c8d1SAneesh Kumar K.V 	n  = build_path_from_dentry(v9ses, dentry, &wnames);
232a534c8d1SAneesh Kumar K.V 	if (n < 0) {
233a534c8d1SAneesh Kumar K.V 		fid = ERR_PTR(n);
234a534c8d1SAneesh Kumar K.V 		goto err_out;
235a534c8d1SAneesh Kumar K.V 	}
236cba83f47STyler Hicks 	fid = root_fid;
237cba83f47STyler Hicks 	old_fid = root_fid;
238ba17674fSLatchesar Ionkov 	i = 0;
239ba17674fSLatchesar Ionkov 	while (i < n) {
240ba17674fSLatchesar Ionkov 		l = min(n - i, P9_MAXWELEM);
241a534c8d1SAneesh Kumar K.V 		/*
242a534c8d1SAneesh Kumar K.V 		 * We need to hold rename lock when doing a multipath
243b296d057STyler Hicks 		 * walk to ensure none of the path components change
244a534c8d1SAneesh Kumar K.V 		 */
245c58c72d3STyler Hicks 		fid = p9_client_walk(old_fid, l, &wnames[i],
246c58c72d3STyler Hicks 				     old_fid == root_fid /* clone */);
2472a3dcbccSTyler Hicks 		/* non-cloning walk will return the same fid */
2482a3dcbccSTyler Hicks 		if (fid != old_fid) {
249b48dbb99SDominique Martinet 			p9_fid_put(old_fid);
2502a3dcbccSTyler Hicks 			old_fid = fid;
2515b0fa207SAneesh Kumar K.V 		}
2522a3dcbccSTyler Hicks 		if (IS_ERR(fid)) {
253ba17674fSLatchesar Ionkov 			kfree(wnames);
254a534c8d1SAneesh Kumar K.V 			goto err_out;
2550b8dd177SLatchesar Ionkov 		}
256ba17674fSLatchesar Ionkov 		i += l;
257ba17674fSLatchesar Ionkov 	}
258ba17674fSLatchesar Ionkov 	kfree(wnames);
259a534c8d1SAneesh Kumar K.V fid_out:
2605e608671SAl Viro 	if (!IS_ERR(fid)) {
2615e608671SAl Viro 		spin_lock(&dentry->d_lock);
2625e608671SAl Viro 		if (d_unhashed(dentry)) {
2635e608671SAl Viro 			spin_unlock(&dentry->d_lock);
264b48dbb99SDominique Martinet 			p9_fid_put(fid);
2655e608671SAl Viro 			fid = ERR_PTR(-ENOENT);
2665e608671SAl Viro 		} else {
2675e608671SAl Viro 			__add_fid(dentry, fid);
268b48dbb99SDominique Martinet 			p9_fid_get(fid);
2695e608671SAl Viro 			spin_unlock(&dentry->d_lock);
2705e608671SAl Viro 		}
2715e608671SAl Viro 	}
272a534c8d1SAneesh Kumar K.V err_out:
273a534c8d1SAneesh Kumar K.V 	up_read(&v9ses->rename_sem);
274ba17674fSLatchesar Ionkov 	return fid;
275ba17674fSLatchesar Ionkov }
276da977b2cSEric Van Hensbergen 
2777c9e592eSAneesh Kumar K.V /**
2787c9e592eSAneesh Kumar K.V  * v9fs_fid_lookup - lookup for a fid, try to walk if not found
2797c9e592eSAneesh Kumar K.V  * @dentry: dentry to look for fid in
2807c9e592eSAneesh Kumar K.V  *
2817c9e592eSAneesh Kumar K.V  * Look for a fid in the specified dentry for the current user.
2827c9e592eSAneesh Kumar K.V  * If no fid is found, try to create one walking from a fid from the parent
2837c9e592eSAneesh Kumar K.V  * dentry (if it has one), or the root dentry. If the user haven't accessed
2847c9e592eSAneesh Kumar K.V  * the fs yet, attach now and walk from the root.
2857c9e592eSAneesh Kumar K.V  */
2867c9e592eSAneesh Kumar K.V 
v9fs_fid_lookup(struct dentry * dentry)2877c9e592eSAneesh Kumar K.V struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
2887c9e592eSAneesh Kumar K.V {
289b4642556SEric W. Biederman 	kuid_t uid;
2907c9e592eSAneesh Kumar K.V 	int  any, access;
2917c9e592eSAneesh Kumar K.V 	struct v9fs_session_info *v9ses;
2927c9e592eSAneesh Kumar K.V 
29342869c8aSAneesh Kumar K.V 	v9ses = v9fs_dentry2v9ses(dentry);
2947c9e592eSAneesh Kumar K.V 	access = v9ses->flags & V9FS_ACCESS_MASK;
2957c9e592eSAneesh Kumar K.V 	switch (access) {
2967c9e592eSAneesh Kumar K.V 	case V9FS_ACCESS_SINGLE:
2977c9e592eSAneesh Kumar K.V 	case V9FS_ACCESS_USER:
2987c9e592eSAneesh Kumar K.V 	case V9FS_ACCESS_CLIENT:
2997c9e592eSAneesh Kumar K.V 		uid = current_fsuid();
3007c9e592eSAneesh Kumar K.V 		any = 0;
3017c9e592eSAneesh Kumar K.V 		break;
3027c9e592eSAneesh Kumar K.V 
3037c9e592eSAneesh Kumar K.V 	case V9FS_ACCESS_ANY:
3047c9e592eSAneesh Kumar K.V 		uid = v9ses->uid;
3057c9e592eSAneesh Kumar K.V 		any = 1;
3067c9e592eSAneesh Kumar K.V 		break;
3077c9e592eSAneesh Kumar K.V 
3087c9e592eSAneesh Kumar K.V 	default:
309b4642556SEric W. Biederman 		uid = INVALID_UID;
3107c9e592eSAneesh Kumar K.V 		any = 0;
3117c9e592eSAneesh Kumar K.V 		break;
3127c9e592eSAneesh Kumar K.V 	}
3137c9e592eSAneesh Kumar K.V 	return v9fs_fid_lookup_with_uid(dentry, uid, any);
3147c9e592eSAneesh Kumar K.V }
3157c9e592eSAneesh Kumar K.V 
316