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
);
136 ret
= v9fs_fid_find_inode(dentry
->d_inode
, false, uid
, any
);
143 * We need to hold v9ses->rename_sem as long as we hold references
144 * to returned path array. Array element contain pointers to
147 static int build_path_from_dentry(struct v9fs_session_info
*v9ses
,
148 struct dentry
*dentry
, const unsigned char ***names
)
151 const unsigned char **wnames
;
154 for (ds
= dentry
; !IS_ROOT(ds
); ds
= ds
->d_parent
)
157 wnames
= kmalloc_array(n
, sizeof(char *), GFP_KERNEL
);
161 for (ds
= dentry
, i
= (n
-1); i
>= 0; i
--, ds
= ds
->d_parent
)
162 wnames
[i
] = ds
->d_name
.name
;
170 static struct p9_fid
*v9fs_fid_lookup_with_uid(struct dentry
*dentry
,
174 const unsigned char **wnames
, *uname
;
176 struct v9fs_session_info
*v9ses
;
177 struct p9_fid
*fid
, *root_fid
, *old_fid
;
179 v9ses
= v9fs_dentry2v9ses(dentry
);
180 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
181 fid
= v9fs_fid_find(dentry
, uid
, any
);
185 * we don't have a matching fid. To do a TWALK we need
186 * parent fid. We need to prevent rename when we want to
187 * look at the parent.
189 down_read(&v9ses
->rename_sem
);
190 ds
= dentry
->d_parent
;
191 fid
= v9fs_fid_find(ds
, uid
, any
);
193 /* Found the parent fid do a lookup with that */
196 fid
= p9_client_walk(old_fid
, 1, &dentry
->d_name
.name
, 1);
200 up_read(&v9ses
->rename_sem
);
202 /* start from the root and try to do a lookup */
203 root_fid
= v9fs_fid_find(dentry
->d_sb
->s_root
, uid
, any
);
205 /* the user is not attached to the fs yet */
206 if (access
== V9FS_ACCESS_SINGLE
)
207 return ERR_PTR(-EPERM
);
209 if (v9fs_proto_dotu(v9ses
) || v9fs_proto_dotl(v9ses
))
212 uname
= v9ses
->uname
;
214 fid
= p9_client_attach(v9ses
->clnt
, NULL
, uname
, uid
,
219 root_fid
= p9_fid_get(fid
);
220 v9fs_fid_add(dentry
->d_sb
->s_root
, &fid
);
222 /* If we are root ourself just return that */
223 if (dentry
->d_sb
->s_root
== dentry
)
227 * Do a multipath walk with attached root.
228 * When walking parent we need to make sure we
229 * don't have a parallel rename happening
231 down_read(&v9ses
->rename_sem
);
232 n
= build_path_from_dentry(v9ses
, dentry
, &wnames
);
241 l
= min(n
- i
, P9_MAXWELEM
);
243 * We need to hold rename lock when doing a multipath
244 * walk to ensure none of the path components change
246 fid
= p9_client_walk(old_fid
, l
, &wnames
[i
],
247 old_fid
== root_fid
/* clone */);
248 /* non-cloning walk will return the same fid */
249 if (fid
!= old_fid
) {
262 spin_lock(&dentry
->d_lock
);
263 if (d_unhashed(dentry
)) {
264 spin_unlock(&dentry
->d_lock
);
266 fid
= ERR_PTR(-ENOENT
);
268 __add_fid(dentry
, fid
);
270 spin_unlock(&dentry
->d_lock
);
274 up_read(&v9ses
->rename_sem
);
279 * v9fs_fid_lookup - lookup for a fid, try to walk if not found
280 * @dentry: dentry to look for fid in
282 * Look for a fid in the specified dentry for the current user.
283 * If no fid is found, try to create one walking from a fid from the parent
284 * dentry (if it has one), or the root dentry. If the user haven't accessed
285 * the fs yet, attach now and walk from the root.
288 struct p9_fid
*v9fs_fid_lookup(struct dentry
*dentry
)
292 struct v9fs_session_info
*v9ses
;
294 v9ses
= v9fs_dentry2v9ses(dentry
);
295 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
297 case V9FS_ACCESS_SINGLE
:
298 case V9FS_ACCESS_USER
:
299 case V9FS_ACCESS_CLIENT
:
300 uid
= current_fsuid();
304 case V9FS_ACCESS_ANY
:
314 return v9fs_fid_lookup_with_uid(dentry
, uid
, any
);