aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2010-06-30 09:48:50 -0400
committerEric Van Hensbergen <ericvh@gmail.com>2010-08-02 15:28:35 -0400
commita534c8d15b1f1d0f861fc2bb9e0529bd8486ec3f (patch)
tree0451da7a61bd7e3eb5968d65beee3243f7f51b5d
parentebf46264a004818fe5b23f0ac18ac7336897d807 (diff)
fs/9p: Prevent parallel rename when doing fid_lookup
During fid lookup we need to make sure that the dentry->d_parent doesn't change so that we can safely walk the parent dentries. To ensure that we need to prevent cross directory rename during fid_lookup. Add a per superblock rename_sem rw_semaphore to prevent parallel fid lookup and rename. Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com> Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
-rw-r--r--fs/9p/fid.c114
-rw-r--r--fs/9p/v9fs.c1
-rw-r--r--fs/9p/v9fs.h1
-rw-r--r--fs/9p/vfs_inode.c13
-rw-r--r--fs/9p/vfs_super.c1
5 files changed, 91 insertions, 39 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c
index 5d6cfcbf73e7..358563689064 100644
--- a/fs/9p/fid.c
+++ b/fs/9p/fid.c
@@ -97,6 +97,34 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any)
97 return ret; 97 return ret;
98} 98}
99 99
100/*
101 * We need to hold v9ses->rename_sem as long as we hold references
102 * to returned path array. Array element contain pointers to
103 * dentry names.
104 */
105static int build_path_from_dentry(struct v9fs_session_info *v9ses,
106 struct dentry *dentry, char ***names)
107{
108 int n = 0, i;
109 char **wnames;
110 struct dentry *ds;
111
112 for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
113 n++;
114
115 wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
116 if (!wnames)
117 goto err_out;
118
119 for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
120 wnames[i] = (char *)ds->d_name.name;
121
122 *names = wnames;
123 return n;
124err_out:
125 return -ENOMEM;
126}
127
100/** 128/**
101 * v9fs_fid_lookup - lookup for a fid, try to walk if not found 129 * v9fs_fid_lookup - lookup for a fid, try to walk if not found
102 * @dentry: dentry to look for fid in 130 * @dentry: dentry to look for fid in
@@ -112,7 +140,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
112 int i, n, l, clone, any, access; 140 int i, n, l, clone, any, access;
113 u32 uid; 141 u32 uid;
114 struct p9_fid *fid, *old_fid = NULL; 142 struct p9_fid *fid, *old_fid = NULL;
115 struct dentry *d, *ds; 143 struct dentry *ds;
116 struct v9fs_session_info *v9ses; 144 struct v9fs_session_info *v9ses;
117 char **wnames, *uname; 145 char **wnames, *uname;
118 146
@@ -139,50 +167,62 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
139 fid = v9fs_fid_find(dentry, uid, any); 167 fid = v9fs_fid_find(dentry, uid, any);
140 if (fid) 168 if (fid)
141 return fid; 169 return fid;
142 170 /*
171 * we don't have a matching fid. To do a TWALK we need
172 * parent fid. We need to prevent rename when we want to
173 * look at the parent.
174 */
175 down_read(&v9ses->rename_sem);
143 ds = dentry->d_parent; 176 ds = dentry->d_parent;
144 fid = v9fs_fid_find(ds, uid, any); 177 fid = v9fs_fid_find(ds, uid, any);
145 if (!fid) { /* walk from the root */ 178 if (fid) {
146 n = 0; 179 /* Found the parent fid do a lookup with that */
147 for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) 180 fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
148 n++; 181 goto fid_out;
149 182 }
150 fid = v9fs_fid_find(ds, uid, any); 183 up_read(&v9ses->rename_sem);
151 if (!fid) { /* the user is not attached to the fs yet */
152 if (access == V9FS_ACCESS_SINGLE)
153 return ERR_PTR(-EPERM);
154
155 if (v9fs_proto_dotu(v9ses) ||
156 v9fs_proto_dotl(v9ses))
157 uname = NULL;
158 else
159 uname = v9ses->uname;
160 184
161 fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, 185 /* start from the root and try to do a lookup */
162 v9ses->aname); 186 fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
187 if (!fid) {
188 /* the user is not attached to the fs yet */
189 if (access == V9FS_ACCESS_SINGLE)
190 return ERR_PTR(-EPERM);
163 191
164 if (IS_ERR(fid)) 192 if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
165 return fid; 193 uname = NULL;
194 else
195 uname = v9ses->uname;
166 196
167 v9fs_fid_add(ds, fid); 197 fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
168 } 198 v9ses->aname);
169 } else /* walk from the parent */ 199 if (IS_ERR(fid))
170 n = 1; 200 return fid;
171 201
172 if (ds == dentry) 202 v9fs_fid_add(dentry->d_sb->s_root, fid);
203 }
204 /* If we are root ourself just return that */
205 if (dentry->d_sb->s_root == dentry)
173 return fid; 206 return fid;
174 207 /*
175 wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); 208 * Do a multipath walk with attached root.
176 if (!wnames) 209 * When walking parent we need to make sure we
177 return ERR_PTR(-ENOMEM); 210 * don't have a parallel rename happening
178 211 */
179 for (d = dentry, i = (n-1); i >= 0; i--, d = d->d_parent) 212 down_read(&v9ses->rename_sem);
180 wnames[i] = (char *) d->d_name.name; 213 n = build_path_from_dentry(v9ses, dentry, &wnames);
181 214 if (n < 0) {
215 fid = ERR_PTR(n);
216 goto err_out;
217 }
182 clone = 1; 218 clone = 1;
183 i = 0; 219 i = 0;
184 while (i < n) { 220 while (i < n) {
185 l = min(n - i, P9_MAXWELEM); 221 l = min(n - i, P9_MAXWELEM);
222 /*
223 * We need to hold rename lock when doing a multipath
224 * walk to ensure none of the patch component change
225 */
186 fid = p9_client_walk(fid, l, &wnames[i], clone); 226 fid = p9_client_walk(fid, l, &wnames[i], clone);
187 if (IS_ERR(fid)) { 227 if (IS_ERR(fid)) {
188 if (old_fid) { 228 if (old_fid) {
@@ -194,15 +234,17 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
194 p9_client_clunk(old_fid); 234 p9_client_clunk(old_fid);
195 } 235 }
196 kfree(wnames); 236 kfree(wnames);
197 return fid; 237 goto err_out;
198 } 238 }
199 old_fid = fid; 239 old_fid = fid;
200 i += l; 240 i += l;
201 clone = 0; 241 clone = 0;
202 } 242 }
203
204 kfree(wnames); 243 kfree(wnames);
244fid_out:
205 v9fs_fid_add(dentry, fid); 245 v9fs_fid_add(dentry, fid);
246err_out:
247 up_read(&v9ses->rename_sem);
206 return fid; 248 return fid;
207} 249}
208 250
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 3c492011221c..38dc0e067599 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -237,6 +237,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
237 __putname(v9ses->uname); 237 __putname(v9ses->uname);
238 return ERR_PTR(-ENOMEM); 238 return ERR_PTR(-ENOMEM);
239 } 239 }
240 init_rwsem(&v9ses->rename_sem);
240 241
241 rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY); 242 rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY);
242 if (rc) { 243 if (rc) {
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
index bec4d0bcb458..4c963c9fc41f 100644
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -104,6 +104,7 @@ struct v9fs_session_info {
104 struct p9_client *clnt; /* 9p client */ 104 struct p9_client *clnt; /* 9p client */
105 struct list_head slist; /* list of sessions registered with v9fs */ 105 struct list_head slist; /* list of sessions registered with v9fs */
106 struct backing_dev_info bdi; 106 struct backing_dev_info bdi;
107 struct rw_semaphore rename_sem;
107}; 108};
108 109
109struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, 110struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 39352ef954dc..75c261fdc7b4 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -948,6 +948,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
948 948
949 sb = dir->i_sb; 949 sb = dir->i_sb;
950 v9ses = v9fs_inode2v9ses(dir); 950 v9ses = v9fs_inode2v9ses(dir);
951 /* We can walk d_parent because we hold the dir->i_mutex */
951 dfid = v9fs_fid_lookup(dentry->d_parent); 952 dfid = v9fs_fid_lookup(dentry->d_parent);
952 if (IS_ERR(dfid)) 953 if (IS_ERR(dfid))
953 return ERR_CAST(dfid); 954 return ERR_CAST(dfid);
@@ -1055,27 +1056,33 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
1055 goto clunk_olddir; 1056 goto clunk_olddir;
1056 } 1057 }
1057 1058
1059 down_write(&v9ses->rename_sem);
1058 if (v9fs_proto_dotl(v9ses)) { 1060 if (v9fs_proto_dotl(v9ses)) {
1059 retval = p9_client_rename(oldfid, newdirfid, 1061 retval = p9_client_rename(oldfid, newdirfid,
1060 (char *) new_dentry->d_name.name); 1062 (char *) new_dentry->d_name.name);
1061 if (retval != -ENOSYS) 1063 if (retval != -ENOSYS)
1062 goto clunk_newdir; 1064 goto clunk_newdir;
1063 } 1065 }
1066 if (old_dentry->d_parent != new_dentry->d_parent) {
1067 /*
1068 * 9P .u can only handle file rename in the same directory
1069 */
1064 1070
1065 /* 9P can only handle file rename in the same directory */
1066 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
1067 P9_DPRINTK(P9_DEBUG_ERROR, 1071 P9_DPRINTK(P9_DEBUG_ERROR,
1068 "old dir and new dir are different\n"); 1072 "old dir and new dir are different\n");
1069 retval = -EXDEV; 1073 retval = -EXDEV;
1070 goto clunk_newdir; 1074 goto clunk_newdir;
1071 } 1075 }
1072
1073 v9fs_blank_wstat(&wstat); 1076 v9fs_blank_wstat(&wstat);
1074 wstat.muid = v9ses->uname; 1077 wstat.muid = v9ses->uname;
1075 wstat.name = (char *) new_dentry->d_name.name; 1078 wstat.name = (char *) new_dentry->d_name.name;
1076 retval = p9_client_wstat(oldfid, &wstat); 1079 retval = p9_client_wstat(oldfid, &wstat);
1077 1080
1078clunk_newdir: 1081clunk_newdir:
1082 if (!retval)
1083 /* successful rename */
1084 d_move(old_dentry, new_dentry);
1085 up_write(&v9ses->rename_sem);
1079 p9_client_clunk(newdirfid); 1086 p9_client_clunk(newdirfid);
1080 1087
1081clunk_olddir: 1088clunk_olddir:
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index 0a2e2030c844..4b9ede0b41b7 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -287,4 +287,5 @@ struct file_system_type v9fs_fs_type = {
287 .get_sb = v9fs_get_sb, 287 .get_sb = v9fs_get_sb,
288 .kill_sb = v9fs_kill_super, 288 .kill_sb = v9fs_kill_super,
289 .owner = THIS_MODULE, 289 .owner = THIS_MODULE,
290 .fs_flags = FS_RENAME_DOES_D_MOVE,
290}; 291};