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