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 | |
| 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')
| -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); |
