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
42extern 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
63int
64recall_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 */
101int
102deleg_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 */
126int
127deleg_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 */
154int
155deleg_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 */
175int
176deleg_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 */
194int
195deleg_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. */
212int
213deleg_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 */
232int
233deleg_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
256int
257deleg_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. */
276int
277deleg_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
293int
294deleg_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
308int
309deleg_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
326int
327deleg_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
343int
344deleg_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
360int
361deleg_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
400int
401deleg_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