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