diff options
author | J. Bruce Fields <bfields@redhat.com> | 2013-09-10 11:41:12 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-11-09 00:16:32 -0500 |
commit | 950ee9566a5b6cc45d15f5fe044bab4f1e8b62cb (patch) | |
tree | 423998e42ff71313d207f9a6f006d81498547280 /fs/exportfs/expfs.c | |
parent | b7a6ec52dd4eced4a9bcda9ca85b3c8af84d3c90 (diff) |
exportfs: fix 32-bit nfsd handling of 64-bit inode numbers
Symptoms were spurious -ENOENTs on stat of an NFS filesystem from a
32-bit NFS server exporting a very large XFS filesystem, when the
server's cache is cold (so the inodes in question are not in cache).
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reported-by: Trevor Cordes <trevor@tecnopolis.ca>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/exportfs/expfs.c')
-rw-r--r-- | fs/exportfs/expfs.c | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index a235f0016889..c43fe9b39ff2 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -215,7 +215,7 @@ struct getdents_callback { | |||
215 | struct dir_context ctx; | 215 | struct dir_context ctx; |
216 | char *name; /* name that was found. It already points to a | 216 | char *name; /* name that was found. It already points to a |
217 | buffer NAME_MAX+1 is size */ | 217 | buffer NAME_MAX+1 is size */ |
218 | unsigned long ino; /* the inum we are looking for */ | 218 | u64 ino; /* the inum we are looking for */ |
219 | int found; /* inode matched? */ | 219 | int found; /* inode matched? */ |
220 | int sequence; /* sequence counter */ | 220 | int sequence; /* sequence counter */ |
221 | }; | 221 | }; |
@@ -255,10 +255,14 @@ static int get_name(const struct path *path, char *name, struct dentry *child) | |||
255 | struct inode *dir = path->dentry->d_inode; | 255 | struct inode *dir = path->dentry->d_inode; |
256 | int error; | 256 | int error; |
257 | struct file *file; | 257 | struct file *file; |
258 | struct kstat stat; | ||
259 | struct path child_path = { | ||
260 | .mnt = path->mnt, | ||
261 | .dentry = child, | ||
262 | }; | ||
258 | struct getdents_callback buffer = { | 263 | struct getdents_callback buffer = { |
259 | .ctx.actor = filldir_one, | 264 | .ctx.actor = filldir_one, |
260 | .name = name, | 265 | .name = name, |
261 | .ino = child->d_inode->i_ino | ||
262 | }; | 266 | }; |
263 | 267 | ||
264 | error = -ENOTDIR; | 268 | error = -ENOTDIR; |
@@ -268,6 +272,16 @@ static int get_name(const struct path *path, char *name, struct dentry *child) | |||
268 | if (!dir->i_fop) | 272 | if (!dir->i_fop) |
269 | goto out; | 273 | goto out; |
270 | /* | 274 | /* |
275 | * inode->i_ino is unsigned long, kstat->ino is u64, so the | ||
276 | * former would be insufficient on 32-bit hosts when the | ||
277 | * filesystem supports 64-bit inode numbers. So we need to | ||
278 | * actually call ->getattr, not just read i_ino: | ||
279 | */ | ||
280 | error = vfs_getattr_nosec(&child_path, &stat); | ||
281 | if (error) | ||
282 | return error; | ||
283 | buffer.ino = stat.ino; | ||
284 | /* | ||
271 | * Open the directory ... | 285 | * Open the directory ... |
272 | */ | 286 | */ |
273 | file = dentry_open(path, O_RDONLY, cred); | 287 | file = dentry_open(path, O_RDONLY, cred); |