aboutsummaryrefslogtreecommitdiffstats
path: root/fs/attr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/attr.c')
-rw-r--r--fs/attr.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/fs/attr.c b/fs/attr.c
index ae58bd3f875f..966b73e25f82 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -103,12 +103,11 @@ EXPORT_SYMBOL(inode_setattr);
103int notify_change(struct dentry * dentry, struct iattr * attr) 103int notify_change(struct dentry * dentry, struct iattr * attr)
104{ 104{
105 struct inode *inode = dentry->d_inode; 105 struct inode *inode = dentry->d_inode;
106 mode_t mode; 106 mode_t mode = inode->i_mode;
107 int error; 107 int error;
108 struct timespec now; 108 struct timespec now;
109 unsigned int ia_valid = attr->ia_valid; 109 unsigned int ia_valid = attr->ia_valid;
110 110
111 mode = inode->i_mode;
112 now = current_fs_time(inode->i_sb); 111 now = current_fs_time(inode->i_sb);
113 112
114 attr->ia_ctime = now; 113 attr->ia_ctime = now;
@@ -125,18 +124,25 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
125 if (error) 124 if (error)
126 return error; 125 return error;
127 } 126 }
127
128 /*
129 * We now pass ATTR_KILL_S*ID to the lower level setattr function so
130 * that the function has the ability to reinterpret a mode change
131 * that's due to these bits. This adds an implicit restriction that
132 * no function will ever call notify_change with both ATTR_MODE and
133 * ATTR_KILL_S*ID set.
134 */
135 if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
136 (ia_valid & ATTR_MODE))
137 BUG();
138
128 if (ia_valid & ATTR_KILL_SUID) { 139 if (ia_valid & ATTR_KILL_SUID) {
129 attr->ia_valid &= ~ATTR_KILL_SUID;
130 if (mode & S_ISUID) { 140 if (mode & S_ISUID) {
131 if (!(ia_valid & ATTR_MODE)) { 141 ia_valid = attr->ia_valid |= ATTR_MODE;
132 ia_valid = attr->ia_valid |= ATTR_MODE; 142 attr->ia_mode = (inode->i_mode & ~S_ISUID);
133 attr->ia_mode = inode->i_mode;
134 }
135 attr->ia_mode &= ~S_ISUID;
136 } 143 }
137 } 144 }
138 if (ia_valid & ATTR_KILL_SGID) { 145 if (ia_valid & ATTR_KILL_SGID) {
139 attr->ia_valid &= ~ ATTR_KILL_SGID;
140 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { 146 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
141 if (!(ia_valid & ATTR_MODE)) { 147 if (!(ia_valid & ATTR_MODE)) {
142 ia_valid = attr->ia_valid |= ATTR_MODE; 148 ia_valid = attr->ia_valid |= ATTR_MODE;
@@ -145,7 +151,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
145 attr->ia_mode &= ~S_ISGID; 151 attr->ia_mode &= ~S_ISGID;
146 } 152 }
147 } 153 }
148 if (!attr->ia_valid) 154 if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID)))
149 return 0; 155 return 0;
150 156
151 if (ia_valid & ATTR_SIZE) 157 if (ia_valid & ATTR_SIZE)