diff options
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 102 |
1 files changed, 91 insertions, 11 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2c3eb33b904d..7237672216c8 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -44,6 +44,7 @@ | |||
44 | /* #define NFS_DEBUG_VERBOSE 1 */ | 44 | /* #define NFS_DEBUG_VERBOSE 1 */ |
45 | 45 | ||
46 | static int nfs_opendir(struct inode *, struct file *); | 46 | static int nfs_opendir(struct inode *, struct file *); |
47 | static int nfs_closedir(struct inode *, struct file *); | ||
47 | static int nfs_readdir(struct file *, void *, filldir_t); | 48 | static int nfs_readdir(struct file *, void *, filldir_t); |
48 | static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *); | 49 | static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *); |
49 | static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *); | 50 | static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *); |
@@ -64,7 +65,7 @@ const struct file_operations nfs_dir_operations = { | |||
64 | .read = generic_read_dir, | 65 | .read = generic_read_dir, |
65 | .readdir = nfs_readdir, | 66 | .readdir = nfs_readdir, |
66 | .open = nfs_opendir, | 67 | .open = nfs_opendir, |
67 | .release = nfs_release, | 68 | .release = nfs_closedir, |
68 | .fsync = nfs_fsync_dir, | 69 | .fsync = nfs_fsync_dir, |
69 | }; | 70 | }; |
70 | 71 | ||
@@ -133,13 +134,35 @@ const struct inode_operations nfs4_dir_inode_operations = { | |||
133 | 134 | ||
134 | #endif /* CONFIG_NFS_V4 */ | 135 | #endif /* CONFIG_NFS_V4 */ |
135 | 136 | ||
137 | static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred) | ||
138 | { | ||
139 | struct nfs_open_dir_context *ctx; | ||
140 | ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | ||
141 | if (ctx != NULL) { | ||
142 | ctx->duped = 0; | ||
143 | ctx->dir_cookie = 0; | ||
144 | ctx->dup_cookie = 0; | ||
145 | ctx->cred = get_rpccred(cred); | ||
146 | } else | ||
147 | ctx = ERR_PTR(-ENOMEM); | ||
148 | return ctx; | ||
149 | } | ||
150 | |||
151 | static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) | ||
152 | { | ||
153 | put_rpccred(ctx->cred); | ||
154 | kfree(ctx); | ||
155 | } | ||
156 | |||
136 | /* | 157 | /* |
137 | * Open file | 158 | * Open file |
138 | */ | 159 | */ |
139 | static int | 160 | static int |
140 | nfs_opendir(struct inode *inode, struct file *filp) | 161 | nfs_opendir(struct inode *inode, struct file *filp) |
141 | { | 162 | { |
142 | int res; | 163 | int res = 0; |
164 | struct nfs_open_dir_context *ctx; | ||
165 | struct rpc_cred *cred; | ||
143 | 166 | ||
144 | dfprintk(FILE, "NFS: open dir(%s/%s)\n", | 167 | dfprintk(FILE, "NFS: open dir(%s/%s)\n", |
145 | filp->f_path.dentry->d_parent->d_name.name, | 168 | filp->f_path.dentry->d_parent->d_name.name, |
@@ -147,8 +170,15 @@ nfs_opendir(struct inode *inode, struct file *filp) | |||
147 | 170 | ||
148 | nfs_inc_stats(inode, NFSIOS_VFSOPEN); | 171 | nfs_inc_stats(inode, NFSIOS_VFSOPEN); |
149 | 172 | ||
150 | /* Call generic open code in order to cache credentials */ | 173 | cred = rpc_lookup_cred(); |
151 | res = nfs_open(inode, filp); | 174 | if (IS_ERR(cred)) |
175 | return PTR_ERR(cred); | ||
176 | ctx = alloc_nfs_open_dir_context(cred); | ||
177 | if (IS_ERR(ctx)) { | ||
178 | res = PTR_ERR(ctx); | ||
179 | goto out; | ||
180 | } | ||
181 | filp->private_data = ctx; | ||
152 | if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) { | 182 | if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) { |
153 | /* This is a mountpoint, so d_revalidate will never | 183 | /* This is a mountpoint, so d_revalidate will never |
154 | * have been called, so we need to refresh the | 184 | * have been called, so we need to refresh the |
@@ -156,9 +186,18 @@ nfs_opendir(struct inode *inode, struct file *filp) | |||
156 | */ | 186 | */ |
157 | __nfs_revalidate_inode(NFS_SERVER(inode), inode); | 187 | __nfs_revalidate_inode(NFS_SERVER(inode), inode); |
158 | } | 188 | } |
189 | out: | ||
190 | put_rpccred(cred); | ||
159 | return res; | 191 | return res; |
160 | } | 192 | } |
161 | 193 | ||
194 | static int | ||
195 | nfs_closedir(struct inode *inode, struct file *filp) | ||
196 | { | ||
197 | put_nfs_open_dir_context(filp->private_data); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
162 | struct nfs_cache_array_entry { | 201 | struct nfs_cache_array_entry { |
163 | u64 cookie; | 202 | u64 cookie; |
164 | u64 ino; | 203 | u64 ino; |
@@ -284,19 +323,20 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri | |||
284 | { | 323 | { |
285 | loff_t diff = desc->file->f_pos - desc->current_index; | 324 | loff_t diff = desc->file->f_pos - desc->current_index; |
286 | unsigned int index; | 325 | unsigned int index; |
326 | struct nfs_open_dir_context *ctx = desc->file->private_data; | ||
287 | 327 | ||
288 | if (diff < 0) | 328 | if (diff < 0) |
289 | goto out_eof; | 329 | goto out_eof; |
290 | if (diff >= array->size) { | 330 | if (diff >= array->size) { |
291 | if (array->eof_index >= 0) | 331 | if (array->eof_index >= 0) |
292 | goto out_eof; | 332 | goto out_eof; |
293 | desc->current_index += array->size; | ||
294 | return -EAGAIN; | 333 | return -EAGAIN; |
295 | } | 334 | } |
296 | 335 | ||
297 | index = (unsigned int)diff; | 336 | index = (unsigned int)diff; |
298 | *desc->dir_cookie = array->array[index].cookie; | 337 | *desc->dir_cookie = array->array[index].cookie; |
299 | desc->cache_entry_index = index; | 338 | desc->cache_entry_index = index; |
339 | ctx->duped = 0; | ||
300 | return 0; | 340 | return 0; |
301 | out_eof: | 341 | out_eof: |
302 | desc->eof = 1; | 342 | desc->eof = 1; |
@@ -307,10 +347,18 @@ static | |||
307 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | 347 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) |
308 | { | 348 | { |
309 | int i; | 349 | int i; |
350 | loff_t new_pos; | ||
310 | int status = -EAGAIN; | 351 | int status = -EAGAIN; |
352 | struct nfs_open_dir_context *ctx = desc->file->private_data; | ||
311 | 353 | ||
312 | for (i = 0; i < array->size; i++) { | 354 | for (i = 0; i < array->size; i++) { |
313 | if (array->array[i].cookie == *desc->dir_cookie) { | 355 | if (array->array[i].cookie == *desc->dir_cookie) { |
356 | new_pos = desc->current_index + i; | ||
357 | if (new_pos < desc->file->f_pos) { | ||
358 | ctx->dup_cookie = *desc->dir_cookie; | ||
359 | ctx->duped = 1; | ||
360 | } | ||
361 | desc->file->f_pos = new_pos; | ||
314 | desc->cache_entry_index = i; | 362 | desc->cache_entry_index = i; |
315 | return 0; | 363 | return 0; |
316 | } | 364 | } |
@@ -342,6 +390,7 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) | |||
342 | 390 | ||
343 | if (status == -EAGAIN) { | 391 | if (status == -EAGAIN) { |
344 | desc->last_cookie = array->last_cookie; | 392 | desc->last_cookie = array->last_cookie; |
393 | desc->current_index += array->size; | ||
345 | desc->page_index++; | 394 | desc->page_index++; |
346 | } | 395 | } |
347 | nfs_readdir_release_array(desc->page); | 396 | nfs_readdir_release_array(desc->page); |
@@ -354,7 +403,8 @@ static | |||
354 | int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, | 403 | int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, |
355 | struct nfs_entry *entry, struct file *file, struct inode *inode) | 404 | struct nfs_entry *entry, struct file *file, struct inode *inode) |
356 | { | 405 | { |
357 | struct rpc_cred *cred = nfs_file_cred(file); | 406 | struct nfs_open_dir_context *ctx = file->private_data; |
407 | struct rpc_cred *cred = ctx->cred; | ||
358 | unsigned long timestamp, gencount; | 408 | unsigned long timestamp, gencount; |
359 | int error; | 409 | int error; |
360 | 410 | ||
@@ -693,6 +743,20 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
693 | int i = 0; | 743 | int i = 0; |
694 | int res = 0; | 744 | int res = 0; |
695 | struct nfs_cache_array *array = NULL; | 745 | struct nfs_cache_array *array = NULL; |
746 | struct nfs_open_dir_context *ctx = file->private_data; | ||
747 | |||
748 | if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) { | ||
749 | if (printk_ratelimit()) { | ||
750 | pr_notice("NFS: directory %s/%s contains a readdir loop. " | ||
751 | "Please contact your server vendor. " | ||
752 | "Offending cookie: %llu\n", | ||
753 | file->f_dentry->d_parent->d_name.name, | ||
754 | file->f_dentry->d_name.name, | ||
755 | *desc->dir_cookie); | ||
756 | } | ||
757 | res = -ELOOP; | ||
758 | goto out; | ||
759 | } | ||
696 | 760 | ||
697 | array = nfs_readdir_get_array(desc->page); | 761 | array = nfs_readdir_get_array(desc->page); |
698 | if (IS_ERR(array)) { | 762 | if (IS_ERR(array)) { |
@@ -785,6 +849,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
785 | struct inode *inode = dentry->d_inode; | 849 | struct inode *inode = dentry->d_inode; |
786 | nfs_readdir_descriptor_t my_desc, | 850 | nfs_readdir_descriptor_t my_desc, |
787 | *desc = &my_desc; | 851 | *desc = &my_desc; |
852 | struct nfs_open_dir_context *dir_ctx = filp->private_data; | ||
788 | int res; | 853 | int res; |
789 | 854 | ||
790 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", | 855 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", |
@@ -801,7 +866,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
801 | memset(desc, 0, sizeof(*desc)); | 866 | memset(desc, 0, sizeof(*desc)); |
802 | 867 | ||
803 | desc->file = filp; | 868 | desc->file = filp; |
804 | desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie; | 869 | desc->dir_cookie = &dir_ctx->dir_cookie; |
805 | desc->decode = NFS_PROTO(inode)->decode_dirent; | 870 | desc->decode = NFS_PROTO(inode)->decode_dirent; |
806 | desc->plus = NFS_USE_READDIRPLUS(inode); | 871 | desc->plus = NFS_USE_READDIRPLUS(inode); |
807 | 872 | ||
@@ -853,6 +918,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) | |||
853 | { | 918 | { |
854 | struct dentry *dentry = filp->f_path.dentry; | 919 | struct dentry *dentry = filp->f_path.dentry; |
855 | struct inode *inode = dentry->d_inode; | 920 | struct inode *inode = dentry->d_inode; |
921 | struct nfs_open_dir_context *dir_ctx = filp->private_data; | ||
856 | 922 | ||
857 | dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n", | 923 | dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n", |
858 | dentry->d_parent->d_name.name, | 924 | dentry->d_parent->d_name.name, |
@@ -872,7 +938,8 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) | |||
872 | } | 938 | } |
873 | if (offset != filp->f_pos) { | 939 | if (offset != filp->f_pos) { |
874 | filp->f_pos = offset; | 940 | filp->f_pos = offset; |
875 | nfs_file_open_context(filp)->dir_cookie = 0; | 941 | dir_ctx->dir_cookie = 0; |
942 | dir_ctx->duped = 0; | ||
876 | } | 943 | } |
877 | out: | 944 | out: |
878 | mutex_unlock(&inode->i_mutex); | 945 | mutex_unlock(&inode->i_mutex); |
@@ -1068,7 +1135,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
1068 | if (fhandle == NULL || fattr == NULL) | 1135 | if (fhandle == NULL || fattr == NULL) |
1069 | goto out_error; | 1136 | goto out_error; |
1070 | 1137 | ||
1071 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); | 1138 | error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); |
1072 | if (error) | 1139 | if (error) |
1073 | goto out_bad; | 1140 | goto out_bad; |
1074 | if (nfs_compare_fh(NFS_FH(inode), fhandle)) | 1141 | if (nfs_compare_fh(NFS_FH(inode), fhandle)) |
@@ -1169,11 +1236,23 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) | |||
1169 | iput(inode); | 1236 | iput(inode); |
1170 | } | 1237 | } |
1171 | 1238 | ||
1239 | static void nfs_d_release(struct dentry *dentry) | ||
1240 | { | ||
1241 | /* free cached devname value, if it survived that far */ | ||
1242 | if (unlikely(dentry->d_fsdata)) { | ||
1243 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1244 | WARN_ON(1); | ||
1245 | else | ||
1246 | kfree(dentry->d_fsdata); | ||
1247 | } | ||
1248 | } | ||
1249 | |||
1172 | const struct dentry_operations nfs_dentry_operations = { | 1250 | const struct dentry_operations nfs_dentry_operations = { |
1173 | .d_revalidate = nfs_lookup_revalidate, | 1251 | .d_revalidate = nfs_lookup_revalidate, |
1174 | .d_delete = nfs_dentry_delete, | 1252 | .d_delete = nfs_dentry_delete, |
1175 | .d_iput = nfs_dentry_iput, | 1253 | .d_iput = nfs_dentry_iput, |
1176 | .d_automount = nfs_d_automount, | 1254 | .d_automount = nfs_d_automount, |
1255 | .d_release = nfs_d_release, | ||
1177 | }; | 1256 | }; |
1178 | 1257 | ||
1179 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) | 1258 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) |
@@ -1212,7 +1291,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru | |||
1212 | parent = dentry->d_parent; | 1291 | parent = dentry->d_parent; |
1213 | /* Protect against concurrent sillydeletes */ | 1292 | /* Protect against concurrent sillydeletes */ |
1214 | nfs_block_sillyrename(parent); | 1293 | nfs_block_sillyrename(parent); |
1215 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); | 1294 | error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); |
1216 | if (error == -ENOENT) | 1295 | if (error == -ENOENT) |
1217 | goto no_entry; | 1296 | goto no_entry; |
1218 | if (error < 0) { | 1297 | if (error < 0) { |
@@ -1248,6 +1327,7 @@ const struct dentry_operations nfs4_dentry_operations = { | |||
1248 | .d_delete = nfs_dentry_delete, | 1327 | .d_delete = nfs_dentry_delete, |
1249 | .d_iput = nfs_dentry_iput, | 1328 | .d_iput = nfs_dentry_iput, |
1250 | .d_automount = nfs_d_automount, | 1329 | .d_automount = nfs_d_automount, |
1330 | .d_release = nfs_d_release, | ||
1251 | }; | 1331 | }; |
1252 | 1332 | ||
1253 | /* | 1333 | /* |
@@ -1549,7 +1629,7 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, | |||
1549 | if (dentry->d_inode) | 1629 | if (dentry->d_inode) |
1550 | goto out; | 1630 | goto out; |
1551 | if (fhandle->size == 0) { | 1631 | if (fhandle->size == 0) { |
1552 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); | 1632 | error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr); |
1553 | if (error) | 1633 | if (error) |
1554 | goto out_error; | 1634 | goto out_error; |
1555 | } | 1635 | } |