xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_deleg_ops.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/systm.h>
29 #include <rpc/auth.h>
30 #include <rpc/clnt.h>
31 #include <nfs/nfs4_kprot.h>
32 #include <nfs/nfs4.h>
33 #include <sys/types.h>
34 #include <sys/mutex.h>
35 #include <sys/condvar.h>
36 #include <sys/vfs.h>
37 #include <sys/vnode.h>
38 #include <sys/time.h>
39 #include <sys/fem.h>
40 #include <sys/cmn_err.h>
41 
42 
43 extern u_longlong_t nfs4_srv_caller_id;
44 
45 /*
46  * This file contains the code for the monitors which are placed on the vnodes
47  * of files that are granted delegations by the nfsV4 server.  These monitors
48  * will detect local access, as well as access from other servers
49  * (NFS and CIFS), that conflict with the delegations and recall the
50  * delegation from the client before letting the offending operation continue.
51  *
52  * If the caller does not want to block while waiting for the delegation to
53  * be returned, then it should set CC_DONTBLOCK in the flags of caller context.
54  * This does not work for vnevnents; remove and rename, they always block.
55  */
56 
57 /*
58  * This is the function to recall a delegation.  It will check if the caller
59  * wishes to block or not while waiting for the delegation to be returned.
60  * If the caller context flag has CC_DONTBLOCK set, then it will return
61  * an error and set CC_WOULDBLOCK instead of waiting for the delegation.
62  */
63 
64 int
65 recall_all_delegations(
66 	rfs4_file_t *fp,
67 	bool_t trunc,
68 	caller_context_t *ct)
69 {
70 	clock_t rc;
71 
72 	rfs4_recall_deleg(fp, trunc, NULL);
73 
74 	/* optimization that may not stay */
75 	delay(NFS4_DELEGATION_CONFLICT_DELAY);
76 
77 	/* if it has been returned, we're done. */
78 	rfs4_dbe_lock(fp->dbe);
79 	if (fp->dinfo->dtype == OPEN_DELEGATE_NONE) {
80 		rfs4_dbe_unlock(fp->dbe);
81 		return (0);
82 	}
83 
84 	if (ct != NULL && ct->cc_flags & CC_DONTBLOCK) {
85 		rfs4_dbe_unlock(fp->dbe);
86 		ct->cc_flags |= CC_WOULDBLOCK;
87 		return (NFS4ERR_DELAY);
88 	}
89 
90 	while (fp->dinfo->dtype != OPEN_DELEGATE_NONE) {
91 		rc = rfs4_dbe_twait(fp->dbe,
92 		    lbolt + SEC_TO_TICK(rfs4_lease_time));
93 		if (rc == -1) { /* timed out */
94 			rfs4_dbe_unlock(fp->dbe);
95 			rfs4_recall_deleg(fp, trunc, NULL);
96 			rfs4_dbe_lock(fp->dbe);
97 		}
98 	}
99 	rfs4_dbe_unlock(fp->dbe);
100 
101 	return (0);
102 }
103 
104 /* monitor for open on read delegated file */
105 int
106 deleg_rd_open(
107 	femarg_t *arg,
108 	int mode,
109 	cred_t *cr,
110 	caller_context_t *ct)
111 {
112 	int rc;
113 	rfs4_file_t *fp;
114 
115 	/*
116 	 * Now that the NFSv4 server calls VOP_OPEN, we need to check to
117 	 * to make sure it is not us calling open (like for DELEG_CUR) or
118 	 * we will end up panicing the system.
119 	 * Since this monitor is for a read delegated file, we know that
120 	 * only an open for write will cause a conflict.
121 	 */
122 	if ((ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) &&
123 	    (mode & (FWRITE|FTRUNC))) {
124 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
125 		rc = recall_all_delegations(fp, FALSE, ct);
126 		if (rc == NFS4ERR_DELAY)
127 			return (EAGAIN);
128 	}
129 
130 	return (vnext_open(arg, mode, cr, ct));
131 }
132 
133 /* monitor for open on write delegated file */
134 int
135 deleg_wr_open(
136 	femarg_t *arg,
137 	int mode,
138 	cred_t *cr,
139 	caller_context_t *ct)
140 {
141 	int rc;
142 	rfs4_file_t *fp;
143 
144 	/*
145 	 * Now that the NFSv4 server calls VOP_OPEN, we need to check to
146 	 * to make sure it is not us calling open (like for DELEG_CUR) or
147 	 * we will end up panicing the system.
148 	 * Since this monitor is for a write delegated file, we know that
149 	 * any open will cause a conflict.
150 	 */
151 	if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
152 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
153 		rc = recall_all_delegations(fp, FALSE, ct);
154 		if (rc == NFS4ERR_DELAY)
155 			return (EAGAIN);
156 	}
157 
158 	return (vnext_open(arg, mode, cr, ct));
159 }
160 
161 /*
162  * This is op is for write delegations only and should only be hit
163  * by the owner of the delegation.  If not, then someone is
164  * doing a read without doing an open first. Like from nfs2/3.
165  */
166 int
167 deleg_wr_read(
168 	femarg_t *arg,
169 	uio_t *uiop,
170 	int ioflag,
171 	cred_t *cr,
172 	struct caller_context *ct)
173 {
174 	int rc;
175 	rfs4_file_t *fp;
176 
177 	/* Use caller context to compare caller to delegation owner */
178 	if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
179 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
180 		rc = recall_all_delegations(fp, FALSE, ct);
181 		if (rc == NFS4ERR_DELAY)
182 			return (EAGAIN);
183 	}
184 	return (vnext_read(arg, uiop, ioflag, cr, ct));
185 }
186 
187 /*
188  * If someone is doing a write on a read delegated file, it is a conflict.
189  * conflicts should be caught at open, but NFSv2&3 don't use OPEN.
190  */
191 int
192 deleg_rd_write(
193 	femarg_t *arg,
194 	uio_t *uiop,
195 	int ioflag,
196 	cred_t *cr,
197 	struct caller_context *ct)
198 {
199 	int rc;
200 	rfs4_file_t *fp;
201 
202 	fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
203 	rc = recall_all_delegations(fp, FALSE, ct);
204 	if (rc == NFS4ERR_DELAY)
205 		return (EAGAIN);
206 
207 	return (vnext_write(arg, uiop, ioflag, cr, ct));
208 }
209 
210 /*
211  * The owner of the delegation can write the file, but nobody else can.
212  * Conflicts should be caught at open, but NFSv2&3 don't use OPEN.
213  */
214 int
215 deleg_wr_write(
216 	femarg_t *arg,
217 	uio_t *uiop,
218 	int ioflag,
219 	cred_t *cr,
220 	struct caller_context *ct)
221 {
222 	int rc;
223 	rfs4_file_t *fp;
224 
225 	/* Use caller context to compare caller to delegation owner */
226 	if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
227 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
228 		rc = recall_all_delegations(fp, FALSE, ct);
229 		if (rc == NFS4ERR_DELAY)
230 			return (EAGAIN);
231 	}
232 	return (vnext_write(arg, uiop, ioflag, cr, ct));
233 }
234 
235 /* Doing a setattr on a read delegated file is a conflict. */
236 int
237 deleg_rd_setattr(
238 	femarg_t *arg,
239 	vattr_t *vap,
240 	int flags,
241 	cred_t *cr,
242 	caller_context_t *ct)
243 {
244 	int rc;
245 	bool_t trunc = FALSE;
246 	rfs4_file_t *fp;
247 
248 	if ((vap->va_mask & AT_SIZE) && (vap->va_size == 0))
249 		trunc = TRUE;
250 
251 	fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
252 	rc = recall_all_delegations(fp, trunc, ct);
253 	if (rc == NFS4ERR_DELAY)
254 		return (EAGAIN);
255 
256 	return (vnext_setattr(arg, vap, flags, cr, ct));
257 }
258 
259 /* Only the owner of the write delegation can do a setattr */
260 int
261 deleg_wr_setattr(
262 	femarg_t *arg,
263 	vattr_t *vap,
264 	int flags,
265 	cred_t *cr,
266 	caller_context_t *ct)
267 {
268 	int rc;
269 	bool_t trunc = FALSE;
270 	rfs4_file_t *fp;
271 
272 	/*
273 	 * Use caller context to compare caller to delegation owner
274 	 */
275 	if (ct == NULL || (ct->cc_caller_id != nfs4_srv_caller_id)) {
276 		if ((vap->va_mask & AT_SIZE) && (vap->va_size == 0))
277 			trunc = TRUE;
278 
279 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
280 		rc = recall_all_delegations(fp, trunc, ct);
281 		if (rc == NFS4ERR_DELAY)
282 			return (EAGAIN);
283 	}
284 
285 	return (vnext_setattr(arg, vap, flags, cr, ct));
286 }
287 
288 int
289 deleg_rd_rwlock(
290 	femarg_t *arg,
291 	int write_lock,
292 	caller_context_t *ct)
293 {
294 	int rc;
295 	rfs4_file_t *fp;
296 
297 	/*
298 	 * If this is a write lock, then we got us a conflict.
299 	 */
300 	if (write_lock) {
301 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
302 		rc = recall_all_delegations(fp, FALSE, ct);
303 		if (rc == NFS4ERR_DELAY)
304 			return (EAGAIN);
305 	}
306 
307 	return (vnext_rwlock(arg, write_lock, ct));
308 }
309 
310 /* Only the owner of the write delegation should be doing this. */
311 int
312 deleg_wr_rwlock(
313 	femarg_t *arg,
314 	int write_lock,
315 	caller_context_t *ct)
316 {
317 	int rc;
318 	rfs4_file_t *fp;
319 
320 	/* Use caller context to compare caller to delegation owner */
321 	if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
322 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
323 		rc = recall_all_delegations(fp, FALSE, ct);
324 		if (rc == NFS4ERR_DELAY)
325 			return (EAGAIN);
326 	}
327 
328 	return (vnext_rwlock(arg, write_lock, ct));
329 }
330 
331 int
332 deleg_rd_space(
333 	femarg_t *arg,
334 	int cmd,
335 	flock64_t *bfp,
336 	int flag,
337 	offset_t offset,
338 	cred_t *cr,
339 	caller_context_t *ct)
340 {
341 	int rc;
342 	rfs4_file_t *fp;
343 
344 	fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
345 	rc = recall_all_delegations(fp, FALSE, ct);
346 	if (rc == NFS4ERR_DELAY)
347 		return (EAGAIN);
348 
349 	return (vnext_space(arg, cmd, bfp, flag, offset, cr, ct));
350 }
351 
352 int
353 deleg_wr_space(
354 	femarg_t *arg,
355 	int cmd,
356 	flock64_t *bfp,
357 	int flag,
358 	offset_t offset,
359 	cred_t *cr,
360 	caller_context_t *ct)
361 {
362 	int rc;
363 	rfs4_file_t *fp;
364 
365 	/* Use caller context to compare caller to delegation owner */
366 	if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
367 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
368 		rc = recall_all_delegations(fp, FALSE, ct);
369 		if (rc == NFS4ERR_DELAY)
370 			return (EAGAIN);
371 	}
372 
373 	return (vnext_space(arg, cmd, bfp, flag, offset, cr, ct));
374 }
375 
376 int
377 deleg_rd_setsecattr(
378 	femarg_t *arg,
379 	vsecattr_t *vsap,
380 	int flag,
381 	cred_t *cr,
382 	caller_context_t *ct)
383 {
384 	int rc;
385 	rfs4_file_t *fp;
386 
387 	fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
388 
389 	/* Changing security attribute triggers recall */
390 	rc = recall_all_delegations(fp, FALSE, ct);
391 	if (rc == NFS4ERR_DELAY)
392 		return (EAGAIN);
393 
394 	return (vnext_setsecattr(arg, vsap, flag, cr, ct));
395 }
396 
397 int
398 deleg_wr_setsecattr(
399 	femarg_t *arg,
400 	vsecattr_t *vsap,
401 	int flag,
402 	cred_t *cr,
403 	caller_context_t *ct)
404 {
405 	int rc;
406 	rfs4_file_t *fp;
407 
408 	fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
409 
410 	/* Changing security attribute triggers recall */
411 	rc = recall_all_delegations(fp, FALSE, ct);
412 	if (rc == NFS4ERR_DELAY)
413 		return (EAGAIN);
414 
415 	return (vnext_setsecattr(arg, vsap, flag, cr, ct));
416 }
417 
418 int
419 deleg_rd_vnevent(
420 	femarg_t *arg,
421 	vnevent_t vnevent,
422 	vnode_t *dvp,
423 	char *name,
424 	caller_context_t *ct)
425 {
426 	clock_t rc;
427 	rfs4_file_t *fp;
428 	bool_t trunc = FALSE;
429 
430 	switch (vnevent) {
431 	case VE_REMOVE:
432 	case VE_RENAME_DEST:
433 		trunc = TRUE;
434 		/*FALLTHROUGH*/
435 
436 	case VE_RENAME_SRC:
437 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
438 		rfs4_recall_deleg(fp, trunc, NULL);
439 
440 		rfs4_dbe_lock(fp->dbe);
441 		while (fp->dinfo->dtype != OPEN_DELEGATE_NONE) {
442 			rc = rfs4_dbe_twait(fp->dbe,
443 			    lbolt + SEC_TO_TICK(rfs4_lease_time));
444 			if (rc == -1) { /* timed out */
445 				rfs4_dbe_unlock(fp->dbe);
446 				rfs4_recall_deleg(fp, trunc, NULL);
447 				rfs4_dbe_lock(fp->dbe);
448 			}
449 		}
450 		rfs4_dbe_unlock(fp->dbe);
451 
452 		break;
453 
454 	default:
455 		break;
456 	}
457 	return (vnext_vnevent(arg, vnevent, dvp, name, ct));
458 }
459 
460 int
461 deleg_wr_vnevent(
462 	femarg_t *arg,
463 	vnevent_t vnevent,
464 	vnode_t *dvp,
465 	char *name,
466 	caller_context_t *ct)
467 {
468 	clock_t rc;
469 	rfs4_file_t *fp;
470 	bool_t trunc = FALSE;
471 
472 	switch (vnevent) {
473 	case VE_REMOVE:
474 	case VE_RENAME_DEST:
475 		trunc = TRUE;
476 		/*FALLTHROUGH*/
477 
478 	case VE_RENAME_SRC:
479 		fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
480 		rfs4_recall_deleg(fp, trunc, NULL);
481 		rfs4_dbe_lock(fp->dbe);
482 		while (fp->dinfo->dtype != OPEN_DELEGATE_NONE) {
483 			rc = rfs4_dbe_twait(fp->dbe,
484 			    lbolt + SEC_TO_TICK(rfs4_lease_time));
485 			if (rc == -1) { /* timed out */
486 				rfs4_dbe_unlock(fp->dbe);
487 				rfs4_recall_deleg(fp, trunc, NULL);
488 				rfs4_dbe_lock(fp->dbe);
489 			}
490 		}
491 		rfs4_dbe_unlock(fp->dbe);
492 
493 		break;
494 
495 	default:
496 		break;
497 	}
498 	return (vnext_vnevent(arg, vnevent, dvp, name, ct));
499 }
500