21cb47be6f
The inode_owner_or_capable() helper determines whether the caller is the owner of the inode or is capable with respect to that inode. Allow it to handle idmapped mounts. If the inode is accessed through an idmapped mount it according to the mount's user namespace. Afterwards the checks are identical to non-idmapped mounts. If the initial user namespace is passed nothing changes so non-idmapped mounts will see identical behavior as before. Similarly, allow the inode_init_owner() helper to handle idmapped mounts. It initializes a new inode on idmapped mounts by mapping the fsuid and fsgid of the caller from the mount's user namespace. If the initial user namespace is passed nothing changes so non-idmapped mounts will see identical behavior as before. Link: https://lore.kernel.org/r/20210121131959.646623-7-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: James Morris <jamorris@linux.microsoft.com> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
233 lines
5.3 KiB
C
233 lines
5.3 KiB
C
/*
|
|
* Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
|
|
*/
|
|
|
|
#include <linux/capability.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mount.h>
|
|
#include "reiserfs.h"
|
|
#include <linux/time.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/compat.h>
|
|
|
|
/*
|
|
* reiserfs_ioctl - handler for ioctl for inode
|
|
* supported commands:
|
|
* 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect
|
|
* and prevent packing file (argument arg has t
|
|
* be non-zero)
|
|
* 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION
|
|
* 3) That's all for a while ...
|
|
*/
|
|
long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct inode *inode = file_inode(filp);
|
|
unsigned int flags;
|
|
int err = 0;
|
|
|
|
reiserfs_write_lock(inode->i_sb);
|
|
|
|
switch (cmd) {
|
|
case REISERFS_IOC_UNPACK:
|
|
if (S_ISREG(inode->i_mode)) {
|
|
if (arg)
|
|
err = reiserfs_unpack(inode, filp);
|
|
} else
|
|
err = -ENOTTY;
|
|
break;
|
|
/*
|
|
* following two cases are taken from fs/ext2/ioctl.c by Remy
|
|
* Card (card@masi.ibp.fr)
|
|
*/
|
|
case REISERFS_IOC_GETFLAGS:
|
|
if (!reiserfs_attrs(inode->i_sb)) {
|
|
err = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
flags = REISERFS_I(inode)->i_attrs;
|
|
err = put_user(flags, (int __user *)arg);
|
|
break;
|
|
case REISERFS_IOC_SETFLAGS:{
|
|
if (!reiserfs_attrs(inode->i_sb)) {
|
|
err = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
err = mnt_want_write_file(filp);
|
|
if (err)
|
|
break;
|
|
|
|
if (!inode_owner_or_capable(&init_user_ns, inode)) {
|
|
err = -EPERM;
|
|
goto setflags_out;
|
|
}
|
|
if (get_user(flags, (int __user *)arg)) {
|
|
err = -EFAULT;
|
|
goto setflags_out;
|
|
}
|
|
/*
|
|
* Is it quota file? Do not allow user to mess with it
|
|
*/
|
|
if (IS_NOQUOTA(inode)) {
|
|
err = -EPERM;
|
|
goto setflags_out;
|
|
}
|
|
err = vfs_ioc_setflags_prepare(inode,
|
|
REISERFS_I(inode)->i_attrs,
|
|
flags);
|
|
if (err)
|
|
goto setflags_out;
|
|
if ((flags & REISERFS_NOTAIL_FL) &&
|
|
S_ISREG(inode->i_mode)) {
|
|
int result;
|
|
|
|
result = reiserfs_unpack(inode, filp);
|
|
if (result) {
|
|
err = result;
|
|
goto setflags_out;
|
|
}
|
|
}
|
|
sd_attrs_to_i_attrs(flags, inode);
|
|
REISERFS_I(inode)->i_attrs = flags;
|
|
inode->i_ctime = current_time(inode);
|
|
mark_inode_dirty(inode);
|
|
setflags_out:
|
|
mnt_drop_write_file(filp);
|
|
break;
|
|
}
|
|
case REISERFS_IOC_GETVERSION:
|
|
err = put_user(inode->i_generation, (int __user *)arg);
|
|
break;
|
|
case REISERFS_IOC_SETVERSION:
|
|
if (!inode_owner_or_capable(&init_user_ns, inode)) {
|
|
err = -EPERM;
|
|
break;
|
|
}
|
|
err = mnt_want_write_file(filp);
|
|
if (err)
|
|
break;
|
|
if (get_user(inode->i_generation, (int __user *)arg)) {
|
|
err = -EFAULT;
|
|
goto setversion_out;
|
|
}
|
|
inode->i_ctime = current_time(inode);
|
|
mark_inode_dirty(inode);
|
|
setversion_out:
|
|
mnt_drop_write_file(filp);
|
|
break;
|
|
default:
|
|
err = -ENOTTY;
|
|
}
|
|
|
|
reiserfs_write_unlock(inode->i_sb);
|
|
|
|
return err;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
long reiserfs_compat_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
/*
|
|
* These are just misnamed, they actually
|
|
* get/put from/to user an int
|
|
*/
|
|
switch (cmd) {
|
|
case REISERFS_IOC32_UNPACK:
|
|
cmd = REISERFS_IOC_UNPACK;
|
|
break;
|
|
case REISERFS_IOC32_GETFLAGS:
|
|
cmd = REISERFS_IOC_GETFLAGS;
|
|
break;
|
|
case REISERFS_IOC32_SETFLAGS:
|
|
cmd = REISERFS_IOC_SETFLAGS;
|
|
break;
|
|
case REISERFS_IOC32_GETVERSION:
|
|
cmd = REISERFS_IOC_GETVERSION;
|
|
break;
|
|
case REISERFS_IOC32_SETVERSION:
|
|
cmd = REISERFS_IOC_SETVERSION;
|
|
break;
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
|
|
return reiserfs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
|
|
}
|
|
#endif
|
|
|
|
int reiserfs_commit_write(struct file *f, struct page *page,
|
|
unsigned from, unsigned to);
|
|
/*
|
|
* reiserfs_unpack
|
|
* Function try to convert tail from direct item into indirect.
|
|
* It set up nopack attribute in the REISERFS_I(inode)->nopack
|
|
*/
|
|
int reiserfs_unpack(struct inode *inode, struct file *filp)
|
|
{
|
|
int retval = 0;
|
|
int index;
|
|
struct page *page;
|
|
struct address_space *mapping;
|
|
unsigned long write_from;
|
|
unsigned long blocksize = inode->i_sb->s_blocksize;
|
|
|
|
if (inode->i_size == 0) {
|
|
REISERFS_I(inode)->i_flags |= i_nopack_mask;
|
|
return 0;
|
|
}
|
|
/* ioctl already done */
|
|
if (REISERFS_I(inode)->i_flags & i_nopack_mask) {
|
|
return 0;
|
|
}
|
|
|
|
/* we need to make sure nobody is changing the file size beneath us */
|
|
{
|
|
int depth = reiserfs_write_unlock_nested(inode->i_sb);
|
|
|
|
inode_lock(inode);
|
|
reiserfs_write_lock_nested(inode->i_sb, depth);
|
|
}
|
|
|
|
reiserfs_write_lock(inode->i_sb);
|
|
|
|
write_from = inode->i_size & (blocksize - 1);
|
|
/* if we are on a block boundary, we are already unpacked. */
|
|
if (write_from == 0) {
|
|
REISERFS_I(inode)->i_flags |= i_nopack_mask;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* we unpack by finding the page with the tail, and calling
|
|
* __reiserfs_write_begin on that page. This will force a
|
|
* reiserfs_get_block to unpack the tail for us.
|
|
*/
|
|
index = inode->i_size >> PAGE_SHIFT;
|
|
mapping = inode->i_mapping;
|
|
page = grab_cache_page(mapping, index);
|
|
retval = -ENOMEM;
|
|
if (!page) {
|
|
goto out;
|
|
}
|
|
retval = __reiserfs_write_begin(page, write_from, 0);
|
|
if (retval)
|
|
goto out_unlock;
|
|
|
|
/* conversion can change page contents, must flush */
|
|
flush_dcache_page(page);
|
|
retval = reiserfs_commit_write(NULL, page, write_from, write_from);
|
|
REISERFS_I(inode)->i_flags |= i_nopack_mask;
|
|
|
|
out_unlock:
|
|
unlock_page(page);
|
|
put_page(page);
|
|
|
|
out:
|
|
inode_unlock(inode);
|
|
reiserfs_write_unlock(inode->i_sb);
|
|
return retval;
|
|
}
|