1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
6 * Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
9 #include <linux/module.h>
10 #include <linux/errno.h>
12 #include <linux/slab.h>
13 #include <linux/sched.h>
14 #include <net/9p/9p.h>
15 #include <net/9p/client.h>
21 static inline void __add_fid(struct dentry
*dentry
, struct p9_fid
*fid
)
23 hlist_add_head(&fid
->dlist
, (struct hlist_head
*)&dentry
->d_fsdata
);
28 * v9fs_fid_add - add a fid to a dentry
29 * @dentry: dentry that the fid is being added to
30 * @pfid: fid to add, NULLed out
33 void v9fs_fid_add(struct dentry
*dentry
, struct p9_fid
**pfid
)
35 struct p9_fid
*fid
= *pfid
;
37 spin_lock(&dentry
->d_lock
);
38 __add_fid(dentry
, fid
);
39 spin_unlock(&dentry
->d_lock
);
44 static bool v9fs_is_writeable(int mode
)
46 if (mode
& (P9_OWRITE
|P9_ORDWR
))
53 * v9fs_fid_find_inode - search for an open fid off of the inode list
54 * @inode: return a fid pointing to a specific inode
55 * @want_writeable: only consider fids which are writeable
56 * @uid: return a fid belonging to the specified user
57 * @any: ignore uid as a selection criteria
60 struct p9_fid
*v9fs_fid_find_inode(struct inode
*inode
, bool want_writeable
,
64 struct p9_fid
*fid
, *ret
= NULL
;
66 p9_debug(P9_DEBUG_VFS
, " inode: %p\n", inode
);
68 spin_lock(&inode
->i_lock
);
69 h
= (struct hlist_head
*)&inode
->i_private
;
70 hlist_for_each_entry(fid
, h
, ilist
) {
71 if (any
|| uid_eq(fid
->uid
, uid
)) {
72 if (want_writeable
&& !v9fs_is_writeable(fid
->mode
)) {
73 p9_debug(P9_DEBUG_VFS
, " mode: %x not writeable?\n",
82 spin_unlock(&inode
->i_lock
);
87 * v9fs_open_fid_add - add an open fid to an inode
88 * @inode: inode that the fid is being added to
89 * @pfid: fid to add, NULLed out
93 void v9fs_open_fid_add(struct inode
*inode
, struct p9_fid
**pfid
)
95 struct p9_fid
*fid
= *pfid
;
97 spin_lock(&inode
->i_lock
);
98 hlist_add_head(&fid
->ilist
, (struct hlist_head
*)&inode
->i_private
);
99 spin_unlock(&inode
->i_lock
);
106 * v9fs_fid_find - retrieve a fid that belongs to the specified uid
107 * @dentry: dentry to look for fid in
108 * @uid: return fid that belongs to the specified user
109 * @any: if non-zero, return any fid associated with the dentry
113 static struct p9_fid
*v9fs_fid_find(struct dentry
*dentry
, kuid_t uid
, int any
)
115 struct p9_fid
*fid
, *ret
;
117 p9_debug(P9_DEBUG_VFS
, " dentry: %pd (%p) uid %d any %d\n",
118 dentry
, dentry
, from_kuid(&init_user_ns
, uid
),
121 /* we'll recheck under lock if there's anything to look in */
122 if (dentry
->d_fsdata
) {
123 struct hlist_head
*h
= (struct hlist_head
*)&dentry
->d_fsdata
;
125 spin_lock(&dentry
->d_lock
);
126 hlist_for_each_entry(fid
, h
, dlist
) {
127 if (any
|| uid_eq(fid
->uid
, uid
)) {
133 spin_unlock(&dentry
->d_lock
);
135 if (!ret
&& dentry
->d_inode
)
136 ret
= v9fs_fid_find_inode(dentry
->d_inode
, false, uid
, any
);
142 * We need to hold v9ses->rename_sem as long as we hold references
143 * to returned path array. Array element contain pointers to
146 static int build_path_from_dentry(struct v9fs_session_info
*v9ses
,
147 struct dentry
*dentry
, const unsigned char ***names
)
150 const unsigned char **wnames
;
153 for (ds
= dentry
; !IS_ROOT(ds
); ds
= ds
->d_parent
)
156 wnames
= kmalloc_array(n
, sizeof(char *), GFP_KERNEL
);
160 for (ds
= dentry
, i
= (n
-1); i
>= 0; i
--, ds
= ds
->d_parent
)
161 wnames
[i
] = ds
->d_name
.name
;
169 static struct p9_fid
*v9fs_fid_lookup_with_uid(struct dentry
*dentry
,
173 const unsigned char **wnames
, *uname
;
175 struct v9fs_session_info
*v9ses
;
176 struct p9_fid
*fid
, *root_fid
, *old_fid
;
178 v9ses
= v9fs_dentry2v9ses(dentry
);
179 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
180 fid
= v9fs_fid_find(dentry
, uid
, any
);
184 * we don't have a matching fid. To do a TWALK we need
185 * parent fid. We need to prevent rename when we want to
186 * look at the parent.
188 down_read(&v9ses
->rename_sem
);
189 ds
= dentry
->d_parent
;
190 fid
= v9fs_fid_find(ds
, uid
, any
);
192 /* Found the parent fid do a lookup with that */
195 fid
= p9_client_walk(old_fid
, 1, &dentry
->d_name
.name
, 1);
199 up_read(&v9ses
->rename_sem
);
201 /* start from the root and try to do a lookup */
202 root_fid
= v9fs_fid_find(dentry
->d_sb
->s_root
, uid
, any
);
204 /* the user is not attached to the fs yet */
205 if (access
== V9FS_ACCESS_SINGLE
)
206 return ERR_PTR(-EPERM
);
208 if (v9fs_proto_dotu(v9ses
) || v9fs_proto_dotl(v9ses
))
211 uname
= v9ses
->uname
;
213 fid
= p9_client_attach(v9ses
->clnt
, NULL
, uname
, uid
,
218 root_fid
= p9_fid_get(fid
);
219 v9fs_fid_add(dentry
->d_sb
->s_root
, &fid
);
221 /* If we are root ourself just return that */
222 if (dentry
->d_sb
->s_root
== dentry
)
226 * Do a multipath walk with attached root.
227 * When walking parent we need to make sure we
228 * don't have a parallel rename happening
230 down_read(&v9ses
->rename_sem
);
231 n
= build_path_from_dentry(v9ses
, dentry
, &wnames
);
240 l
= min(n
- i
, P9_MAXWELEM
);
242 * We need to hold rename lock when doing a multipath
243 * walk to ensure none of the path components change
245 fid
= p9_client_walk(old_fid
, l
, &wnames
[i
],
246 old_fid
== root_fid
/* clone */);
247 /* non-cloning walk will return the same fid */
248 if (fid
!= old_fid
) {
261 spin_lock(&dentry
->d_lock
);
262 if (d_unhashed(dentry
)) {
263 spin_unlock(&dentry
->d_lock
);
265 fid
= ERR_PTR(-ENOENT
);
267 __add_fid(dentry
, fid
);
269 spin_unlock(&dentry
->d_lock
);
273 up_read(&v9ses
->rename_sem
);
278 * v9fs_fid_lookup - lookup for a fid, try to walk if not found
279 * @dentry: dentry to look for fid in
281 * Look for a fid in the specified dentry for the current user.
282 * If no fid is found, try to create one walking from a fid from the parent
283 * dentry (if it has one), or the root dentry. If the user haven't accessed
284 * the fs yet, attach now and walk from the root.
287 struct p9_fid
*v9fs_fid_lookup(struct dentry
*dentry
)
291 struct v9fs_session_info
*v9ses
;
293 v9ses
= v9fs_dentry2v9ses(dentry
);
294 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
296 case V9FS_ACCESS_SINGLE
:
297 case V9FS_ACCESS_USER
:
298 case V9FS_ACCESS_CLIENT
:
299 uid
= current_fsuid();
303 case V9FS_ACCESS_ANY
:
313 return v9fs_fid_lookup_with_uid(dentry
, uid
, any
);