1*c2c66affSColin Finck /* NFSv4.1 client for Windows
2*c2c66affSColin Finck * Copyright � 2012 The Regents of the University of Michigan
3*c2c66affSColin Finck *
4*c2c66affSColin Finck * Olga Kornievskaia <aglo@umich.edu>
5*c2c66affSColin Finck * Casey Bodley <cbodley@umich.edu>
6*c2c66affSColin Finck *
7*c2c66affSColin Finck * This library is free software; you can redistribute it and/or modify it
8*c2c66affSColin Finck * under the terms of the GNU Lesser General Public License as published by
9*c2c66affSColin Finck * the Free Software Foundation; either version 2.1 of the License, or (at
10*c2c66affSColin Finck * your option) any later version.
11*c2c66affSColin Finck *
12*c2c66affSColin Finck * This library is distributed in the hope that it will be useful, but
13*c2c66affSColin Finck * without any warranty; without even the implied warranty of merchantability
14*c2c66affSColin Finck * or fitness for a particular purpose. See the GNU Lesser General Public
15*c2c66affSColin Finck * License for more details.
16*c2c66affSColin Finck *
17*c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public License
18*c2c66affSColin Finck * along with this library; if not, write to the Free Software Foundation,
19*c2c66affSColin Finck * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20*c2c66affSColin Finck */
21*c2c66affSColin Finck
22*c2c66affSColin Finck #include <windows.h>
23*c2c66affSColin Finck #include <process.h>
24*c2c66affSColin Finck #include <stdio.h>
25*c2c66affSColin Finck
26*c2c66affSColin Finck #include "nfs41_ops.h"
27*c2c66affSColin Finck #include "nfs41_callback.h"
28*c2c66affSColin Finck #include "util.h"
29*c2c66affSColin Finck #include "daemon_debug.h"
30*c2c66affSColin Finck
31*c2c66affSColin Finck
32*c2c66affSColin Finck /* after a CB_RECALL_SLOT or NFS4ERR_BADSLOT, wait a short time for the
33*c2c66affSColin Finck * SEQUENCE.target_highest_slotid to catch up before updating max_slots again */
34*c2c66affSColin Finck #define MAX_SLOTS_DELAY 2000 /* in milliseconds */
35*c2c66affSColin Finck
36*c2c66affSColin Finck
37*c2c66affSColin Finck /* predicate for nfs41_slot_table.cond */
slot_table_avail(IN const nfs41_slot_table * table)38*c2c66affSColin Finck static int slot_table_avail(
39*c2c66affSColin Finck IN const nfs41_slot_table *table)
40*c2c66affSColin Finck {
41*c2c66affSColin Finck return table->num_used < table->max_slots;
42*c2c66affSColin Finck }
43*c2c66affSColin Finck
44*c2c66affSColin Finck /* session slot mechanism */
init_slot_table(nfs41_slot_table * table)45*c2c66affSColin Finck static void init_slot_table(nfs41_slot_table *table)
46*c2c66affSColin Finck {
47*c2c66affSColin Finck uint32_t i;
48*c2c66affSColin Finck EnterCriticalSection(&table->lock);
49*c2c66affSColin Finck table->max_slots = NFS41_MAX_NUM_SLOTS;
50*c2c66affSColin Finck for (i = 0; i < NFS41_MAX_NUM_SLOTS; i++) {
51*c2c66affSColin Finck table->seq_nums[i] = 1;
52*c2c66affSColin Finck table->used_slots[i] = 0;
53*c2c66affSColin Finck }
54*c2c66affSColin Finck table->highest_used = table->num_used = 0;
55*c2c66affSColin Finck table->target_delay = 0;
56*c2c66affSColin Finck
57*c2c66affSColin Finck /* wake any threads waiting on a slot */
58*c2c66affSColin Finck if (slot_table_avail(table))
59*c2c66affSColin Finck WakeAllConditionVariable(&table->cond);
60*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
61*c2c66affSColin Finck }
62*c2c66affSColin Finck
resize_slot_table(IN nfs41_slot_table * table,IN uint32_t target_highest_slotid)63*c2c66affSColin Finck static void resize_slot_table(
64*c2c66affSColin Finck IN nfs41_slot_table *table,
65*c2c66affSColin Finck IN uint32_t target_highest_slotid)
66*c2c66affSColin Finck {
67*c2c66affSColin Finck if (target_highest_slotid >= NFS41_MAX_NUM_SLOTS)
68*c2c66affSColin Finck target_highest_slotid = NFS41_MAX_NUM_SLOTS - 1;
69*c2c66affSColin Finck
70*c2c66affSColin Finck if (table->max_slots != target_highest_slotid + 1) {
71*c2c66affSColin Finck dprintf(2, "updated max_slots %u to %u\n",
72*c2c66affSColin Finck table->max_slots, target_highest_slotid + 1);
73*c2c66affSColin Finck table->max_slots = target_highest_slotid + 1;
74*c2c66affSColin Finck
75*c2c66affSColin Finck if (slot_table_avail(table))
76*c2c66affSColin Finck WakeAllConditionVariable(&table->cond);
77*c2c66affSColin Finck }
78*c2c66affSColin Finck }
79*c2c66affSColin Finck
nfs41_session_bump_seq(IN nfs41_session * session,IN uint32_t slotid,IN uint32_t target_highest_slotid)80*c2c66affSColin Finck void nfs41_session_bump_seq(
81*c2c66affSColin Finck IN nfs41_session *session,
82*c2c66affSColin Finck IN uint32_t slotid,
83*c2c66affSColin Finck IN uint32_t target_highest_slotid)
84*c2c66affSColin Finck {
85*c2c66affSColin Finck nfs41_slot_table *table = &session->table;
86*c2c66affSColin Finck
87*c2c66affSColin Finck AcquireSRWLockShared(&session->client->session_lock);
88*c2c66affSColin Finck EnterCriticalSection(&table->lock);
89*c2c66affSColin Finck
90*c2c66affSColin Finck if (slotid < NFS41_MAX_NUM_SLOTS)
91*c2c66affSColin Finck table->seq_nums[slotid]++;
92*c2c66affSColin Finck
93*c2c66affSColin Finck /* adjust max_slots in response to changes in target_highest_slotid,
94*c2c66affSColin Finck * but not immediately after a CB_RECALL_SLOT or NFS4ERR_BADSLOT error */
95*c2c66affSColin Finck if (table->target_delay <= GetTickCount64())
96*c2c66affSColin Finck resize_slot_table(table, target_highest_slotid);
97*c2c66affSColin Finck
98*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
99*c2c66affSColin Finck ReleaseSRWLockShared(&session->client->session_lock);
100*c2c66affSColin Finck }
101*c2c66affSColin Finck
nfs41_session_free_slot(IN nfs41_session * session,IN uint32_t slotid)102*c2c66affSColin Finck void nfs41_session_free_slot(
103*c2c66affSColin Finck IN nfs41_session *session,
104*c2c66affSColin Finck IN uint32_t slotid)
105*c2c66affSColin Finck {
106*c2c66affSColin Finck nfs41_slot_table *table = &session->table;
107*c2c66affSColin Finck
108*c2c66affSColin Finck AcquireSRWLockShared(&session->client->session_lock);
109*c2c66affSColin Finck EnterCriticalSection(&table->lock);
110*c2c66affSColin Finck
111*c2c66affSColin Finck /* flag the slot as unused */
112*c2c66affSColin Finck if (slotid < NFS41_MAX_NUM_SLOTS && table->used_slots[slotid]) {
113*c2c66affSColin Finck table->used_slots[slotid] = 0;
114*c2c66affSColin Finck table->num_used--;
115*c2c66affSColin Finck }
116*c2c66affSColin Finck /* update highest_used if necessary */
117*c2c66affSColin Finck if (slotid == table->highest_used) {
118*c2c66affSColin Finck while (table->highest_used && !table->used_slots[table->highest_used])
119*c2c66affSColin Finck table->highest_used--;
120*c2c66affSColin Finck }
121*c2c66affSColin Finck dprintf(3, "freeing slot#=%d used=%d highest=%d\n",
122*c2c66affSColin Finck slotid, table->num_used, table->highest_used);
123*c2c66affSColin Finck
124*c2c66affSColin Finck /* wake any threads waiting on a slot */
125*c2c66affSColin Finck if (slot_table_avail(table))
126*c2c66affSColin Finck WakeAllConditionVariable(&table->cond);
127*c2c66affSColin Finck
128*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
129*c2c66affSColin Finck ReleaseSRWLockShared(&session->client->session_lock);
130*c2c66affSColin Finck }
131*c2c66affSColin Finck
nfs41_session_get_slot(IN nfs41_session * session,OUT uint32_t * slot,OUT uint32_t * seqid,OUT uint32_t * highest)132*c2c66affSColin Finck void nfs41_session_get_slot(
133*c2c66affSColin Finck IN nfs41_session *session,
134*c2c66affSColin Finck OUT uint32_t *slot,
135*c2c66affSColin Finck OUT uint32_t *seqid,
136*c2c66affSColin Finck OUT uint32_t *highest)
137*c2c66affSColin Finck {
138*c2c66affSColin Finck nfs41_slot_table *table = &session->table;
139*c2c66affSColin Finck uint32_t i;
140*c2c66affSColin Finck
141*c2c66affSColin Finck AcquireSRWLockShared(&session->client->session_lock);
142*c2c66affSColin Finck EnterCriticalSection(&table->lock);
143*c2c66affSColin Finck
144*c2c66affSColin Finck /* wait for an available slot */
145*c2c66affSColin Finck while (!slot_table_avail(table))
146*c2c66affSColin Finck SleepConditionVariableCS(&table->cond, &table->lock, INFINITE);
147*c2c66affSColin Finck
148*c2c66affSColin Finck for (i = 0; i < table->max_slots; i++) {
149*c2c66affSColin Finck if (table->used_slots[i])
150*c2c66affSColin Finck continue;
151*c2c66affSColin Finck
152*c2c66affSColin Finck table->used_slots[i] = 1;
153*c2c66affSColin Finck table->num_used++;
154*c2c66affSColin Finck if (i > table->highest_used)
155*c2c66affSColin Finck table->highest_used = i;
156*c2c66affSColin Finck
157*c2c66affSColin Finck *slot = i;
158*c2c66affSColin Finck *seqid = table->seq_nums[i];
159*c2c66affSColin Finck *highest = table->highest_used;
160*c2c66affSColin Finck break;
161*c2c66affSColin Finck }
162*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
163*c2c66affSColin Finck ReleaseSRWLockShared(&session->client->session_lock);
164*c2c66affSColin Finck
165*c2c66affSColin Finck dprintf(2, "session %p: using slot#=%d with seq#=%d highest=%d\n",
166*c2c66affSColin Finck session, *slot, *seqid, *highest);
167*c2c66affSColin Finck }
168*c2c66affSColin Finck
nfs41_session_recall_slot(IN nfs41_session * session,IN OUT uint32_t target_highest_slotid)169*c2c66affSColin Finck int nfs41_session_recall_slot(
170*c2c66affSColin Finck IN nfs41_session *session,
171*c2c66affSColin Finck IN OUT uint32_t target_highest_slotid)
172*c2c66affSColin Finck {
173*c2c66affSColin Finck nfs41_slot_table *table = &session->table;
174*c2c66affSColin Finck
175*c2c66affSColin Finck AcquireSRWLockShared(&session->client->session_lock);
176*c2c66affSColin Finck EnterCriticalSection(&table->lock);
177*c2c66affSColin Finck resize_slot_table(table, target_highest_slotid);
178*c2c66affSColin Finck table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY;
179*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
180*c2c66affSColin Finck ReleaseSRWLockShared(&session->client->session_lock);
181*c2c66affSColin Finck
182*c2c66affSColin Finck return NFS4_OK;
183*c2c66affSColin Finck }
184*c2c66affSColin Finck
nfs41_session_bad_slot(IN nfs41_session * session,IN OUT nfs41_sequence_args * args)185*c2c66affSColin Finck int nfs41_session_bad_slot(
186*c2c66affSColin Finck IN nfs41_session *session,
187*c2c66affSColin Finck IN OUT nfs41_sequence_args *args)
188*c2c66affSColin Finck {
189*c2c66affSColin Finck nfs41_slot_table *table = &session->table;
190*c2c66affSColin Finck int status = NFS4ERR_BADSLOT;
191*c2c66affSColin Finck
192*c2c66affSColin Finck if (args->sa_slotid == 0) {
193*c2c66affSColin Finck eprintf("server bug detected: NFS4ERR_BADSLOT for slotid=0\n");
194*c2c66affSColin Finck goto out;
195*c2c66affSColin Finck }
196*c2c66affSColin Finck
197*c2c66affSColin Finck /* avoid using any slots >= bad_slotid */
198*c2c66affSColin Finck EnterCriticalSection(&table->lock);
199*c2c66affSColin Finck if (table->max_slots > args->sa_slotid) {
200*c2c66affSColin Finck resize_slot_table(table, args->sa_slotid);
201*c2c66affSColin Finck table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY;
202*c2c66affSColin Finck }
203*c2c66affSColin Finck LeaveCriticalSection(&table->lock);
204*c2c66affSColin Finck
205*c2c66affSColin Finck /* get a new slot */
206*c2c66affSColin Finck nfs41_session_free_slot(session, args->sa_slotid);
207*c2c66affSColin Finck nfs41_session_get_slot(session, &args->sa_slotid,
208*c2c66affSColin Finck &args->sa_sequenceid, &args->sa_highest_slotid);
209*c2c66affSColin Finck status = NFS4_OK;
210*c2c66affSColin Finck out:
211*c2c66affSColin Finck return status;
212*c2c66affSColin Finck }
213*c2c66affSColin Finck
nfs41_session_sequence(nfs41_sequence_args * args,nfs41_session * session,bool_t cachethis)214*c2c66affSColin Finck void nfs41_session_sequence(
215*c2c66affSColin Finck nfs41_sequence_args *args,
216*c2c66affSColin Finck nfs41_session *session,
217*c2c66affSColin Finck bool_t cachethis)
218*c2c66affSColin Finck {
219*c2c66affSColin Finck nfs41_session_get_slot(session, &args->sa_slotid,
220*c2c66affSColin Finck &args->sa_sequenceid, &args->sa_highest_slotid);
221*c2c66affSColin Finck args->sa_sessionid = session->session_id;
222*c2c66affSColin Finck args->sa_cachethis = cachethis;
223*c2c66affSColin Finck }
224*c2c66affSColin Finck
225*c2c66affSColin Finck
226*c2c66affSColin Finck /* session renewal */
renew_session(void * args)227*c2c66affSColin Finck static unsigned int WINAPI renew_session(void *args)
228*c2c66affSColin Finck {
229*c2c66affSColin Finck int status = NO_ERROR;
230*c2c66affSColin Finck nfs41_session *session = (nfs41_session *)args;
231*c2c66affSColin Finck /* sleep for 2/3 of lease_time */
232*c2c66affSColin Finck const uint32_t sleep_time = (2 * session->lease_time*1000)/3;
233*c2c66affSColin Finck
234*c2c66affSColin Finck dprintf(1, "Creating renew_session thread: %p\n", session->renew_thread);
235*c2c66affSColin Finck while(1) {
236*c2c66affSColin Finck dprintf(1, "Going to sleep for %dmsecs\n", sleep_time);
237*c2c66affSColin Finck Sleep(sleep_time);
238*c2c66affSColin Finck status = nfs41_send_sequence(session);
239*c2c66affSColin Finck if (status)
240*c2c66affSColin Finck dprintf(1, "renewal thread: nfs41_send_sequence failed %d\n", status);
241*c2c66affSColin Finck }
242*c2c66affSColin Finck return status;
243*c2c66affSColin Finck }
244*c2c66affSColin Finck
245*c2c66affSColin Finck /* session creation */
session_alloc(IN nfs41_client * client,OUT nfs41_session ** session_out)246*c2c66affSColin Finck static int session_alloc(
247*c2c66affSColin Finck IN nfs41_client *client,
248*c2c66affSColin Finck OUT nfs41_session **session_out)
249*c2c66affSColin Finck {
250*c2c66affSColin Finck nfs41_session *session;
251*c2c66affSColin Finck int status = NO_ERROR;
252*c2c66affSColin Finck
253*c2c66affSColin Finck session = calloc(1, sizeof(nfs41_session));
254*c2c66affSColin Finck if (session == NULL) {
255*c2c66affSColin Finck status = GetLastError();
256*c2c66affSColin Finck goto out;
257*c2c66affSColin Finck }
258*c2c66affSColin Finck session->client = client;
259*c2c66affSColin Finck session->renew_thread = INVALID_HANDLE_VALUE;
260*c2c66affSColin Finck session->isValidState = FALSE;
261*c2c66affSColin Finck
262*c2c66affSColin Finck InitializeCriticalSection(&session->table.lock);
263*c2c66affSColin Finck InitializeConditionVariable(&session->table.cond);
264*c2c66affSColin Finck
265*c2c66affSColin Finck init_slot_table(&session->table);
266*c2c66affSColin Finck
267*c2c66affSColin Finck //initialize session lock
268*c2c66affSColin Finck InitializeSRWLock(&client->session_lock);
269*c2c66affSColin Finck
270*c2c66affSColin Finck /* initialize the back channel */
271*c2c66affSColin Finck nfs41_callback_session_init(session);
272*c2c66affSColin Finck
273*c2c66affSColin Finck *session_out = session;
274*c2c66affSColin Finck out:
275*c2c66affSColin Finck return status;
276*c2c66affSColin Finck }
277*c2c66affSColin Finck
nfs41_session_create(IN nfs41_client * client,IN nfs41_session ** session_out)278*c2c66affSColin Finck int nfs41_session_create(
279*c2c66affSColin Finck IN nfs41_client *client,
280*c2c66affSColin Finck IN nfs41_session **session_out)
281*c2c66affSColin Finck {
282*c2c66affSColin Finck nfs41_session *session;
283*c2c66affSColin Finck int status;
284*c2c66affSColin Finck
285*c2c66affSColin Finck status = session_alloc(client, &session);
286*c2c66affSColin Finck if (status) {
287*c2c66affSColin Finck eprintf("session_alloc() failed with %d\n", status);
288*c2c66affSColin Finck goto out;
289*c2c66affSColin Finck }
290*c2c66affSColin Finck
291*c2c66affSColin Finck AcquireSRWLockShared(&client->exid_lock);
292*c2c66affSColin Finck if (client->rpc->needcb)
293*c2c66affSColin Finck session->flags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
294*c2c66affSColin Finck session->flags |= CREATE_SESSION4_FLAG_PERSIST;
295*c2c66affSColin Finck ReleaseSRWLockShared(&client->exid_lock);
296*c2c66affSColin Finck
297*c2c66affSColin Finck status = nfs41_create_session(client, session, TRUE);
298*c2c66affSColin Finck if (status) {
299*c2c66affSColin Finck eprintf("nfs41_create_session failed %d\n", status);
300*c2c66affSColin Finck status = ERROR_BAD_NET_RESP;
301*c2c66affSColin Finck goto out_err;
302*c2c66affSColin Finck }
303*c2c66affSColin Finck
304*c2c66affSColin Finck AcquireSRWLockExclusive(&session->client->session_lock);
305*c2c66affSColin Finck client->session = session;
306*c2c66affSColin Finck session->isValidState = TRUE;
307*c2c66affSColin Finck ReleaseSRWLockExclusive(&session->client->session_lock);
308*c2c66affSColin Finck *session_out = session;
309*c2c66affSColin Finck out:
310*c2c66affSColin Finck return status;
311*c2c66affSColin Finck
312*c2c66affSColin Finck out_err:
313*c2c66affSColin Finck nfs41_session_free(session);
314*c2c66affSColin Finck goto out;
315*c2c66affSColin Finck }
316*c2c66affSColin Finck
317*c2c66affSColin Finck /* session renewal */
nfs41_session_renew(IN nfs41_session * session)318*c2c66affSColin Finck int nfs41_session_renew(
319*c2c66affSColin Finck IN nfs41_session *session)
320*c2c66affSColin Finck {
321*c2c66affSColin Finck int status;
322*c2c66affSColin Finck
323*c2c66affSColin Finck AcquireSRWLockExclusive(&session->client->session_lock);
324*c2c66affSColin Finck session->cb_session.cb_seqnum = 0;
325*c2c66affSColin Finck init_slot_table(&session->table);
326*c2c66affSColin Finck
327*c2c66affSColin Finck status = nfs41_create_session(session->client, session, FALSE);
328*c2c66affSColin Finck ReleaseSRWLockExclusive(&session->client->session_lock);
329*c2c66affSColin Finck return status;
330*c2c66affSColin Finck }
331*c2c66affSColin Finck
nfs41_session_set_lease(IN nfs41_session * session,IN uint32_t lease_time)332*c2c66affSColin Finck int nfs41_session_set_lease(
333*c2c66affSColin Finck IN nfs41_session *session,
334*c2c66affSColin Finck IN uint32_t lease_time)
335*c2c66affSColin Finck {
336*c2c66affSColin Finck int status = NO_ERROR;
337*c2c66affSColin Finck uint32_t thread_id;
338*c2c66affSColin Finck
339*c2c66affSColin Finck if (valid_handle(session->renew_thread)) {
340*c2c66affSColin Finck eprintf("nfs41_session_set_lease(): session "
341*c2c66affSColin Finck "renewal thread already started!\n");
342*c2c66affSColin Finck goto out;
343*c2c66affSColin Finck }
344*c2c66affSColin Finck
345*c2c66affSColin Finck if (lease_time == 0) {
346*c2c66affSColin Finck eprintf("nfs41_session_set_lease(): invalid lease_time=0\n");
347*c2c66affSColin Finck status = ERROR_INVALID_PARAMETER;
348*c2c66affSColin Finck goto out;
349*c2c66affSColin Finck }
350*c2c66affSColin Finck
351*c2c66affSColin Finck session->lease_time = lease_time;
352*c2c66affSColin Finck session->renew_thread = (HANDLE)_beginthreadex(NULL,
353*c2c66affSColin Finck 0, renew_session, session, 0, &thread_id);
354*c2c66affSColin Finck if (!valid_handle(session->renew_thread)) {
355*c2c66affSColin Finck status = GetLastError();
356*c2c66affSColin Finck eprintf("_beginthreadex failed %d\n", status);
357*c2c66affSColin Finck goto out;
358*c2c66affSColin Finck }
359*c2c66affSColin Finck out:
360*c2c66affSColin Finck return status;
361*c2c66affSColin Finck }
362*c2c66affSColin Finck
nfs41_session_free(IN nfs41_session * session)363*c2c66affSColin Finck void nfs41_session_free(
364*c2c66affSColin Finck IN nfs41_session *session)
365*c2c66affSColin Finck {
366*c2c66affSColin Finck AcquireSRWLockExclusive(&session->client->session_lock);
367*c2c66affSColin Finck if (valid_handle(session->renew_thread)) {
368*c2c66affSColin Finck dprintf(1, "nfs41_session_free: terminating session renewal thread\n");
369*c2c66affSColin Finck if (!TerminateThread(session->renew_thread, NO_ERROR))
370*c2c66affSColin Finck eprintf("failed to terminate renewal thread %p\n",
371*c2c66affSColin Finck session->renew_thread);
372*c2c66affSColin Finck }
373*c2c66affSColin Finck
374*c2c66affSColin Finck if (session->isValidState) {
375*c2c66affSColin Finck session->client->rpc->is_valid_session = FALSE;
376*c2c66affSColin Finck nfs41_destroy_session(session);
377*c2c66affSColin Finck }
378*c2c66affSColin Finck DeleteCriticalSection(&session->table.lock);
379*c2c66affSColin Finck ReleaseSRWLockExclusive(&session->client->session_lock);
380*c2c66affSColin Finck free(session);
381*c2c66affSColin Finck }
382