diff options
Diffstat (limited to 'fs/9p/fid.c')
-rw-r--r-- | fs/9p/fid.c | 111 |
1 files changed, 77 insertions, 34 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 7317b39b2815..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 | */ | ||
105 | static 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; | ||
124 | err_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,49 +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; |
182 | } | ||
183 | up_read(&v9ses->rename_sem); | ||
149 | 184 | ||
150 | fid = v9fs_fid_find(ds, uid, any); | 185 | /* start from the root and try to do a lookup */ |
151 | if (!fid) { /* the user is not attached to the fs yet */ | 186 | fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any); |
152 | if (access == V9FS_ACCESS_SINGLE) | 187 | if (!fid) { |
153 | return ERR_PTR(-EPERM); | 188 | /* the user is not attached to the fs yet */ |
189 | if (access == V9FS_ACCESS_SINGLE) | ||
190 | return ERR_PTR(-EPERM); | ||
154 | 191 | ||
155 | if (v9fs_proto_dotu(v9ses)) | 192 | if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) |
156 | uname = NULL; | 193 | uname = NULL; |
157 | else | 194 | else |
158 | uname = v9ses->uname; | 195 | uname = v9ses->uname; |
159 | 196 | ||
160 | fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, | 197 | fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, |
161 | v9ses->aname); | 198 | v9ses->aname); |
162 | 199 | if (IS_ERR(fid)) | |
163 | if (IS_ERR(fid)) | 200 | return fid; |
164 | return fid; | ||
165 | |||
166 | v9fs_fid_add(ds, fid); | ||
167 | } | ||
168 | } else /* walk from the parent */ | ||
169 | n = 1; | ||
170 | 201 | ||
171 | 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) | ||
172 | return fid; | 206 | return fid; |
173 | 207 | /* | |
174 | wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); | 208 | * Do a multipath walk with attached root. |
175 | if (!wnames) | 209 | * When walking parent we need to make sure we |
176 | return ERR_PTR(-ENOMEM); | 210 | * don't have a parallel rename happening |
177 | 211 | */ | |
178 | for (d = dentry, i = (n-1); i >= 0; i--, d = d->d_parent) | 212 | down_read(&v9ses->rename_sem); |
179 | wnames[i] = (char *) d->d_name.name; | 213 | n = build_path_from_dentry(v9ses, dentry, &wnames); |
180 | 214 | if (n < 0) { | |
215 | fid = ERR_PTR(n); | ||
216 | goto err_out; | ||
217 | } | ||
181 | clone = 1; | 218 | clone = 1; |
182 | i = 0; | 219 | i = 0; |
183 | while (i < n) { | 220 | while (i < n) { |
184 | 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 | */ | ||
185 | fid = p9_client_walk(fid, l, &wnames[i], clone); | 226 | fid = p9_client_walk(fid, l, &wnames[i], clone); |
186 | if (IS_ERR(fid)) { | 227 | if (IS_ERR(fid)) { |
187 | if (old_fid) { | 228 | if (old_fid) { |
@@ -193,15 +234,17 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
193 | p9_client_clunk(old_fid); | 234 | p9_client_clunk(old_fid); |
194 | } | 235 | } |
195 | kfree(wnames); | 236 | kfree(wnames); |
196 | return fid; | 237 | goto err_out; |
197 | } | 238 | } |
198 | old_fid = fid; | 239 | old_fid = fid; |
199 | i += l; | 240 | i += l; |
200 | clone = 0; | 241 | clone = 0; |
201 | } | 242 | } |
202 | |||
203 | kfree(wnames); | 243 | kfree(wnames); |
244 | fid_out: | ||
204 | v9fs_fid_add(dentry, fid); | 245 | v9fs_fid_add(dentry, fid); |
246 | err_out: | ||
247 | up_read(&v9ses->rename_sem); | ||
205 | return fid; | 248 | return fid; |
206 | } | 249 | } |
207 | 250 | ||