]> Pileus Git - ~andy/linux/blobdiff - fs/open.c
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[~andy/linux] / fs / open.c
index 48afc5c139d247e634d9926507d2a8d9354015db..f83ca80cc59a1d72c8b933d7562efc853cfe3f8c 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -573,13 +573,15 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
 {
        struct path path;
        int error = -EINVAL;
-       int follow;
+       int lookup_flags;
 
-       if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
+       if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
                goto out;
 
-       follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
-       error = user_path_at(dfd, filename, follow, &path);
+       lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+       if (flag & AT_EMPTY_PATH)
+               lookup_flags |= LOOKUP_EMPTY;
+       error = user_path_at(dfd, filename, lookup_flags, &path);
        if (error)
                goto out;
        error = mnt_want_write(path.mnt);
@@ -669,11 +671,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                                        int (*open)(struct inode *, struct file *),
                                        const struct cred *cred)
 {
+       static const struct file_operations empty_fops = {};
        struct inode *inode;
        int error;
 
        f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
                                FMODE_PREAD | FMODE_PWRITE;
+
+       if (unlikely(f->f_flags & O_PATH))
+               f->f_mode = FMODE_PATH;
+
        inode = dentry->d_inode;
        if (f->f_mode & FMODE_WRITE) {
                error = __get_file_write_access(inode, mnt);
@@ -687,9 +694,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        f->f_path.dentry = dentry;
        f->f_path.mnt = mnt;
        f->f_pos = 0;
-       f->f_op = fops_get(inode->i_fop);
        file_sb_list_add(f, inode->i_sb);
 
+       if (unlikely(f->f_mode & FMODE_PATH)) {
+               f->f_op = &empty_fops;
+               return f;
+       }
+
+       f->f_op = fops_get(inode->i_fop);
+
        error = security_dentry_open(f, cred);
        if (error)
                goto cleanup_all;
@@ -701,7 +714,8 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                if (error)
                        goto cleanup_all;
        }
-       ima_counts_get(f);
+       if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+               i_readcount_inc(inode);
 
        f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
@@ -911,9 +925,18 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)
        if (flags & __O_SYNC)
                flags |= O_DSYNC;
 
-       op->open_flag = flags;
+       /*
+        * If we have O_PATH in the open flag. Then we
+        * cannot have anything other than the below set of flags
+        */
+       if (flags & O_PATH) {
+               flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
+               acc_mode = 0;
+       } else {
+               acc_mode = MAY_OPEN | ACC_MODE(flags);
+       }
 
-       acc_mode = MAY_OPEN | ACC_MODE(flags);
+       op->open_flag = flags;
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flags & O_TRUNC)
@@ -926,7 +949,8 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op)
 
        op->acc_mode = acc_mode;
 
-       op->intent = LOOKUP_OPEN;
+       op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
+
        if (flags & O_CREAT) {
                op->intent |= LOOKUP_CREATE;
                if (flags & O_EXCL)
@@ -1053,8 +1077,10 @@ int filp_close(struct file *filp, fl_owner_t id)
        if (filp->f_op && filp->f_op->flush)
                retval = filp->f_op->flush(filp, id);
 
-       dnotify_flush(filp, id);
-       locks_remove_posix(filp, id);
+       if (likely(!(filp->f_mode & FMODE_PATH))) {
+               dnotify_flush(filp, id);
+               locks_remove_posix(filp, id);
+       }
        fput(filp);
        return retval;
 }