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