diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2009-12-08 08:33:16 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2009-12-08 08:33:16 -0500 |
commit | 88069f77e1ac580a495762ce7a631c251c52cb90 (patch) | |
tree | e30011cdb39fa167370b4d35bbc9b3cc04a4bc27 /fs | |
parent | 74e7bb73a3e0d15a7db10b0f2b2efdeeef36609e (diff) |
NFSv41: Fix a potential state leakage when restarting nfs4_close_prepare
Currently, if the call to nfs4_setup_sequence() in nfs4_close_prepare
fails, any later retries will fail to launch an RPC call, due to the fact
that the &state->flags will have been cleared.
Ditto if nfs4_close_done() triggers a call to the NFSv4.1 version of
nfs_restart_rpc().
We therefore move the actual clearing of the state->flags to
nfs4_close_done(), when we know that the RPC call was successful.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cdf17d628450..9f5f11ecfd93 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -761,13 +761,16 @@ static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode | |||
761 | goto out; | 761 | goto out; |
762 | switch (mode & (FMODE_READ|FMODE_WRITE)) { | 762 | switch (mode & (FMODE_READ|FMODE_WRITE)) { |
763 | case FMODE_READ: | 763 | case FMODE_READ: |
764 | ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0; | 764 | ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0 |
765 | && state->n_rdonly != 0; | ||
765 | break; | 766 | break; |
766 | case FMODE_WRITE: | 767 | case FMODE_WRITE: |
767 | ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0; | 768 | ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0 |
769 | && state->n_wronly != 0; | ||
768 | break; | 770 | break; |
769 | case FMODE_READ|FMODE_WRITE: | 771 | case FMODE_READ|FMODE_WRITE: |
770 | ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0; | 772 | ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0 |
773 | && state->n_rdwr != 0; | ||
771 | } | 774 | } |
772 | out: | 775 | out: |
773 | return ret; | 776 | return ret; |
@@ -1711,6 +1714,18 @@ static void nfs4_free_closedata(void *data) | |||
1711 | kfree(calldata); | 1714 | kfree(calldata); |
1712 | } | 1715 | } |
1713 | 1716 | ||
1717 | static void nfs4_close_clear_stateid_flags(struct nfs4_state *state, | ||
1718 | fmode_t fmode) | ||
1719 | { | ||
1720 | spin_lock(&state->owner->so_lock); | ||
1721 | if (!(fmode & FMODE_READ)) | ||
1722 | clear_bit(NFS_O_RDONLY_STATE, &state->flags); | ||
1723 | if (!(fmode & FMODE_WRITE)) | ||
1724 | clear_bit(NFS_O_WRONLY_STATE, &state->flags); | ||
1725 | clear_bit(NFS_O_RDWR_STATE, &state->flags); | ||
1726 | spin_unlock(&state->owner->so_lock); | ||
1727 | } | ||
1728 | |||
1714 | static void nfs4_close_done(struct rpc_task *task, void *data) | 1729 | static void nfs4_close_done(struct rpc_task *task, void *data) |
1715 | { | 1730 | { |
1716 | struct nfs4_closedata *calldata = data; | 1731 | struct nfs4_closedata *calldata = data; |
@@ -1727,6 +1742,8 @@ static void nfs4_close_done(struct rpc_task *task, void *data) | |||
1727 | case 0: | 1742 | case 0: |
1728 | nfs_set_open_stateid(state, &calldata->res.stateid, 0); | 1743 | nfs_set_open_stateid(state, &calldata->res.stateid, 0); |
1729 | renew_lease(server, calldata->timestamp); | 1744 | renew_lease(server, calldata->timestamp); |
1745 | nfs4_close_clear_stateid_flags(state, | ||
1746 | calldata->arg.fmode); | ||
1730 | break; | 1747 | break; |
1731 | case -NFS4ERR_STALE_STATEID: | 1748 | case -NFS4ERR_STALE_STATEID: |
1732 | case -NFS4ERR_OLD_STATEID: | 1749 | case -NFS4ERR_OLD_STATEID: |
@@ -1747,38 +1764,39 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) | |||
1747 | { | 1764 | { |
1748 | struct nfs4_closedata *calldata = data; | 1765 | struct nfs4_closedata *calldata = data; |
1749 | struct nfs4_state *state = calldata->state; | 1766 | struct nfs4_state *state = calldata->state; |
1750 | int clear_rd, clear_wr, clear_rdwr; | 1767 | int call_close = 0; |
1751 | 1768 | ||
1752 | if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) | 1769 | if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) |
1753 | return; | 1770 | return; |
1754 | 1771 | ||
1755 | clear_rd = clear_wr = clear_rdwr = 0; | 1772 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; |
1773 | calldata->arg.fmode = FMODE_READ|FMODE_WRITE; | ||
1756 | spin_lock(&state->owner->so_lock); | 1774 | spin_lock(&state->owner->so_lock); |
1757 | /* Calculate the change in open mode */ | 1775 | /* Calculate the change in open mode */ |
1758 | if (state->n_rdwr == 0) { | 1776 | if (state->n_rdwr == 0) { |
1759 | if (state->n_rdonly == 0) { | 1777 | if (state->n_rdonly == 0) { |
1760 | clear_rd |= test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags); | 1778 | call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); |
1761 | clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags); | 1779 | call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); |
1780 | calldata->arg.fmode &= ~FMODE_READ; | ||
1762 | } | 1781 | } |
1763 | if (state->n_wronly == 0) { | 1782 | if (state->n_wronly == 0) { |
1764 | clear_wr |= test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags); | 1783 | call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); |
1765 | clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags); | 1784 | call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); |
1785 | calldata->arg.fmode &= ~FMODE_WRITE; | ||
1766 | } | 1786 | } |
1767 | } | 1787 | } |
1768 | spin_unlock(&state->owner->so_lock); | 1788 | spin_unlock(&state->owner->so_lock); |
1769 | if (!clear_rd && !clear_wr && !clear_rdwr) { | 1789 | |
1790 | if (!call_close) { | ||
1770 | /* Note: exit _without_ calling nfs4_close_done */ | 1791 | /* Note: exit _without_ calling nfs4_close_done */ |
1771 | task->tk_action = NULL; | 1792 | task->tk_action = NULL; |
1772 | return; | 1793 | return; |
1773 | } | 1794 | } |
1795 | |||
1796 | if (calldata->arg.fmode == 0) | ||
1797 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; | ||
1798 | |||
1774 | nfs_fattr_init(calldata->res.fattr); | 1799 | nfs_fattr_init(calldata->res.fattr); |
1775 | if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) { | ||
1776 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; | ||
1777 | calldata->arg.fmode = FMODE_READ; | ||
1778 | } else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) { | ||
1779 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; | ||
1780 | calldata->arg.fmode = FMODE_WRITE; | ||
1781 | } | ||
1782 | calldata->timestamp = jiffies; | 1800 | calldata->timestamp = jiffies; |
1783 | if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client, | 1801 | if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client, |
1784 | &calldata->arg.seq_args, &calldata->res.seq_res, | 1802 | &calldata->arg.seq_args, &calldata->res.seq_res, |