aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/file.c')
-rw-r--r--fs/fat/file.c44
1 files changed, 27 insertions, 17 deletions
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 27cc1164ec36..771326b8047e 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -257,26 +257,34 @@ int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
257} 257}
258EXPORT_SYMBOL_GPL(fat_getattr); 258EXPORT_SYMBOL_GPL(fat_getattr);
259 259
260static int fat_check_mode(const struct msdos_sb_info *sbi, struct inode *inode, 260static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
261 mode_t mode) 261 struct inode *inode, umode_t *mode_ptr)
262{ 262{
263 mode_t mask, req = mode & ~S_IFMT; 263 mode_t mask, perm;
264 264
265 if (S_ISREG(mode)) 265 /*
266 * Note, the basic check is already done by a caller of
267 * (attr->ia_mode & ~MSDOS_VALID_MODE)
268 */
269
270 if (S_ISREG(inode->i_mode))
266 mask = sbi->options.fs_fmask; 271 mask = sbi->options.fs_fmask;
267 else 272 else
268 mask = sbi->options.fs_dmask; 273 mask = sbi->options.fs_dmask;
269 274
275 perm = *mode_ptr & ~(S_IFMT | mask);
276
270 /* 277 /*
271 * Of the r and x bits, all (subject to umask) must be present. Of the 278 * Of the r and x bits, all (subject to umask) must be present. Of the
272 * w bits, either all (subject to umask) or none must be present. 279 * w bits, either all (subject to umask) or none must be present.
273 */ 280 */
274 req &= ~mask; 281 if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
275 if ((req & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
276 return -EPERM; 282 return -EPERM;
277 if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask))) 283 if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
278 return -EPERM; 284 return -EPERM;
279 285
286 *mode_ptr &= S_IFMT | perm;
287
280 return 0; 288 return 0;
281} 289}
282 290
@@ -299,7 +307,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
299{ 307{
300 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); 308 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
301 struct inode *inode = dentry->d_inode; 309 struct inode *inode = dentry->d_inode;
302 int mask, error = 0; 310 int error = 0;
303 unsigned int ia_valid; 311 unsigned int ia_valid;
304 312
305 lock_kernel(); 313 lock_kernel();
@@ -332,12 +340,13 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
332 error = 0; 340 error = 0;
333 goto out; 341 goto out;
334 } 342 }
343
335 if (((attr->ia_valid & ATTR_UID) && 344 if (((attr->ia_valid & ATTR_UID) &&
336 (attr->ia_uid != sbi->options.fs_uid)) || 345 (attr->ia_uid != sbi->options.fs_uid)) ||
337 ((attr->ia_valid & ATTR_GID) && 346 ((attr->ia_valid & ATTR_GID) &&
338 (attr->ia_gid != sbi->options.fs_gid)) || 347 (attr->ia_gid != sbi->options.fs_gid)) ||
339 ((attr->ia_valid & ATTR_MODE) && 348 ((attr->ia_valid & ATTR_MODE) &&
340 fat_check_mode(sbi, inode, attr->ia_mode) < 0)) 349 (attr->ia_mode & ~MSDOS_VALID_MODE)))
341 error = -EPERM; 350 error = -EPERM;
342 351
343 if (error) { 352 if (error) {
@@ -346,15 +355,16 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
346 goto out; 355 goto out;
347 } 356 }
348 357
349 error = inode_setattr(inode, attr); 358 /*
350 if (error) 359 * We don't return -EPERM here. Yes, strange, but this is too
351 goto out; 360 * old behavior.
361 */
362 if (attr->ia_valid & ATTR_MODE) {
363 if (fat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
364 attr->ia_valid &= ~ATTR_MODE;
365 }
352 366
353 if (S_ISDIR(inode->i_mode)) 367 error = inode_setattr(inode, attr);
354 mask = sbi->options.fs_dmask;
355 else
356 mask = sbi->options.fs_fmask;
357 inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
358out: 368out:
359 unlock_kernel(); 369 unlock_kernel();
360 return error; 370 return error;