Release 4.7 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/capability.h>
#include <linux/fsnotify.h>
#include <linux/fcntl.h>
#include <linux/security.h>
#include <linux/evm.h>
#include <linux/ima.h>
/**
* 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)
{
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)
return 0;
/* 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;
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
pre-git | pre-git | 157 | 62.55% | 4 | 28.57% |
christoph hellwig | christoph hellwig | 48 | 19.12% | 1 | 7.14% |
eric w. biederman | eric w. biederman | 26 | 10.36% | 2 | 14.29% |
chris wright | chris wright | 6 | 2.39% | 1 | 7.14% |
david howells | david howells | 4 | 1.59% | 1 | 7.14% |
andy lutomirski | andy lutomirski | 3 | 1.20% | 1 | 7.14% |
satyam sharma | satyam sharma | 2 | 0.80% | 1 | 7.14% |
miklos szeredi | miklos szeredi | 2 | 0.80% | 1 | 7.14% |
serge hallyn | serge hallyn | 2 | 0.80% | 1 | 7.14% |
nick piggin | nick piggin | 1 | 0.40% | 1 | 7.14% |
| Total | 251 | 100.00% | 14 | 100.00% |
EXPORT_SYMBOL(inode_change_ok);
/**
* 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 |
nick piggin | nick piggin | 93 | 96.88% | 1 | 50.00% |
jiri slaby | 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 |
pre-git | pre-git | 112 | 67.88% | 5 | 41.67% |
andi kleen | andi kleen | 27 | 16.36% | 1 | 8.33% |
andrew morton | andrew morton | 11 | 6.67% | 1 | 8.33% |
linus torvalds | linus torvalds | 8 | 4.85% | 1 | 8.33% |
nick piggin | nick piggin | 3 | 1.82% | 1 | 8.33% |
eric w. biederman | eric w. biederman | 2 | 1.21% | 1 | 8.33% |
christoph hellwig | christoph hellwig | 1 | 0.61% | 1 | 8.33% |
andy lutomirski | andy lutomirski | 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 ((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_fs_time(inode->i_sb);
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) {
attr->ia_valid &= ~ATTR_KILL_PRIV;
ia_valid &= ~ATTR_KILL_PRIV;
error = security_inode_need_killpriv(dentry);
if (error > 0)
error = security_inode_killpriv(dentry);
if (error)
return error;
}
/*
* 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;
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 |
pre-git | pre-git | 113 | 25.17% | 6 | 23.08% |
al viro | al viro | 99 | 22.05% | 4 | 15.38% |
miklos szeredi | miklos szeredi | 48 | 10.69% | 2 | 7.69% |
serge hallyn | serge hallyn | 47 | 10.47% | 1 | 3.85% |
jeff layton | jeff layton | 35 | 7.80% | 1 | 3.85% |
andi kleen | andi kleen | 33 | 7.35% | 2 | 7.69% |
j. bruce fields | j. bruce fields | 22 | 4.90% | 1 | 3.85% |
mimi zohar | mimi zohar | 14 | 3.12% | 2 | 7.69% |
chuck lever | chuck lever | 12 | 2.67% | 1 | 3.85% |
adrian bunk | adrian bunk | 9 | 2.00% | 1 | 3.85% |
andrew morton | andrew morton | 8 | 1.78% | 1 | 3.85% |
dave hansen | dave hansen | 3 | 0.67% | 1 | 3.85% |
robert love | robert love | 2 | 0.45% | 1 | 3.85% |
stephen d. smalley | stephen d. smalley | 2 | 0.45% | 1 | 3.85% |
christoph hellwig | christoph hellwig | 2 | 0.45% | 1 | 3.85% |
| Total | 449 | 100.00% | 26 | 100.00% |
EXPORT_SYMBOL(notify_change);
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp |
pre-git | pre-git | 396 | 38.98% | 10 | 19.23% |
nick piggin | nick piggin | 107 | 10.53% | 2 | 3.85% |
al viro | al viro | 99 | 9.74% | 4 | 7.69% |
andi kleen | andi kleen | 60 | 5.91% | 3 | 5.77% |
christoph hellwig | christoph hellwig | 53 | 5.22% | 3 | 5.77% |
miklos szeredi | miklos szeredi | 50 | 4.92% | 3 | 5.77% |
serge hallyn | serge hallyn | 49 | 4.82% | 2 | 3.85% |
jeff layton | jeff layton | 35 | 3.44% | 1 | 1.92% |
eric w. biederman | eric w. biederman | 28 | 2.76% | 2 | 3.85% |
j. bruce fields | j. bruce fields | 23 | 2.26% | 1 | 1.92% |
mimi zohar | mimi zohar | 20 | 1.97% | 2 | 3.85% |
andrew morton | andrew morton | 19 | 1.87% | 2 | 3.85% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 12 | 1.18% | 1 | 1.92% |
chuck lever | chuck lever | 12 | 1.18% | 1 | 1.92% |
linus torvalds | linus torvalds | 10 | 0.98% | 2 | 3.85% |
adrian bunk | adrian bunk | 9 | 0.89% | 1 | 1.92% |
chris wright | chris wright | 6 | 0.59% | 1 | 1.92% |
david howells | david howells | 4 | 0.39% | 1 | 1.92% |
andy lutomirski | andy lutomirski | 4 | 0.39% | 1 | 1.92% |
jiri slaby | jiri slaby | 3 | 0.30% | 1 | 1.92% |
robert love | robert love | 3 | 0.30% | 1 | 1.92% |
stephen d. smalley | stephen d. smalley | 3 | 0.30% | 1 | 1.92% |
randy dunlap | randy dunlap | 3 | 0.30% | 1 | 1.92% |
dave hansen | dave hansen | 3 | 0.30% | 1 | 1.92% |
satyam sharma | satyam sharma | 2 | 0.20% | 1 | 1.92% |
paul gortmaker | paul gortmaker | 1 | 0.10% | 1 | 1.92% |
dave jones | dave jones | 1 | 0.10% | 1 | 1.92% |
lucas de marchi | lucas de marchi | 1 | 0.10% | 1 | 1.92% |
| Total | 1016 | 100.00% | 52 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.