NFSv4: Don't fail nfs4_xdr_dec_open if decode_restorefh() failed
[pandora-kernel.git] / fs / open.c
index 89e0c23..a6b054e 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)
 {
@@ -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);
@@ -302,7 +305,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 +353,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 = -ENOSYS;
+
+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
@@ -448,8 +509,8 @@ asmlinkage long sys_fchdir(unsigned int fd)
        if (!file)
                goto out;
 
-       dentry = file->f_dentry;
-       mnt = file->f_vfsmnt;
+       dentry = file->f_path.dentry;
+       mnt = file->f_path.mnt;
        inode = dentry->d_inode;
 
        error = -ENOTDIR;
@@ -503,7 +564,7 @@ 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);
@@ -662,7 +723,7 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
        if (!file)
                goto out;
 
-       dentry = file->f_dentry;
+       dentry = file->f_path.dentry;
        audit_inode(NULL, dentry->d_inode);
        error = chown_common(dentry, user, group);
        fput(file);
@@ -688,8 +749,8 @@ 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);
@@ -723,8 +784,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);
@@ -822,7 +883,7 @@ 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)
+       if (filp->f_path.dentry == NULL)
                filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
        else
                path_release(nd);
@@ -853,7 +914,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 +925,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 +950,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 +969,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)
@@ -958,14 +1026,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);
                        }
                }
@@ -1087,6 +1155,7 @@ EXPORT_SYMBOL(sys_close);
 asmlinkage long sys_vhangup(void)
 {
        if (capable(CAP_SYS_TTY_CONFIG)) {
+               /* XXX: this needs locking */
                tty_vhangup(current->signal->tty);
                return 0;
        }