diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-04-13 11:11:31 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-04-15 23:24:43 -0400 |
commit | 1f2edbe3fe2111a59fcd1bb3b9725066bc9ed686 (patch) | |
tree | 26934b98d98a9f3a05f7cf77df90ecf3b33e4dd6 /fs/nfs/write.c | |
parent | 43b6535e717d2f656f71d9bd16022136b781c934 (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.c | 35 |
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 | */ | ||
1359 | static 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! */ |