diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4state.c | 41 |
1 files changed, 26 insertions, 15 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b73e96db1f50..b83b58da0cc0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1975,6 +1975,26 @@ io_during_grace_disallowed(struct inode *inode, int flags) | |||
1975 | && mandatory_lock(inode); | 1975 | && mandatory_lock(inode); |
1976 | } | 1976 | } |
1977 | 1977 | ||
1978 | static int check_stateid_generation(stateid_t *in, stateid_t *ref) | ||
1979 | { | ||
1980 | /* If the client sends us a stateid from the future, it's buggy: */ | ||
1981 | if (in->si_generation > ref->si_generation) | ||
1982 | return nfserr_bad_stateid; | ||
1983 | /* | ||
1984 | * The following, however, can happen. For example, if the | ||
1985 | * client sends an open and some IO at the same time, the open | ||
1986 | * may bump si_generation while the IO is still in flight. | ||
1987 | * Thanks to hard links and renames, the client never knows what | ||
1988 | * file an open will affect. So it could avoid that situation | ||
1989 | * only by serializing all opens and IO from the same open | ||
1990 | * owner. To recover from the old_stateid error, the client | ||
1991 | * will just have to retry the IO: | ||
1992 | */ | ||
1993 | if (in->si_generation < ref->si_generation) | ||
1994 | return nfserr_old_stateid; | ||
1995 | return nfs_ok; | ||
1996 | } | ||
1997 | |||
1978 | /* | 1998 | /* |
1979 | * Checks for stateid operations | 1999 | * Checks for stateid operations |
1980 | */ | 2000 | */ |
@@ -2023,12 +2043,8 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl | |||
2023 | goto out; | 2043 | goto out; |
2024 | stidp = &stp->st_stateid; | 2044 | stidp = &stp->st_stateid; |
2025 | } | 2045 | } |
2026 | if (stateid->si_generation > stidp->si_generation) | 2046 | status = check_stateid_generation(stateid, stidp); |
2027 | goto out; | 2047 | if (status) |
2028 | |||
2029 | /* OLD STATEID */ | ||
2030 | status = nfserr_old_stateid; | ||
2031 | if (stateid->si_generation < stidp->si_generation) | ||
2032 | goto out; | 2048 | goto out; |
2033 | if (stp) { | 2049 | if (stp) { |
2034 | if ((status = nfs4_check_openmode(stp,flags))) | 2050 | if ((status = nfs4_check_openmode(stp,flags))) |
@@ -2065,6 +2081,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei | |||
2065 | { | 2081 | { |
2066 | struct nfs4_stateid *stp; | 2082 | struct nfs4_stateid *stp; |
2067 | struct nfs4_stateowner *sop; | 2083 | struct nfs4_stateowner *sop; |
2084 | __be32 status; | ||
2068 | 2085 | ||
2069 | dprintk("NFSD: preprocess_seqid_op: seqid=%d " | 2086 | dprintk("NFSD: preprocess_seqid_op: seqid=%d " |
2070 | "stateid = (%08x/%08x/%08x/%08x)\n", seqid, | 2087 | "stateid = (%08x/%08x/%08x/%08x)\n", seqid, |
@@ -2150,15 +2167,9 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei | |||
2150 | " confirmed yet!\n"); | 2167 | " confirmed yet!\n"); |
2151 | return nfserr_bad_stateid; | 2168 | return nfserr_bad_stateid; |
2152 | } | 2169 | } |
2153 | if (stateid->si_generation > stp->st_stateid.si_generation) { | 2170 | status = check_stateid_generation(stateid, &stp->st_stateid); |
2154 | dprintk("NFSD: preprocess_seqid_op: future stateid?!\n"); | 2171 | if (status) |
2155 | return nfserr_bad_stateid; | 2172 | return status; |
2156 | } | ||
2157 | |||
2158 | if (stateid->si_generation < stp->st_stateid.si_generation) { | ||
2159 | dprintk("NFSD: preprocess_seqid_op: old stateid!\n"); | ||
2160 | return nfserr_old_stateid; | ||
2161 | } | ||
2162 | renew_client(sop->so_client); | 2173 | renew_client(sop->so_client); |
2163 | return nfs_ok; | 2174 | return nfs_ok; |
2164 | 2175 | ||