0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/errno.h>
0011 #include <linux/fs.h>
0012 #include <linux/slab.h>
0013 #include <linux/sched.h>
0014 #include <linux/idr.h>
0015 #include <net/9p/9p.h>
0016 #include <net/9p/client.h>
0017
0018 #include "v9fs.h"
0019 #include "v9fs_vfs.h"
0020 #include "fid.h"
0021
0022 static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
0023 {
0024 hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
0025 }
0026
0027
0028
0029
0030
0031
0032
0033
0034 void v9fs_fid_add(struct dentry *dentry, struct p9_fid **pfid)
0035 {
0036 struct p9_fid *fid = *pfid;
0037
0038 spin_lock(&dentry->d_lock);
0039 __add_fid(dentry, fid);
0040 spin_unlock(&dentry->d_lock);
0041
0042 *pfid = NULL;
0043 }
0044
0045
0046
0047
0048
0049
0050
0051
0052 static struct p9_fid *v9fs_fid_find_inode(struct inode *inode, kuid_t uid)
0053 {
0054 struct hlist_head *h;
0055 struct p9_fid *fid, *ret = NULL;
0056
0057 p9_debug(P9_DEBUG_VFS, " inode: %p\n", inode);
0058
0059 spin_lock(&inode->i_lock);
0060 h = (struct hlist_head *)&inode->i_private;
0061 hlist_for_each_entry(fid, h, ilist) {
0062 if (uid_eq(fid->uid, uid)) {
0063 p9_fid_get(fid);
0064 ret = fid;
0065 break;
0066 }
0067 }
0068 spin_unlock(&inode->i_lock);
0069 return ret;
0070 }
0071
0072
0073
0074
0075
0076
0077
0078
0079 void v9fs_open_fid_add(struct inode *inode, struct p9_fid **pfid)
0080 {
0081 struct p9_fid *fid = *pfid;
0082
0083 spin_lock(&inode->i_lock);
0084 hlist_add_head(&fid->ilist, (struct hlist_head *)&inode->i_private);
0085 spin_unlock(&inode->i_lock);
0086
0087 *pfid = NULL;
0088 }
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099 static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
0100 {
0101 struct p9_fid *fid, *ret;
0102
0103 p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
0104 dentry, dentry, from_kuid(&init_user_ns, uid),
0105 any);
0106 ret = NULL;
0107
0108 if (dentry->d_fsdata) {
0109 struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
0110
0111 spin_lock(&dentry->d_lock);
0112 hlist_for_each_entry(fid, h, dlist) {
0113 if (any || uid_eq(fid->uid, uid)) {
0114 ret = fid;
0115 p9_fid_get(ret);
0116 break;
0117 }
0118 }
0119 spin_unlock(&dentry->d_lock);
0120 } else {
0121 if (dentry->d_inode)
0122 ret = v9fs_fid_find_inode(dentry->d_inode, uid);
0123 }
0124
0125 return ret;
0126 }
0127
0128
0129
0130
0131
0132
0133 static int build_path_from_dentry(struct v9fs_session_info *v9ses,
0134 struct dentry *dentry, const unsigned char ***names)
0135 {
0136 int n = 0, i;
0137 const unsigned char **wnames;
0138 struct dentry *ds;
0139
0140 for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
0141 n++;
0142
0143 wnames = kmalloc_array(n, sizeof(char *), GFP_KERNEL);
0144 if (!wnames)
0145 goto err_out;
0146
0147 for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
0148 wnames[i] = ds->d_name.name;
0149
0150 *names = wnames;
0151 return n;
0152 err_out:
0153 return -ENOMEM;
0154 }
0155
0156 static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
0157 kuid_t uid, int any)
0158 {
0159 struct dentry *ds;
0160 const unsigned char **wnames, *uname;
0161 int i, n, l, access;
0162 struct v9fs_session_info *v9ses;
0163 struct p9_fid *fid, *root_fid, *old_fid;
0164
0165 v9ses = v9fs_dentry2v9ses(dentry);
0166 access = v9ses->flags & V9FS_ACCESS_MASK;
0167 fid = v9fs_fid_find(dentry, uid, any);
0168 if (fid)
0169 return fid;
0170
0171
0172
0173
0174
0175 down_read(&v9ses->rename_sem);
0176 ds = dentry->d_parent;
0177 fid = v9fs_fid_find(ds, uid, any);
0178 if (fid) {
0179
0180 old_fid = fid;
0181
0182 fid = p9_client_walk(old_fid, 1, &dentry->d_name.name, 1);
0183 p9_fid_put(old_fid);
0184 goto fid_out;
0185 }
0186 up_read(&v9ses->rename_sem);
0187
0188
0189 root_fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
0190 if (!root_fid) {
0191
0192 if (access == V9FS_ACCESS_SINGLE)
0193 return ERR_PTR(-EPERM);
0194
0195 if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
0196 uname = NULL;
0197 else
0198 uname = v9ses->uname;
0199
0200 fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
0201 v9ses->aname);
0202 if (IS_ERR(fid))
0203 return fid;
0204
0205 root_fid = p9_fid_get(fid);
0206 v9fs_fid_add(dentry->d_sb->s_root, &fid);
0207 }
0208
0209 if (dentry->d_sb->s_root == dentry)
0210 return root_fid;
0211
0212
0213
0214
0215
0216
0217 down_read(&v9ses->rename_sem);
0218 n = build_path_from_dentry(v9ses, dentry, &wnames);
0219 if (n < 0) {
0220 fid = ERR_PTR(n);
0221 goto err_out;
0222 }
0223 fid = root_fid;
0224 old_fid = root_fid;
0225 i = 0;
0226 while (i < n) {
0227 l = min(n - i, P9_MAXWELEM);
0228
0229
0230
0231
0232 fid = p9_client_walk(old_fid, l, &wnames[i],
0233 old_fid == root_fid );
0234
0235 if (fid != old_fid) {
0236 p9_fid_put(old_fid);
0237 old_fid = fid;
0238 }
0239 if (IS_ERR(fid)) {
0240 kfree(wnames);
0241 goto err_out;
0242 }
0243 i += l;
0244 }
0245 kfree(wnames);
0246 fid_out:
0247 if (!IS_ERR(fid)) {
0248 spin_lock(&dentry->d_lock);
0249 if (d_unhashed(dentry)) {
0250 spin_unlock(&dentry->d_lock);
0251 p9_fid_put(fid);
0252 fid = ERR_PTR(-ENOENT);
0253 } else {
0254 __add_fid(dentry, fid);
0255 p9_fid_get(fid);
0256 spin_unlock(&dentry->d_lock);
0257 }
0258 }
0259 err_out:
0260 up_read(&v9ses->rename_sem);
0261 return fid;
0262 }
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274 struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
0275 {
0276 kuid_t uid;
0277 int any, access;
0278 struct v9fs_session_info *v9ses;
0279
0280 v9ses = v9fs_dentry2v9ses(dentry);
0281 access = v9ses->flags & V9FS_ACCESS_MASK;
0282 switch (access) {
0283 case V9FS_ACCESS_SINGLE:
0284 case V9FS_ACCESS_USER:
0285 case V9FS_ACCESS_CLIENT:
0286 uid = current_fsuid();
0287 any = 0;
0288 break;
0289
0290 case V9FS_ACCESS_ANY:
0291 uid = v9ses->uid;
0292 any = 1;
0293 break;
0294
0295 default:
0296 uid = INVALID_UID;
0297 any = 0;
0298 break;
0299 }
0300 return v9fs_fid_lookup_with_uid(dentry, uid, any);
0301 }
0302
0303 struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
0304 {
0305 int err;
0306 struct p9_fid *fid, *ofid;
0307
0308 ofid = v9fs_fid_lookup_with_uid(dentry, GLOBAL_ROOT_UID, 0);
0309 fid = clone_fid(ofid);
0310 if (IS_ERR(fid))
0311 goto error_out;
0312 p9_fid_put(ofid);
0313
0314
0315
0316
0317
0318
0319 err = p9_client_open(fid, O_RDWR);
0320 if (err < 0) {
0321 p9_fid_put(fid);
0322 fid = ERR_PTR(err);
0323 goto error_out;
0324 }
0325 error_out:
0326 return fid;
0327 }