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 | |
| 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>
| -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, |
