Fix undefined count_partial if !CONFIG_SLABINFO
[safe/jmp/linux-2.6] / fs / open.c
index 3b56192..a4b1202 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -7,7 +7,6 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/file.h>
-#include <linux/smp_lock.h>
 #include <linux/quotaops.h>
 #include <linux/fsnotify.h>
 #include <linux/module.h>
@@ -27,6 +26,7 @@
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
 #include <linux/audit.h>
+#include <linux/falloc.h>
 
 int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
@@ -127,10 +127,10 @@ asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
        error = user_path_walk(path, &nd);
        if (!error) {
                struct statfs tmp;
-               error = vfs_statfs_native(nd.dentry, &tmp);
+               error = vfs_statfs_native(nd.path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_release(&nd);
+               path_put(&nd.path);
        }
        return error;
 }
@@ -146,10 +146,10 @@ asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64
        error = user_path_walk(path, &nd);
        if (!error) {
                struct statfs64 tmp;
-               error = vfs_statfs64(nd.dentry, &tmp);
+               error = vfs_statfs64(nd.path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_release(&nd);
+               path_put(&nd.path);
        }
        return error;
 }
@@ -165,7 +165,7 @@ asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
        file = fget(fd);
        if (!file)
                goto out;
-       error = vfs_statfs_native(file->f_dentry, &tmp);
+       error = vfs_statfs_native(file->f_path.dentry, &tmp);
        if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                error = -EFAULT;
        fput(file);
@@ -186,7 +186,7 @@ asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user
        file = fget(fd);
        if (!file)
                goto out;
-       error = vfs_statfs64(file->f_dentry, &tmp);
+       error = vfs_statfs64(file->f_path.dentry, &tmp);
        if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                error = -EFAULT;
        fput(file);
@@ -211,6 +211,9 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
                newattrs.ia_valid |= ATTR_FILE;
        }
 
+       /* Remove suid/sgid on truncate too */
+       newattrs.ia_valid |= should_remove_suid(dentry);
+
        mutex_lock(&dentry->d_inode->i_mutex);
        err = notify_change(dentry, &newattrs);
        mutex_unlock(&dentry->d_inode->i_mutex);
@@ -230,7 +233,7 @@ static long do_sys_truncate(const char __user * path, loff_t length)
        error = user_path_walk(path, &nd);
        if (error)
                goto out;
-       inode = nd.dentry->d_inode;
+       inode = nd.path.dentry->d_inode;
 
        /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
        error = -EISDIR;
@@ -253,26 +256,28 @@ static long do_sys_truncate(const char __user * path, loff_t length)
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                goto dput_and_out;
 
-       /*
-        * Make sure that there are no leases.
-        */
-       error = break_lease(inode, FMODE_WRITE);
+       error = get_write_access(inode);
        if (error)
                goto dput_and_out;
 
-       error = get_write_access(inode);
+       /*
+        * Make sure that there are no leases.  get_write_access() protects
+        * against the truncate racing with a lease-granting setlease().
+        */
+       error = break_lease(inode, FMODE_WRITE);
        if (error)
-               goto dput_and_out;
+               goto put_write_and_out;
 
        error = locks_verify_truncate(inode, NULL, length);
        if (!error) {
                DQUOT_INIT(inode);
-               error = do_truncate(nd.dentry, length, 0, NULL);
+               error = do_truncate(nd.path.dentry, length, 0, NULL);
        }
-       put_write_access(inode);
 
+put_write_and_out:
+       put_write_access(inode);
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -302,7 +307,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
        if (file->f_flags & O_LARGEFILE)
                small = 0;
 
-       dentry = file->f_dentry;
+       dentry = file->f_path.dentry;
        inode = dentry->d_inode;
        error = -EINVAL;
        if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
@@ -350,6 +355,64 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
 }
 #endif
 
+asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len)
+{
+       struct file *file;
+       struct inode *inode;
+       long ret = -EINVAL;
+
+       if (offset < 0 || len <= 0)
+               goto out;
+
+       /* Return error if mode is not supported */
+       ret = -EOPNOTSUPP;
+       if (mode && !(mode & FALLOC_FL_KEEP_SIZE))
+               goto out;
+
+       ret = -EBADF;
+       file = fget(fd);
+       if (!file)
+               goto out;
+       if (!(file->f_mode & FMODE_WRITE))
+               goto out_fput;
+       /*
+        * Revalidate the write permissions, in case security policy has
+        * changed since the files were opened.
+        */
+       ret = security_file_permission(file, MAY_WRITE);
+       if (ret)
+               goto out_fput;
+
+       inode = file->f_path.dentry->d_inode;
+
+       ret = -ESPIPE;
+       if (S_ISFIFO(inode->i_mode))
+               goto out_fput;
+
+       ret = -ENODEV;
+       /*
+        * Let individual file system decide if it supports preallocation
+        * for directories or not.
+        */
+       if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+               goto out_fput;
+
+       ret = -EFBIG;
+       /* Check for wrap through zero too */
+       if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
+               goto out_fput;
+
+       if (inode->i_op && inode->i_op->fallocate)
+               ret = inode->i_op->fallocate(inode, mode, offset, len);
+       else
+               ret = -EOPNOTSUPP;
+
+out_fput:
+       fput(file);
+out:
+       return ret;
+}
+
 /*
  * access() needs to use the real uid/gid, not the effective uid/gid.
  * We do this by temporarily clearing all FS-related capabilities and
@@ -392,14 +455,14 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
        res = vfs_permission(&nd, mode);
        /* SuS v2 requires we report a read only fs too */
        if(res || !(mode & S_IWOTH) ||
-          special_file(nd.dentry->d_inode->i_mode))
+          special_file(nd.path.dentry->d_inode->i_mode))
                goto out_path_release;
 
-       if(IS_RDONLY(nd.dentry->d_inode))
+       if(IS_RDONLY(nd.path.dentry->d_inode))
                res = -EROFS;
 
 out_path_release:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        current->fsuid = old_fsuid;
        current->fsgid = old_fsgid;
@@ -427,10 +490,10 @@ asmlinkage long sys_chdir(const char __user * filename)
        if (error)
                goto dput_and_out;
 
-       set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+       set_fs_pwd(current->fs, &nd.path);
 
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -438,9 +501,7 @@ out:
 asmlinkage long sys_fchdir(unsigned int fd)
 {
        struct file *file;
-       struct dentry *dentry;
        struct inode *inode;
-       struct vfsmount *mnt;
        int error;
 
        error = -EBADF;
@@ -448,9 +509,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
        if (!file)
                goto out;
 
-       dentry = file->f_dentry;
-       mnt = file->f_vfsmnt;
-       inode = dentry->d_inode;
+       inode = file->f_path.dentry->d_inode;
 
        error = -ENOTDIR;
        if (!S_ISDIR(inode->i_mode))
@@ -458,7 +517,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
 
        error = file_permission(file, MAY_EXEC);
        if (!error)
-               set_fs_pwd(current->fs, mnt, dentry);
+               set_fs_pwd(current->fs, &file->f_path);
 out_putf:
        fput(file);
 out:
@@ -482,11 +541,11 @@ asmlinkage long sys_chroot(const char __user * filename)
        if (!capable(CAP_SYS_CHROOT))
                goto dput_and_out;
 
-       set_fs_root(current->fs, nd.mnt, nd.dentry);
+       set_fs_root(current->fs, &nd.path);
        set_fs_altroot();
        error = 0;
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -503,10 +562,10 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        if (!file)
                goto out;
 
-       dentry = file->f_dentry;
+       dentry = file->f_path.dentry;
        inode = dentry->d_inode;
 
-       audit_inode(NULL, inode);
+       audit_inode(NULL, dentry);
 
        err = -EROFS;
        if (IS_RDONLY(inode))
@@ -539,7 +598,7 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
        error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
        if (error)
                goto out;
-       inode = nd.dentry->d_inode;
+       inode = nd.path.dentry->d_inode;
 
        error = -EROFS;
        if (IS_RDONLY(inode))
@@ -554,11 +613,11 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
                mode = inode->i_mode;
        newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-       error = notify_change(nd.dentry, &newattrs);
+       error = notify_change(nd.path.dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
 
 dput_and_out:
-       path_release(&nd);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -595,7 +654,8 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
                newattrs.ia_gid = group;
        }
        if (!S_ISDIR(inode->i_mode))
-               newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
+               newattrs.ia_valid |=
+                       ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
        mutex_lock(&inode->i_mutex);
        error = notify_change(dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
@@ -611,8 +671,8 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
        error = user_path_walk(filename, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = chown_common(nd.path.dentry, user, group);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -631,8 +691,8 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
        error = __user_walk_fd(dfd, filename, follow, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = chown_common(nd.path.dentry, user, group);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -645,8 +705,8 @@ asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group
        error = user_path_walk_link(filename, &nd);
        if (error)
                goto out;
-       error = chown_common(nd.dentry, user, group);
-       path_release(&nd);
+       error = chown_common(nd.path.dentry, user, group);
+       path_put(&nd.path);
 out:
        return error;
 }
@@ -662,8 +722,8 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
        if (!file)
                goto out;
 
-       dentry = file->f_dentry;
-       audit_inode(NULL, dentry->d_inode);
+       dentry = file->f_path.dentry;
+       audit_inode(NULL, dentry);
        error = chown_common(dentry, user, group);
        fput(file);
 out:
@@ -688,12 +748,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        }
 
        f->f_mapping = inode->i_mapping;
-       f->f_dentry = dentry;
-       f->f_vfsmnt = mnt;
+       f->f_path.dentry = dentry;
+       f->f_path.mnt = mnt;
        f->f_pos = 0;
        f->f_op = fops_get(inode->i_fop);
        file_move(f, &inode->i_sb->s_files);
 
+       error = security_dentry_open(f);
+       if (error)
+               goto cleanup_all;
+
        if (!open && f->f_op)
                open = f->f_op->open;
        if (open) {
@@ -723,8 +787,8 @@ cleanup_all:
        if (f->f_mode & FMODE_WRITE)
                put_write_access(inode);
        file_kill(f);
-       f->f_dentry = NULL;
-       f->f_vfsmnt = NULL;
+       f->f_path.dentry = NULL;
+       f->f_path.mnt = NULL;
 cleanup_file:
        put_filp(f);
        dput(dentry);
@@ -795,7 +859,7 @@ struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry
                goto out;
        if (IS_ERR(dentry))
                goto out_err;
-       nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt),
+       nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt),
                                             nd->intent.open.flags - 1,
                                             nd->intent.open.file,
                                             open);
@@ -822,10 +886,11 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
        /* Pick up the filp from the open intent */
        filp = nd->intent.open.file;
        /* Has the filesystem initialised the file for us? */
-       if (filp->f_dentry == NULL)
-               filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
+       if (filp->f_path.dentry == NULL)
+               filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp,
+                                    NULL);
        else
-               path_release(nd);
+               path_put(&nd->path);
        return filp;
 }
 
@@ -838,6 +903,18 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
        int error;
        struct file *f;
 
+       /*
+        * We must always pass in a valid mount pointer.   Historically
+        * callers got away with not passing it, but we must enforce this at
+        * the earliest possible point now to avoid strange problems deep in the
+        * filesystem stack.
+        */
+       if (!mnt) {
+               printk(KERN_WARNING "%s called with NULL vfsmount\n", __func__);
+               dump_stack();
+               return ERR_PTR(-EINVAL);
+       }
+
        error = -ENFILE;
        f = get_empty_filp();
        if (f == NULL) {
@@ -853,7 +930,7 @@ EXPORT_SYMBOL(dentry_open);
 /*
  * Find an empty file descriptor entry, and mark it busy.
  */
-int get_unused_fd(void)
+int get_unused_fd_flags(int flags)
 {
        struct files_struct * files = current->files;
        int fd, error;
@@ -864,8 +941,7 @@ int get_unused_fd(void)
 
 repeat:
        fdt = files_fdtable(files);
-       fd = find_next_zero_bit(fdt->open_fds->fds_bits,
-                               fdt->max_fdset,
+       fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
                                files->next_fd);
 
        /*
@@ -890,7 +966,10 @@ repeat:
        }
 
        FD_SET(fd, fdt->open_fds);
-       FD_CLR(fd, fdt->close_on_exec);
+       if (flags & O_CLOEXEC)
+               FD_SET(fd, fdt->close_on_exec);
+       else
+               FD_CLR(fd, fdt->close_on_exec);
        files->next_fd = fd + 1;
 #if 1
        /* Sanity check */
@@ -906,6 +985,11 @@ out:
        return error;
 }
 
+int get_unused_fd(void)
+{
+       return get_unused_fd_flags(0);
+}
+
 EXPORT_SYMBOL(get_unused_fd);
 
 static void __put_unused_fd(struct files_struct *files, unsigned int fd)
@@ -916,7 +1000,7 @@ static void __put_unused_fd(struct files_struct *files, unsigned int fd)
                files->next_fd = fd;
 }
 
-void fastcall put_unused_fd(unsigned int fd)
+void put_unused_fd(unsigned int fd)
 {
        struct files_struct *files = current->files;
        spin_lock(&files->file_lock);
@@ -939,7 +1023,7 @@ EXPORT_SYMBOL(put_unused_fd);
  * will follow.
  */
 
-void fastcall fd_install(unsigned int fd, struct file * file)
+void fd_install(unsigned int fd, struct file *file)
 {
        struct files_struct *files = current->files;
        struct fdtable *fdt;
@@ -958,14 +1042,14 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
        int fd = PTR_ERR(tmp);
 
        if (!IS_ERR(tmp)) {
-               fd = get_unused_fd();
+               fd = get_unused_fd_flags(flags);
                if (fd >= 0) {
                        struct file *f = do_filp_open(dfd, tmp, flags, mode);
                        if (IS_ERR(f)) {
                                put_unused_fd(fd);
                                fd = PTR_ERR(f);
                        } else {
-                               fsnotify_open(f->f_dentry);
+                               fsnotify_open(f->f_path.dentry);
                                fd_install(fd, f);
                        }
                }
@@ -986,7 +1070,6 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode)
        prevent_tail_call(ret);
        return ret;
 }
-EXPORT_SYMBOL_GPL(sys_open);
 
 asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
                           int mode)
@@ -1103,7 +1186,7 @@ asmlinkage long sys_vhangup(void)
 int generic_file_open(struct inode * inode, struct file * filp)
 {
        if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
-               return -EFBIG;
+               return -EOVERFLOW;
        return 0;
 }