1    	/*
2    	 * vim:noexpandtab:shiftwidth=8:tabstop=8:
3    	 *
4    	 * Copyright CEA/DAM/DIF  (2008)
5    	 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
6    	 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
7    	 *
8    	 *
9    	 * This program is free software; you can redistribute it and/or
10   	 * modify it under the terms of the GNU Lesser General Public License
11   	 * as published by the Free Software Foundation; either version 3 of
12   	 * the License, or (at your option) any later version.
13   	 *
14   	 * This program is distributed in the hope that it will be useful, but
15   	 * WITHOUT ANY WARRANTY; without even the implied warranty of
16   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   	 * Lesser General Public License for more details.
18   	 *
19   	 * You should have received a copy of the GNU Lesser General Public
20   	 * License along with this library; if not, write to the Free Software
21   	 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22   	 * 02110-1301 USA
23   	 */
24   	
25   	/**
26   	 * @defgroup SAL State abstraction layer
27   	 * @{
28   	 */
29   	
30   	/**
31   	 * @file nfs4_owner.c
32   	 * @brief The management of the NFS4 Owner cache.
33   	 */
34   	
35   	#include "config.h"
36   	#include <pthread.h>
37   	#include <ctype.h>
38   	#include "log.h"
39   	#include "hashtable.h"
40   	#include "nfs4.h"
41   	#include "sal_functions.h"
42   	#include "nfs_proto_functions.h"
43   	#include "nfs_proto_tools.h"
44   	#include "nfs_core.h"
45   	
46   	hash_table_t *ht_nfs4_owner;
47   	
48   	/**
49   	 * @brief Display an NFSv4 owner key
50   	 *
51   	 * @param[in]  buff Key to display
52   	 * @param[out] str  Output buffer
53   	 *
54   	 * @return Length of output string.
55   	 */
56   	int display_nfs4_owner_key(struct gsh_buffdesc *buff, char *str)
57   	{
58   		struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str};
59   	
60   		display_nfs4_owner(&dspbuf, buff->addr);
61   		return display_buffer_len(&dspbuf);
62   	}
63   	
64   	/**
65   	 * @brief Display NFSv4 owner
66   	 *
67   	 * @param[in]  owner The state owner
68   	 * @param[out] str   Output string
69   	 *
70   	 * @return the bytes remaining in the buffer.
71   	 */
72   	int display_nfs4_owner(struct display_buffer *dspbuf, state_owner_t *owner)
73   	{
74   		int b_left;
75   		time_t texpire;
76   		struct state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner;
77   	
78   		if (owner == NULL)
79   			return display_cat(dspbuf, "<NULL>");
80   	
81   		b_left = display_printf(dspbuf,  "%s %p:",
82   					state_owner_type_to_str(owner->so_type),
83   					owner);
84   	
85   		if (b_left <= 0)
86   			return b_left;
87   	
88   		b_left = display_printf(dspbuf, " clientid={");
89   	
90   		if (b_left <= 0)
91   			return b_left;
92   	
93   		b_left = display_client_id_rec(dspbuf, nfs4_owner->so_clientrec);
94   	
95   		if (b_left <= 0)
96   			return b_left;
97   	
98   		b_left = display_printf(dspbuf, "} owner=");
99   	
100  		if (b_left <= 0)
101  			return b_left;
102  	
103  		b_left = display_opaque_value(dspbuf,
104  					      owner->so_owner_val,
105  					      owner->so_owner_len);
106  	
107  		if (b_left <= 0)
108  			return b_left;
109  	
110  		b_left = display_printf(dspbuf, " confirmed=%u seqid=%u",
111  					nfs4_owner->so_confirmed,
112  					nfs4_owner->so_seqid);
113  	
114  		if (b_left <= 0)
115  			return b_left;
116  	
117  		if (nfs4_owner->so_related_owner != NULL) {
118  			b_left = display_printf(dspbuf, " related_owner={");
119  	
120  			if (b_left <= 0)
121  				return b_left;
122  	
123  			b_left =
124  			    display_nfs4_owner(dspbuf, nfs4_owner->so_related_owner);
125  	
126  			if (b_left <= 0)
127  				return b_left;
128  	
129  			b_left = display_printf(dspbuf, "}");
130  	
131  			if (b_left <= 0)
132  				return b_left;
133  		}
134  	
135  		texpire = atomic_fetch_time_t(&nfs4_owner->so_cache_expire);
136  	
137  		if (texpire != 0) {
138  			b_left = display_printf(dspbuf,
139  						" cached(expires in %d secs)",
(1) Event invalid_type: Argument "texpire - time(NULL)" to format specifier "%d" was expected to have type "int" but has type "long". [details]
140  						texpire - time(NULL));
141  	
142  			if (b_left <= 0)
143  				return b_left;
144  		}
145  	
146  		return display_printf(dspbuf, " refcount=%d",
147  			    atomic_fetch_int32_t(&owner->so_refcount));
148  	}
149  	
150  	/**
151  	 * @brief Display owner from hash table
152  	 *
153  	 * @param[in]  buff Buffer
154  	 * @param[out] str  Output buffer
155  	 *
156  	 * @return Length of the output string.
157  	 */
158  	int display_nfs4_owner_val(struct gsh_buffdesc *buff, char *str)
159  	{
160  		struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str};
161  	
162  		display_nfs4_owner(&dspbuf, buff->addr);
163  		return display_buffer_len(&dspbuf);
164  	}
165  	
166  	/**
167  	 * @brief Compare two NFSv4 owners
168  	 *
169  	 * @param[in] owner1 One owner
170  	 * @param[in] owner2 Another owner
171  	 *
172  	 * @retval 0 on equality.
173  	 * @retval 1 on inequality.
174  	 */
175  	int compare_nfs4_owner(state_owner_t *owner1, state_owner_t *owner2)
176  	{
177  		if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) {
178  			char str1[LOG_BUFF_LEN / 2] = "\0";
179  			char str2[LOG_BUFF_LEN / 2] = "\0";
180  			struct display_buffer dspbuf1 = {sizeof(str1), str1, str1};
181  			struct display_buffer dspbuf2 = {sizeof(str2), str2, str2};
182  	
183  			display_nfs4_owner(&dspbuf1, owner1);
184  			display_nfs4_owner(&dspbuf2, owner2);
185  			LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2);
186  		}
187  	
188  		if (owner1 == NULL || owner2 == NULL)
189  			return 1;
190  	
191  		if (owner1 == owner2)
192  			return 0;
193  	
194  		if (owner1->so_type != owner2->so_type)
195  			return 1;
196  	
197  		if (owner1->so_owner.so_nfs4_owner.so_clientid !=
198  		    owner2->so_owner.so_nfs4_owner.so_clientid)
199  			return 1;
200  	
201  		if (owner1->so_owner_len != owner2->so_owner_len)
202  			return 1;
203  	
204  		return memcmp(owner1->so_owner_val, owner2->so_owner_val,
205  			      owner1->so_owner_len);
206  	}
207  	
208  	/**
209  	 * @brief Compare two NFSv4 owners in the hash table
210  	 *
211  	 * @param[in] buff1 One key
212  	 * @param[in] buff2 Another owner
213  	 *
214  	 * @retval 0 on equality.
215  	 * @retval 1 on inequality.
216  	 */
217  	int compare_nfs4_owner_key(struct gsh_buffdesc *buff1,
218  				   struct gsh_buffdesc *buff2)
219  	{
220  		state_owner_t *pkey1 = buff1->addr;
221  		state_owner_t *pkey2 = buff2->addr;
222  	
223  		if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) {
224  			char str1[LOG_BUFF_LEN / 2] = "\0";
225  			char str2[LOG_BUFF_LEN / 2] = "\0";
226  			struct display_buffer dspbuf1 = {sizeof(str1), str1, str1};
227  			struct display_buffer dspbuf2 = {sizeof(str2), str2, str2};
228  	
229  			display_owner(&dspbuf1, pkey1);
230  			display_owner(&dspbuf2, pkey2);
231  	
232  			if (isDebug(COMPONENT_HASHTABLE))
233  				LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1,
234  					     str2);
235  		}
236  	
237  		if (pkey1 == NULL || pkey2 == NULL)
238  			return 1;
239  	
240  		if (pkey1->so_type != pkey2->so_type)
241  			return 1;
242  	
243  		return compare_nfs4_owner(pkey1, pkey2);
244  	}
245  	
246  	/**
247  	 * @brief Compute the hash index for an NFSv4 owner
248  	 *
249  	 * @todo Destroy this function and replace it with a real hash.
250  	 *
251  	 * @param[in] hparam Hash parameter
252  	 * @param[in] key    The key
253  	 *
254  	 * @return The hash index.
255  	 */
256  	uint32_t nfs4_owner_value_hash_func(hash_parameter_t *hparam,
257  					    struct gsh_buffdesc *key)
258  	{
259  		unsigned int sum = 0;
260  		unsigned int i = 0;
261  		unsigned char c = 0;
262  		uint32_t res = 0;
263  	
264  		state_owner_t *pkey = key->addr;
265  	
266  		/* Compute the sum of all the characters */
267  		for (i = 0; i < pkey->so_owner_len; i++) {
268  			c = ((char *)pkey->so_owner_val)[i];
269  			sum += c;
270  		}
271  	
272  		res =
273  		    ((uint32_t) pkey->so_owner.so_nfs4_owner.so_clientid +
274  		     (uint32_t) sum + pkey->so_owner_len +
275  		     (uint32_t) pkey->so_type) % (uint32_t) hparam->index_size;
276  	
277  		if (isDebug(COMPONENT_HASHTABLE))
278  			LogFullDebug(COMPONENT_STATE, "value = %" PRIu32, res);
279  	
280  		return res;
281  	}
282  	
283  	/**
284  	 * @brief Compute the RBT hash for an NFSv4 owner
285  	 *
286  	 * @todo Destroy this function and replace it with a real hash.
287  	 *
288  	 * @param[in] hparam Hash parameter
289  	 * @param[in] key    The key
290  	 *
291  	 * @return The RBT hash.
292  	 */
293  	uint64_t nfs4_owner_rbt_hash_func(hash_parameter_t *hparam,
294  					  struct gsh_buffdesc *key)
295  	{
296  		state_owner_t *pkey = key->addr;
297  	
298  		unsigned int sum = 0;
299  		unsigned int i = 0;
300  		unsigned char c = 0;
301  		uint64_t res = 0;
302  	
303  		/* Compute the sum of all the characters */
304  		for (i = 0; i < pkey->so_owner_len; i++) {
305  			c = ((char *)pkey->so_owner_val)[i];
306  			sum += c;
307  		}
308  	
309  		res =
310  		    (uint64_t) pkey->so_owner.so_nfs4_owner.so_clientid +
311  		    (uint64_t) sum + pkey->so_owner_len + (uint64_t) pkey->so_type;
312  	
313  		if (isDebug(COMPONENT_HASHTABLE))
314  			LogFullDebug(COMPONENT_STATE, "rbt = %" PRIu64, res);
315  	
316  		return res;
317  	}
318  	
319  	/**
320  	 * @brief Free an NFS4 owner object
321  	 *
322  	 * @param[in] owner Owner to remove
323  	 */
324  	
325  	void free_nfs4_owner(state_owner_t *owner)
326  	{
327  		state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner;
328  	
329  		if (nfs4_owner->so_related_owner != NULL)
330  			dec_state_owner_ref(nfs4_owner->so_related_owner);
331  	
332  		/* Release the saved response. */
333  		nfs4_Compound_FreeOne(&nfs4_owner->so_resp);
334  	
335  		/* Remove the owner from the owners per clientid list. */
336  		PTHREAD_MUTEX_lock(&nfs4_owner->so_clientrec->cid_mutex);
337  	
338  		glist_del(&nfs4_owner->so_perclient);
339  	
340  		PTHREAD_MUTEX_unlock(&nfs4_owner->so_clientrec->cid_mutex);
341  	
342  		dec_client_id_ref(nfs4_owner->so_clientrec);
343  	}
344  	
345  	static hash_parameter_t nfs4_owner_param = {
346  		.index_size = PRIME_STATE,
347  		.hash_func_key = nfs4_owner_value_hash_func,
348  		.hash_func_rbt = nfs4_owner_rbt_hash_func,
349  		.compare_key = compare_nfs4_owner_key,
350  		.key_to_str = display_nfs4_owner_key,
351  		.val_to_str = display_nfs4_owner_val,
352  		.flags = HT_FLAG_CACHE,
353  	};
354  	
355  	/**
356  	 * @brief Init the hashtable for NFSv4 owner cache
357  	 *
358  	 * @retval 0 if successful.
359  	 * @retval -1 if we failed.
360  	 */
361  	int Init_nfs4_owner(void)
362  	{
363  		ht_nfs4_owner = hashtable_init(&nfs4_owner_param);
364  	
365  		if (ht_nfs4_owner == NULL) {
366  			LogCrit(COMPONENT_STATE, "Cannot init NFS Open Owner cache");
367  			return -1;
368  		}
369  	
370  		return 0;
371  	}				/* nfs4_Init_nfs4_owner */
372  	
373  	/**
374  	 * @brief Initialize an NFS4 open owner object
375  	 *
376  	 * @param[in] owner The owner record
377  	 *
378  	 */
379  	static void init_nfs4_owner(state_owner_t *owner)
380  	{
381  		state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner;
382  	
383  		glist_init(&nfs4_owner->so_state_list);
384  	
385  		/* Increment refcount on related owner */
386  		if (nfs4_owner->so_related_owner != NULL)
387  			inc_state_owner_ref(nfs4_owner->so_related_owner);
388  	
389  		/* Increment reference count for clientid record */
390  		inc_client_id_ref(nfs4_owner->so_clientrec);
391  	
392  		PTHREAD_MUTEX_lock(&nfs4_owner->so_clientrec->cid_mutex);
393  	
394  		if (owner->so_type == STATE_OPEN_OWNER_NFSV4) {
395  			/* If open owner, add to clientid lock owner list */
396  			glist_add_tail(&nfs4_owner->so_clientrec->cid_openowners,
397  				       &nfs4_owner->so_perclient);
398  		} else if (owner->so_type == STATE_LOCK_OWNER_NFSV4) {
399  			/* If lock owner, add to clientid open owner list */
400  			glist_add_tail(&nfs4_owner->so_clientrec->cid_lockowners,
401  				       &nfs4_owner->so_perclient);
402  		}
403  	
404  		PTHREAD_MUTEX_unlock(&nfs4_owner->so_clientrec->cid_mutex);
405  	}
406  	
407  	/**
408  	 * @brief Display the NFSv4 owner table
409  	 */
410  	void nfs4_owner_PrintAll(void)
411  	{
412  		hashtable_log(COMPONENT_STATE, ht_nfs4_owner);
413  	}
414  	
415  	/**
416  	 * @brief Create an NFSv4 state owner
417  	 *
418  	 * @param[in]  name          Owner name
419  	 * @param[in]  clientid      Client record
420  	 * @param[in]  type          Owner type
421  	 * @param[in]  related_owner For lock owners, the related open owner
422  	 * @param[in]  init_seqid    The starting seqid (for NFSv4.0)
423  	 * @param[out] pisnew        Whether the owner actually is new
424  	 * @param[in]  care          Care flag (to unify v3/v4 owners?)
425  	 * @param[in]  confirm       Create with it already confirmed?
426  	 *
427  	 * @return A new state owner or NULL.
428  	 */
429  	state_owner_t *create_nfs4_owner(state_nfs4_owner_name_t *name,
430  					 nfs_client_id_t *clientid,
431  					 state_owner_type_t type,
432  					 state_owner_t *related_owner,
433  					 unsigned int init_seqid, bool_t *pisnew,
434  					 care_t care, bool_t confirm)
435  	{
436  		state_owner_t key;
437  		state_owner_t *owner;
438  		bool_t isnew;
439  	
440  		/* set up the content of the open_owner */
441  		memset(&key, 0, sizeof(key));
442  	
443  		key.so_type = type;
444  		key.so_owner.so_nfs4_owner.so_seqid = init_seqid;
445  		key.so_owner.so_nfs4_owner.so_related_owner = related_owner;
446  		key.so_owner.so_nfs4_owner.so_clientid = clientid->cid_clientid;
447  		key.so_owner.so_nfs4_owner.so_clientrec = clientid;
448  		key.so_owner_len = name->son_owner_len;
449  		key.so_owner_val = name->son_owner_val;
450  		key.so_owner.so_nfs4_owner.so_resp.resop = NFS4_OP_ILLEGAL;
451  		key.so_owner.so_nfs4_owner.so_args.argop = NFS4_OP_ILLEGAL;
452  		key.so_refcount = 1;
453  		key.so_owner.so_nfs4_owner.so_confirmed = confirm;
454  	
455  		if (isFullDebug(COMPONENT_STATE)) {
456  			char str[LOG_BUFF_LEN] = "\0";
457  			struct display_buffer dspbuf = {sizeof(str), str, str};
458  	
459  			display_owner(&dspbuf, &key);
460  			LogFullDebug(COMPONENT_STATE, "Key=%s", str);
461  		}
462  	
463  		owner = get_state_owner(care, &key, init_nfs4_owner, &isnew);
464  	
465  		if (owner != NULL && related_owner != NULL) {
466  			PTHREAD_MUTEX_lock(&owner->so_mutex);
467  			/* Related owner already exists. */
468  			if (owner->so_owner.so_nfs4_owner.so_related_owner == NULL) {
469  				/* Attach related owner to owner now that we know it. */
470  				inc_state_owner_ref(related_owner);
471  				owner->so_owner.so_nfs4_owner.so_related_owner =
472  				    related_owner;
473  			} else if (owner->so_owner.so_nfs4_owner.so_related_owner !=
474  				   related_owner) {
475  				char str1[LOG_BUFF_LEN / 2] = "\0";
476  				char str2[LOG_BUFF_LEN / 2] = "\0";
477  				struct display_buffer dspbuf1 = {
478  							sizeof(str1), str1, str1};
479  				struct display_buffer dspbuf2 = {
480  							sizeof(str2), str2, str2};
481  	
482  				display_owner(&dspbuf1, related_owner);
483  				display_owner(&dspbuf2, owner);
484  	
485  				LogCrit(COMPONENT_NFS_V4_LOCK,
486  					"Related {%s} doesn't match for {%s}", str1,
487  					str2);
488  				PTHREAD_MUTEX_unlock(&owner->so_mutex);
489  	
490  				/* Release the reference to the owner. */
491  				dec_state_owner_ref(owner);
492  	
493  				return NULL;
494  			}
495  			PTHREAD_MUTEX_unlock(&owner->so_mutex);
496  		}
497  	
498  		if (!isnew && owner != NULL && pisnew != NULL) {
499  			if (isDebug(COMPONENT_STATE)) {
500  				char str[LOG_BUFF_LEN] = "\0";
501  				struct display_buffer dspbuf = {sizeof(str), str, str};
502  	
503  				display_owner(&dspbuf, owner);
504  	
505  				LogDebug(COMPONENT_STATE,
506  					 "Previously known owner {%s} is being reused",
507  					 str);
508  			}
509  		}
510  	
511  		if (pisnew != NULL)
512  			*pisnew = isnew;
513  	
514  		return owner;
515  	}
516  	
517  	/**
518  	 * @brief Fill out an NFSv4 lock conflict
519  	 *
520  	 * @param[out] denied   NFSv4 LOCK4denied structure
521  	 * @param[in]  holder   Holder of the conflicting lock
522  	 * @param[in]  conflict The conflicting lock
523  	 * @param[in]  data     The nfsv4 compound data
524  	 */
525  	
526  	#define BASE_RESP_SIZE (sizeof(nfsstat4) + sizeof(offset4) + sizeof(length4) + \
527  				sizeof(nfs_lock_type4) + sizeof(clientid4) + \
528  				sizeof(uint32_t))
529  	
530  	nfsstat4 Process_nfs4_conflict(LOCK4denied *denied, state_owner_t *holder,
531  				       fsal_lock_param_t *conflict,
532  				       compound_data_t *data)
533  	{
534  		nfsstat4 status;
535  		size_t owner_len;
536  	
537  		if (holder != NULL && holder->so_owner_len != 0)
538  			owner_len = holder->so_owner_len;
539  		else
540  			owner_len = unknown_owner.so_owner_len;
541  	
542  		/* First check if the response will fit, this is a response to a
543  		 * LOCK or LOCKT operation.
544  		 */
545  		status = check_resp_room(data, BASE_RESP_SIZE + owner_len);
546  	
547  		if (status != NFS4_OK)
548  			return status;
549  	
550  		/* Now set the op_resp_size. */
551  		data->op_resp_size = BASE_RESP_SIZE + owner_len;
552  	
553  		/* A  conflicting lock from a different lock_owner,
554  		 * returns NFS4ERR_DENIED
555  		 */
556  		denied->offset = conflict->lock_start;
557  		denied->length = conflict->lock_length;
558  	
559  		if (conflict->lock_type == FSAL_LOCK_R)
560  			denied->locktype = READ_LT;
561  		else
562  			denied->locktype = WRITE_LT;
563  	
564  		if (holder != NULL && holder->so_owner_len != 0) {
565  			denied->owner.owner.owner_val =
566  			    gsh_malloc(holder->so_owner_len);
567  	
568  			denied->owner.owner.owner_len = holder->so_owner_len;
569  	
570  			memcpy(denied->owner.owner.owner_val, holder->so_owner_val,
571  			       holder->so_owner_len);
572  		} else {
573  			denied->owner.owner.owner_len = unknown_owner.so_owner_len;
574  			denied->owner.owner.owner_val = unknown_owner.so_owner_val;
575  		}
576  	
577  		LogFullDebug(COMPONENT_STATE, "denied->owner.owner.owner_val = %p",
578  			     denied->owner.owner.owner_val);
579  	
580  		if (holder != NULL && holder->so_type == STATE_LOCK_OWNER_NFSV4)
581  			denied->owner.clientid =
582  			    holder->so_owner.so_nfs4_owner.so_clientid;
583  		else
584  			denied->owner.clientid = 0;
585  	
586  		/* Release any lock owner reference passed back from SAL */
587  		if (holder != NULL)
588  			dec_state_owner_ref(holder);
589  	
590  		return NFS4ERR_DENIED;
591  	}
592  	
593  	/**
594  	 * @brief Release data allocated for LOCK4denied
595  	 *
596  	 * @param[in] denied Structure to release
597  	 */
598  	void Release_nfs4_denied(LOCK4denied *denied)
599  	{
600  		if (denied->owner.owner.owner_val != unknown_owner.so_owner_val) {
601  			gsh_free(denied->owner.owner.owner_val);
602  			denied->owner.owner.owner_val = NULL;
603  		}
604  	}
605  	
606  	/**
607  	 * @brief Deep copy a LOCK4denied
608  	 *
609  	 * @param[out] denied_dst Target
610  	 * @param[in]  denied_src Source
611  	 */
612  	void Copy_nfs4_denied(LOCK4denied *denied_dst, LOCK4denied *denied_src)
613  	{
614  		memcpy(denied_dst, denied_src, sizeof(*denied_dst));
615  	
616  		if (denied_src->owner.owner.owner_val != unknown_owner.so_owner_val
617  		    && denied_src->owner.owner.owner_val != NULL) {
618  			denied_dst->owner.owner.owner_val =
619  			    gsh_malloc(denied_src->owner.owner.owner_len);
620  			LogFullDebug(COMPONENT_STATE,
621  				     "denied_dst->owner.owner.owner_val = %p",
622  				     denied_dst->owner.owner.owner_val);
623  			memcpy(denied_dst->owner.owner.owner_val,
624  			       denied_src->owner.owner.owner_val,
625  			       denied_src->owner.owner.owner_len);
626  		}
627  	
628  		if (denied_dst->owner.owner.owner_val == NULL) {
629  			denied_dst->owner.owner.owner_len = unknown_owner.so_owner_len;
630  			denied_dst->owner.owner.owner_val = unknown_owner.so_owner_val;
631  		}
632  	}
633  	
634  	/**
635  	 * @brief Copy a operation into a state owner
636  	 *
637  	 * This is only used for NFSv4.0 and only for a specific subset of
638  	 * operations for which it guarantees At-Most Once Semantics.
639  	 *
640  	 * @param[in,out] owner The owner to hold the operation
641  	 * @param[in]     seqid Seqid of this operation
642  	 * @param[in]     args  Arguments of operation to copy
643  	 * @param[in]     data  Compound data
644  	 * @param[in]     resp  Response to copy
645  	 * @param[in]     tag   Arbitrary string for logging/debugging
646  	 */
647  	void Copy_nfs4_state_req(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args,
648  				 struct fsal_obj_handle *obj, nfs_resop4 *resp,
649  				 const char *tag)
650  	{
651  		/* Simplify use of this function when we may not be keeping any data
652  		 * for the state owner
653  		 */
654  		if (owner == NULL)
655  			return;
656  	
657  		LogFullDebug(COMPONENT_STATE,
658  			     "%s: saving response %p so_seqid %u new seqid %u", tag,
659  			     owner, owner->so_owner.so_nfs4_owner.so_seqid, seqid);
660  	
661  		/* Free previous response */
662  		nfs4_Compound_FreeOne(&owner->so_owner.so_nfs4_owner.so_resp);
663  	
664  		/* Copy new response */
665  		nfs4_Compound_CopyResOne(&owner->so_owner.so_nfs4_owner.so_resp, resp);
666  	
667  		/** @todo Deep copy OPEN args?
668  		 * if (owner->so_owner.so_nfs4_owner.so_args.argop == NFS4_OP_OPEN)
669  		 */
670  	
671  		/* Copy bnew args */
672  		memcpy(&owner->so_owner.so_nfs4_owner.so_args, args,
673  		       sizeof(owner->so_owner.so_nfs4_owner.so_args));
674  	
675  		/* Copy new file, note we don't take any reference, so this entry
676  		 * might not remain valid, but the pointer value suffices here.
677  		 */
678  		owner->so_owner.so_nfs4_owner.so_last_entry = obj;
679  	
680  		/** @todo Deep copy OPEN args?
681  		 * if (args->argop == NFS4_OP_OPEN)
682  		 */
683  	
684  		/* Store new seqid */
685  		owner->so_owner.so_nfs4_owner.so_seqid = seqid;
686  	}
687  	
688  	/**
689  	 * @brief Check NFS4 request for valid seqid for replay, next request, or
690  	 *        BAD_SEQID.
691  	 *
692  	 * Returns true if the request is the next seqid.  If the request is a
693  	 * replay, copies the saved response and returns false.  Otherwise,
694  	 * sets status to NFS4ERR_BAD_SEQID and returns false.
695  	 *
696  	 * In either case, on a false return, the caller should send the
697  	 * resulting response back to the client.
698  	 *
699  	 * @param[in]  owner Owner to check
700  	 * @param[in]  seqid Seqid to check
701  	 * @param[in]  args  Arguments of operation
702  	 * @param[in]  data  Compound data
703  	 * @param[out] resp  Cached request, if replay
704  	 * @param[in]  tag   Arbitrary string for logging/debugging
705  	 *
706  	 * @retval true if the caller should process the operation.
707  	 * @retval false if the caller should immediately return the provides response.
708  	 */
709  	bool Check_nfs4_seqid(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args,
710  			      struct fsal_obj_handle *obj, nfs_resop4 *resp,
711  			      const char *tag)
712  	{
713  		seqid4 next;
714  		char str[LOG_BUFF_LEN] = "\0";
715  		struct display_buffer dspbuf = {sizeof(str), str, str};
716  		bool str_valid = false;
717  	
718  		/* Check if any owner to verify seqid against */
719  		if (owner == NULL) {
720  			LogFullDebug(COMPONENT_STATE,
721  				     "%s: Unknown owner doesn't have saved seqid, req seqid %u",
722  				     tag, seqid);
723  			return true;
724  		}
725  	
726  		if (isDebug(COMPONENT_STATE)) {
727  			display_owner(&dspbuf, owner);
728  			str_valid = true;
729  		}
730  	
731  		/* If this is a new state owner, client may start with any seqid */
732  		if (owner->so_owner.so_nfs4_owner.so_last_entry == NULL) {
733  			if (str_valid)
734  				LogFullDebug(COMPONENT_STATE,
735  					     "%s: New {%s} doesn't have saved seqid, req seqid %u",
736  					     tag, str, seqid);
737  			return true;
738  		}
739  	
740  		/* Check for valid next seqid */
741  		next = owner->so_owner.so_nfs4_owner.so_seqid + 1;
742  	
743  		if (str_valid)
744  			LogFullDebug(COMPONENT_STATE,
745  				     "%s: Check {%s} next %u req seqid %u",
746  				     tag, str, next, seqid);
747  	
748  		if (seqid == next)
749  			return true;
750  	
751  		/* All NFS4 responses have the status in the same place, so use any to
752  		 * set NFS4ERR_BAD_SEQID
753  		 */
754  		resp->nfs_resop4_u.oplock.status = NFS4ERR_BAD_SEQID;
755  	
756  		/* Now check for valid replay */
757  		if (owner->so_owner.so_nfs4_owner.so_seqid != seqid) {
758  			if (str_valid)
759  				LogDebug(COMPONENT_STATE,
760  					 "%s: Invalid seqid %u in request (not replay), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
761  					 tag, seqid, str);
762  			return false;
763  		}
764  	
765  		if (args->argop != owner->so_owner.so_nfs4_owner.so_args.argop) {
766  			if (str_valid)
767  				LogDebug(COMPONENT_STATE,
768  					 "%s: Invalid seqid %u in request (not replay - not same op), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
769  					 tag, seqid, str);
770  			return false;
771  		}
772  	
773  		if (owner->so_owner.so_nfs4_owner.so_last_entry != obj) {
774  			if (str_valid)
775  				LogDebug(COMPONENT_STATE,
776  					 "%s: Invalid seqid %u in request (not replay - wrong file), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
777  					 tag, seqid, str);
778  			return false;
779  		}
780  	
781  		/** @todo FSF: add more checks here... */
782  	
783  		if (str_valid)
784  			LogDebug(COMPONENT_STATE,
785  				 "%s: Copying saved response for seqid %u into {%s}",
786  				 tag, seqid, str);
787  	
788  		/* Copy the saved response and tell caller to use it */
789  		nfs4_Compound_CopyResOne(resp, &owner->so_owner.so_nfs4_owner.so_resp);
790  	
791  		return false;
792  	}
793  	
794  	/** @} */
795