Blame view
fs/attr.c
4.78 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 |
/* * linux/fs/attr.c * * Copyright (C) 1991, 1992 Linus Torvalds * changes by Thomas Schoebel-Theuer */ #include <linux/module.h> #include <linux/time.h> #include <linux/mm.h> #include <linux/string.h> |
16f7e0fe2
|
12 |
#include <linux/capability.h> |
0eeca2830
|
13 |
#include <linux/fsnotify.h> |
1da177e4c
|
14 15 16 |
#include <linux/fcntl.h> #include <linux/quotaops.h> #include <linux/security.h> |
1da177e4c
|
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* Taken over from the old code... */ /* POSIX UID/GID verification for setting inode attributes. */ int inode_change_ok(struct inode *inode, struct iattr *attr) { int retval = -EPERM; unsigned int ia_valid = attr->ia_valid; /* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) goto fine; /* Make sure a caller can chown. */ if ((ia_valid & ATTR_UID) && |
da9592ede
|
32 |
(current_fsuid() != inode->i_uid || |
1da177e4c
|
33 34 35 36 37 |
attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN)) goto error; /* Make sure caller can chgrp. */ if ((ia_valid & ATTR_GID) && |
da9592ede
|
38 |
(current_fsuid() != inode->i_uid || |
1da177e4c
|
39 40 41 42 43 44 |
(!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) && !capable(CAP_CHOWN)) goto error; /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { |
3bd858ab1
|
45 |
if (!is_owner_or_cap(inode)) |
1da177e4c
|
46 47 48 49 50 51 52 53 |
goto error; /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : inode->i_gid) && !capable(CAP_FSETID)) attr->ia_mode &= ~S_ISGID; } /* Check for setting the inode time. */ |
9767d7495
|
54 |
if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { |
3bd858ab1
|
55 |
if (!is_owner_or_cap(inode)) |
1da177e4c
|
56 57 58 59 60 61 62 63 64 65 66 67 68 |
goto error; } fine: retval = 0; error: return retval; } EXPORT_SYMBOL(inode_change_ok); int inode_setattr(struct inode * inode, struct iattr * attr) { unsigned int ia_valid = attr->ia_valid; |
4a30131e7
|
69 70 71 72 73 74 |
if (ia_valid & ATTR_SIZE && attr->ia_size != i_size_read(inode)) { int error = vmtruncate(inode, attr->ia_size); if (error) return error; |
1da177e4c
|
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
} 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(CAP_FSETID)) mode &= ~S_ISGID; inode->i_mode = mode; } mark_inode_dirty(inode); |
4a30131e7
|
98 99 |
return 0; |
1da177e4c
|
100 |
} |
1da177e4c
|
101 |
EXPORT_SYMBOL(inode_setattr); |
1da177e4c
|
102 103 104 |
int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; |
6de0ec00b
|
105 |
mode_t mode = inode->i_mode; |
1da177e4c
|
106 107 108 |
int error; struct timespec now; unsigned int ia_valid = attr->ia_valid; |
beb29e058
|
109 110 111 112 |
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) { if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; } |
1da177e4c
|
113 114 115 116 117 118 119 |
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; |
b53767719
|
120 121 122 123 124 125 126 127 128 |
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; } |
6de0ec00b
|
129 130 131 132 133 134 135 136 137 138 139 |
/* * 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(); |
1da177e4c
|
140 |
if (ia_valid & ATTR_KILL_SUID) { |
1da177e4c
|
141 |
if (mode & S_ISUID) { |
6de0ec00b
|
142 143 |
ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = (inode->i_mode & ~S_ISUID); |
1da177e4c
|
144 145 146 |
} } if (ia_valid & ATTR_KILL_SGID) { |
1da177e4c
|
147 148 149 150 151 152 153 154 |
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; } } |
6de0ec00b
|
155 |
if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID))) |
1da177e4c
|
156 |
return 0; |
a77b72da2
|
157 158 159 |
error = security_inode_setattr(dentry, attr); if (error) return error; |
1da177e4c
|
160 161 162 163 |
if (ia_valid & ATTR_SIZE) down_write(&dentry->d_inode->i_alloc_sem); if (inode->i_op && inode->i_op->setattr) { |
a77b72da2
|
164 |
error = inode->i_op->setattr(dentry, attr); |
1da177e4c
|
165 166 |
} else { error = inode_change_ok(inode, attr); |
1da177e4c
|
167 168 169 170 171 172 173 174 175 176 177 |
if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; if (!error) error = inode_setattr(inode, attr); } } if (ia_valid & ATTR_SIZE) up_write(&dentry->d_inode->i_alloc_sem); |
0eeca2830
|
178 179 |
if (!error) fsnotify_change(dentry, ia_valid); |
1da177e4c
|
180 181 182 183 |
return error; } EXPORT_SYMBOL(notify_change); |