4 * Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
5 * Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to:
18 * Free Software Foundation
19 * 51 Franklin Street, Fifth Floor
20 * Boston, MA 02111-1301 USA
24 #include <linux/module.h>
25 #include <linux/errno.h>
27 #include <linux/slab.h>
28 #include <linux/sched.h>
29 #include <linux/idr.h>
30 #include <net/9p/9p.h>
31 #include <net/9p/client.h>
38 * v9fs_fid_add - add a fid to a dentry
39 * @dentry: dentry that the fid is being added to
44 static inline void __add_fid(struct dentry
*dentry
, struct p9_fid
*fid
)
46 hlist_add_head(&fid
->dlist
, (struct hlist_head
*)&dentry
->d_fsdata
);
49 void v9fs_fid_add(struct dentry
*dentry
, struct p9_fid
*fid
)
51 spin_lock(&dentry
->d_lock
);
52 __add_fid(dentry
, fid
);
53 spin_unlock(&dentry
->d_lock
);
57 * v9fs_fid_find - retrieve a fid that belongs to the specified uid
58 * @dentry: dentry to look for fid in
59 * @uid: return fid that belongs to the specified user
60 * @any: if non-zero, return any fid associated with the dentry
64 static struct p9_fid
*v9fs_fid_find(struct dentry
*dentry
, kuid_t uid
, int any
)
66 struct p9_fid
*fid
, *ret
;
68 p9_debug(P9_DEBUG_VFS
, " dentry: %pd (%p) uid %d any %d\n",
69 dentry
, dentry
, from_kuid(&init_user_ns
, uid
),
72 /* we'll recheck under lock if there's anything to look in */
73 if (dentry
->d_fsdata
) {
74 struct hlist_head
*h
= (struct hlist_head
*)&dentry
->d_fsdata
;
75 spin_lock(&dentry
->d_lock
);
76 hlist_for_each_entry(fid
, h
, dlist
) {
77 if (any
|| uid_eq(fid
->uid
, uid
)) {
82 spin_unlock(&dentry
->d_lock
);
89 * We need to hold v9ses->rename_sem as long as we hold references
90 * to returned path array. Array element contain pointers to
93 static int build_path_from_dentry(struct v9fs_session_info
*v9ses
,
94 struct dentry
*dentry
, const unsigned char ***names
)
97 const unsigned char **wnames
;
100 for (ds
= dentry
; !IS_ROOT(ds
); ds
= ds
->d_parent
)
103 wnames
= kmalloc_array(n
, sizeof(char *), GFP_KERNEL
);
107 for (ds
= dentry
, i
= (n
-1); i
>= 0; i
--, ds
= ds
->d_parent
)
108 wnames
[i
] = ds
->d_name
.name
;
116 static struct p9_fid
*v9fs_fid_lookup_with_uid(struct dentry
*dentry
,
120 const unsigned char **wnames
, *uname
;
121 int i
, n
, l
, clone
, access
;
122 struct v9fs_session_info
*v9ses
;
123 struct p9_fid
*fid
, *old_fid
= NULL
;
125 v9ses
= v9fs_dentry2v9ses(dentry
);
126 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
127 fid
= v9fs_fid_find(dentry
, uid
, any
);
131 * we don't have a matching fid. To do a TWALK we need
132 * parent fid. We need to prevent rename when we want to
133 * look at the parent.
135 down_read(&v9ses
->rename_sem
);
136 ds
= dentry
->d_parent
;
137 fid
= v9fs_fid_find(ds
, uid
, any
);
139 /* Found the parent fid do a lookup with that */
140 fid
= p9_client_walk(fid
, 1, &dentry
->d_name
.name
, 1);
143 up_read(&v9ses
->rename_sem
);
145 /* start from the root and try to do a lookup */
146 fid
= v9fs_fid_find(dentry
->d_sb
->s_root
, uid
, any
);
148 /* the user is not attached to the fs yet */
149 if (access
== V9FS_ACCESS_SINGLE
)
150 return ERR_PTR(-EPERM
);
152 if (v9fs_proto_dotu(v9ses
) || v9fs_proto_dotl(v9ses
))
155 uname
= v9ses
->uname
;
157 fid
= p9_client_attach(v9ses
->clnt
, NULL
, uname
, uid
,
162 v9fs_fid_add(dentry
->d_sb
->s_root
, fid
);
164 /* If we are root ourself just return that */
165 if (dentry
->d_sb
->s_root
== dentry
)
168 * Do a multipath walk with attached root.
169 * When walking parent we need to make sure we
170 * don't have a parallel rename happening
172 down_read(&v9ses
->rename_sem
);
173 n
= build_path_from_dentry(v9ses
, dentry
, &wnames
);
181 l
= min(n
- i
, P9_MAXWELEM
);
183 * We need to hold rename lock when doing a multipath
184 * walk to ensure none of the patch component change
186 fid
= p9_client_walk(fid
, l
, &wnames
[i
], clone
);
190 * If we fail, clunk fid which are mapping
191 * to path component and not the last component
194 p9_client_clunk(old_fid
);
206 spin_lock(&dentry
->d_lock
);
207 if (d_unhashed(dentry
)) {
208 spin_unlock(&dentry
->d_lock
);
209 p9_client_clunk(fid
);
210 fid
= ERR_PTR(-ENOENT
);
212 __add_fid(dentry
, fid
);
213 spin_unlock(&dentry
->d_lock
);
217 up_read(&v9ses
->rename_sem
);
222 * v9fs_fid_lookup - lookup for a fid, try to walk if not found
223 * @dentry: dentry to look for fid in
225 * Look for a fid in the specified dentry for the current user.
226 * If no fid is found, try to create one walking from a fid from the parent
227 * dentry (if it has one), or the root dentry. If the user haven't accessed
228 * the fs yet, attach now and walk from the root.
231 struct p9_fid
*v9fs_fid_lookup(struct dentry
*dentry
)
235 struct v9fs_session_info
*v9ses
;
237 v9ses
= v9fs_dentry2v9ses(dentry
);
238 access
= v9ses
->flags
& V9FS_ACCESS_MASK
;
240 case V9FS_ACCESS_SINGLE
:
241 case V9FS_ACCESS_USER
:
242 case V9FS_ACCESS_CLIENT
:
243 uid
= current_fsuid();
247 case V9FS_ACCESS_ANY
:
257 return v9fs_fid_lookup_with_uid(dentry
, uid
, any
);
260 struct p9_fid
*v9fs_writeback_fid(struct dentry
*dentry
)
265 fid
= clone_fid(v9fs_fid_lookup_with_uid(dentry
, GLOBAL_ROOT_UID
, 0));
269 * writeback fid will only be used to write back the
270 * dirty pages. We always request for the open fid in read-write
271 * mode so that a partial page write which result in page
274 err
= p9_client_open(fid
, O_RDWR
);
276 p9_client_clunk(fid
);