diff options
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 235 |
1 files changed, 158 insertions, 77 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 316ec843dec2..43f42290e5df 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -97,19 +97,20 @@ nfs4_lock_state(void) | |||
97 | 97 | ||
98 | static void free_session(struct nfsd4_session *); | 98 | static void free_session(struct nfsd4_session *); |
99 | 99 | ||
100 | void nfsd4_put_session(struct nfsd4_session *ses) | 100 | static bool is_session_dead(struct nfsd4_session *ses) |
101 | { | 101 | { |
102 | atomic_dec(&ses->se_ref); | 102 | return ses->se_flags & NFS4_SESSION_DEAD; |
103 | } | 103 | } |
104 | 104 | ||
105 | static bool is_session_dead(struct nfsd4_session *ses) | 105 | void nfsd4_put_session(struct nfsd4_session *ses) |
106 | { | 106 | { |
107 | return ses->se_flags & NFS4_SESSION_DEAD; | 107 | if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses)) |
108 | free_session(ses); | ||
108 | } | 109 | } |
109 | 110 | ||
110 | static __be32 mark_session_dead_locked(struct nfsd4_session *ses) | 111 | static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me) |
111 | { | 112 | { |
112 | if (atomic_read(&ses->se_ref)) | 113 | if (atomic_read(&ses->se_ref) > ref_held_by_me) |
113 | return nfserr_jukebox; | 114 | return nfserr_jukebox; |
114 | ses->se_flags |= NFS4_SESSION_DEAD; | 115 | ses->se_flags |= NFS4_SESSION_DEAD; |
115 | return nfs_ok; | 116 | return nfs_ok; |
@@ -364,19 +365,12 @@ static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp) | |||
364 | } | 365 | } |
365 | 366 | ||
366 | static struct nfs4_delegation * | 367 | static struct nfs4_delegation * |
367 | alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) | 368 | alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh) |
368 | { | 369 | { |
369 | struct nfs4_delegation *dp; | 370 | struct nfs4_delegation *dp; |
370 | struct nfs4_file *fp = stp->st_file; | 371 | struct nfs4_file *fp = stp->st_file; |
371 | 372 | ||
372 | dprintk("NFSD alloc_init_deleg\n"); | 373 | dprintk("NFSD alloc_init_deleg\n"); |
373 | /* | ||
374 | * Major work on the lease subsystem (for example, to support | ||
375 | * calbacks on stat) will be required before we can support | ||
376 | * write delegations properly. | ||
377 | */ | ||
378 | if (type != NFS4_OPEN_DELEGATE_READ) | ||
379 | return NULL; | ||
380 | if (fp->fi_had_conflict) | 374 | if (fp->fi_had_conflict) |
381 | return NULL; | 375 | return NULL; |
382 | if (num_delegations > max_delegations) | 376 | if (num_delegations > max_delegations) |
@@ -397,7 +391,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv | |||
397 | INIT_LIST_HEAD(&dp->dl_recall_lru); | 391 | INIT_LIST_HEAD(&dp->dl_recall_lru); |
398 | get_nfs4_file(fp); | 392 | get_nfs4_file(fp); |
399 | dp->dl_file = fp; | 393 | dp->dl_file = fp; |
400 | dp->dl_type = type; | 394 | dp->dl_type = NFS4_OPEN_DELEGATE_READ; |
401 | fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); | 395 | fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); |
402 | dp->dl_time = 0; | 396 | dp->dl_time = 0; |
403 | atomic_set(&dp->dl_count, 1); | 397 | atomic_set(&dp->dl_count, 1); |
@@ -1188,6 +1182,9 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) | |||
1188 | target->cr_gid = source->cr_gid; | 1182 | target->cr_gid = source->cr_gid; |
1189 | target->cr_group_info = source->cr_group_info; | 1183 | target->cr_group_info = source->cr_group_info; |
1190 | get_group_info(target->cr_group_info); | 1184 | get_group_info(target->cr_group_info); |
1185 | target->cr_gss_mech = source->cr_gss_mech; | ||
1186 | if (source->cr_gss_mech) | ||
1187 | gss_mech_get(source->cr_gss_mech); | ||
1191 | return 0; | 1188 | return 0; |
1192 | } | 1189 | } |
1193 | 1190 | ||
@@ -1262,6 +1259,33 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) | |||
1262 | return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); | 1259 | return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); |
1263 | } | 1260 | } |
1264 | 1261 | ||
1262 | static bool svc_rqst_integrity_protected(struct svc_rqst *rqstp) | ||
1263 | { | ||
1264 | struct svc_cred *cr = &rqstp->rq_cred; | ||
1265 | u32 service; | ||
1266 | |||
1267 | if (!cr->cr_gss_mech) | ||
1268 | return false; | ||
1269 | service = gss_pseudoflavor_to_service(cr->cr_gss_mech, cr->cr_flavor); | ||
1270 | return service == RPC_GSS_SVC_INTEGRITY || | ||
1271 | service == RPC_GSS_SVC_PRIVACY; | ||
1272 | } | ||
1273 | |||
1274 | static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp) | ||
1275 | { | ||
1276 | struct svc_cred *cr = &rqstp->rq_cred; | ||
1277 | |||
1278 | if (!cl->cl_mach_cred) | ||
1279 | return true; | ||
1280 | if (cl->cl_cred.cr_gss_mech != cr->cr_gss_mech) | ||
1281 | return false; | ||
1282 | if (!svc_rqst_integrity_protected(rqstp)) | ||
1283 | return false; | ||
1284 | if (!cr->cr_principal) | ||
1285 | return false; | ||
1286 | return 0 == strcmp(cl->cl_cred.cr_principal, cr->cr_principal); | ||
1287 | } | ||
1288 | |||
1265 | static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) | 1289 | static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) |
1266 | { | 1290 | { |
1267 | static u32 current_clientid = 1; | 1291 | static u32 current_clientid = 1; |
@@ -1639,16 +1663,16 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, | |||
1639 | if (exid->flags & ~EXCHGID4_FLAG_MASK_A) | 1663 | if (exid->flags & ~EXCHGID4_FLAG_MASK_A) |
1640 | return nfserr_inval; | 1664 | return nfserr_inval; |
1641 | 1665 | ||
1642 | /* Currently only support SP4_NONE */ | ||
1643 | switch (exid->spa_how) { | 1666 | switch (exid->spa_how) { |
1667 | case SP4_MACH_CRED: | ||
1668 | if (!svc_rqst_integrity_protected(rqstp)) | ||
1669 | return nfserr_inval; | ||
1644 | case SP4_NONE: | 1670 | case SP4_NONE: |
1645 | break; | 1671 | break; |
1646 | default: /* checked by xdr code */ | 1672 | default: /* checked by xdr code */ |
1647 | WARN_ON_ONCE(1); | 1673 | WARN_ON_ONCE(1); |
1648 | case SP4_SSV: | 1674 | case SP4_SSV: |
1649 | return nfserr_encr_alg_unsupp; | 1675 | return nfserr_encr_alg_unsupp; |
1650 | case SP4_MACH_CRED: | ||
1651 | return nfserr_serverfault; /* no excuse :-/ */ | ||
1652 | } | 1676 | } |
1653 | 1677 | ||
1654 | /* Cases below refer to rfc 5661 section 18.35.4: */ | 1678 | /* Cases below refer to rfc 5661 section 18.35.4: */ |
@@ -1663,6 +1687,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, | |||
1663 | status = nfserr_inval; | 1687 | status = nfserr_inval; |
1664 | goto out; | 1688 | goto out; |
1665 | } | 1689 | } |
1690 | if (!mach_creds_match(conf, rqstp)) { | ||
1691 | status = nfserr_wrong_cred; | ||
1692 | goto out; | ||
1693 | } | ||
1666 | if (!creds_match) { /* case 9 */ | 1694 | if (!creds_match) { /* case 9 */ |
1667 | status = nfserr_perm; | 1695 | status = nfserr_perm; |
1668 | goto out; | 1696 | goto out; |
@@ -1709,7 +1737,8 @@ out_new: | |||
1709 | status = nfserr_jukebox; | 1737 | status = nfserr_jukebox; |
1710 | goto out; | 1738 | goto out; |
1711 | } | 1739 | } |
1712 | new->cl_minorversion = 1; | 1740 | new->cl_minorversion = cstate->minorversion; |
1741 | new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED); | ||
1713 | 1742 | ||
1714 | gen_clid(new, nn); | 1743 | gen_clid(new, nn); |
1715 | add_to_unconfirmed(new); | 1744 | add_to_unconfirmed(new); |
@@ -1839,6 +1868,24 @@ static __be32 check_backchannel_attrs(struct nfsd4_channel_attrs *ca) | |||
1839 | return nfs_ok; | 1868 | return nfs_ok; |
1840 | } | 1869 | } |
1841 | 1870 | ||
1871 | static __be32 nfsd4_check_cb_sec(struct nfsd4_cb_sec *cbs) | ||
1872 | { | ||
1873 | switch (cbs->flavor) { | ||
1874 | case RPC_AUTH_NULL: | ||
1875 | case RPC_AUTH_UNIX: | ||
1876 | return nfs_ok; | ||
1877 | default: | ||
1878 | /* | ||
1879 | * GSS case: the spec doesn't allow us to return this | ||
1880 | * error. But it also doesn't allow us not to support | ||
1881 | * GSS. | ||
1882 | * I'd rather this fail hard than return some error the | ||
1883 | * client might think it can already handle: | ||
1884 | */ | ||
1885 | return nfserr_encr_alg_unsupp; | ||
1886 | } | ||
1887 | } | ||
1888 | |||
1842 | __be32 | 1889 | __be32 |
1843 | nfsd4_create_session(struct svc_rqst *rqstp, | 1890 | nfsd4_create_session(struct svc_rqst *rqstp, |
1844 | struct nfsd4_compound_state *cstate, | 1891 | struct nfsd4_compound_state *cstate, |
@@ -1854,6 +1901,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1854 | 1901 | ||
1855 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) | 1902 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) |
1856 | return nfserr_inval; | 1903 | return nfserr_inval; |
1904 | status = nfsd4_check_cb_sec(&cr_ses->cb_sec); | ||
1905 | if (status) | ||
1906 | return status; | ||
1857 | status = check_forechannel_attrs(&cr_ses->fore_channel, nn); | 1907 | status = check_forechannel_attrs(&cr_ses->fore_channel, nn); |
1858 | if (status) | 1908 | if (status) |
1859 | return status; | 1909 | return status; |
@@ -1874,6 +1924,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1874 | WARN_ON_ONCE(conf && unconf); | 1924 | WARN_ON_ONCE(conf && unconf); |
1875 | 1925 | ||
1876 | if (conf) { | 1926 | if (conf) { |
1927 | status = nfserr_wrong_cred; | ||
1928 | if (!mach_creds_match(conf, rqstp)) | ||
1929 | goto out_free_conn; | ||
1877 | cs_slot = &conf->cl_cs_slot; | 1930 | cs_slot = &conf->cl_cs_slot; |
1878 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); | 1931 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); |
1879 | if (status == nfserr_replay_cache) { | 1932 | if (status == nfserr_replay_cache) { |
@@ -1890,6 +1943,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1890 | status = nfserr_clid_inuse; | 1943 | status = nfserr_clid_inuse; |
1891 | goto out_free_conn; | 1944 | goto out_free_conn; |
1892 | } | 1945 | } |
1946 | status = nfserr_wrong_cred; | ||
1947 | if (!mach_creds_match(unconf, rqstp)) | ||
1948 | goto out_free_conn; | ||
1893 | cs_slot = &unconf->cl_cs_slot; | 1949 | cs_slot = &unconf->cl_cs_slot; |
1894 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); | 1950 | status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); |
1895 | if (status) { | 1951 | if (status) { |
@@ -1957,7 +2013,11 @@ __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state | |||
1957 | { | 2013 | { |
1958 | struct nfsd4_session *session = cstate->session; | 2014 | struct nfsd4_session *session = cstate->session; |
1959 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | 2015 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
2016 | __be32 status; | ||
1960 | 2017 | ||
2018 | status = nfsd4_check_cb_sec(&bc->bc_cb_sec); | ||
2019 | if (status) | ||
2020 | return status; | ||
1961 | spin_lock(&nn->client_lock); | 2021 | spin_lock(&nn->client_lock); |
1962 | session->se_cb_prog = bc->bc_cb_program; | 2022 | session->se_cb_prog = bc->bc_cb_program; |
1963 | session->se_cb_sec = bc->bc_cb_sec; | 2023 | session->se_cb_sec = bc->bc_cb_sec; |
@@ -1986,6 +2046,9 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, | |||
1986 | status = nfserr_badsession; | 2046 | status = nfserr_badsession; |
1987 | if (!session) | 2047 | if (!session) |
1988 | goto out; | 2048 | goto out; |
2049 | status = nfserr_wrong_cred; | ||
2050 | if (!mach_creds_match(session->se_client, rqstp)) | ||
2051 | goto out; | ||
1989 | status = nfsd4_map_bcts_dir(&bcts->dir); | 2052 | status = nfsd4_map_bcts_dir(&bcts->dir); |
1990 | if (status) | 2053 | if (status) |
1991 | goto out; | 2054 | goto out; |
@@ -2014,6 +2077,7 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
2014 | { | 2077 | { |
2015 | struct nfsd4_session *ses; | 2078 | struct nfsd4_session *ses; |
2016 | __be32 status; | 2079 | __be32 status; |
2080 | int ref_held_by_me = 0; | ||
2017 | struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); | 2081 | struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); |
2018 | 2082 | ||
2019 | nfs4_lock_state(); | 2083 | nfs4_lock_state(); |
@@ -2021,6 +2085,7 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
2021 | if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { | 2085 | if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { |
2022 | if (!nfsd4_last_compound_op(r)) | 2086 | if (!nfsd4_last_compound_op(r)) |
2023 | goto out; | 2087 | goto out; |
2088 | ref_held_by_me++; | ||
2024 | } | 2089 | } |
2025 | dump_sessionid(__func__, &sessionid->sessionid); | 2090 | dump_sessionid(__func__, &sessionid->sessionid); |
2026 | spin_lock(&nn->client_lock); | 2091 | spin_lock(&nn->client_lock); |
@@ -2028,17 +2093,22 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
2028 | status = nfserr_badsession; | 2093 | status = nfserr_badsession; |
2029 | if (!ses) | 2094 | if (!ses) |
2030 | goto out_client_lock; | 2095 | goto out_client_lock; |
2031 | status = mark_session_dead_locked(ses); | 2096 | status = nfserr_wrong_cred; |
2032 | if (status) | 2097 | if (!mach_creds_match(ses->se_client, r)) |
2033 | goto out_client_lock; | 2098 | goto out_client_lock; |
2099 | nfsd4_get_session_locked(ses); | ||
2100 | status = mark_session_dead_locked(ses, 1 + ref_held_by_me); | ||
2101 | if (status) | ||
2102 | goto out_put_session; | ||
2034 | unhash_session(ses); | 2103 | unhash_session(ses); |
2035 | spin_unlock(&nn->client_lock); | 2104 | spin_unlock(&nn->client_lock); |
2036 | 2105 | ||
2037 | nfsd4_probe_callback_sync(ses->se_client); | 2106 | nfsd4_probe_callback_sync(ses->se_client); |
2038 | 2107 | ||
2039 | spin_lock(&nn->client_lock); | 2108 | spin_lock(&nn->client_lock); |
2040 | free_session(ses); | ||
2041 | status = nfs_ok; | 2109 | status = nfs_ok; |
2110 | out_put_session: | ||
2111 | nfsd4_put_session(ses); | ||
2042 | out_client_lock: | 2112 | out_client_lock: |
2043 | spin_unlock(&nn->client_lock); | 2113 | spin_unlock(&nn->client_lock); |
2044 | out: | 2114 | out: |
@@ -2058,26 +2128,31 @@ static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_s | |||
2058 | return NULL; | 2128 | return NULL; |
2059 | } | 2129 | } |
2060 | 2130 | ||
2061 | static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses) | 2131 | static __be32 nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses) |
2062 | { | 2132 | { |
2063 | struct nfs4_client *clp = ses->se_client; | 2133 | struct nfs4_client *clp = ses->se_client; |
2064 | struct nfsd4_conn *c; | 2134 | struct nfsd4_conn *c; |
2135 | __be32 status = nfs_ok; | ||
2065 | int ret; | 2136 | int ret; |
2066 | 2137 | ||
2067 | spin_lock(&clp->cl_lock); | 2138 | spin_lock(&clp->cl_lock); |
2068 | c = __nfsd4_find_conn(new->cn_xprt, ses); | 2139 | c = __nfsd4_find_conn(new->cn_xprt, ses); |
2069 | if (c) { | 2140 | if (c) |
2070 | spin_unlock(&clp->cl_lock); | 2141 | goto out_free; |
2071 | free_conn(new); | 2142 | status = nfserr_conn_not_bound_to_session; |
2072 | return; | 2143 | if (clp->cl_mach_cred) |
2073 | } | 2144 | goto out_free; |
2074 | __nfsd4_hash_conn(new, ses); | 2145 | __nfsd4_hash_conn(new, ses); |
2075 | spin_unlock(&clp->cl_lock); | 2146 | spin_unlock(&clp->cl_lock); |
2076 | ret = nfsd4_register_conn(new); | 2147 | ret = nfsd4_register_conn(new); |
2077 | if (ret) | 2148 | if (ret) |
2078 | /* oops; xprt is already down: */ | 2149 | /* oops; xprt is already down: */ |
2079 | nfsd4_conn_lost(&new->cn_xpt_user); | 2150 | nfsd4_conn_lost(&new->cn_xpt_user); |
2080 | return; | 2151 | return nfs_ok; |
2152 | out_free: | ||
2153 | spin_unlock(&clp->cl_lock); | ||
2154 | free_conn(new); | ||
2155 | return status; | ||
2081 | } | 2156 | } |
2082 | 2157 | ||
2083 | static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_session *session) | 2158 | static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_session *session) |
@@ -2169,8 +2244,10 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2169 | if (status) | 2244 | if (status) |
2170 | goto out_put_session; | 2245 | goto out_put_session; |
2171 | 2246 | ||
2172 | nfsd4_sequence_check_conn(conn, session); | 2247 | status = nfsd4_sequence_check_conn(conn, session); |
2173 | conn = NULL; | 2248 | conn = NULL; |
2249 | if (status) | ||
2250 | goto out_put_session; | ||
2174 | 2251 | ||
2175 | /* Success! bump slot seqid */ | 2252 | /* Success! bump slot seqid */ |
2176 | slot->sl_seqid = seq->seqid; | 2253 | slot->sl_seqid = seq->seqid; |
@@ -2232,7 +2309,10 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta | |||
2232 | status = nfserr_stale_clientid; | 2309 | status = nfserr_stale_clientid; |
2233 | goto out; | 2310 | goto out; |
2234 | } | 2311 | } |
2235 | 2312 | if (!mach_creds_match(clp, rqstp)) { | |
2313 | status = nfserr_wrong_cred; | ||
2314 | goto out; | ||
2315 | } | ||
2236 | expire_client(clp); | 2316 | expire_client(clp); |
2237 | out: | 2317 | out: |
2238 | nfs4_unlock_state(); | 2318 | nfs4_unlock_state(); |
@@ -2645,13 +2725,13 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp) | |||
2645 | 2725 | ||
2646 | list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); | 2726 | list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); |
2647 | 2727 | ||
2648 | /* only place dl_time is set. protected by lock_flocks*/ | 2728 | /* Only place dl_time is set; protected by i_lock: */ |
2649 | dp->dl_time = get_seconds(); | 2729 | dp->dl_time = get_seconds(); |
2650 | 2730 | ||
2651 | nfsd4_cb_recall(dp); | 2731 | nfsd4_cb_recall(dp); |
2652 | } | 2732 | } |
2653 | 2733 | ||
2654 | /* Called from break_lease() with lock_flocks() held. */ | 2734 | /* Called from break_lease() with i_lock held. */ |
2655 | static void nfsd_break_deleg_cb(struct file_lock *fl) | 2735 | static void nfsd_break_deleg_cb(struct file_lock *fl) |
2656 | { | 2736 | { |
2657 | struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; | 2737 | struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; |
@@ -2940,13 +3020,13 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f | |||
2940 | return fl; | 3020 | return fl; |
2941 | } | 3021 | } |
2942 | 3022 | ||
2943 | static int nfs4_setlease(struct nfs4_delegation *dp, int flag) | 3023 | static int nfs4_setlease(struct nfs4_delegation *dp) |
2944 | { | 3024 | { |
2945 | struct nfs4_file *fp = dp->dl_file; | 3025 | struct nfs4_file *fp = dp->dl_file; |
2946 | struct file_lock *fl; | 3026 | struct file_lock *fl; |
2947 | int status; | 3027 | int status; |
2948 | 3028 | ||
2949 | fl = nfs4_alloc_init_lease(dp, flag); | 3029 | fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ); |
2950 | if (!fl) | 3030 | if (!fl) |
2951 | return -ENOMEM; | 3031 | return -ENOMEM; |
2952 | fl->fl_file = find_readable_file(fp); | 3032 | fl->fl_file = find_readable_file(fp); |
@@ -2964,12 +3044,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp, int flag) | |||
2964 | return 0; | 3044 | return 0; |
2965 | } | 3045 | } |
2966 | 3046 | ||
2967 | static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag) | 3047 | static int nfs4_set_delegation(struct nfs4_delegation *dp) |
2968 | { | 3048 | { |
2969 | struct nfs4_file *fp = dp->dl_file; | 3049 | struct nfs4_file *fp = dp->dl_file; |
2970 | 3050 | ||
2971 | if (!fp->fi_lease) | 3051 | if (!fp->fi_lease) |
2972 | return nfs4_setlease(dp, flag); | 3052 | return nfs4_setlease(dp); |
2973 | spin_lock(&recall_lock); | 3053 | spin_lock(&recall_lock); |
2974 | if (fp->fi_had_conflict) { | 3054 | if (fp->fi_had_conflict) { |
2975 | spin_unlock(&recall_lock); | 3055 | spin_unlock(&recall_lock); |
@@ -3005,6 +3085,9 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) | |||
3005 | 3085 | ||
3006 | /* | 3086 | /* |
3007 | * Attempt to hand out a delegation. | 3087 | * Attempt to hand out a delegation. |
3088 | * | ||
3089 | * Note we don't support write delegations, and won't until the vfs has | ||
3090 | * proper support for them. | ||
3008 | */ | 3091 | */ |
3009 | static void | 3092 | static void |
3010 | nfs4_open_delegation(struct net *net, struct svc_fh *fh, | 3093 | nfs4_open_delegation(struct net *net, struct svc_fh *fh, |
@@ -3013,39 +3096,45 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, | |||
3013 | struct nfs4_delegation *dp; | 3096 | struct nfs4_delegation *dp; |
3014 | struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); | 3097 | struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); |
3015 | int cb_up; | 3098 | int cb_up; |
3016 | int status = 0, flag = 0; | 3099 | int status = 0; |
3017 | 3100 | ||
3018 | cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); | 3101 | cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); |
3019 | flag = NFS4_OPEN_DELEGATE_NONE; | ||
3020 | open->op_recall = 0; | 3102 | open->op_recall = 0; |
3021 | switch (open->op_claim_type) { | 3103 | switch (open->op_claim_type) { |
3022 | case NFS4_OPEN_CLAIM_PREVIOUS: | 3104 | case NFS4_OPEN_CLAIM_PREVIOUS: |
3023 | if (!cb_up) | 3105 | if (!cb_up) |
3024 | open->op_recall = 1; | 3106 | open->op_recall = 1; |
3025 | flag = open->op_delegate_type; | 3107 | if (open->op_delegate_type != NFS4_OPEN_DELEGATE_READ) |
3026 | if (flag == NFS4_OPEN_DELEGATE_NONE) | 3108 | goto out_no_deleg; |
3027 | goto out; | ||
3028 | break; | 3109 | break; |
3029 | case NFS4_OPEN_CLAIM_NULL: | 3110 | case NFS4_OPEN_CLAIM_NULL: |
3030 | /* Let's not give out any delegations till everyone's | 3111 | /* |
3031 | * had the chance to reclaim theirs.... */ | 3112 | * Let's not give out any delegations till everyone's |
3113 | * had the chance to reclaim theirs.... | ||
3114 | */ | ||
3032 | if (locks_in_grace(net)) | 3115 | if (locks_in_grace(net)) |
3033 | goto out; | 3116 | goto out_no_deleg; |
3034 | if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) | 3117 | if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) |
3035 | goto out; | 3118 | goto out_no_deleg; |
3119 | /* | ||
3120 | * Also, if the file was opened for write or | ||
3121 | * create, there's a good chance the client's | ||
3122 | * about to write to it, resulting in an | ||
3123 | * immediate recall (since we don't support | ||
3124 | * write delegations): | ||
3125 | */ | ||
3036 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | 3126 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) |
3037 | flag = NFS4_OPEN_DELEGATE_WRITE; | 3127 | goto out_no_deleg; |
3038 | else | 3128 | if (open->op_create == NFS4_OPEN_CREATE) |
3039 | flag = NFS4_OPEN_DELEGATE_READ; | 3129 | goto out_no_deleg; |
3040 | break; | 3130 | break; |
3041 | default: | 3131 | default: |
3042 | goto out; | 3132 | goto out_no_deleg; |
3043 | } | 3133 | } |
3044 | 3134 | dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh); | |
3045 | dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh, flag); | ||
3046 | if (dp == NULL) | 3135 | if (dp == NULL) |
3047 | goto out_no_deleg; | 3136 | goto out_no_deleg; |
3048 | status = nfs4_set_delegation(dp, flag); | 3137 | status = nfs4_set_delegation(dp); |
3049 | if (status) | 3138 | if (status) |
3050 | goto out_free; | 3139 | goto out_free; |
3051 | 3140 | ||
@@ -3053,24 +3142,23 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, | |||
3053 | 3142 | ||
3054 | dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", | 3143 | dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", |
3055 | STATEID_VAL(&dp->dl_stid.sc_stateid)); | 3144 | STATEID_VAL(&dp->dl_stid.sc_stateid)); |
3056 | out: | 3145 | open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; |
3057 | open->op_delegate_type = flag; | ||
3058 | if (flag == NFS4_OPEN_DELEGATE_NONE) { | ||
3059 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && | ||
3060 | open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) | ||
3061 | dprintk("NFSD: WARNING: refusing delegation reclaim\n"); | ||
3062 | |||
3063 | /* 4.1 client asking for a delegation? */ | ||
3064 | if (open->op_deleg_want) | ||
3065 | nfsd4_open_deleg_none_ext(open, status); | ||
3066 | } | ||
3067 | return; | 3146 | return; |
3068 | out_free: | 3147 | out_free: |
3069 | unhash_stid(&dp->dl_stid); | 3148 | unhash_stid(&dp->dl_stid); |
3070 | nfs4_put_delegation(dp); | 3149 | nfs4_put_delegation(dp); |
3071 | out_no_deleg: | 3150 | out_no_deleg: |
3072 | flag = NFS4_OPEN_DELEGATE_NONE; | 3151 | open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE; |
3073 | goto out; | 3152 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && |
3153 | open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) { | ||
3154 | dprintk("NFSD: WARNING: refusing delegation reclaim\n"); | ||
3155 | open->op_recall = 1; | ||
3156 | } | ||
3157 | |||
3158 | /* 4.1 client asking for a delegation? */ | ||
3159 | if (open->op_deleg_want) | ||
3160 | nfsd4_open_deleg_none_ext(open, status); | ||
3161 | return; | ||
3074 | } | 3162 | } |
3075 | 3163 | ||
3076 | static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open, | 3164 | static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open, |
@@ -3427,7 +3515,7 @@ grace_disallows_io(struct net *net, struct inode *inode) | |||
3427 | /* Returns true iff a is later than b: */ | 3515 | /* Returns true iff a is later than b: */ |
3428 | static bool stateid_generation_after(stateid_t *a, stateid_t *b) | 3516 | static bool stateid_generation_after(stateid_t *a, stateid_t *b) |
3429 | { | 3517 | { |
3430 | return (s32)a->si_generation - (s32)b->si_generation > 0; | 3518 | return (s32)(a->si_generation - b->si_generation) > 0; |
3431 | } | 3519 | } |
3432 | 3520 | ||
3433 | static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) | 3521 | static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) |
@@ -4435,7 +4523,6 @@ __be32 | |||
4435 | nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 4523 | nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
4436 | struct nfsd4_locku *locku) | 4524 | struct nfsd4_locku *locku) |
4437 | { | 4525 | { |
4438 | struct nfs4_lockowner *lo; | ||
4439 | struct nfs4_ol_stateid *stp; | 4526 | struct nfs4_ol_stateid *stp; |
4440 | struct file *filp = NULL; | 4527 | struct file *filp = NULL; |
4441 | struct file_lock *file_lock = NULL; | 4528 | struct file_lock *file_lock = NULL; |
@@ -4468,10 +4555,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4468 | status = nfserr_jukebox; | 4555 | status = nfserr_jukebox; |
4469 | goto out; | 4556 | goto out; |
4470 | } | 4557 | } |
4471 | lo = lockowner(stp->st_stateowner); | ||
4472 | locks_init_lock(file_lock); | 4558 | locks_init_lock(file_lock); |
4473 | file_lock->fl_type = F_UNLCK; | 4559 | file_lock->fl_type = F_UNLCK; |
4474 | file_lock->fl_owner = (fl_owner_t)lo; | 4560 | file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); |
4475 | file_lock->fl_pid = current->tgid; | 4561 | file_lock->fl_pid = current->tgid; |
4476 | file_lock->fl_file = filp; | 4562 | file_lock->fl_file = filp; |
4477 | file_lock->fl_flags = FL_POSIX; | 4563 | file_lock->fl_flags = FL_POSIX; |
@@ -4490,11 +4576,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4490 | update_stateid(&stp->st_stid.sc_stateid); | 4576 | update_stateid(&stp->st_stid.sc_stateid); |
4491 | memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); | 4577 | memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); |
4492 | 4578 | ||
4493 | if (nfsd4_has_session(cstate) && !check_for_locks(stp->st_file, lo)) { | ||
4494 | WARN_ON_ONCE(cstate->replay_owner); | ||
4495 | release_lockowner(lo); | ||
4496 | } | ||
4497 | |||
4498 | out: | 4579 | out: |
4499 | nfsd4_bump_seqid(cstate, status); | 4580 | nfsd4_bump_seqid(cstate, status); |
4500 | if (!cstate->replay_owner) | 4581 | if (!cstate->replay_owner) |
@@ -4520,7 +4601,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner) | |||
4520 | struct inode *inode = filp->fi_inode; | 4601 | struct inode *inode = filp->fi_inode; |
4521 | int status = 0; | 4602 | int status = 0; |
4522 | 4603 | ||
4523 | lock_flocks(); | 4604 | spin_lock(&inode->i_lock); |
4524 | for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { | 4605 | for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { |
4525 | if ((*flpp)->fl_owner == (fl_owner_t)lowner) { | 4606 | if ((*flpp)->fl_owner == (fl_owner_t)lowner) { |
4526 | status = 1; | 4607 | status = 1; |
@@ -4528,7 +4609,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner) | |||
4528 | } | 4609 | } |
4529 | } | 4610 | } |
4530 | out: | 4611 | out: |
4531 | unlock_flocks(); | 4612 | spin_unlock(&inode->i_lock); |
4532 | return status; | 4613 | return status; |
4533 | } | 4614 | } |
4534 | 4615 | ||