overlayfs: add statfs support
[cascardo/linux.git] / fs / namei.c
index a7b05bf..42df664 100644 (file)
@@ -416,6 +416,7 @@ int __inode_permission(struct inode *inode, int mask)
 
        return security_inode_permission(inode, mask);
 }
+EXPORT_SYMBOL(__inode_permission);
 
 /**
  * sb_permission - Check superblock-level permissions
@@ -1306,7 +1307,8 @@ static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir,
                                if (error < 0) {
                                        dput(dentry);
                                        return ERR_PTR(error);
-                               } else if (!d_invalidate(dentry)) {
+                               } else {
+                                       d_invalidate(dentry);
                                        dput(dentry);
                                        dentry = NULL;
                                }
@@ -1435,10 +1437,9 @@ unlazy:
                        dput(dentry);
                        return status;
                }
-               if (!d_invalidate(dentry)) {
-                       dput(dentry);
-                       goto need_lookup;
-               }
+               d_invalidate(dentry);
+               dput(dentry);
+               goto need_lookup;
        }
 
        path->mnt = mnt;
@@ -1950,7 +1951,7 @@ static int path_lookupat(int dfd, const char *name,
        err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base);
 
        if (unlikely(err))
-               return err;
+               goto out;
 
        current->total_link_count = 0;
        err = link_path_walk(name, nd);
@@ -1982,6 +1983,7 @@ static int path_lookupat(int dfd, const char *name,
                }
        }
 
+out:
        if (base)
                fput(base);
 
@@ -2301,7 +2303,7 @@ path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags
 
        err = path_init(dfd, name, flags | LOOKUP_PARENT, &nd, &base);
        if (unlikely(err))
-               return err;
+               goto out;
 
        current->total_link_count = 0;
        err = link_path_walk(name, &nd);
@@ -2382,22 +2384,17 @@ kern_path_mountpoint(int dfd, const char *name, struct path *path,
 }
 EXPORT_SYMBOL(kern_path_mountpoint);
 
-/*
- * It's inline, so penalty for filesystems that don't use sticky bit is
- * minimal.
- */
-static inline int check_sticky(struct inode *dir, struct inode *inode)
+int __check_sticky(struct inode *dir, struct inode *inode)
 {
        kuid_t fsuid = current_fsuid();
 
-       if (!(dir->i_mode & S_ISVTX))
-               return 0;
        if (uid_eq(inode->i_uid, fsuid))
                return 0;
        if (uid_eq(dir->i_uid, fsuid))
                return 0;
        return !capable_wrt_inode_uidgid(inode, CAP_FOWNER);
 }
+EXPORT_SYMBOL(__check_sticky);
 
 /*
  *     Check whether we can remove a link victim from directory dir, check
@@ -3063,9 +3060,12 @@ finish_open_created:
        error = may_open(&nd->path, acc_mode, open_flag);
        if (error)
                goto out;
-       file->f_path.mnt = nd->path.mnt;
-       error = finish_open(file, nd->path.dentry, NULL, opened);
-       if (error) {
+
+       BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
+       error = vfs_open(&nd->path, file, current_cred());
+       if (!error) {
+               *opened |= FILE_OPENED;
+       } else {
                if (error == -EOPENSTALE)
                        goto stale_open;
                goto out;
@@ -3074,7 +3074,7 @@ opened:
        error = open_check_o_direct(file);
        if (error)
                goto exit_fput;
-       error = ima_file_check(file, op->acc_mode);
+       error = ima_file_check(file, op->acc_mode, *opened);
        if (error)
                goto exit_fput;
 
@@ -3565,7 +3565,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        mutex_lock(&dentry->d_inode->i_mutex);
 
        error = -EBUSY;
-       if (d_mountpoint(dentry))
+       if (is_local_mountpoint(dentry))
                goto out;
 
        error = security_inode_rmdir(dir, dentry);
@@ -3579,6 +3579,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 
        dentry->d_inode->i_flags |= S_DEAD;
        dont_mount(dentry);
+       detach_mounts(dentry);
 
 out:
        mutex_unlock(&dentry->d_inode->i_mutex);
@@ -3681,7 +3682,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
                return -EPERM;
 
        mutex_lock(&target->i_mutex);
-       if (d_mountpoint(dentry))
+       if (is_local_mountpoint(dentry))
                error = -EBUSY;
        else {
                error = security_inode_unlink(dir, dentry);
@@ -3690,8 +3691,10 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
                        if (error)
                                goto out;
                        error = dir->i_op->unlink(dir, dentry);
-                       if (!error)
+                       if (!error) {
                                dont_mount(dentry);
+                               detach_mounts(dentry);
+                       }
                }
        }
 out:
@@ -4126,7 +4129,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                mutex_lock(&target->i_mutex);
 
        error = -EBUSY;
-       if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
+       if (is_local_mountpoint(old_dentry) || is_local_mountpoint(new_dentry))
                goto out;
 
        if (max_links && new_dir != old_dir) {
@@ -4164,6 +4167,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                if (is_dir)
                        target->i_flags |= S_DEAD;
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
        }
        if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) {
                if (!(flags & RENAME_EXCHANGE))
@@ -4205,12 +4209,16 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
        bool should_retry = false;
        int error;
 
-       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
+       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
                return -EINVAL;
 
-       if ((flags & RENAME_NOREPLACE) && (flags & RENAME_EXCHANGE))
+       if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) &&
+           (flags & RENAME_EXCHANGE))
                return -EINVAL;
 
+       if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
+               return -EPERM;
+
 retry:
        from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
        if (IS_ERR(from)) {
@@ -4342,6 +4350,20 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
        return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
+int vfs_whiteout(struct inode *dir, struct dentry *dentry)
+{
+       int error = may_create(dir, dentry);
+       if (error)
+               return error;
+
+       if (!dir->i_op->mknod)
+               return -EPERM;
+
+       return dir->i_op->mknod(dir, dentry,
+                               S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+}
+EXPORT_SYMBOL(vfs_whiteout);
+
 int readlink_copy(char __user *buffer, int buflen, const char *link)
 {
        int len = PTR_ERR(link);