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
11   	 * License as published by the Free Software Foundation; either
12   	 * version 3 of the License, or (at your option) any later version.
13   	 *
14   	 * This program is distributed in the hope that it will be useful,
15   	 * but 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   	
27   	/**
28   	 * @defgroup SAL State abstraction layer
29   	 * @{
30   	 */
31   	
32   	/**
33   	 * @file state_share.c
34   	 * @brief Share reservation management
35   	 */
36   	
37   	#include "config.h"
38   	#include <unistd.h>
39   	#include <sys/types.h>
40   	#include <sys/param.h>
41   	#include <time.h>
42   	#include <pthread.h>
43   	#include <string.h>
44   	#include <assert.h>
45   	
46   	#include "fsal.h"
47   	#include "nfs_core.h"
48   	#include "nfs4.h"
49   	#include "sal_functions.h"
50   	/*#include "nlm_util.h"*/
51   	#include "export_mgr.h"
52   	
53   	#ifdef _USE_NLM
54   	/**
55   	 * @brief Remove an NLM share
56   	 *
57   	 * @param[in]     state	The state_t describing the share to remove
58   	 *
59   	 */
60   	void remove_nlm_share(state_t *state)
61   	{
62   		state_owner_t *owner = state->state_owner;
63   		state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client;
64   	
65   		/* Remove from share list for export */
66   		PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock);
67   		glist_del(&state->state_export_list);
68   		PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
69   	
70   		/* Remove the share from the list for the file. */
71   		glist_del(&state->state_list);
72   	
73   		/* Remove the share from the NSM Client list */
74   		PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex);
75   	
76   		glist_del(&state->state_data.nlm_share.share_perclient);
77   	
78   		PTHREAD_MUTEX_unlock(&client->slc_nsm_client->ssc_mutex);
79   	
80   		dec_nsm_client_ref(client->slc_nsm_client);
81   	
82   		/* Remove the share from the NLM Owner list */
83   		PTHREAD_MUTEX_lock(&owner->so_mutex);
84   	
85   		glist_del(&state->state_owner_list);
86   	
87   		PTHREAD_MUTEX_unlock(&owner->so_mutex);
88   	
89   		/* Release the state_t reference for active share. If extended FSAL
90   		 * operations are supported, this will close the file when the last
91   		 * reference is released.
92   		 */
93   		dec_state_t_ref(state);
94   	}
95   	
96   	/**
97   	 * @brief Implement NLM share call with FSAL extended ops
98   	 *
99   	 * @param[in,out] obj          File on which to operate
100  	 * @param[in]     export       Export through which file is accessed
101  	 * @param[in]     share_access Share mode requested
102  	 * @param[in]     share_deny   Deny mode requested
103  	 * @param[in]     owner        Share owner
104  	 * @param[in]     state        state_t to manage the share
105  	 * @param[in]     unshare      Indicates if this was an unshare
106  	 *
107  	 * @return State status.
108  	 */
109  	state_status_t state_nlm_share(struct fsal_obj_handle *obj,
110  				       int share_access,
111  				       int share_deny,
112  				       state_owner_t *owner,
113  				       state_t *state,
114  				       bool unshare)
115  	{
116  		fsal_status_t fsal_status = {0, 0};
117  		fsal_openflags_t openflags = 0;
118  		state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client;
119  		unsigned int old_access;
120  		unsigned int old_deny;
121  		unsigned int new_access = 0;
122  		unsigned int new_deny = 0;
123  		struct state_nlm_share *nlm_share = &state->state_data.nlm_share;
124  		int i, acount = 0, dcount = 0;
125  		fsal_accessflags_t access_mask = 0;
126  	
(1) Event cond_true: Condition "rc == 0", taking true branch.
(2) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(3) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(4) Event if_fallthrough: Falling through to end of if statement.
(5) Event if_end: End of if statement.
127  		PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock);
128  	
129  		old_access = nlm_share->share_access;
130  		old_deny = nlm_share->share_deny;
131  	
(6) Event cond_true: Condition "!!(component_log_level[COMPONENT_STATE] >= NIV_FULL_DEBUG)", taking true branch.
(7) Event cond_true: Condition "!!(component_log_level[COMPONENT_STATE] >= NIV_FULL_DEBUG)", taking true branch.
(8) Event cond_true: Condition "unshare", taking true branch.
132  		LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
133  				"%s access %d, deny %d",
134  				unshare ? "UNSHARE" : "SHARE",
135  				share_access, share_deny);
136  	
(9) Event cond_true: Condition "unshare", taking true branch.
137  		if (unshare) {
138  			/* For FREE_ALL case need to release all shares */
(10) Event cond_true: Condition "share_access == 4", taking true branch.
139  			if (share_access == OPEN4_SHARE_ACCESS_ALL)
(11) Event cond_true: Condition "i <= fsa_RW", taking true branch.
(13) Event loop_begin: Jumped back to beginning of loop.
(14) Event cond_true: Condition "i <= fsa_RW", taking true branch.
(16) Event loop_begin: Jumped back to beginning of loop.
(17) Event cond_false: Condition "i <= fsa_RW", taking false branch.
140  				for (i = 0; i <= fsa_RW; i++)
(12) Event loop: Jumping back to the beginning of the loop.
(15) Event loop: Jumping back to the beginning of the loop.
(18) Event loop_end: Reached end of loop.
(19) Event if_fallthrough: Falling through to end of if statement.
141  					nlm_share->share_access_counts[i] = 0;
142  			else if (nlm_share->share_access_counts[share_access] > 0)
143  				nlm_share->share_access_counts[share_access]--;
144  			else
(20) Event if_end: End of if statement.
145  				LogDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
146  					    "UNSHARE access %d did not match",
147  					    share_access);
148  	
(21) Event cond_true: Condition "share_access == 4", taking true branch.
149  			if (share_access == OPEN4_SHARE_DENY_ALL)
(22) Event cond_true: Condition "i <= fsm_DRW", taking true branch.
(24) Event loop_begin: Jumped back to beginning of loop.
(25) Event cond_true: Condition "i <= fsm_DRW", taking true branch.
(27) Event loop_begin: Jumped back to beginning of loop.
(28) Event cond_false: Condition "i <= fsm_DRW", taking false branch.
150  				for (i = 0; i <= fsm_DRW; i++)
(23) Event loop: Jumping back to the beginning of the loop.
(26) Event loop: Jumping back to the beginning of the loop.
(29) Event loop_end: Reached end of loop.
(30) Event if_fallthrough: Falling through to end of if statement.
151  					nlm_share->share_deny_counts[i] = 0;
152  			else if (nlm_share->share_deny_counts[share_deny] > 0)
153  				nlm_share->share_deny_counts[share_deny]--;
154  			else
(31) Event if_end: End of if statement.
155  				LogDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
156  					    "UNSHARE deny %d did not match",
157  					    share_access);
(32) Event if_fallthrough: Falling through to end of if statement.
158  		} else {
159  			nlm_share->share_access_counts[share_access]++;
160  			nlm_share->share_deny_counts[share_deny]++;
(33) Event if_end: End of if statement.
161  		}
162  	
163  		/* Compute new share_access as union of all remaining shares. */
(34) Event cond_true: Condition "i <= fsa_RW", taking true branch.
(37) Event loop_begin: Jumped back to beginning of loop.
(38) Event cond_true: Condition "i <= fsa_RW", taking true branch.
(41) Event loop_begin: Jumped back to beginning of loop.
(42) Event cond_true: Condition "i <= fsa_RW", taking true branch.
(45) Event loop_begin: Jumped back to beginning of loop.
(46) Event cond_false: Condition "i <= fsa_RW", taking false branch.
164  		for (i = 0; i <= fsa_RW; i++) {
(35) Event cond_true: Condition "nlm_share->share_access_counts[i] != 0", taking true branch.
(39) Event cond_true: Condition "nlm_share->share_access_counts[i] != 0", taking true branch.
(43) Event cond_true: Condition "nlm_share->share_access_counts[i] != 0", taking true branch.
165  			if (nlm_share->share_access_counts[i] != 0) {
166  				new_access |= i;
167  				acount += nlm_share->share_access_counts[i];
168  			}
(36) Event loop: Jumping back to the beginning of the loop.
(40) Event loop: Jumping back to the beginning of the loop.
(44) Event loop: Jumping back to the beginning of the loop.
(47) Event loop_end: Reached end of loop.
169  		}
170  	
171  		/* Compute new share_deny as union of all remaining shares. */
(48) Event cond_true: Condition "i <= fsm_DRW", taking true branch.
(51) Event loop_begin: Jumped back to beginning of loop.
(52) Event cond_true: Condition "i <= fsm_DRW", taking true branch.
(55) Event loop_begin: Jumped back to beginning of loop.
(56) Event cond_true: Condition "i <= fsm_DRW", taking true branch.
(59) Event loop_begin: Jumped back to beginning of loop.
(60) Event cond_false: Condition "i <= fsm_DRW", taking false branch.
172  		for (i = 0; i <= fsm_DRW; i++) {
(49) Event cond_true: Condition "nlm_share->share_deny_counts[i] != 0", taking true branch.
(53) Event cond_true: Condition "nlm_share->share_deny_counts[i] != 0", taking true branch.
(57) Event cond_true: Condition "nlm_share->share_deny_counts[i] != 0", taking true branch.
173  			if (nlm_share->share_deny_counts[i] != 0) {
174  				new_deny |= i;
175  				dcount += nlm_share->share_deny_counts[i];
176  			}
(50) Event loop: Jumping back to the beginning of the loop.
(54) Event loop: Jumping back to the beginning of the loop.
(58) Event loop: Jumping back to the beginning of the loop.
(61) Event loop_end: Reached end of loop.
177  		}
178  	
(62) Event cond_true: Condition "!!(component_log_level[COMPONENT_STATE] >= NIV_FULL_DEBUG)", taking true branch.
(63) Event cond_true: Condition "!!(component_log_level[COMPONENT_STATE] >= NIV_FULL_DEBUG)", taking true branch.
(64) Event cond_true: Condition "unshare", taking true branch.
(65) Event index_parm: Indexing array "nlm_share->share_access_counts" of size 4 with "share_access".
179  		LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
180  				"%s share_access_counts[%d] = %d, total = %d, share_deny_counts[%d] = %d, total = %d",
181  				unshare ? "UNSHARE" : "SHARE",
182  				share_access,
183  				nlm_share->share_access_counts[share_access],
184  				acount,
185  				share_deny,
186  				nlm_share->share_deny_counts[share_deny],
187  				dcount);
188  	
189  		if (new_access == old_access && new_deny == old_deny) {
190  			/* The share or unshare did not affect the union of shares so
191  			 * there is no more work to do.
192  			 */
193  			LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
194  					"%s union share did not change from access %d, deny %d",
195  					unshare ? "UNSHARE" : "SHARE",
196  					old_access, old_deny);
197  			goto out_unlock;
198  		}
199  	
200  		/* Assume new access/deny is update in determining the openflags. */
201  		if ((new_access & fsa_R) != 0)
202  			openflags |= FSAL_O_READ;
203  	
204  		if ((new_access & fsa_W) != 0)
205  			openflags |= FSAL_O_WRITE;
206  	
207  		if (openflags == FSAL_O_CLOSED && unshare) {
208  			/* This unshare is removing the final share. The file will be
209  			 * closed when the final reference to the state is released.
210  			 */
211  			LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
212  					"UNSHARE removed state_t %p, share_access %u, share_deny %u",
213  					state, old_access, old_deny);
214  	
215  			remove_nlm_share(state);
216  			goto out_unlock;
217  		}
218  	
219  		if (openflags == FSAL_O_CLOSED) {
220  			LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
221  					"SHARE with access none, deny %d and file is not already open, modify to read",
222  					share_deny);
223  			openflags |= FSAL_O_READ;
224  		}
225  	
226  		if ((new_deny & fsm_DR) != 0)
227  			openflags |= FSAL_O_DENY_READ;
228  	
229  		if ((new_deny & fsm_DW) != 0)
230  			openflags |= FSAL_O_DENY_WRITE;
231  	
232  		if (openflags & FSAL_O_READ)
233  			access_mask |= FSAL_READ_ACCESS;
234  	
235  		if (openflags & FSAL_O_WRITE)
236  			access_mask |= FSAL_WRITE_ACCESS;
237  	
238  		/* The access check must be same as the read and write calls.
239  		 * Use owner_skip for the access checks
240  		 * The reopen2 call does not use the owner_skip
241  		 * perform test_access first and then call reopen2.
242  		 */
243  		fsal_status = obj->obj_ops->test_access(obj, access_mask,
244  							NULL, NULL, true);
245  		if (FSAL_IS_ERROR(fsal_status)) {
246  			LogDebug(COMPONENT_STATE,
247  				 "test_access failed with %s",
248  				 fsal_err_txt(fsal_status));
249  			goto out_unlock;
250  		}
251  	
252  		/* Use reopen2 to open or re-open the file and check for share
253  		 * conflict.
254  		 */
255  		fsal_status = fsal_reopen2(obj, state, openflags, false);
256  	
257  		if (FSAL_IS_ERROR(fsal_status)) {
258  			LogDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
259  				    "fsal_reopen2 failed with %s",
260  				    fsal_err_txt(fsal_status));
261  			goto out_unlock;
262  		} else {
263  			LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
264  					"fsal_reopen2 succeeded");
265  		}
266  	
267  		/* If we already had a share, skip all the book keeping. */
268  		if (old_access != OPEN4_SHARE_ACCESS_NONE) {
269  			LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
270  					"%s updated state_t %p, share_access %u, share_deny %u",
271  					unshare ? "UNSHARE" : "SHARE",
272  					state, new_access, new_deny);
273  			goto update;
274  		}
275  	
276  		/* Take a reference on the state_t. */
277  		inc_state_t_ref(state);
278  	
279  		/* Add share to list for NLM Owner */
280  		PTHREAD_MUTEX_lock(&owner->so_mutex);
281  	
282  		glist_add_tail(&owner->so_owner.so_nlm_owner.so_nlm_shares,
283  			       &state->state_owner_list);
284  	
285  		PTHREAD_MUTEX_unlock(&owner->so_mutex);
286  	
287  		/* Add share to list for NSM Client */
288  		inc_nsm_client_ref(client->slc_nsm_client);
289  	
290  		PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex);
291  	
292  		glist_add_tail(&client->slc_nsm_client->ssc_share_list,
293  			       &nlm_share->share_perclient);
294  	
295  		PTHREAD_MUTEX_unlock(&client->slc_nsm_client->ssc_mutex);
296  	
297  		/* Add share to list for file. */
298  		glist_add_tail(&obj->state_hdl->file.nlm_share_list,
299  			       &state->state_list);
300  	
301  		/* Add to share list for export */
302  		PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock);
303  		glist_add_tail(&op_ctx->ctx_export->exp_nlm_share_list,
304  			       &state->state_export_list);
305  		PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
306  	
307  		LogFullDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
308  				"SHARE added state_t %p, share_access %u, share_deny %u",
309  				state, new_access, new_deny);
310  	
311  	 update:
312  	
313  		/* Update the current share type */
314  		nlm_share->share_access = new_access;
315  		nlm_share->share_deny = new_deny;
316  	
317  	 out_unlock:
318  	
319  		PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock);
320  	
321  		return state_error_convert(fsal_status);
322  	}
323  	
324  	/**
325  	 * @brief Remove all share state from a file
326  	 *
327  	 * @param[in] obj File to wipe
328  	 */
329  	void state_share_wipe(struct state_hdl *hstate)
330  	{
331  		state_t *state;
332  		struct glist_head *glist;
333  		struct glist_head *glistn;
334  	
335  		glist_for_each_safe(glist, glistn, &hstate->file.nlm_share_list) {
336  			state = glist_entry(glist, state_t, state_list);
337  	
338  			remove_nlm_share(state);
339  		}
340  	}
341  	
342  	void state_export_unshare_all(void)
343  	{
344  		int errcnt = 0;
345  		state_t *state;
346  		state_owner_t *owner;
347  		struct fsal_obj_handle *obj;
348  		state_status_t status;
349  	
(1) Event cond_true: Condition "errcnt < 100", taking true branch.
350  		while (errcnt < STATE_ERR_MAX) {
(2) Event cond_true: Condition "rc == 0", taking true branch.
(3) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(4) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(5) Event if_fallthrough: Falling through to end of if statement.
(6) Event if_end: End of if statement.
351  			PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock);
352  	
(7) Event cond_true: Condition "op_ctx->ctx_export->exp_nlm_share_list.next != &op_ctx->ctx_export->exp_nlm_share_list", taking true branch.
353  			state = glist_first_entry(
354  					&op_ctx->ctx_export->exp_nlm_share_list,
355  					state_t,
356  					state_export_list);
357  	
(8) Event cond_false: Condition "state == NULL", taking false branch.
358  			if (state == NULL) {
359  				PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
360  				break;
(9) Event if_end: End of if statement.
361  			}
362  	
363  			obj = get_state_obj_ref(state);
364  	
(10) Event cond_false: Condition "obj == NULL", taking false branch.
365  			if (obj == NULL) {
366  				LogDebugAlt(COMPONENT_STATE, COMPONENT_NLM,
367  					    "Entry for state is stale");
368  				PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
369  				break;
(11) Event if_end: End of if statement.
370  			}
371  	
372  			owner = state->state_owner;
373  	
374  			/* Get a reference to the state_t */
375  			inc_state_t_ref(state);
376  	
377  			/* get a reference to the owner */
378  			inc_state_owner_ref(owner);
379  	
380  			/* Drop the export mutex to call unshare */
(12) Event cond_true: Condition "rc == 0", taking true branch.
(13) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(14) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(15) Event if_fallthrough: Falling through to end of if statement.
(16) Event if_end: End of if statement.
381  			PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock);
382  	
383  			/* Remove all shares held by this Owner on this export */
(17) Event overrun-call: Overrunning callee's array of size 4 by passing argument "4" in call to "state_nlm_share". [details]
384  			status = state_nlm_share(obj,
385  						 OPEN4_SHARE_ACCESS_ALL,
386  						 OPEN4_SHARE_DENY_ALL,
387  						 owner,
388  						 state,
389  						 true);
390  	
391  			/* Release references taken above. Should free the state_t. */
392  			dec_state_owner_ref(owner);
393  			obj->obj_ops->put_ref(obj);
394  			dec_state_t_ref(state);
395  	
396  			if (!state_unlock_err_ok(status)) {
397  				/* Increment the error count and try the next share,
398  				 * with any luck the memory pressure which is causing
399  				 * the problem will resolve itself.
400  				 */
401  				LogCrit(COMPONENT_STATE,
402  					"state_unlock failed %s",
403  					state_err_str(status));
404  				errcnt++;
405  			}
406  		}
407  	
408  		if (errcnt == STATE_ERR_MAX) {
409  			LogFatal(COMPONENT_STATE,
410  				 "Could not complete cleanup of NLM shares for %s",
411  				 export_path(op_ctx->ctx_export));
412  		}
413  	}
414  	#endif /* _USE_NLM */
415  	
416  	/** @} */
417