fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / open.c
index d92305b..eb5563f 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -6,7 +6,6 @@
 
 #include <linux/string.h>
 #include <linux/mm.h>
-#include <linux/utime.h>
 #include <linux/file.h>
 #include <linux/smp_lock.h>
 #include <linux/quotaops.h>
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
+#include <linux/audit.h>
+#include <linux/vs_base.h>
 #include <linux/vs_limit.h>
 #include <linux/vs_dlimit.h>
-#include <linux/vserver/xid.h>
+#include <linux/vs_tag.h>
+#include <linux/vs_cowbl.h>
 
-#include <asm/unistd.h>
-
-int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
+int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        int retval = -ENODEV;
 
-       if (sb) {
+       if (dentry) {
+               struct super_block *sb = dentry->d_sb;
+
                retval = -ENOSYS;
                if (sb->s_op->statfs) {
                        memset(buf, 0, sizeof(*buf));
-                       retval = security_sb_statfs(sb);
+                       retval = security_sb_statfs(dentry);
                        if (retval)
                                return retval;
-                       retval = sb->s_op->statfs(sb, buf);
+                       retval = sb->s_op->statfs(dentry, buf);
                        if (retval == 0 && buf->f_frsize == 0)
                                buf->f_frsize = buf->f_bsize;
                }
-               if (!vx_check(0, VX_ADMIN|VX_WATCH))
+               if (!vx_check(0, VS_ADMIN|VS_WATCH))
                        vx_vsi_statfs(sb, buf);
        }
        return retval;
@@ -56,12 +58,12 @@ int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
 
 EXPORT_SYMBOL(vfs_statfs);
 
-static int vfs_statfs_native(struct super_block *sb, struct statfs *buf)
+static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
 {
        struct kstatfs st;
        int retval;
 
-       retval = vfs_statfs(sb, &st);
+       retval = vfs_statfs(dentry, &st);
        if (retval)
                return retval;
 
@@ -99,12 +101,12 @@ static int vfs_statfs_native(struct super_block *sb, struct statfs *buf)
        return 0;
 }
 
-static int vfs_statfs64(struct super_block *sb, struct statfs64 *buf)
+static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
 {
        struct kstatfs st;
        int retval;
 
-       retval = vfs_statfs(sb, &st);
+       retval = vfs_statfs(dentry, &st);
        if (retval)
                return retval;
 
@@ -134,7 +136,7 @@ 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->d_inode->i_sb, &tmp);
+               error = vfs_statfs_native(nd.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
                path_release(&nd);
@@ -153,7 +155,7 @@ 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->d_inode->i_sb, &tmp);
+               error = vfs_statfs64(nd.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
                path_release(&nd);
@@ -172,7 +174,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->d_inode->i_sb, &tmp);
+       error = vfs_statfs_native(file->f_path.dentry, &tmp);
        if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                error = -EFAULT;
        fput(file);
@@ -193,7 +195,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->d_inode->i_sb, &tmp);
+       error = vfs_statfs64(file->f_path.dentry, &tmp);
        if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                error = -EFAULT;
        fput(file);
@@ -309,7 +311,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))
@@ -326,7 +328,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 
        error = locks_verify_truncate(inode, file, length);
        if (!error)
-               error = do_truncate(dentry, length, 0, file);
+               error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
 out_putf:
        fput(file);
 out:
@@ -357,137 +359,6 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
 }
 #endif
 
-#ifdef __ARCH_WANT_SYS_UTIME
-
-/*
- * sys_utime() can be implemented in user-level using sys_utimes().
- * Is this for backwards compatibility?  If so, why not move it
- * into the appropriate arch directory (for those architectures that
- * need it).
- */
-
-/* If times==NULL, set access and modification to current time,
- * must be owner or have write permission.
- * Else, update from *times, must be owner or super user.
- */
-asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
-{
-       int error;
-       struct nameidata nd;
-       struct inode * inode;
-       struct iattr newattrs;
-
-       error = user_path_walk(filename, &nd);
-       if (error)
-               goto out;
-       inode = nd.dentry->d_inode;
-
-       error = -EROFS;
-       if (IS_RDONLY(inode) || MNT_IS_RDONLY(nd.mnt))
-               goto dput_and_out;
-
-       /* Don't worry, the checks are done in inode_change_ok() */
-       newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
-       if (times) {
-               error = -EPERM;
-               if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-                       goto dput_and_out;
-
-               error = get_user(newattrs.ia_atime.tv_sec, &times->actime);
-               newattrs.ia_atime.tv_nsec = 0;
-               if (!error)
-                       error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);
-               newattrs.ia_mtime.tv_nsec = 0;
-               if (error)
-                       goto dput_and_out;
-
-               newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
-       } else {
-                error = -EACCES;
-                if (IS_IMMUTABLE(inode))
-                        goto dput_and_out;
-
-               if (current->fsuid != inode->i_uid &&
-                   (error = vfs_permission(&nd, MAY_WRITE)) != 0)
-                       goto dput_and_out;
-       }
-       mutex_lock(&inode->i_mutex);
-       error = notify_change(nd.dentry, &newattrs);
-       mutex_unlock(&inode->i_mutex);
-dput_and_out:
-       path_release(&nd);
-out:
-       return error;
-}
-
-#endif
-
-/* If times==NULL, set access and modification to current time,
- * must be owner or have write permission.
- * Else, update from *times, must be owner or super user.
- */
-long do_utimes(int dfd, char __user *filename, struct timeval *times)
-{
-       int error;
-       struct nameidata nd;
-       struct inode * inode;
-       struct iattr newattrs;
-
-       error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
-
-       if (error)
-               goto out;
-       inode = nd.dentry->d_inode;
-
-       error = -EROFS;
-       if (IS_RDONLY(inode) || MNT_IS_RDONLY(nd.mnt))
-               goto dput_and_out;
-
-       /* Don't worry, the checks are done in inode_change_ok() */
-       newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
-       if (times) {
-               error = -EPERM;
-                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-                        goto dput_and_out;
-
-               newattrs.ia_atime.tv_sec = times[0].tv_sec;
-               newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000;
-               newattrs.ia_mtime.tv_sec = times[1].tv_sec;
-               newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000;
-               newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
-       } else {
-               error = -EACCES;
-                if (IS_IMMUTABLE(inode))
-                        goto dput_and_out;
-
-               if (current->fsuid != inode->i_uid &&
-                   (error = vfs_permission(&nd, MAY_WRITE)) != 0)
-                       goto dput_and_out;
-       }
-       mutex_lock(&inode->i_mutex);
-       error = notify_change(nd.dentry, &newattrs);
-       mutex_unlock(&inode->i_mutex);
-dput_and_out:
-       path_release(&nd);
-out:
-       return error;
-}
-
-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(dfd, filename, utimes ? times : NULL);
-}
-
-asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes)
-{
-       return sys_futimesat(AT_FDCWD, filename, utimes);
-}
-
-
 /*
  * access() needs to use the real uid/gid, not the effective uid/gid.
  * We do this by temporarily clearing all FS-related capabilities and
@@ -524,16 +395,21 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
                current->cap_effective = current->cap_permitted;
 
        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 */
-               if(!res && (mode & S_IWOTH)
-                  && (IS_RDONLY(nd.dentry->d_inode) || MNT_IS_RDONLY(nd.mnt))
-                  && !special_file(nd.dentry->d_inode->i_mode))
-                       res = -EROFS;
-               path_release(&nd);
-       }
+       if (res)
+               goto out;
+
+       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))
+               goto out_path_release;
+
+       if(IS_RDONLY(nd.dentry->d_inode) || MNT_IS_RDONLY(nd.mnt))
+               res = -EROFS;
 
+out_path_release:
+       path_release(&nd);
+out:
        current->fsuid = old_fsuid;
        current->fsgid = old_fsgid;
        current->cap_effective = old_cap;
@@ -551,7 +427,8 @@ asmlinkage long sys_chdir(const char __user * filename)
        struct nameidata nd;
        int error;
 
-       error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd);
+       error = __user_walk(filename,
+                           LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
        if (error)
                goto out;
 
@@ -580,8 +457,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;
@@ -635,9 +512,11 @@ 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);
+
        err = -EROFS;
        if (IS_RDONLY(inode) || MNT_IS_RDONLY(file->f_vfsmnt))
                goto out_putf;
@@ -669,11 +548,11 @@ 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;
 
-       error = -EROFS;
-       if (IS_RDONLY(inode) || MNT_IS_RDONLY(nd.mnt))
+       error = cow_check_and_break(&nd);
+       if (error)
                goto dput_and_out;
+       inode = nd.dentry->d_inode;
 
        error = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
@@ -719,11 +598,11 @@ static int chown_common(struct dentry *dentry, struct vfsmount *mnt,
        newattrs.ia_valid =  ATTR_CTIME;
        if (user != (uid_t) -1) {
                newattrs.ia_valid |= ATTR_UID;
-               newattrs.ia_uid = vx_map_uid(user);
+               newattrs.ia_uid = dx_map_uid(user);
        }
        if (group != (gid_t) -1) {
                newattrs.ia_valid |= ATTR_GID;
-               newattrs.ia_gid = vx_map_gid(group);
+               newattrs.ia_gid = dx_map_gid(group);
        }
        if (!S_ISDIR(inode->i_mode))
                newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
@@ -740,10 +619,15 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
        int error;
 
        error = user_path_walk(filename, &nd);
-       if (!error) {
+       if (error)
+               goto out;
+#ifdef CONFIG_VSERVER_COWBL
+       error = cow_check_and_break(&nd);
+       if (!error)
+#endif
                error = chown_common(nd.dentry, nd.mnt, user, group);
-               path_release(&nd);
-       }
+       path_release(&nd);
+out:
        return error;
 }
 
@@ -759,10 +643,14 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
 
        follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
        error = __user_walk_fd(dfd, filename, follow, &nd);
-       if (!error) {
+       if (error)
+               goto out;
+#ifdef CONFIG_VSERVER_COWBL
+       error = cow_check_and_break(&nd);
+       if (!error)
+#endif
                error = chown_common(nd.dentry, nd.mnt, user, group);
-               path_release(&nd);
-       }
+       path_release(&nd);
 out:
        return error;
 }
@@ -773,10 +661,15 @@ asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group
        int error;
 
        error = user_path_walk_link(filename, &nd);
-       if (!error) {
+       if (error)
+               goto out;
+#ifdef CONFIG_VSERVER_COWBL
+       error = cow_check_and_break(&nd);
+       if (!error)
+#endif
                error = chown_common(nd.dentry, nd.mnt, user, group);
-               path_release(&nd);
-       }
+       path_release(&nd);
+out:
        return error;
 }
 
@@ -785,12 +678,17 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
 {
        struct file * file;
        int error = -EBADF;
+       struct dentry * dentry;
 
        file = fget(fd);
-       if (file) {
-               error = chown_common(file->f_dentry, file->f_vfsmnt, user, group);
-               fput(file);
-       }
+       if (!file)
+               goto out;
+
+       dentry = file->f_path.dentry;
+       audit_inode(NULL, dentry->d_inode);
+       error = chown_common(dentry, file->f_vfsmnt, user, group);
+       fput(file);
+out:
        return error;
 }
 
@@ -812,8 +710,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);
@@ -847,8 +745,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);
@@ -903,6 +801,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()
@@ -942,7 +844,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);
@@ -984,9 +886,8 @@ int get_unused_fd(void)
 
 repeat:
        fdt = files_fdtable(files);
-       fd = find_next_zero_bit(fdt->open_fds->fds_bits,
-                               fdt->max_fdset,
-                               fdt->next_fd);
+       fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
+                               files->next_fd);
 
        /*
         * N.B. For clone tasks sharing a files structure, this test
@@ -1011,7 +912,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;
        vx_openfd_inc(fd);
 #if 1
        /* Sanity check */
@@ -1033,8 +934,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;
        vx_openfd_dec(fd);
 }
 
@@ -1087,7 +988,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
                                put_unused_fd(fd);
                                fd = PTR_ERR(f);
                        } else {
-                               fsnotify_open(f->f_dentry);
+                               fsnotify_open(f->f_path.dentry);
                                fd_install(fd, f);
                        }
                }
@@ -1108,7 +1009,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)
@@ -1123,7 +1023,6 @@ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
        prevent_tail_call(ret);
        return ret;
 }
-EXPORT_SYMBOL_GPL(sys_openat);
 
 #ifndef __alpha__
 
@@ -1152,7 +1051,7 @@ int filp_close(struct file *filp, fl_owner_t id)
        }
 
        if (filp->f_op && filp->f_op->flush)
-               retval = filp->f_op->flush(filp);
+               retval = filp->f_op->flush(filp, id);
 
        dnotify_flush(filp, id);
        locks_remove_posix(filp, id);
@@ -1172,6 +1071,7 @@ asmlinkage long sys_close(unsigned int fd)
        struct file * filp;
        struct files_struct *files = current->files;
        struct fdtable *fdt;
+       int retval;
 
        spin_lock(&files->file_lock);
        fdt = files_fdtable(files);
@@ -1184,7 +1084,16 @@ asmlinkage long sys_close(unsigned int fd)
        FD_CLR(fd, fdt->close_on_exec);
        __put_unused_fd(files, fd);
        spin_unlock(&files->file_lock);
-       return filp_close(filp, files);
+       retval = filp_close(filp, files);
+
+       /* can't restart close syscall because file table entry was cleared */
+       if (unlikely(retval == -ERESTARTSYS ||
+                    retval == -ERESTARTNOINTR ||
+                    retval == -ERESTARTNOHAND ||
+                    retval == -ERESTART_RESTARTBLOCK))
+               retval = -EINTR;
+
+       return retval;
 
 out_unlock:
        spin_unlock(&files->file_lock);
@@ -1200,6 +1109,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;
        }