Release 4.11 fs/attr.c
/*
* linux/fs/attr.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* changes by Thomas Schoebel-Theuer
*/
#include <linux/export.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/sched/signal.h>
#include <linux/capability.h>
#include <linux/fsnotify.h>
#include <linux/fcntl.h>
#include <linux/security.h>
#include <linux/evm.h>
#include <linux/ima.h>
/**
* setattr_prepare - check if attribute changes to a dentry are allowed
* @dentry: dentry to check
* @attr: attributes to change
*
* Check if we are allowed to change the attributes contained in @attr
* in the given dentry. This includes the normal unix access permission
* checks, as well as checks for rlimits and others. The function also clears
* SGID bit from mode if user is not allowed to set it. Also file capabilities
* and IMA extended attributes are cleared if ATTR_KILL_PRIV is set.
*
* Should be called as the first thing in ->setattr implementations,
* possibly after taking additional locks.
*/
int setattr_prepare(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
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 kill_priv;
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) &&
(!uid_eq(current_fsuid(), inode->i_uid) ||
!uid_eq(attr->ia_uid, inode->i_uid)) &&
!capable_wrt_inode_uidgid(inode, CAP_CHOWN))
return -EPERM;
/* Make sure caller can chgrp. */
if ((ia_valid & ATTR_GID) &&
(!uid_eq(current_fsuid(), inode->i_uid) ||
(!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) &&
!capable_wrt_inode_uidgid(inode, CAP_CHOWN))
return -EPERM;
/* Make sure a caller can chmod. */
if (ia_valid & ATTR_MODE) {
if (!inode_owner_or_capable(inode))
return -EPERM;
/* Also check the setgid bit! */
if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
inode->i_gid) &&
!capable_wrt_inode_uidgid(inode, CAP_FSETID))
attr->ia_mode &= ~S_ISGID;
}
/* Check for setting the inode time. */
if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
if (!inode_owner_or_capable(inode))
return -EPERM;
}
kill_priv:
/* User has permission for the change */
if (ia_valid & ATTR_KILL_PRIV) {
int error;
error = security_inode_killpriv(dentry);
if (error)
return error;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 154 | 53.47% | 4 | 26.67% |
Christoph Hellwig | 45 | 15.62% | 1 | 6.67% |
Jan Kara | 44 | 15.28% | 2 | 13.33% |
Eric W. Biedermann | 26 | 9.03% | 2 | 13.33% |
Chris Wright | 6 | 2.08% | 1 | 6.67% |
David Howells | 4 | 1.39% | 1 | 6.67% |
Andrew Lutomirski | 3 | 1.04% | 1 | 6.67% |
Satyam Sharma | 2 | 0.69% | 1 | 6.67% |
Serge E. Hallyn | 2 | 0.69% | 1 | 6.67% |
Miklos Szeredi | 2 | 0.69% | 1 | 6.67% |
Total | 288 | 100.00% | 15 | 100.00% |
EXPORT_SYMBOL(setattr_prepare);
/**
* inode_newsize_ok - may this inode be truncated to a given size
* @inode: the inode to be truncated
* @offset: the new size to assign to the inode
* @Returns: 0 on success, -ve errno on failure
*
* inode_newsize_ok must be called with i_mutex held.
*
* inode_newsize_ok will check filesystem limits and ulimits to check that the
* new inode size is within limits. inode_newsize_ok will also send SIGXFSZ
* when necessary. Caller must not proceed with inode size change if failure is
* returned. @inode must be a file (not directory), with appropriate
* permissions to allow truncate (inode_newsize_ok does NOT check these
* conditions).
*/
int inode_newsize_ok(const struct inode *inode, loff_t offset)
{
if (inode->i_size < offset) {
unsigned long limit;
limit = rlimit(RLIMIT_FSIZE);
if (limit != RLIM_INFINITY && offset > limit)
goto out_sig;
if (offset > inode->i_sb->s_maxbytes)
goto out_big;
} else {
/*
* truncation of in-use swapfiles is disallowed - it would
* cause subsequent swapout to scribble on the now-freed
* blocks.
*/
if (IS_SWAPFILE(inode))
return -ETXTBSY;
}
return 0;
out_sig:
send_sig(SIGXFSZ, current, 0);
out_big:
return -EFBIG;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Nicholas Piggin | 93 | 96.88% | 1 | 50.00% |
Jiri Slaby | 3 | 3.12% | 1 | 50.00% |
Total | 96 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(inode_newsize_ok);
/**
* setattr_copy - copy simple metadata updates into the generic inode
* @inode: the inode to be updated
* @attr: the new attributes
*
* setattr_copy must be called with i_mutex held.
*
* setattr_copy updates the inode's metadata with that specified
* in attr. Noticeably missing is inode size update, which is more complex
* 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.
* The caller is free to mark the inode dirty afterwards if needed.
*/
void setattr_copy(struct inode *inode, const struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
inode->i_atime = timespec_trunc(attr->ia_atime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
inode->i_mtime = timespec_trunc(attr->ia_mtime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
inode->i_ctime = timespec_trunc(attr->ia_ctime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
if (!in_group_p(inode->i_gid) &&
!capable_wrt_inode_uidgid(inode, CAP_FSETID))
mode &= ~S_ISGID;
inode->i_mode = mode;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 112 | 67.88% | 5 | 41.67% |
Andi Kleen | 27 | 16.36% | 1 | 8.33% |
Andrew Morton | 11 | 6.67% | 1 | 8.33% |
Linus Torvalds | 8 | 4.85% | 1 | 8.33% |
Nicholas Piggin | 3 | 1.82% | 1 | 8.33% |
Eric W. Biedermann | 2 | 1.21% | 1 | 8.33% |
Andrew Lutomirski | 1 | 0.61% | 1 | 8.33% |
Christoph Hellwig | 1 | 0.61% | 1 | 8.33% |
Total | 165 | 100.00% | 12 | 100.00% |
EXPORT_SYMBOL(setattr_copy);
/**
* notify_change - modify attributes of a filesytem object
* @dentry: object affected
* @iattr: new attributes
* @delegated_inode: returns inode, if the inode is delegated
*
* The caller must hold the i_mutex on the affected object.
*
* If notify_change discovers a delegation in need of breaking,
* it will return -EWOULDBLOCK and return a reference to the inode in
* delegated_inode. The caller should then break the delegation and
* retry. Because breaking a delegation may take a long time, the
* caller should drop the i_mutex before doing so.
*
* Alternatively, a caller may pass NULL for delegated_inode. This may
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported. Also, passing NULL is fine for callers holding
* the file open for write, as there can be no conflicting delegation in
* that case.
*/
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
int error;
struct timespec now;
unsigned int ia_valid = attr->ia_valid;
WARN_ON_ONCE(!inode_is_locked(inode));
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
return -EPERM;
}
/*
* If utimes(2) and friends are called with times == NULL (or both
* times are UTIME_NOW), then we need to check for write permission
*/
if (ia_valid & ATTR_TOUCH) {
if (IS_IMMUTABLE(inode))
return -EPERM;
if (!inode_owner_or_capable(inode)) {
error = inode_permission(inode, MAY_WRITE);
if (error)
return error;
}
}
if ((ia_valid & ATTR_MODE)) {
umode_t amode = attr->ia_mode;
/* Flag setting protected by i_mutex */
if (is_sxid(amode))
inode->i_flags &= ~S_NOSEC;
}
now = current_time(inode);
attr->ia_ctime = now;
if (!(ia_valid & ATTR_ATIME_SET))
attr->ia_atime = now;
if (!(ia_valid & ATTR_MTIME_SET))
attr->ia_mtime = now;
if (ia_valid & ATTR_KILL_PRIV) {
error = security_inode_need_killpriv(dentry);
if (error < 0)
return error;
if (error == 0)
ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV;
}
/*
* We now pass ATTR_KILL_S*ID to the lower level setattr function so
* that the function has the ability to reinterpret a mode change
* that's due to these bits. This adds an implicit restriction that
* no function will ever call notify_change with both ATTR_MODE and
* ATTR_KILL_S*ID set.
*/
if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
(ia_valid & ATTR_MODE))
BUG();
if (ia_valid & ATTR_KILL_SUID) {
if (mode & S_ISUID) {
ia_valid = attr->ia_valid |= ATTR_MODE;
attr->ia_mode = (inode->i_mode & ~S_ISUID);
}
}
if (ia_valid & ATTR_KILL_SGID) {
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
if (!(ia_valid & ATTR_MODE)) {
ia_valid = attr->ia_valid |= ATTR_MODE;
attr->ia_mode = inode->i_mode;
}
attr->ia_mode &= ~S_ISGID;
}
}
if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID)))
return 0;
/*
* Verify that uid/gid changes are valid in the target
* namespace of the superblock.
*/
if (ia_valid & ATTR_UID &&
!kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid))
return -EOVERFLOW;
if (ia_valid & ATTR_GID &&
!kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid))
return -EOVERFLOW;
/* Don't allow modifications of files with invalid uids or
* gids unless those uids & gids are being made valid.
*/
if (!(ia_valid & ATTR_UID) && !uid_valid(inode->i_uid))
return -EOVERFLOW;
if (!(ia_valid & ATTR_GID) && !gid_valid(inode->i_gid))
return -EOVERFLOW;
error = security_inode_setattr(dentry, attr);
if (error)
return error;
error = try_break_deleg(inode, delegated_inode);
if (error)
return error;
if (inode->i_op->setattr)
error = inode->i_op->setattr(dentry, attr);
else
error = simple_setattr(dentry, attr);
if (!error) {
fsnotify_change(dentry, ia_valid);
ima_inode_post_setattr(dentry);
evm_inode_post_setattr(dentry, ia_valid);
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 113 | 19.58% | 6 | 19.35% |
Al Viro | 99 | 17.16% | 4 | 12.90% |
Miklos Szeredi | 94 | 16.29% | 3 | 9.68% |
Seth Forshee | 49 | 8.49% | 1 | 3.23% |
Eric W. Biedermann | 43 | 7.45% | 1 | 3.23% |
Jeff Layton | 35 | 6.07% | 1 | 3.23% |
Andi Kleen | 33 | 5.72% | 2 | 6.45% |
Serge E. Hallyn | 25 | 4.33% | 1 | 3.23% |
J. Bruce Fields | 22 | 3.81% | 1 | 3.23% |
Jan Kara | 14 | 2.43% | 1 | 3.23% |
Mimi Zohar | 14 | 2.43% | 2 | 6.45% |
Chuck Lever | 12 | 2.08% | 1 | 3.23% |
Andrew Morton | 8 | 1.39% | 1 | 3.23% |
Adrian Bunk | 6 | 1.04% | 1 | 3.23% |
Dave Hansen | 3 | 0.52% | 1 | 3.23% |
Christoph Hellwig | 2 | 0.35% | 1 | 3.23% |
Robert Love | 2 | 0.35% | 1 | 3.23% |
Stephen D. Smalley | 2 | 0.35% | 1 | 3.23% |
Deepa Dinamani | 1 | 0.17% | 1 | 3.23% |
Total | 577 | 100.00% | 31 | 100.00% |
EXPORT_SYMBOL(notify_change);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 393 | 33.19% | 10 | 16.95% |
Nicholas Piggin | 106 | 8.95% | 2 | 3.39% |
Al Viro | 99 | 8.36% | 4 | 6.78% |
Miklos Szeredi | 96 | 8.11% | 4 | 6.78% |
Eric W. Biedermann | 71 | 6.00% | 3 | 5.08% |
Andi Kleen | 60 | 5.07% | 3 | 5.08% |
Jan Kara | 60 | 5.07% | 2 | 3.39% |
Christoph Hellwig | 49 | 4.14% | 3 | 5.08% |
Seth Forshee | 49 | 4.14% | 1 | 1.69% |
Jeff Layton | 35 | 2.96% | 1 | 1.69% |
Serge E. Hallyn | 27 | 2.28% | 2 | 3.39% |
J. Bruce Fields | 23 | 1.94% | 1 | 1.69% |
Mimi Zohar | 20 | 1.69% | 2 | 3.39% |
Andrew Morton | 19 | 1.60% | 2 | 3.39% |
Chuck Lever | 12 | 1.01% | 1 | 1.69% |
Arnaldo Carvalho de Melo | 11 | 0.93% | 1 | 1.69% |
Linus Torvalds | 10 | 0.84% | 2 | 3.39% |
Adrian Bunk | 6 | 0.51% | 1 | 1.69% |
Chris Wright | 6 | 0.51% | 1 | 1.69% |
Andrew Lutomirski | 4 | 0.34% | 1 | 1.69% |
David Howells | 4 | 0.34% | 1 | 1.69% |
Jiri Slaby | 3 | 0.25% | 1 | 1.69% |
Ingo Molnar | 3 | 0.25% | 1 | 1.69% |
Dave Hansen | 3 | 0.25% | 1 | 1.69% |
Stephen D. Smalley | 3 | 0.25% | 1 | 1.69% |
Robert Love | 3 | 0.25% | 1 | 1.69% |
Randy Dunlap | 3 | 0.25% | 1 | 1.69% |
Satyam Sharma | 2 | 0.17% | 1 | 1.69% |
Lucas De Marchi | 1 | 0.08% | 1 | 1.69% |
Deepa Dinamani | 1 | 0.08% | 1 | 1.69% |
Dave Jones | 1 | 0.08% | 1 | 1.69% |
Paul Gortmaker | 1 | 0.08% | 1 | 1.69% |
Total | 1184 | 100.00% | 59 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.