aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-10-03 04:13:46 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-03 11:03:40 -0400
commitafefdbb28a0a2af689926c30b94a14aea6036719 (patch)
tree6ee500575cac928cd90045bcf5b691cf2b8daa09 /fs
parent1d32849b14bc8792e6f35ab27dd990d74b16126c (diff)
[PATCH] VFS: Make filldir_t and struct kstat deal in 64-bit inode numbers
These patches make the kernel pass 64-bit inode numbers internally when communicating to userspace, even on a 32-bit system. They are required because some filesystems have intrinsic 64-bit inode numbers: NFS3+ and XFS for example. The 64-bit inode numbers are then propagated to userspace automatically where the arch supports it. Problems have been seen with userspace (eg: ld.so) using the 64-bit inode number returned by stat64() or getdents64() to differentiate files, and failing because the 64-bit inode number space was compressed to 32-bits, and so overlaps occur. This patch: Make filldir_t take a 64-bit inode number and struct kstat carry a 64-bit inode number so that 64-bit inode numbers can be passed back to userspace. The stat functions then returns the full 64-bit inode number where available and where possible. If it is not possible to represent the inode number supplied by the filesystem in the field provided by userspace, then error EOVERFLOW will be issued. Similarly, the getdents/readdir functions now pass the full 64-bit inode number to userspace where possible, returning EOVERFLOW instead when a directory entry is encountered that can't be properly represented. Note that this means that some inodes will not be stat'able on a 32-bit system with old libraries where they were before - but it does mean that there will be no ambiguity over what a 32-bit inode number refers to. Note similarly that directory scans may be cut short with an error on a 32-bit system with old libraries where the scan would work before for the same reasons. It is judged unlikely that this situation will occur because modern glibc uses 64-bit capable versions of stat and getdents class functions exclusively, and that older systems are unlikely to encounter unrepresentable inode numbers anyway. [akpm: alpha build fix] Signed-off-by: David Howells <dhowells@redhat.com> Cc: Trond Myklebust <trond.myklebust@fys.uio.no> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/dir.c4
-rw-r--r--fs/compat.c18
-rw-r--r--fs/exportfs/expfs.c2
-rw-r--r--fs/fat/dir.c2
-rw-r--r--fs/nfsd/nfs4recover.c2
-rw-r--r--fs/readdir.c18
-rw-r--r--fs/reiserfs/xattr.c6
-rw-r--r--fs/stat.c6
8 files changed, 40 insertions, 18 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 2fc99877cb0d..cf8a2cb28505 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -30,7 +30,7 @@ static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir);
30static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); 30static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
31static int afs_d_delete(struct dentry *dentry); 31static int afs_d_delete(struct dentry *dentry);
32static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, 32static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
33 loff_t fpos, ino_t ino, unsigned dtype); 33 loff_t fpos, u64 ino, unsigned dtype);
34 34
35const struct file_operations afs_dir_file_operations = { 35const struct file_operations afs_dir_file_operations = {
36 .open = afs_dir_open, 36 .open = afs_dir_open,
@@ -409,7 +409,7 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
409 * uniquifier through dtype 409 * uniquifier through dtype
410 */ 410 */
411static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, 411static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,
412 loff_t fpos, ino_t ino, unsigned dtype) 412 loff_t fpos, u64 ino, unsigned dtype)
413{ 413{
414 struct afs_dir_lookup_cookie *cookie = _cookie; 414 struct afs_dir_lookup_cookie *cookie = _cookie;
415 415
diff --git a/fs/compat.c b/fs/compat.c
index d98c96f4a44d..4d3fbcb2ddb1 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -914,20 +914,24 @@ struct compat_readdir_callback {
914}; 914};
915 915
916static int compat_fillonedir(void *__buf, const char *name, int namlen, 916static int compat_fillonedir(void *__buf, const char *name, int namlen,
917 loff_t offset, ino_t ino, unsigned int d_type) 917 loff_t offset, u64 ino, unsigned int d_type)
918{ 918{
919 struct compat_readdir_callback *buf = __buf; 919 struct compat_readdir_callback *buf = __buf;
920 struct compat_old_linux_dirent __user *dirent; 920 struct compat_old_linux_dirent __user *dirent;
921 compat_ulong_t d_ino;
921 922
922 if (buf->result) 923 if (buf->result)
923 return -EINVAL; 924 return -EINVAL;
925 d_ino = ino;
926 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
927 return -EOVERFLOW;
924 buf->result++; 928 buf->result++;
925 dirent = buf->dirent; 929 dirent = buf->dirent;
926 if (!access_ok(VERIFY_WRITE, dirent, 930 if (!access_ok(VERIFY_WRITE, dirent,
927 (unsigned long)(dirent->d_name + namlen + 1) - 931 (unsigned long)(dirent->d_name + namlen + 1) -
928 (unsigned long)dirent)) 932 (unsigned long)dirent))
929 goto efault; 933 goto efault;
930 if ( __put_user(ino, &dirent->d_ino) || 934 if ( __put_user(d_ino, &dirent->d_ino) ||
931 __put_user(offset, &dirent->d_offset) || 935 __put_user(offset, &dirent->d_offset) ||
932 __put_user(namlen, &dirent->d_namlen) || 936 __put_user(namlen, &dirent->d_namlen) ||
933 __copy_to_user(dirent->d_name, name, namlen) || 937 __copy_to_user(dirent->d_name, name, namlen) ||
@@ -978,22 +982,26 @@ struct compat_getdents_callback {
978}; 982};
979 983
980static int compat_filldir(void *__buf, const char *name, int namlen, 984static int compat_filldir(void *__buf, const char *name, int namlen,
981 loff_t offset, ino_t ino, unsigned int d_type) 985 loff_t offset, u64 ino, unsigned int d_type)
982{ 986{
983 struct compat_linux_dirent __user * dirent; 987 struct compat_linux_dirent __user * dirent;
984 struct compat_getdents_callback *buf = __buf; 988 struct compat_getdents_callback *buf = __buf;
989 compat_ulong_t d_ino;
985 int reclen = COMPAT_ROUND_UP(NAME_OFFSET(dirent) + namlen + 2); 990 int reclen = COMPAT_ROUND_UP(NAME_OFFSET(dirent) + namlen + 2);
986 991
987 buf->error = -EINVAL; /* only used if we fail.. */ 992 buf->error = -EINVAL; /* only used if we fail.. */
988 if (reclen > buf->count) 993 if (reclen > buf->count)
989 return -EINVAL; 994 return -EINVAL;
995 d_ino = ino;
996 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
997 return -EOVERFLOW;
990 dirent = buf->previous; 998 dirent = buf->previous;
991 if (dirent) { 999 if (dirent) {
992 if (__put_user(offset, &dirent->d_off)) 1000 if (__put_user(offset, &dirent->d_off))
993 goto efault; 1001 goto efault;
994 } 1002 }
995 dirent = buf->current_dir; 1003 dirent = buf->current_dir;
996 if (__put_user(ino, &dirent->d_ino)) 1004 if (__put_user(d_ino, &dirent->d_ino))
997 goto efault; 1005 goto efault;
998 if (__put_user(reclen, &dirent->d_reclen)) 1006 if (__put_user(reclen, &dirent->d_reclen))
999 goto efault; 1007 goto efault;
@@ -1064,7 +1072,7 @@ struct compat_getdents_callback64 {
1064}; 1072};
1065 1073
1066static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t offset, 1074static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t offset,
1067 ino_t ino, unsigned int d_type) 1075 u64 ino, unsigned int d_type)
1068{ 1076{
1069 struct linux_dirent64 __user *dirent; 1077 struct linux_dirent64 __user *dirent;
1070 struct compat_getdents_callback64 *buf = __buf; 1078 struct compat_getdents_callback64 *buf = __buf;
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 4c39009350f3..93e77c3d2490 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -315,7 +315,7 @@ struct getdents_callback {
315 * the name matching the specified inode number. 315 * the name matching the specified inode number.
316 */ 316 */
317static int filldir_one(void * __buf, const char * name, int len, 317static int filldir_one(void * __buf, const char * name, int len,
318 loff_t pos, ino_t ino, unsigned int d_type) 318 loff_t pos, u64 ino, unsigned int d_type)
319{ 319{
320 struct getdents_callback *buf = __buf; 320 struct getdents_callback *buf = __buf;
321 int result = 0; 321 int result = 0;
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 3e50a4166283..69c439f44387 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -648,7 +648,7 @@ static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
648} 648}
649 649
650static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, 650static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
651 loff_t offset, ino_t ino, unsigned int d_type) 651 loff_t offset, u64 ino, unsigned int d_type)
652{ 652{
653 struct fat_ioctl_filldir_callback *buf = __buf; 653 struct fat_ioctl_filldir_callback *buf = __buf;
654 struct dirent __user *d1 = buf->dirent; 654 struct dirent __user *d1 = buf->dirent;
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index e35d7e52fdeb..1cbd2e4ee122 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -184,7 +184,7 @@ struct dentry_list_arg {
184 184
185static int 185static int
186nfsd4_build_dentrylist(void *arg, const char *name, int namlen, 186nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
187 loff_t offset, ino_t ino, unsigned int d_type) 187 loff_t offset, u64 ino, unsigned int d_type)
188{ 188{
189 struct dentry_list_arg *dla = arg; 189 struct dentry_list_arg *dla = arg;
190 struct list_head *dentries = &dla->dentries; 190 struct list_head *dentries = &dla->dentries;
diff --git a/fs/readdir.c b/fs/readdir.c
index b6109329b607..bff3ee58e2f8 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -69,20 +69,24 @@ struct readdir_callback {
69}; 69};
70 70
71static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, 71static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
72 ino_t ino, unsigned int d_type) 72 u64 ino, unsigned int d_type)
73{ 73{
74 struct readdir_callback * buf = (struct readdir_callback *) __buf; 74 struct readdir_callback * buf = (struct readdir_callback *) __buf;
75 struct old_linux_dirent __user * dirent; 75 struct old_linux_dirent __user * dirent;
76 unsigned long d_ino;
76 77
77 if (buf->result) 78 if (buf->result)
78 return -EINVAL; 79 return -EINVAL;
80 d_ino = ino;
81 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
82 return -EOVERFLOW;
79 buf->result++; 83 buf->result++;
80 dirent = buf->dirent; 84 dirent = buf->dirent;
81 if (!access_ok(VERIFY_WRITE, dirent, 85 if (!access_ok(VERIFY_WRITE, dirent,
82 (unsigned long)(dirent->d_name + namlen + 1) - 86 (unsigned long)(dirent->d_name + namlen + 1) -
83 (unsigned long)dirent)) 87 (unsigned long)dirent))
84 goto efault; 88 goto efault;
85 if ( __put_user(ino, &dirent->d_ino) || 89 if ( __put_user(d_ino, &dirent->d_ino) ||
86 __put_user(offset, &dirent->d_offset) || 90 __put_user(offset, &dirent->d_offset) ||
87 __put_user(namlen, &dirent->d_namlen) || 91 __put_user(namlen, &dirent->d_namlen) ||
88 __copy_to_user(dirent->d_name, name, namlen) || 92 __copy_to_user(dirent->d_name, name, namlen) ||
@@ -138,22 +142,26 @@ struct getdents_callback {
138}; 142};
139 143
140static int filldir(void * __buf, const char * name, int namlen, loff_t offset, 144static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
141 ino_t ino, unsigned int d_type) 145 u64 ino, unsigned int d_type)
142{ 146{
143 struct linux_dirent __user * dirent; 147 struct linux_dirent __user * dirent;
144 struct getdents_callback * buf = (struct getdents_callback *) __buf; 148 struct getdents_callback * buf = (struct getdents_callback *) __buf;
149 unsigned long d_ino;
145 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 2); 150 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 2);
146 151
147 buf->error = -EINVAL; /* only used if we fail.. */ 152 buf->error = -EINVAL; /* only used if we fail.. */
148 if (reclen > buf->count) 153 if (reclen > buf->count)
149 return -EINVAL; 154 return -EINVAL;
155 d_ino = ino;
156 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
157 return -EOVERFLOW;
150 dirent = buf->previous; 158 dirent = buf->previous;
151 if (dirent) { 159 if (dirent) {
152 if (__put_user(offset, &dirent->d_off)) 160 if (__put_user(offset, &dirent->d_off))
153 goto efault; 161 goto efault;
154 } 162 }
155 dirent = buf->current_dir; 163 dirent = buf->current_dir;
156 if (__put_user(ino, &dirent->d_ino)) 164 if (__put_user(d_ino, &dirent->d_ino))
157 goto efault; 165 goto efault;
158 if (__put_user(reclen, &dirent->d_reclen)) 166 if (__put_user(reclen, &dirent->d_reclen))
159 goto efault; 167 goto efault;
@@ -222,7 +230,7 @@ struct getdents_callback64 {
222}; 230};
223 231
224static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, 232static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
225 ino_t ino, unsigned int d_type) 233 u64 ino, unsigned int d_type)
226{ 234{
227 struct linux_dirent64 __user *dirent; 235 struct linux_dirent64 __user *dirent;
228 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; 236 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index d935fb9394e3..7bdb0ed443e1 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -773,7 +773,7 @@ int reiserfs_xattr_del(struct inode *inode, const char *name)
773 773
774static int 774static int
775reiserfs_delete_xattrs_filler(void *buf, const char *name, int namelen, 775reiserfs_delete_xattrs_filler(void *buf, const char *name, int namelen,
776 loff_t offset, ino_t ino, unsigned int d_type) 776 loff_t offset, u64 ino, unsigned int d_type)
777{ 777{
778 struct dentry *xadir = (struct dentry *)buf; 778 struct dentry *xadir = (struct dentry *)buf;
779 779
@@ -851,7 +851,7 @@ struct reiserfs_chown_buf {
851/* XXX: If there is a better way to do this, I'd love to hear about it */ 851/* XXX: If there is a better way to do this, I'd love to hear about it */
852static int 852static int
853reiserfs_chown_xattrs_filler(void *buf, const char *name, int namelen, 853reiserfs_chown_xattrs_filler(void *buf, const char *name, int namelen,
854 loff_t offset, ino_t ino, unsigned int d_type) 854 loff_t offset, u64 ino, unsigned int d_type)
855{ 855{
856 struct reiserfs_chown_buf *chown_buf = (struct reiserfs_chown_buf *)buf; 856 struct reiserfs_chown_buf *chown_buf = (struct reiserfs_chown_buf *)buf;
857 struct dentry *xafile, *xadir = chown_buf->xadir; 857 struct dentry *xafile, *xadir = chown_buf->xadir;
@@ -1036,7 +1036,7 @@ struct reiserfs_listxattr_buf {
1036 1036
1037static int 1037static int
1038reiserfs_listxattr_filler(void *buf, const char *name, int namelen, 1038reiserfs_listxattr_filler(void *buf, const char *name, int namelen,
1039 loff_t offset, ino_t ino, unsigned int d_type) 1039 loff_t offset, u64 ino, unsigned int d_type)
1040{ 1040{
1041 struct reiserfs_listxattr_buf *b = (struct reiserfs_listxattr_buf *)buf; 1041 struct reiserfs_listxattr_buf *b = (struct reiserfs_listxattr_buf *)buf;
1042 int len = 0; 1042 int len = 0;
diff --git a/fs/stat.c b/fs/stat.c
index 60a31d5e5966..bca07eb2003c 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -140,6 +140,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
140 memset(&tmp, 0, sizeof(struct __old_kernel_stat)); 140 memset(&tmp, 0, sizeof(struct __old_kernel_stat));
141 tmp.st_dev = old_encode_dev(stat->dev); 141 tmp.st_dev = old_encode_dev(stat->dev);
142 tmp.st_ino = stat->ino; 142 tmp.st_ino = stat->ino;
143 if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
144 return -EOVERFLOW;
143 tmp.st_mode = stat->mode; 145 tmp.st_mode = stat->mode;
144 tmp.st_nlink = stat->nlink; 146 tmp.st_nlink = stat->nlink;
145 if (tmp.st_nlink != stat->nlink) 147 if (tmp.st_nlink != stat->nlink)
@@ -210,6 +212,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
210 tmp.st_dev = new_encode_dev(stat->dev); 212 tmp.st_dev = new_encode_dev(stat->dev);
211#endif 213#endif
212 tmp.st_ino = stat->ino; 214 tmp.st_ino = stat->ino;
215 if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
216 return -EOVERFLOW;
213 tmp.st_mode = stat->mode; 217 tmp.st_mode = stat->mode;
214 tmp.st_nlink = stat->nlink; 218 tmp.st_nlink = stat->nlink;
215 if (tmp.st_nlink != stat->nlink) 219 if (tmp.st_nlink != stat->nlink)
@@ -347,6 +351,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
347 tmp.st_rdev = huge_encode_dev(stat->rdev); 351 tmp.st_rdev = huge_encode_dev(stat->rdev);
348#endif 352#endif
349 tmp.st_ino = stat->ino; 353 tmp.st_ino = stat->ino;
354 if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
355 return -EOVERFLOW;
350#ifdef STAT64_HAS_BROKEN_ST_INO 356#ifdef STAT64_HAS_BROKEN_ST_INO
351 tmp.__st_ino = stat->ino; 357 tmp.__st_ino = stat->ino;
352#endif 358#endif