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
recall_all_delegations(rfs4_file_t * fp,bool_t trunc,caller_context_t * ct)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
deleg_rd_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)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
deleg_wr_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)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
deleg_wr_read(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,struct caller_context * ct)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
deleg_rd_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,struct caller_context * ct)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
deleg_wr_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,struct caller_context * ct)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
deleg_rd_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)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
deleg_wr_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)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
deleg_rd_rwlock(femarg_t * arg,int write_lock,caller_context_t * ct)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
deleg_wr_rwlock(femarg_t * arg,int write_lock,caller_context_t * ct)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
deleg_rd_space(femarg_t * arg,int cmd,flock64_t * bfp,int flag,offset_t offset,cred_t * cr,caller_context_t * ct)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
deleg_wr_space(femarg_t * arg,int cmd,flock64_t * bfp,int flag,offset_t offset,cred_t * cr,caller_context_t * ct)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
deleg_rd_setsecattr(femarg_t * arg,vsecattr_t * vsap,int flag,cred_t * cr,caller_context_t * ct)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
deleg_wr_setsecattr(femarg_t * arg,vsecattr_t * vsap,int flag,cred_t * cr,caller_context_t * ct)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
deleg_rd_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)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
deleg_wr_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)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