xref: /openbmc/linux/fs/attr.c (revision 0eeca28300df110bd6ed54b31193c83b87921443)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/fs/attr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992  Linus Torvalds
51da177e4SLinus Torvalds  *  changes by Thomas Schoebel-Theuer
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <linux/module.h>
91da177e4SLinus Torvalds #include <linux/time.h>
101da177e4SLinus Torvalds #include <linux/mm.h>
111da177e4SLinus Torvalds #include <linux/string.h>
121da177e4SLinus Torvalds #include <linux/smp_lock.h>
13*0eeca283SRobert Love #include <linux/fsnotify.h>
141da177e4SLinus Torvalds #include <linux/fcntl.h>
151da177e4SLinus Torvalds #include <linux/quotaops.h>
161da177e4SLinus Torvalds #include <linux/security.h>
171da177e4SLinus Torvalds #include <linux/time.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /* Taken over from the old code... */
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds /* POSIX UID/GID verification for setting inode attributes. */
221da177e4SLinus Torvalds int inode_change_ok(struct inode *inode, struct iattr *attr)
231da177e4SLinus Torvalds {
241da177e4SLinus Torvalds 	int retval = -EPERM;
251da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds 	/* If force is set do it anyway. */
281da177e4SLinus Torvalds 	if (ia_valid & ATTR_FORCE)
291da177e4SLinus Torvalds 		goto fine;
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 	/* Make sure a caller can chown. */
321da177e4SLinus Torvalds 	if ((ia_valid & ATTR_UID) &&
331da177e4SLinus Torvalds 	    (current->fsuid != inode->i_uid ||
341da177e4SLinus Torvalds 	     attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
351da177e4SLinus Torvalds 		goto error;
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	/* Make sure caller can chgrp. */
381da177e4SLinus Torvalds 	if ((ia_valid & ATTR_GID) &&
391da177e4SLinus Torvalds 	    (current->fsuid != inode->i_uid ||
401da177e4SLinus Torvalds 	    (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
411da177e4SLinus Torvalds 	    !capable(CAP_CHOWN))
421da177e4SLinus Torvalds 		goto error;
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 	/* Make sure a caller can chmod. */
451da177e4SLinus Torvalds 	if (ia_valid & ATTR_MODE) {
461da177e4SLinus Torvalds 		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
471da177e4SLinus Torvalds 			goto error;
481da177e4SLinus Torvalds 		/* Also check the setgid bit! */
491da177e4SLinus Torvalds 		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
501da177e4SLinus Torvalds 				inode->i_gid) && !capable(CAP_FSETID))
511da177e4SLinus Torvalds 			attr->ia_mode &= ~S_ISGID;
521da177e4SLinus Torvalds 	}
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	/* Check for setting the inode time. */
551da177e4SLinus Torvalds 	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
561da177e4SLinus Torvalds 		if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
571da177e4SLinus Torvalds 			goto error;
581da177e4SLinus Torvalds 	}
591da177e4SLinus Torvalds fine:
601da177e4SLinus Torvalds 	retval = 0;
611da177e4SLinus Torvalds error:
621da177e4SLinus Torvalds 	return retval;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds EXPORT_SYMBOL(inode_change_ok);
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds int inode_setattr(struct inode * inode, struct iattr * attr)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
701da177e4SLinus Torvalds 	int error = 0;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	if (ia_valid & ATTR_SIZE) {
731da177e4SLinus Torvalds 		if (attr->ia_size != i_size_read(inode)) {
741da177e4SLinus Torvalds 			error = vmtruncate(inode, attr->ia_size);
751da177e4SLinus Torvalds 			if (error || (ia_valid == ATTR_SIZE))
761da177e4SLinus Torvalds 				goto out;
771da177e4SLinus Torvalds 		} else {
781da177e4SLinus Torvalds 			/*
791da177e4SLinus Torvalds 			 * We skipped the truncate but must still update
801da177e4SLinus Torvalds 			 * timestamps
811da177e4SLinus Torvalds 			 */
821da177e4SLinus Torvalds 			ia_valid |= ATTR_MTIME|ATTR_CTIME;
831da177e4SLinus Torvalds 		}
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	if (ia_valid & ATTR_UID)
871da177e4SLinus Torvalds 		inode->i_uid = attr->ia_uid;
881da177e4SLinus Torvalds 	if (ia_valid & ATTR_GID)
891da177e4SLinus Torvalds 		inode->i_gid = attr->ia_gid;
901da177e4SLinus Torvalds 	if (ia_valid & ATTR_ATIME)
911da177e4SLinus Torvalds 		inode->i_atime = timespec_trunc(attr->ia_atime,
921da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
931da177e4SLinus Torvalds 	if (ia_valid & ATTR_MTIME)
941da177e4SLinus Torvalds 		inode->i_mtime = timespec_trunc(attr->ia_mtime,
951da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
961da177e4SLinus Torvalds 	if (ia_valid & ATTR_CTIME)
971da177e4SLinus Torvalds 		inode->i_ctime = timespec_trunc(attr->ia_ctime,
981da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
991da177e4SLinus Torvalds 	if (ia_valid & ATTR_MODE) {
1001da177e4SLinus Torvalds 		umode_t mode = attr->ia_mode;
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
1031da177e4SLinus Torvalds 			mode &= ~S_ISGID;
1041da177e4SLinus Torvalds 		inode->i_mode = mode;
1051da177e4SLinus Torvalds 	}
1061da177e4SLinus Torvalds 	mark_inode_dirty(inode);
1071da177e4SLinus Torvalds out:
1081da177e4SLinus Torvalds 	return error;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds EXPORT_SYMBOL(inode_setattr);
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds int notify_change(struct dentry * dentry, struct iattr * attr)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	struct inode *inode = dentry->d_inode;
1151da177e4SLinus Torvalds 	mode_t mode;
1161da177e4SLinus Torvalds 	int error;
1171da177e4SLinus Torvalds 	struct timespec now;
1181da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	if (!inode)
1211da177e4SLinus Torvalds 		BUG();
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	mode = inode->i_mode;
1241da177e4SLinus Torvalds 	now = current_fs_time(inode->i_sb);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	attr->ia_ctime = now;
1271da177e4SLinus Torvalds 	if (!(ia_valid & ATTR_ATIME_SET))
1281da177e4SLinus Torvalds 		attr->ia_atime = now;
1291da177e4SLinus Torvalds 	if (!(ia_valid & ATTR_MTIME_SET))
1301da177e4SLinus Torvalds 		attr->ia_mtime = now;
1311da177e4SLinus Torvalds 	if (ia_valid & ATTR_KILL_SUID) {
1321da177e4SLinus Torvalds 		attr->ia_valid &= ~ATTR_KILL_SUID;
1331da177e4SLinus Torvalds 		if (mode & S_ISUID) {
1341da177e4SLinus Torvalds 			if (!(ia_valid & ATTR_MODE)) {
1351da177e4SLinus Torvalds 				ia_valid = attr->ia_valid |= ATTR_MODE;
1361da177e4SLinus Torvalds 				attr->ia_mode = inode->i_mode;
1371da177e4SLinus Torvalds 			}
1381da177e4SLinus Torvalds 			attr->ia_mode &= ~S_ISUID;
1391da177e4SLinus Torvalds 		}
1401da177e4SLinus Torvalds 	}
1411da177e4SLinus Torvalds 	if (ia_valid & ATTR_KILL_SGID) {
1421da177e4SLinus Torvalds 		attr->ia_valid &= ~ ATTR_KILL_SGID;
1431da177e4SLinus Torvalds 		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
1441da177e4SLinus Torvalds 			if (!(ia_valid & ATTR_MODE)) {
1451da177e4SLinus Torvalds 				ia_valid = attr->ia_valid |= ATTR_MODE;
1461da177e4SLinus Torvalds 				attr->ia_mode = inode->i_mode;
1471da177e4SLinus Torvalds 			}
1481da177e4SLinus Torvalds 			attr->ia_mode &= ~S_ISGID;
1491da177e4SLinus Torvalds 		}
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 	if (!attr->ia_valid)
1521da177e4SLinus Torvalds 		return 0;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	if (ia_valid & ATTR_SIZE)
1551da177e4SLinus Torvalds 		down_write(&dentry->d_inode->i_alloc_sem);
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	if (inode->i_op && inode->i_op->setattr) {
1581da177e4SLinus Torvalds 		error = security_inode_setattr(dentry, attr);
1591da177e4SLinus Torvalds 		if (!error)
1601da177e4SLinus Torvalds 			error = inode->i_op->setattr(dentry, attr);
1611da177e4SLinus Torvalds 	} else {
1621da177e4SLinus Torvalds 		error = inode_change_ok(inode, attr);
1631da177e4SLinus Torvalds 		if (!error)
1641da177e4SLinus Torvalds 			error = security_inode_setattr(dentry, attr);
1651da177e4SLinus Torvalds 		if (!error) {
1661da177e4SLinus Torvalds 			if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
1671da177e4SLinus Torvalds 			    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
1681da177e4SLinus Torvalds 				error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
1691da177e4SLinus Torvalds 			if (!error)
1701da177e4SLinus Torvalds 				error = inode_setattr(inode, attr);
1711da177e4SLinus Torvalds 		}
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	if (ia_valid & ATTR_SIZE)
1751da177e4SLinus Torvalds 		up_write(&dentry->d_inode->i_alloc_sem);
1761da177e4SLinus Torvalds 
177*0eeca283SRobert Love 	if (!error)
178*0eeca283SRobert Love 		fsnotify_change(dentry, ia_valid);
179*0eeca283SRobert Love 
1801da177e4SLinus Torvalds 	return error;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds EXPORT_SYMBOL(notify_change);
184