~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/9p/fid.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * V9FS FID Management
  4  *
  5  *  Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
  6  *  Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
  7  */
  8 
  9 #include <linux/module.h>
 10 #include <linux/errno.h>
 11 #include <linux/fs.h>
 12 #include <linux/slab.h>
 13 #include <linux/sched.h>
 14 #include <net/9p/9p.h>
 15 #include <net/9p/client.h>
 16 
 17 #include "v9fs.h"
 18 #include "v9fs_vfs.h"
 19 #include "fid.h"
 20 
 21 static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
 22 {
 23         hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
 24 }
 25 
 26 
 27 /**
 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
 31  *
 32  */
 33 void v9fs_fid_add(struct dentry *dentry, struct p9_fid **pfid)
 34 {
 35         struct p9_fid *fid = *pfid;
 36 
 37         spin_lock(&dentry->d_lock);
 38         __add_fid(dentry, fid);
 39         spin_unlock(&dentry->d_lock);
 40 
 41         *pfid = NULL;
 42 }
 43 
 44 static bool v9fs_is_writeable(int mode)
 45 {
 46         if (mode & (P9_OWRITE|P9_ORDWR))
 47                 return true;
 48         else
 49                 return false;
 50 }
 51 
 52 /**
 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
 58  *
 59  */
 60 struct p9_fid *v9fs_fid_find_inode(struct inode *inode, bool want_writeable,
 61         kuid_t uid, bool any)
 62 {
 63         struct hlist_head *h;
 64         struct p9_fid *fid, *ret = NULL;
 65 
 66         p9_debug(P9_DEBUG_VFS, " inode: %p\n", inode);
 67 
 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",
 74                                                         fid->mode);
 75                                 continue;
 76                         }
 77                         p9_fid_get(fid);
 78                         ret = fid;
 79                         break;
 80                 }
 81         }
 82         spin_unlock(&inode->i_lock);
 83         return ret;
 84 }
 85 
 86 /**
 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
 90  *
 91  */
 92 
 93 void v9fs_open_fid_add(struct inode *inode, struct p9_fid **pfid)
 94 {
 95         struct p9_fid *fid = *pfid;
 96 
 97         spin_lock(&inode->i_lock);
 98         hlist_add_head(&fid->ilist, (struct hlist_head *)&inode->i_private);
 99         spin_unlock(&inode->i_lock);
100 
101         *pfid = NULL;
102 }
103 
104 
105 /**
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
110  *
111  */
112 
113 static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
114 {
115         struct p9_fid *fid, *ret;
116 
117         p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
118                  dentry, dentry, from_kuid(&init_user_ns, uid),
119                  any);
120         ret = NULL;
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;
124 
125                 spin_lock(&dentry->d_lock);
126                 hlist_for_each_entry(fid, h, dlist) {
127                         if (any || uid_eq(fid->uid, uid)) {
128                                 ret = fid;
129                                 p9_fid_get(ret);
130                                 break;
131                         }
132                 }
133                 spin_unlock(&dentry->d_lock);
134         } else {
135                 if (dentry->d_inode)
136                         ret = v9fs_fid_find_inode(dentry->d_inode, false, uid, any);
137         }
138 
139         return ret;
140 }
141 
142 /*
143  * We need to hold v9ses->rename_sem as long as we hold references
144  * to returned path array. Array element contain pointers to
145  * dentry names.
146  */
147 static int build_path_from_dentry(struct v9fs_session_info *v9ses,
148                                   struct dentry *dentry, const unsigned char ***names)
149 {
150         int n = 0, i;
151         const unsigned char **wnames;
152         struct dentry *ds;
153 
154         for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
155                 n++;
156 
157         wnames = kmalloc_array(n, sizeof(char *), GFP_KERNEL);
158         if (!wnames)
159                 goto err_out;
160 
161         for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
162                 wnames[i] = ds->d_name.name;
163 
164         *names = wnames;
165         return n;
166 err_out:
167         return -ENOMEM;
168 }
169 
170 static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
171                                                kuid_t uid, int any)
172 {
173         struct dentry *ds;
174         const unsigned char **wnames, *uname;
175         int i, n, l, access;
176         struct v9fs_session_info *v9ses;
177         struct p9_fid *fid, *root_fid, *old_fid;
178 
179         v9ses = v9fs_dentry2v9ses(dentry);
180         access = v9ses->flags & V9FS_ACCESS_MASK;
181         fid = v9fs_fid_find(dentry, uid, any);
182         if (fid)
183                 return fid;
184         /*
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.
188          */
189         down_read(&v9ses->rename_sem);
190         ds = dentry->d_parent;
191         fid = v9fs_fid_find(ds, uid, any);
192         if (fid) {
193                 /* Found the parent fid do a lookup with that */
194                 old_fid = fid;
195 
196                 fid = p9_client_walk(old_fid, 1, &dentry->d_name.name, 1);
197                 p9_fid_put(old_fid);
198                 goto fid_out;
199         }
200         up_read(&v9ses->rename_sem);
201 
202         /* start from the root and try to do a lookup */
203         root_fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
204         if (!root_fid) {
205                 /* the user is not attached to the fs yet */
206                 if (access == V9FS_ACCESS_SINGLE)
207                         return ERR_PTR(-EPERM);
208 
209                 if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
210                         uname = NULL;
211                 else
212                         uname = v9ses->uname;
213 
214                 fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
215                                        v9ses->aname);
216                 if (IS_ERR(fid))
217                         return fid;
218 
219                 root_fid = p9_fid_get(fid);
220                 v9fs_fid_add(dentry->d_sb->s_root, &fid);
221         }
222         /* If we are root ourself just return that */
223         if (dentry->d_sb->s_root == dentry)
224                 return root_fid;
225 
226         /*
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
230          */
231         down_read(&v9ses->rename_sem);
232         n  = build_path_from_dentry(v9ses, dentry, &wnames);
233         if (n < 0) {
234                 fid = ERR_PTR(n);
235                 goto err_out;
236         }
237         fid = root_fid;
238         old_fid = root_fid;
239         i = 0;
240         while (i < n) {
241                 l = min(n - i, P9_MAXWELEM);
242                 /*
243                  * We need to hold rename lock when doing a multipath
244                  * walk to ensure none of the path components change
245                  */
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) {
250                         p9_fid_put(old_fid);
251                         old_fid = fid;
252                 }
253                 if (IS_ERR(fid)) {
254                         kfree(wnames);
255                         goto err_out;
256                 }
257                 i += l;
258         }
259         kfree(wnames);
260 fid_out:
261         if (!IS_ERR(fid)) {
262                 spin_lock(&dentry->d_lock);
263                 if (d_unhashed(dentry)) {
264                         spin_unlock(&dentry->d_lock);
265                         p9_fid_put(fid);
266                         fid = ERR_PTR(-ENOENT);
267                 } else {
268                         __add_fid(dentry, fid);
269                         p9_fid_get(fid);
270                         spin_unlock(&dentry->d_lock);
271                 }
272         }
273 err_out:
274         up_read(&v9ses->rename_sem);
275         return fid;
276 }
277 
278 /**
279  * v9fs_fid_lookup - lookup for a fid, try to walk if not found
280  * @dentry: dentry to look for fid in
281  *
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.
286  */
287 
288 struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
289 {
290         kuid_t uid;
291         int  any, access;
292         struct v9fs_session_info *v9ses;
293 
294         v9ses = v9fs_dentry2v9ses(dentry);
295         access = v9ses->flags & V9FS_ACCESS_MASK;
296         switch (access) {
297         case V9FS_ACCESS_SINGLE:
298         case V9FS_ACCESS_USER:
299         case V9FS_ACCESS_CLIENT:
300                 uid = current_fsuid();
301                 any = 0;
302                 break;
303 
304         case V9FS_ACCESS_ANY:
305                 uid = v9ses->uid;
306                 any = 1;
307                 break;
308 
309         default:
310                 uid = INVALID_UID;
311                 any = 0;
312                 break;
313         }
314         return v9fs_fid_lookup_with_uid(dentry, uid, any);
315 }
316 
317 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php