Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / readdir.c
index f02cae6..bff3ee5 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/fs.h>
 #include <linux/dirent.h>
 #include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/unistd.h>
 
 #include <asm/uaccess.h>
 
@@ -28,13 +30,13 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
        if (res)
                goto out;
 
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        res = -ENOENT;
        if (!IS_DEADDIR(inode)) {
                res = file->f_op->readdir(file, buf, filler);
                file_accessed(file);
        }
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 out:
        return res;
 }
@@ -49,10 +51,10 @@ EXPORT_SYMBOL(vfs_readdir);
  * anyway. Thus the special "fillonedir()" function for that
  * case (the low-level handlers don't need to care about this).
  */
-#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
 
-#ifndef __ia64__
+#ifdef __ARCH_WANT_OLD_READDIR
 
 struct old_linux_dirent {
        unsigned long   d_ino;
@@ -67,20 +69,24 @@ struct readdir_callback {
 };
 
 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
-                     ino_t ino, unsigned int d_type)
+                     u64 ino, unsigned int d_type)
 {
        struct readdir_callback * buf = (struct readdir_callback *) __buf;
        struct old_linux_dirent __user * dirent;
+       unsigned long d_ino;
 
        if (buf->result)
                return -EINVAL;
+       d_ino = ino;
+       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
+               return -EOVERFLOW;
        buf->result++;
        dirent = buf->dirent;
-       if (!access_ok(VERIFY_WRITE, (unsigned long)dirent,
+       if (!access_ok(VERIFY_WRITE, dirent,
                        (unsigned long)(dirent->d_name + namlen + 1) -
                                (unsigned long)dirent))
                goto efault;
-       if (    __put_user(ino, &dirent->d_ino) ||
+       if (    __put_user(d_ino, &dirent->d_ino) ||
                __put_user(offset, &dirent->d_offset) ||
                __put_user(namlen, &dirent->d_namlen) ||
                __copy_to_user(dirent->d_name, name, namlen) ||
@@ -115,7 +121,7 @@ out:
        return error;
 }
 
-#endif /* !__ia64__ */
+#endif /* __ARCH_WANT_OLD_READDIR */
 
 /*
  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
@@ -136,22 +142,26 @@ struct getdents_callback {
 };
 
 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
-                  ino_t ino, unsigned int d_type)
+                  u64 ino, unsigned int d_type)
 {
        struct linux_dirent __user * dirent;
        struct getdents_callback * buf = (struct getdents_callback *) __buf;
+       unsigned long d_ino;
        int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 2);
 
        buf->error = -EINVAL;   /* only used if we fail.. */
        if (reclen > buf->count)
                return -EINVAL;
+       d_ino = ino;
+       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
+               return -EOVERFLOW;
        dirent = buf->previous;
        if (dirent) {
                if (__put_user(offset, &dirent->d_off))
                        goto efault;
        }
        dirent = buf->current_dir;
-       if (__put_user(ino, &dirent->d_ino))
+       if (__put_user(d_ino, &dirent->d_ino))
                goto efault;
        if (__put_user(reclen, &dirent->d_reclen))
                goto efault;
@@ -159,7 +169,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
                goto efault;
        if (__put_user(0, dirent->d_name + namlen))
                goto efault;
-       if (__put_user(d_type, (char *) dirent + reclen - 1))
+       if (__put_user(d_type, (char __user *) dirent + reclen - 1))
                goto efault;
        buf->previous = dirent;
        dirent = (void __user *)dirent + reclen;
@@ -220,7 +230,7 @@ struct getdents_callback64 {
 };
 
 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
-                    ino_t ino, unsigned int d_type)
+                    u64 ino, unsigned int d_type)
 {
        struct linux_dirent64 __user *dirent;
        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
@@ -285,7 +295,9 @@ asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * d
        lastdirent = buf.previous;
        if (lastdirent) {
                typeof(lastdirent->d_off) d_off = file->f_pos;
-               __put_user(d_off, &lastdirent->d_off);
+               error = -EFAULT;
+               if (__put_user(d_off, &lastdirent->d_off))
+                       goto out_putf;
                error = count - buf.count;
        }