Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c27c65e authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Al Viro
Browse files

check ATTR_SIZE contraints in inode_change_ok



Make sure we check the truncate constraints early on in ->setattr by adding
those checks to inode_change_ok.  Also clean up and document inode_change_ok
to make this obvious.

As a fallout we don't have to call inode_newsize_ok from simple_setsize and
simplify it down to a truncate_setsize which doesn't return an error.  This
simplifies a lot of setattr implementations and means we use truncate_setsize
almost everywhere.  Get rid of fat_setsize now that it's trivial and mark
ext2_setsize static to make the calling convention obvious.

Keep the inode_newsize_ok in vmtruncate for now as all callers need an
audit for its removal anyway.

Note: setattr code in ecryptfs doesn't call inode_change_ok at all and
needs a deeper audit, but that is left for later.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent db78b877
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -333,10 +333,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)

	/* XXX: this is missing some actual on-disk truncation.. */
	if (ia_valid & ATTR_SIZE)
		error = simple_setsize(inode, attr->ia_size);

	if (error)
		goto out;
		truncate_setsize(inode, attr->ia_size);

	if (ia_valid & ATTR_MTIME) {
		inode->i_mtime = attr->ia_mtime;
+30 −14
Original line number Diff line number Diff line
@@ -14,35 +14,53 @@
#include <linux/fcntl.h>
#include <linux/security.h>

/* Taken over from the old code... */

/* POSIX UID/GID verification for setting inode attributes. */
/**
 * inode_change_ok - check if attribute changes to an inode are allowed
 * @inode:	inode to check
 * @attr:	attributes to change
 *
 * Check if we are allowed to change the attributes contained in @attr
 * in the given inode.  This includes the normal unix access permission
 * checks, as well as checks for rlimits and others.
 *
 * Should be called as the first thing in ->setattr implementations,
 * possibly after taking additional locks.
 */
int inode_change_ok(const struct inode *inode, struct iattr *attr)
{
	int retval = -EPERM;
	unsigned int ia_valid = attr->ia_valid;

	/*
	 * First check size constraints.  These can't be overriden using
	 * ATTR_FORCE.
	 */
	if (ia_valid & ATTR_SIZE) {
		int error = inode_newsize_ok(inode, attr->ia_size);
		if (error)
			return error;
	}

	/* If force is set do it anyway. */
	if (ia_valid & ATTR_FORCE)
		goto fine;
		return 0;

	/* Make sure a caller can chown. */
	if ((ia_valid & ATTR_UID) &&
	    (current_fsuid() != inode->i_uid ||
	     attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
		goto error;
		return -EPERM;

	/* Make sure caller can chgrp. */
	if ((ia_valid & ATTR_GID) &&
	    (current_fsuid() != inode->i_uid ||
	    (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
	    !capable(CAP_CHOWN))
		goto error;
		return -EPERM;

	/* Make sure a caller can chmod. */
	if (ia_valid & ATTR_MODE) {
		if (!is_owner_or_cap(inode))
			goto error;
			return -EPERM;
		/* Also check the setgid bit! */
		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
				inode->i_gid) && !capable(CAP_FSETID))
@@ -52,12 +70,10 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)
	/* Check for setting the inode time. */
	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
		if (!is_owner_or_cap(inode))
			goto error;
			return -EPERM;
	}
fine:
	retval = 0;
error:
	return retval;

	return 0;
}
EXPORT_SYMBOL(inode_change_ok);

@@ -113,7 +129,7 @@ EXPORT_SYMBOL(inode_newsize_ok);
 *
 * setattr_copy updates the inode's metadata with that specified
 * in attr. Noticably missing is inode size update, which is more complex
 * as it requires pagecache updates. See simple_setsize.
 * as it requires pagecache updates.
 *
 * The inode is not marked as dirty after this operation. The rationale is
 * that for "simple" filesystems, the struct inode is the inode storage.
+14 −4
Original line number Diff line number Diff line
@@ -804,10 +804,20 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
		size_t num_zeros = (PAGE_CACHE_SIZE
				    - (ia->ia_size & ~PAGE_CACHE_MASK));

		if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
			rc = simple_setsize(inode, ia->ia_size);

		/*
		 * XXX(truncate) this should really happen at the begginning
		 * of ->setattr.  But the code is too messy to that as part
		 * of a larger patch.  ecryptfs is also totally missing out
		 * on the inode_change_ok check at the beginning of
		 * ->setattr while would include this.
		 */
		rc = inode_newsize_ok(inode, ia->ia_size);
		if (rc)
			goto out;

		if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
			truncate_setsize(inode, ia->ia_size);
			lower_ia->ia_size = ia->ia_size;
			lower_ia->ia_valid |= ATTR_SIZE;
			goto out;
@@ -830,7 +840,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
				goto out;
			}
		}
		simple_setsize(inode, ia->ia_size);
		truncate_setsize(inode, ia->ia_size);
		rc = ecryptfs_write_inode_size_to_metadata(inode);
		if (rc) {
			printk(KERN_ERR	"Problem with "
+2 −10
Original line number Diff line number Diff line
@@ -1156,15 +1156,10 @@ static void ext2_truncate_blocks(struct inode *inode, loff_t offset)
	__ext2_truncate_blocks(inode, offset);
}

int ext2_setsize(struct inode *inode, loff_t newsize)
static int ext2_setsize(struct inode *inode, loff_t newsize)
{
	loff_t oldsize;
	int error;

	error = inode_newsize_ok(inode, newsize);
	if (error)
		return error;

	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
	    S_ISLNK(inode->i_mode)))
		return -EINVAL;
@@ -1184,10 +1179,7 @@ int ext2_setsize(struct inode *inode, loff_t newsize)
	if (error)
		return error;

	oldsize = inode->i_size;
	i_size_write(inode, newsize);
	truncate_pagecache(inode, oldsize, newsize);

	truncate_setsize(inode, newsize);
	__ext2_truncate_blocks(inode, newsize);

	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+0 −1
Original line number Diff line number Diff line
@@ -306,7 +306,6 @@ extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
extern const struct file_operations fat_file_operations;
extern const struct inode_operations fat_file_inode_operations;
extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
extern int fat_setsize(struct inode *inode, loff_t offset);
extern void fat_truncate_blocks(struct inode *inode, loff_t offset);
extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
		       struct kstat *stat);
Loading