aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2014-04-13 11:11:31 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2014-04-15 23:24:43 -0400
commit1f2edbe3fe2111a59fcd1bb3b9725066bc9ed686 (patch)
tree26934b98d98a9f3a05f7cf77df90ecf3b33e4dd6 /fs/nfs/write.c
parent43b6535e717d2f656f71d9bd16022136b781c934 (diff)
NFS: Don't ignore suid/sgid bit changes after a successful write
If we suspect that the server may have cleared the suid/sgid bit, then mark the inode for revalidation. Reported-by: Kinglong Mee <kinglongmee@gmail.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 9a3b6a4cd6b9..cd7c651f9b84 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1353,6 +1353,30 @@ static const struct rpc_call_ops nfs_write_common_ops = {
1353 .rpc_release = nfs_writeback_release_common, 1353 .rpc_release = nfs_writeback_release_common,
1354}; 1354};
1355 1355
1356/*
1357 * Special version of should_remove_suid() that ignores capabilities.
1358 */
1359static int nfs_should_remove_suid(const struct inode *inode)
1360{
1361 umode_t mode = inode->i_mode;
1362 int kill = 0;
1363
1364 /* suid always must be killed */
1365 if (unlikely(mode & S_ISUID))
1366 kill = ATTR_KILL_SUID;
1367
1368 /*
1369 * sgid without any exec bits is just a mandatory locking mark; leave
1370 * it alone. If some exec bits are set, it's a real sgid; kill it.
1371 */
1372 if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
1373 kill |= ATTR_KILL_SGID;
1374
1375 if (unlikely(kill && S_ISREG(mode)))
1376 return kill;
1377
1378 return 0;
1379}
1356 1380
1357/* 1381/*
1358 * This function is called when the WRITE call is complete. 1382 * This function is called when the WRITE call is complete.
@@ -1401,9 +1425,16 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
1401 } 1425 }
1402 } 1426 }
1403#endif 1427#endif
1404 if (task->tk_status < 0) 1428 if (task->tk_status < 0) {
1405 nfs_set_pgio_error(data->header, task->tk_status, argp->offset); 1429 nfs_set_pgio_error(data->header, task->tk_status, argp->offset);
1406 else if (resp->count < argp->count) { 1430 return;
1431 }
1432
1433 /* Deal with the suid/sgid bit corner case */
1434 if (nfs_should_remove_suid(inode))
1435 nfs_mark_for_revalidate(inode);
1436
1437 if (resp->count < argp->count) {
1407 static unsigned long complain; 1438 static unsigned long complain;
1408 1439
1409 /* This a short write! */ 1440 /* This a short write! */