{
int err = 0;
+ /*
+ * For the most part we want to set the mode bits before setting
+ * the user, otherwise the current context might lack permission
+ * for setting the mode. However for sxid/sticky bits we want
+ * the operation to fail if the current user isn't privileged
+ * towards the resulting inode. So we first set the mode but
+ * exclude the sxid/sticky bits, then set the user, then set the
+ * mode again if any of the sxid/sticky bits are set.
+ */
if (!S_ISLNK(stat->mode)) {
struct iattr attr = {
.ia_valid = ATTR_MODE,
- .ia_mode = stat->mode,
+ .ia_mode = stat->mode & ~(S_ISUID|S_ISGID|S_ISVTX),
};
err = notify_change(upperdentry, &attr, NULL);
}
};
err = notify_change(upperdentry, &attr, NULL);
}
+ if (!err && !S_ISLNK(stat->mode) &&
+ (stat->mode & (S_ISUID|S_ISGID|S_ISVTX))) {
+ struct iattr attr = {
+ .ia_valid = ATTR_MODE,
+ .ia_mode = stat->mode,
+ };
+ err = notify_change(upperdentry, &attr, NULL);
+ }
if (!err)
ovl_set_timestamps(upperdentry, stat);