xref: /openbmc/linux/fs/attr.c (revision 7bb46a6734a7e1ad4beaecc11cae7ed3ff81d30f)
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>
1216f7e0feSRandy Dunlap #include <linux/capability.h>
130eeca283SRobert Love #include <linux/fsnotify.h>
141da177e4SLinus Torvalds #include <linux/fcntl.h>
151da177e4SLinus Torvalds #include <linux/security.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* Taken over from the old code... */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /* POSIX UID/GID verification for setting inode attributes. */
2025d9e2d1Snpiggin@suse.de int inode_change_ok(const struct inode *inode, struct iattr *attr)
211da177e4SLinus Torvalds {
221da177e4SLinus Torvalds 	int retval = -EPERM;
231da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	/* If force is set do it anyway. */
261da177e4SLinus Torvalds 	if (ia_valid & ATTR_FORCE)
271da177e4SLinus Torvalds 		goto fine;
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 	/* Make sure a caller can chown. */
301da177e4SLinus Torvalds 	if ((ia_valid & ATTR_UID) &&
31da9592edSDavid Howells 	    (current_fsuid() != inode->i_uid ||
321da177e4SLinus Torvalds 	     attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
331da177e4SLinus Torvalds 		goto error;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	/* Make sure caller can chgrp. */
361da177e4SLinus Torvalds 	if ((ia_valid & ATTR_GID) &&
37da9592edSDavid Howells 	    (current_fsuid() != inode->i_uid ||
381da177e4SLinus Torvalds 	    (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
391da177e4SLinus Torvalds 	    !capable(CAP_CHOWN))
401da177e4SLinus Torvalds 		goto error;
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	/* Make sure a caller can chmod. */
431da177e4SLinus Torvalds 	if (ia_valid & ATTR_MODE) {
443bd858abSSatyam Sharma 		if (!is_owner_or_cap(inode))
451da177e4SLinus Torvalds 			goto error;
461da177e4SLinus Torvalds 		/* Also check the setgid bit! */
471da177e4SLinus Torvalds 		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
481da177e4SLinus Torvalds 				inode->i_gid) && !capable(CAP_FSETID))
491da177e4SLinus Torvalds 			attr->ia_mode &= ~S_ISGID;
501da177e4SLinus Torvalds 	}
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	/* Check for setting the inode time. */
539767d749SMiklos Szeredi 	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
543bd858abSSatyam Sharma 		if (!is_owner_or_cap(inode))
551da177e4SLinus Torvalds 			goto error;
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds fine:
581da177e4SLinus Torvalds 	retval = 0;
591da177e4SLinus Torvalds error:
601da177e4SLinus Torvalds 	return retval;
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds EXPORT_SYMBOL(inode_change_ok);
631da177e4SLinus Torvalds 
6425d9e2d1Snpiggin@suse.de /**
6525d9e2d1Snpiggin@suse.de  * inode_newsize_ok - may this inode be truncated to a given size
6625d9e2d1Snpiggin@suse.de  * @inode:	the inode to be truncated
6725d9e2d1Snpiggin@suse.de  * @offset:	the new size to assign to the inode
6825d9e2d1Snpiggin@suse.de  * @Returns:	0 on success, -ve errno on failure
6925d9e2d1Snpiggin@suse.de  *
70*7bb46a67Snpiggin@suse.de  * inode_newsize_ok must be called with i_mutex held.
71*7bb46a67Snpiggin@suse.de  *
7225d9e2d1Snpiggin@suse.de  * inode_newsize_ok will check filesystem limits and ulimits to check that the
7325d9e2d1Snpiggin@suse.de  * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ
7425d9e2d1Snpiggin@suse.de  * when necessary. Caller must not proceed with inode size change if failure is
7525d9e2d1Snpiggin@suse.de  * returned. @inode must be a file (not directory), with appropriate
7625d9e2d1Snpiggin@suse.de  * permissions to allow truncate (inode_newsize_ok does NOT check these
7725d9e2d1Snpiggin@suse.de  * conditions).
7825d9e2d1Snpiggin@suse.de  */
7925d9e2d1Snpiggin@suse.de int inode_newsize_ok(const struct inode *inode, loff_t offset)
8025d9e2d1Snpiggin@suse.de {
8125d9e2d1Snpiggin@suse.de 	if (inode->i_size < offset) {
8225d9e2d1Snpiggin@suse.de 		unsigned long limit;
8325d9e2d1Snpiggin@suse.de 
84d554ed89SJiri Slaby 		limit = rlimit(RLIMIT_FSIZE);
8525d9e2d1Snpiggin@suse.de 		if (limit != RLIM_INFINITY && offset > limit)
8625d9e2d1Snpiggin@suse.de 			goto out_sig;
8725d9e2d1Snpiggin@suse.de 		if (offset > inode->i_sb->s_maxbytes)
8825d9e2d1Snpiggin@suse.de 			goto out_big;
8925d9e2d1Snpiggin@suse.de 	} else {
9025d9e2d1Snpiggin@suse.de 		/*
9125d9e2d1Snpiggin@suse.de 		 * truncation of in-use swapfiles is disallowed - it would
9225d9e2d1Snpiggin@suse.de 		 * cause subsequent swapout to scribble on the now-freed
9325d9e2d1Snpiggin@suse.de 		 * blocks.
9425d9e2d1Snpiggin@suse.de 		 */
9525d9e2d1Snpiggin@suse.de 		if (IS_SWAPFILE(inode))
9625d9e2d1Snpiggin@suse.de 			return -ETXTBSY;
9725d9e2d1Snpiggin@suse.de 	}
9825d9e2d1Snpiggin@suse.de 
9925d9e2d1Snpiggin@suse.de 	return 0;
10025d9e2d1Snpiggin@suse.de out_sig:
10125d9e2d1Snpiggin@suse.de 	send_sig(SIGXFSZ, current, 0);
10225d9e2d1Snpiggin@suse.de out_big:
10325d9e2d1Snpiggin@suse.de 	return -EFBIG;
10425d9e2d1Snpiggin@suse.de }
10525d9e2d1Snpiggin@suse.de EXPORT_SYMBOL(inode_newsize_ok);
10625d9e2d1Snpiggin@suse.de 
107*7bb46a67Snpiggin@suse.de /**
108*7bb46a67Snpiggin@suse.de  * generic_setattr - copy simple metadata updates into the generic inode
109*7bb46a67Snpiggin@suse.de  * @inode:	the inode to be updated
110*7bb46a67Snpiggin@suse.de  * @attr:	the new attributes
111*7bb46a67Snpiggin@suse.de  *
112*7bb46a67Snpiggin@suse.de  * generic_setattr must be called with i_mutex held.
113*7bb46a67Snpiggin@suse.de  *
114*7bb46a67Snpiggin@suse.de  * generic_setattr updates the inode's metadata with that specified
115*7bb46a67Snpiggin@suse.de  * in attr. Noticably missing is inode size update, which is more complex
116*7bb46a67Snpiggin@suse.de  * as it requires pagecache updates. See simple_setsize.
117*7bb46a67Snpiggin@suse.de  *
118*7bb46a67Snpiggin@suse.de  * The inode is not marked as dirty after this operation. The rationale is
119*7bb46a67Snpiggin@suse.de  * that for "simple" filesystems, the struct inode is the inode storage.
120*7bb46a67Snpiggin@suse.de  * The caller is free to mark the inode dirty afterwards if needed.
121*7bb46a67Snpiggin@suse.de  */
122*7bb46a67Snpiggin@suse.de void generic_setattr(struct inode *inode, const struct iattr *attr)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	if (ia_valid & ATTR_UID)
1271da177e4SLinus Torvalds 		inode->i_uid = attr->ia_uid;
1281da177e4SLinus Torvalds 	if (ia_valid & ATTR_GID)
1291da177e4SLinus Torvalds 		inode->i_gid = attr->ia_gid;
1301da177e4SLinus Torvalds 	if (ia_valid & ATTR_ATIME)
1311da177e4SLinus Torvalds 		inode->i_atime = timespec_trunc(attr->ia_atime,
1321da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
1331da177e4SLinus Torvalds 	if (ia_valid & ATTR_MTIME)
1341da177e4SLinus Torvalds 		inode->i_mtime = timespec_trunc(attr->ia_mtime,
1351da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
1361da177e4SLinus Torvalds 	if (ia_valid & ATTR_CTIME)
1371da177e4SLinus Torvalds 		inode->i_ctime = timespec_trunc(attr->ia_ctime,
1381da177e4SLinus Torvalds 						inode->i_sb->s_time_gran);
1391da177e4SLinus Torvalds 	if (ia_valid & ATTR_MODE) {
1401da177e4SLinus Torvalds 		umode_t mode = attr->ia_mode;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
1431da177e4SLinus Torvalds 			mode &= ~S_ISGID;
1441da177e4SLinus Torvalds 		inode->i_mode = mode;
1451da177e4SLinus Torvalds 	}
146*7bb46a67Snpiggin@suse.de }
147*7bb46a67Snpiggin@suse.de EXPORT_SYMBOL(generic_setattr);
148*7bb46a67Snpiggin@suse.de 
149*7bb46a67Snpiggin@suse.de /*
150*7bb46a67Snpiggin@suse.de  * note this function is deprecated, the new truncate sequence should be
151*7bb46a67Snpiggin@suse.de  * used instead -- see eg. simple_setsize, generic_setattr.
152*7bb46a67Snpiggin@suse.de  */
153*7bb46a67Snpiggin@suse.de int inode_setattr(struct inode *inode, const struct iattr *attr)
154*7bb46a67Snpiggin@suse.de {
155*7bb46a67Snpiggin@suse.de 	unsigned int ia_valid = attr->ia_valid;
156*7bb46a67Snpiggin@suse.de 
157*7bb46a67Snpiggin@suse.de 	if (ia_valid & ATTR_SIZE &&
158*7bb46a67Snpiggin@suse.de 	    attr->ia_size != i_size_read(inode)) {
159*7bb46a67Snpiggin@suse.de 		int error;
160*7bb46a67Snpiggin@suse.de 
161*7bb46a67Snpiggin@suse.de 		error = vmtruncate(inode, attr->ia_size);
162*7bb46a67Snpiggin@suse.de 		if (error)
163*7bb46a67Snpiggin@suse.de 			return error;
164*7bb46a67Snpiggin@suse.de 	}
165*7bb46a67Snpiggin@suse.de 
166*7bb46a67Snpiggin@suse.de 	generic_setattr(inode, attr);
167*7bb46a67Snpiggin@suse.de 
1681da177e4SLinus Torvalds 	mark_inode_dirty(inode);
1694a30131eSNeilBrown 
1704a30131eSNeilBrown 	return 0;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds EXPORT_SYMBOL(inode_setattr);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds int notify_change(struct dentry * dentry, struct iattr * attr)
1751da177e4SLinus Torvalds {
1761da177e4SLinus Torvalds 	struct inode *inode = dentry->d_inode;
1776de0ec00SJeff Layton 	mode_t mode = inode->i_mode;
1781da177e4SLinus Torvalds 	int error;
1791da177e4SLinus Torvalds 	struct timespec now;
1801da177e4SLinus Torvalds 	unsigned int ia_valid = attr->ia_valid;
1811da177e4SLinus Torvalds 
182beb29e05SMiklos Szeredi 	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
183beb29e05SMiklos Szeredi 		if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
184beb29e05SMiklos Szeredi 			return -EPERM;
185beb29e05SMiklos Szeredi 	}
186beb29e05SMiklos Szeredi 
1871da177e4SLinus Torvalds 	now = current_fs_time(inode->i_sb);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	attr->ia_ctime = now;
1901da177e4SLinus Torvalds 	if (!(ia_valid & ATTR_ATIME_SET))
1911da177e4SLinus Torvalds 		attr->ia_atime = now;
1921da177e4SLinus Torvalds 	if (!(ia_valid & ATTR_MTIME_SET))
1931da177e4SLinus Torvalds 		attr->ia_mtime = now;
194b5376771SSerge E. Hallyn 	if (ia_valid & ATTR_KILL_PRIV) {
195b5376771SSerge E. Hallyn 		attr->ia_valid &= ~ATTR_KILL_PRIV;
196b5376771SSerge E. Hallyn 		ia_valid &= ~ATTR_KILL_PRIV;
197b5376771SSerge E. Hallyn 		error = security_inode_need_killpriv(dentry);
198b5376771SSerge E. Hallyn 		if (error > 0)
199b5376771SSerge E. Hallyn 			error = security_inode_killpriv(dentry);
200b5376771SSerge E. Hallyn 		if (error)
201b5376771SSerge E. Hallyn 			return error;
202b5376771SSerge E. Hallyn 	}
2036de0ec00SJeff Layton 
2046de0ec00SJeff Layton 	/*
2056de0ec00SJeff Layton 	 * We now pass ATTR_KILL_S*ID to the lower level setattr function so
2066de0ec00SJeff Layton 	 * that the function has the ability to reinterpret a mode change
2076de0ec00SJeff Layton 	 * that's due to these bits. This adds an implicit restriction that
2086de0ec00SJeff Layton 	 * no function will ever call notify_change with both ATTR_MODE and
2096de0ec00SJeff Layton 	 * ATTR_KILL_S*ID set.
2106de0ec00SJeff Layton 	 */
2116de0ec00SJeff Layton 	if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
2126de0ec00SJeff Layton 	    (ia_valid & ATTR_MODE))
2136de0ec00SJeff Layton 		BUG();
2146de0ec00SJeff Layton 
2151da177e4SLinus Torvalds 	if (ia_valid & ATTR_KILL_SUID) {
2161da177e4SLinus Torvalds 		if (mode & S_ISUID) {
2171da177e4SLinus Torvalds 			ia_valid = attr->ia_valid |= ATTR_MODE;
2186de0ec00SJeff Layton 			attr->ia_mode = (inode->i_mode & ~S_ISUID);
2191da177e4SLinus Torvalds 		}
2201da177e4SLinus Torvalds 	}
2211da177e4SLinus Torvalds 	if (ia_valid & ATTR_KILL_SGID) {
2221da177e4SLinus Torvalds 		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
2231da177e4SLinus Torvalds 			if (!(ia_valid & ATTR_MODE)) {
2241da177e4SLinus Torvalds 				ia_valid = attr->ia_valid |= ATTR_MODE;
2251da177e4SLinus Torvalds 				attr->ia_mode = inode->i_mode;
2261da177e4SLinus Torvalds 			}
2271da177e4SLinus Torvalds 			attr->ia_mode &= ~S_ISGID;
2281da177e4SLinus Torvalds 		}
2291da177e4SLinus Torvalds 	}
2306de0ec00SJeff Layton 	if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID)))
2311da177e4SLinus Torvalds 		return 0;
2321da177e4SLinus Torvalds 
233a77b72daSMiklos Szeredi 	error = security_inode_setattr(dentry, attr);
234a77b72daSMiklos Szeredi 	if (error)
235a77b72daSMiklos Szeredi 		return error;
236a77b72daSMiklos Szeredi 
2371da177e4SLinus Torvalds 	if (ia_valid & ATTR_SIZE)
2381da177e4SLinus Torvalds 		down_write(&dentry->d_inode->i_alloc_sem);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	if (inode->i_op && inode->i_op->setattr) {
2411da177e4SLinus Torvalds 		error = inode->i_op->setattr(dentry, attr);
2421da177e4SLinus Torvalds 	} else {
2431da177e4SLinus Torvalds 		error = inode_change_ok(inode, attr);
2441da177e4SLinus Torvalds 		if (!error)
2451da177e4SLinus Torvalds 			error = inode_setattr(inode, attr);
2461da177e4SLinus Torvalds 	}
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	if (ia_valid & ATTR_SIZE)
2491da177e4SLinus Torvalds 		up_write(&dentry->d_inode->i_alloc_sem);
2501da177e4SLinus Torvalds 
2510eeca283SRobert Love 	if (!error)
2520eeca283SRobert Love 		fsnotify_change(dentry, ia_valid);
2530eeca283SRobert Love 
2541da177e4SLinus Torvalds 	return error;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds EXPORT_SYMBOL(notify_change);
258