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