diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 13 | ||||
-rw-r--r-- | fs/nfs/getroot.c | 36 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 20 |
3 files changed, 65 insertions, 4 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2c3eb33b904d..abdf38d5971d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1169,11 +1169,23 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) | |||
1169 | iput(inode); | 1169 | iput(inode); |
1170 | } | 1170 | } |
1171 | 1171 | ||
1172 | static void nfs_d_release(struct dentry *dentry) | ||
1173 | { | ||
1174 | /* free cached devname value, if it survived that far */ | ||
1175 | if (unlikely(dentry->d_fsdata)) { | ||
1176 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1177 | WARN_ON(1); | ||
1178 | else | ||
1179 | kfree(dentry->d_fsdata); | ||
1180 | } | ||
1181 | } | ||
1182 | |||
1172 | const struct dentry_operations nfs_dentry_operations = { | 1183 | const struct dentry_operations nfs_dentry_operations = { |
1173 | .d_revalidate = nfs_lookup_revalidate, | 1184 | .d_revalidate = nfs_lookup_revalidate, |
1174 | .d_delete = nfs_dentry_delete, | 1185 | .d_delete = nfs_dentry_delete, |
1175 | .d_iput = nfs_dentry_iput, | 1186 | .d_iput = nfs_dentry_iput, |
1176 | .d_automount = nfs_d_automount, | 1187 | .d_automount = nfs_d_automount, |
1188 | .d_release = nfs_d_release, | ||
1177 | }; | 1189 | }; |
1178 | 1190 | ||
1179 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) | 1191 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) |
@@ -1248,6 +1260,7 @@ const struct dentry_operations nfs4_dentry_operations = { | |||
1248 | .d_delete = nfs_dentry_delete, | 1260 | .d_delete = nfs_dentry_delete, |
1249 | .d_iput = nfs_dentry_iput, | 1261 | .d_iput = nfs_dentry_iput, |
1250 | .d_automount = nfs_d_automount, | 1262 | .d_automount = nfs_d_automount, |
1263 | .d_release = nfs_d_release, | ||
1251 | }; | 1264 | }; |
1252 | 1265 | ||
1253 | /* | 1266 | /* |
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index 4d6e5a317e6d..1084792bc0fe 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c | |||
@@ -82,12 +82,18 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, | |||
82 | struct nfs_fsinfo fsinfo; | 82 | struct nfs_fsinfo fsinfo; |
83 | struct dentry *ret; | 83 | struct dentry *ret; |
84 | struct inode *inode; | 84 | struct inode *inode; |
85 | void *name = kstrdup(devname, GFP_KERNEL); | ||
85 | int error; | 86 | int error; |
86 | 87 | ||
88 | if (!name) | ||
89 | return ERR_PTR(-ENOMEM); | ||
90 | |||
87 | /* get the actual root for this mount */ | 91 | /* get the actual root for this mount */ |
88 | fsinfo.fattr = nfs_alloc_fattr(); | 92 | fsinfo.fattr = nfs_alloc_fattr(); |
89 | if (fsinfo.fattr == NULL) | 93 | if (fsinfo.fattr == NULL) { |
94 | kfree(name); | ||
90 | return ERR_PTR(-ENOMEM); | 95 | return ERR_PTR(-ENOMEM); |
96 | } | ||
91 | 97 | ||
92 | error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); | 98 | error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); |
93 | if (error < 0) { | 99 | if (error < 0) { |
@@ -120,7 +126,15 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, | |||
120 | } | 126 | } |
121 | 127 | ||
122 | security_d_instantiate(ret, inode); | 128 | security_d_instantiate(ret, inode); |
129 | spin_lock(&ret->d_lock); | ||
130 | if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) { | ||
131 | ret->d_fsdata = name; | ||
132 | name = NULL; | ||
133 | } | ||
134 | spin_unlock(&ret->d_lock); | ||
123 | out: | 135 | out: |
136 | if (name) | ||
137 | kfree(name); | ||
124 | nfs_free_fattr(fsinfo.fattr); | 138 | nfs_free_fattr(fsinfo.fattr); |
125 | return ret; | 139 | return ret; |
126 | } | 140 | } |
@@ -177,21 +191,28 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh, | |||
177 | struct nfs_fattr *fattr = NULL; | 191 | struct nfs_fattr *fattr = NULL; |
178 | struct dentry *ret; | 192 | struct dentry *ret; |
179 | struct inode *inode; | 193 | struct inode *inode; |
194 | void *name = kstrdup(devname, GFP_KERNEL); | ||
180 | int error; | 195 | int error; |
181 | 196 | ||
182 | dprintk("--> nfs4_get_root()\n"); | 197 | dprintk("--> nfs4_get_root()\n"); |
183 | 198 | ||
199 | if (!name) | ||
200 | return ERR_PTR(-ENOMEM); | ||
201 | |||
184 | /* get the info about the server and filesystem */ | 202 | /* get the info about the server and filesystem */ |
185 | error = nfs4_server_capabilities(server, mntfh); | 203 | error = nfs4_server_capabilities(server, mntfh); |
186 | if (error < 0) { | 204 | if (error < 0) { |
187 | dprintk("nfs_get_root: getcaps error = %d\n", | 205 | dprintk("nfs_get_root: getcaps error = %d\n", |
188 | -error); | 206 | -error); |
207 | kfree(name); | ||
189 | return ERR_PTR(error); | 208 | return ERR_PTR(error); |
190 | } | 209 | } |
191 | 210 | ||
192 | fattr = nfs_alloc_fattr(); | 211 | fattr = nfs_alloc_fattr(); |
193 | if (fattr == NULL) | 212 | if (fattr == NULL) { |
194 | return ERR_PTR(-ENOMEM);; | 213 | kfree(name); |
214 | return ERR_PTR(-ENOMEM); | ||
215 | } | ||
195 | 216 | ||
196 | /* get the actual root for this mount */ | 217 | /* get the actual root for this mount */ |
197 | error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); | 218 | error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); |
@@ -225,8 +246,15 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh, | |||
225 | } | 246 | } |
226 | 247 | ||
227 | security_d_instantiate(ret, inode); | 248 | security_d_instantiate(ret, inode); |
228 | 249 | spin_lock(&ret->d_lock); | |
250 | if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) { | ||
251 | ret->d_fsdata = name; | ||
252 | name = NULL; | ||
253 | } | ||
254 | spin_unlock(&ret->d_lock); | ||
229 | out: | 255 | out: |
256 | if (name) | ||
257 | kfree(name); | ||
230 | nfs_free_fattr(fattr); | 258 | nfs_free_fattr(fattr); |
231 | dprintk("<-- nfs4_get_root()\n"); | 259 | dprintk("<-- nfs4_get_root()\n"); |
232 | return ret; | 260 | return ret; |
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 6481d537d69d..8d6864c2a5fa 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
@@ -148,6 +148,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
148 | alias = d_lookup(parent, &data->args.name); | 148 | alias = d_lookup(parent, &data->args.name); |
149 | if (alias != NULL) { | 149 | if (alias != NULL) { |
150 | int ret = 0; | 150 | int ret = 0; |
151 | void *devname_garbage = NULL; | ||
151 | 152 | ||
152 | /* | 153 | /* |
153 | * Hey, we raced with lookup... See if we need to transfer | 154 | * Hey, we raced with lookup... See if we need to transfer |
@@ -157,6 +158,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
157 | spin_lock(&alias->d_lock); | 158 | spin_lock(&alias->d_lock); |
158 | if (alias->d_inode != NULL && | 159 | if (alias->d_inode != NULL && |
159 | !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { | 160 | !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { |
161 | devname_garbage = alias->d_fsdata; | ||
160 | alias->d_fsdata = data; | 162 | alias->d_fsdata = data; |
161 | alias->d_flags |= DCACHE_NFSFS_RENAMED; | 163 | alias->d_flags |= DCACHE_NFSFS_RENAMED; |
162 | ret = 1; | 164 | ret = 1; |
@@ -164,6 +166,13 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n | |||
164 | spin_unlock(&alias->d_lock); | 166 | spin_unlock(&alias->d_lock); |
165 | nfs_dec_sillycount(dir); | 167 | nfs_dec_sillycount(dir); |
166 | dput(alias); | 168 | dput(alias); |
169 | /* | ||
170 | * If we'd displaced old cached devname, free it. At that | ||
171 | * point dentry is definitely not a root, so we won't need | ||
172 | * that anymore. | ||
173 | */ | ||
174 | if (devname_garbage) | ||
175 | kfree(devname_garbage); | ||
167 | return ret; | 176 | return ret; |
168 | } | 177 | } |
169 | data->dir = igrab(dir); | 178 | data->dir = igrab(dir); |
@@ -252,6 +261,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) | |||
252 | { | 261 | { |
253 | struct nfs_unlinkdata *data; | 262 | struct nfs_unlinkdata *data; |
254 | int status = -ENOMEM; | 263 | int status = -ENOMEM; |
264 | void *devname_garbage = NULL; | ||
255 | 265 | ||
256 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 266 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
257 | if (data == NULL) | 267 | if (data == NULL) |
@@ -269,8 +279,16 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) | |||
269 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | 279 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) |
270 | goto out_unlock; | 280 | goto out_unlock; |
271 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | 281 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; |
282 | devname_garbage = dentry->d_fsdata; | ||
272 | dentry->d_fsdata = data; | 283 | dentry->d_fsdata = data; |
273 | spin_unlock(&dentry->d_lock); | 284 | spin_unlock(&dentry->d_lock); |
285 | /* | ||
286 | * If we'd displaced old cached devname, free it. At that | ||
287 | * point dentry is definitely not a root, so we won't need | ||
288 | * that anymore. | ||
289 | */ | ||
290 | if (devname_garbage) | ||
291 | kfree(devname_garbage); | ||
274 | return 0; | 292 | return 0; |
275 | out_unlock: | 293 | out_unlock: |
276 | spin_unlock(&dentry->d_lock); | 294 | spin_unlock(&dentry->d_lock); |
@@ -299,6 +317,7 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode) | |||
299 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { | 317 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { |
300 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | 318 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; |
301 | data = dentry->d_fsdata; | 319 | data = dentry->d_fsdata; |
320 | dentry->d_fsdata = NULL; | ||
302 | } | 321 | } |
303 | spin_unlock(&dentry->d_lock); | 322 | spin_unlock(&dentry->d_lock); |
304 | 323 | ||
@@ -315,6 +334,7 @@ nfs_cancel_async_unlink(struct dentry *dentry) | |||
315 | struct nfs_unlinkdata *data = dentry->d_fsdata; | 334 | struct nfs_unlinkdata *data = dentry->d_fsdata; |
316 | 335 | ||
317 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | 336 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; |
337 | dentry->d_fsdata = NULL; | ||
318 | spin_unlock(&dentry->d_lock); | 338 | spin_unlock(&dentry->d_lock); |
319 | nfs_free_unlinkdata(data); | 339 | nfs_free_unlinkdata(data); |
320 | return; | 340 | return; |