ACPI: make acpi_bus_register_driver() return success/failure, not device count
[pandora-kernel.git] / fs / open.c
index 75f3329..c32c89d 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
 #include <linux/tty.h>
 #include <linux/namei.h>
 #include <linux/backing-dev.h>
+#include <linux/capability.h>
 #include <linux/security.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
+#include <linux/fcntl.h>
 #include <asm/uaccess.h>
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
+#include <linux/audit.h>
 
 #include <asm/unistd.h>
 
@@ -211,9 +214,9 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
                newattrs.ia_valid |= ATTR_FILE;
        }
 
-       down(&dentry->d_inode->i_sem);
+       mutex_lock(&dentry->d_inode->i_mutex);
        err = notify_change(dentry, &newattrs);
-       up(&dentry->d_inode->i_sem);
+       mutex_unlock(&dentry->d_inode->i_mutex);
        return err;
 }
 
@@ -382,7 +385,7 @@ asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
 
                error = get_user(newattrs.ia_atime.tv_sec, &times->actime);
                newattrs.ia_atime.tv_nsec = 0;
-               if (!error) 
+               if (!error)
                        error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);
                newattrs.ia_mtime.tv_nsec = 0;
                if (error)
@@ -398,9 +401,9 @@ asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
                    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
                        goto dput_and_out;
        }
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        error = notify_change(nd.dentry, &newattrs);
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 dput_and_out:
        path_release(&nd);
 out:
@@ -413,14 +416,14 @@ out:
  * must be owner or have write permission.
  * Else, update from *times, must be owner or super user.
  */
-long do_utimes(char __user * filename, struct timeval * times)
+long do_utimes(int dfd, char __user *filename, struct timeval *times)
 {
        int error;
        struct nameidata nd;
        struct inode * inode;
        struct iattr newattrs;
 
-       error = user_path_walk(filename, &nd);
+       error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
 
        if (error)
                goto out;
@@ -451,22 +454,27 @@ long do_utimes(char __user * filename, struct timeval * times)
                    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
                        goto dput_and_out;
        }
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        error = notify_change(nd.dentry, &newattrs);
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 dput_and_out:
        path_release(&nd);
 out:
        return error;
 }
 
-asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utimes)
+asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes)
 {
        struct timeval times[2];
 
        if (utimes && copy_from_user(&times, utimes, sizeof(times)))
                return -EFAULT;
-       return do_utimes(filename, utimes ? times : NULL);
+       return do_utimes(dfd, filename, utimes ? times : NULL);
+}
+
+asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes)
+{
+       return sys_futimesat(AT_FDCWD, filename, utimes);
 }
 
 
@@ -475,7 +483,7 @@ asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utime
  * We do this by temporarily clearing all FS-related capabilities and
  * switching the fsuid/fsgid around to the real ones.
  */
-asmlinkage long sys_access(const char __user * filename, int mode)
+asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
        struct nameidata nd;
        int old_fsuid, old_fsgid;
@@ -505,7 +513,7 @@ asmlinkage long sys_access(const char __user * filename, int mode)
        else
                current->cap_effective = current->cap_permitted;
 
-       res = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+       res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
        if (!res) {
                res = vfs_permission(&nd, mode);
                /* SuS v2 requires we report a read only fs too */
@@ -522,6 +530,11 @@ asmlinkage long sys_access(const char __user * filename, int mode)
        return res;
 }
 
+asmlinkage long sys_access(const char __user *filename, int mode)
+{
+       return sys_faccessat(AT_FDCWD, filename, mode);
+}
+
 asmlinkage long sys_chdir(const char __user * filename)
 {
        struct nameidata nd;
@@ -614,19 +627,21 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        dentry = file->f_dentry;
        inode = dentry->d_inode;
 
+       audit_inode(NULL, inode, 0);
+
        err = -EROFS;
        if (IS_RDONLY(inode))
                goto out_putf;
        err = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                goto out_putf;
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        if (mode == (mode_t) -1)
                mode = inode->i_mode;
        newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
        err = notify_change(dentry, &newattrs);
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 
 out_putf:
        fput(file);
@@ -634,14 +649,15 @@ out:
        return err;
 }
 
-asmlinkage long sys_chmod(const char __user * filename, mode_t mode)
+asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
+                            mode_t mode)
 {
        struct nameidata nd;
        struct inode * inode;
        int error;
        struct iattr newattrs;
 
-       error = user_path_walk(filename, &nd);
+       error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
        if (error)
                goto out;
        inode = nd.dentry->d_inode;
@@ -654,13 +670,13 @@ asmlinkage long sys_chmod(const char __user * filename, mode_t mode)
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                goto dput_and_out;
 
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        if (mode == (mode_t) -1)
                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);
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 
 dput_and_out:
        path_release(&nd);
@@ -668,6 +684,11 @@ out:
        return error;
 }
 
+asmlinkage long sys_chmod(const char __user *filename, mode_t mode)
+{
+       return sys_fchmodat(AT_FDCWD, filename, mode);
+}
+
 static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
 {
        struct inode * inode;
@@ -696,9 +717,9 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
        }
        if (!S_ISDIR(inode->i_mode))
                newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
        error = notify_change(dentry, &newattrs);
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
 out:
        return error;
 }
@@ -716,6 +737,26 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
        return error;
 }
 
+asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
+                            gid_t group, int flag)
+{
+       struct nameidata nd;
+       int error = -EINVAL;
+       int follow;
+
+       if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
+               goto out;
+
+       follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+       error = __user_walk_fd(dfd, filename, follow, &nd);
+       if (!error) {
+               error = chown_common(nd.dentry, user, group);
+               path_release(&nd);
+       }
+out:
+       return error;
+}
+
 asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
 {
        struct nameidata nd;
@@ -737,7 +778,10 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
 
        file = fget(fd);
        if (file) {
-               error = chown_common(file->f_dentry, user, group);
+               struct dentry * dentry;
+               dentry = file->f_dentry;
+               audit_inode(NULL, dentry->d_inode, 0);
+               error = chown_common(dentry, user, group);
                fput(file);
        }
        return error;
@@ -819,7 +863,8 @@ cleanup_file:
  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
  * used by symlinks.
  */
-struct file *filp_open(const char * filename, int flags, int mode)
+static struct file *do_filp_open(int dfd, const char *filename, int flags,
+                                int mode)
 {
        int namei_flags, error;
        struct nameidata nd;
@@ -828,12 +873,17 @@ struct file *filp_open(const char * filename, int flags, int mode)
        if ((namei_flags+1) & O_ACCMODE)
                namei_flags++;
 
-       error = open_namei(filename, namei_flags, mode, &nd);
+       error = open_namei(dfd, filename, namei_flags, mode, &nd);
        if (!error)
                return nameidata_to_filp(&nd, flags);
 
        return ERR_PTR(error);
 }
+
+struct file *filp_open(const char *filename, int flags, int mode)
+{
+       return do_filp_open(AT_FDCWD, filename, flags, mode);
+}
 EXPORT_SYMBOL(filp_open);
 
 /**
@@ -846,6 +896,10 @@ EXPORT_SYMBOL(filp_open);
  * a fully instantiated struct file to the caller.
  * This function is meant to be called from within a filesystem's
  * lookup method.
+ * Beware of calling it for non-regular files! Those ->open methods might block
+ * (e.g. in fifo_open), leaving you with parent locked (and in case of fifo,
+ * leading to a deadlock, as nobody can open that fifo anymore, because
+ * another process to open fifo will block on locked parent when doing lookup).
  * Note that in case of error, nd->intent.open.file is destroyed, but the
  * path information remains valid.
  * If the open callback is set to NULL, then the standard f_op->open()
@@ -929,7 +983,7 @@ repeat:
        fdt = files_fdtable(files);
        fd = find_next_zero_bit(fdt->open_fds->fds_bits,
                                fdt->max_fdset,
-                               fdt->next_fd);
+                               files->next_fd);
 
        /*
         * N.B. For clone tasks sharing a files structure, this test
@@ -954,7 +1008,7 @@ repeat:
 
        FD_SET(fd, fdt->open_fds);
        FD_CLR(fd, fdt->close_on_exec);
-       fdt->next_fd = fd + 1;
+       files->next_fd = fd + 1;
 #if 1
        /* Sanity check */
        if (fdt->fd[fd] != NULL) {
@@ -975,8 +1029,8 @@ static void __put_unused_fd(struct files_struct *files, unsigned int fd)
 {
        struct fdtable *fdt = files_fdtable(files);
        __FD_CLR(fd, fdt->open_fds);
-       if (fd < fdt->next_fd)
-               fdt->next_fd = fd;
+       if (fd < files->next_fd)
+               files->next_fd = fd;
 }
 
 void fastcall put_unused_fd(unsigned int fd)
@@ -990,7 +1044,7 @@ void fastcall put_unused_fd(unsigned int fd)
 EXPORT_SYMBOL(put_unused_fd);
 
 /*
- * Install a file pointer in the fd array.  
+ * Install a file pointer in the fd array.
  *
  * The VFS is full of places where we drop the files lock between
  * setting the open_fds bitmap and installing the file in the file
@@ -1015,7 +1069,7 @@ void fastcall fd_install(unsigned int fd, struct file * file)
 
 EXPORT_SYMBOL(fd_install);
 
-long do_sys_open(const char __user *filename, int flags, int mode)
+long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
 {
        char *tmp = getname(filename);
        int fd = PTR_ERR(tmp);
@@ -1023,7 +1077,7 @@ long do_sys_open(const char __user *filename, int flags, int mode)
        if (!IS_ERR(tmp)) {
                fd = get_unused_fd();
                if (fd >= 0) {
-                       struct file *f = filp_open(tmp, flags, mode);
+                       struct file *f = do_filp_open(dfd, tmp, flags, mode);
                        if (IS_ERR(f)) {
                                put_unused_fd(fd);
                                fd = PTR_ERR(f);
@@ -1042,10 +1096,20 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode)
        if (force_o_largefile())
                flags |= O_LARGEFILE;
 
-       return do_sys_open(filename, flags, mode);
+       return do_sys_open(AT_FDCWD, filename, flags, mode);
 }
 EXPORT_SYMBOL_GPL(sys_open);
 
+asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
+                          int mode)
+{
+       if (force_o_largefile())
+               flags |= O_LARGEFILE;
+
+       return do_sys_open(dfd, filename, flags, mode);
+}
+EXPORT_SYMBOL_GPL(sys_openat);
+
 #ifndef __alpha__
 
 /*