Print this page
Caution with use after exi_rele()
Be far more judicious in the use of curzone-using macros.
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/nfs/nfs3_srv.c
+++ new/usr/src/uts/common/fs/nfs/nfs3_srv.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2018 Nexenta Systems, Inc.
24 24 * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
25 25 * Copyright (c) 2013 by Delphix. All rights reserved.
26 26 */
27 27
28 28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29 29 /* All Rights Reserved */
30 30
31 31
32 32 #include <sys/param.h>
33 33 #include <sys/types.h>
34 34 #include <sys/systm.h>
35 35 #include <sys/cred.h>
36 36 #include <sys/buf.h>
37 37 #include <sys/vfs.h>
38 38 #include <sys/vnode.h>
39 39 #include <sys/uio.h>
40 40 #include <sys/errno.h>
41 41 #include <sys/sysmacros.h>
42 42 #include <sys/statvfs.h>
43 43 #include <sys/kmem.h>
44 44 #include <sys/dirent.h>
45 45 #include <sys/cmn_err.h>
46 46 #include <sys/debug.h>
47 47 #include <sys/systeminfo.h>
48 48 #include <sys/flock.h>
49 49 #include <sys/nbmlock.h>
50 50 #include <sys/policy.h>
51 51 #include <sys/sdt.h>
52 52
53 53 #include <rpc/types.h>
54 54 #include <rpc/auth.h>
55 55 #include <rpc/svc.h>
56 56 #include <rpc/rpc_rdma.h>
57 57
58 58 #include <nfs/nfs.h>
59 59 #include <nfs/export.h>
60 60 #include <nfs/nfs_cmd.h>
61 61
62 62 #include <sys/strsubr.h>
63 63 #include <sys/tsol/label.h>
64 64 #include <sys/tsol/tndb.h>
65 65
66 66 #include <sys/zone.h>
67 67
68 68 #include <inet/ip.h>
69 69 #include <inet/ip6.h>
70 70
71 71 /*
72 72 * Zone global variables of NFSv3 server
73 73 */
74 74 typedef struct nfs3_srv {
75 75 writeverf3 write3verf;
76 76 } nfs3_srv_t;
77 77
78 78 /*
79 79 * These are the interface routines for the server side of the
80 80 * Network File System. See the NFS version 3 protocol specification
81 81 * for a description of this interface.
82 82 */
83 83
84 84 static int sattr3_to_vattr(sattr3 *, struct vattr *);
85 85 static int vattr_to_fattr3(struct vattr *, fattr3 *);
86 86 static int vattr_to_wcc_attr(struct vattr *, wcc_attr *);
87 87 static void vattr_to_pre_op_attr(struct vattr *, pre_op_attr *);
88 88 static void vattr_to_wcc_data(struct vattr *, struct vattr *, wcc_data *);
89 89 static int rdma_setup_read_data3(READ3args *, READ3resok *);
90 90
91 91 extern int nfs_loaned_buffers;
92 92
93 93 u_longlong_t nfs3_srv_caller_id;
94 94
95 95 static nfs3_srv_t *
96 96 nfs3_get_srv(void)
97 97 {
98 98 nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
99 99 nfs3_srv_t *srv = ng->nfs3_srv;
100 100 ASSERT(srv != NULL);
101 101 return (srv);
102 102 }
103 103
104 104 /* ARGSUSED */
105 105 void
106 106 rfs3_getattr(GETATTR3args *args, GETATTR3res *resp, struct exportinfo *exi,
107 107 struct svc_req *req, cred_t *cr, bool_t ro)
108 108 {
109 109 int error;
110 110 vnode_t *vp;
111 111 struct vattr va;
112 112
113 113 vp = nfs3_fhtovp(&args->object, exi);
114 114
115 115 DTRACE_NFSV3_5(op__getattr__start, struct svc_req *, req,
116 116 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
117 117 GETATTR3args *, args);
118 118
119 119 if (vp == NULL) {
120 120 error = ESTALE;
121 121 goto out;
122 122 }
123 123
124 124 va.va_mask = AT_ALL;
125 125 error = rfs4_delegated_getattr(vp, &va, 0, cr);
126 126
127 127 if (!error) {
128 128 /* Lie about the object type for a referral */
129 129 if (vn_is_nfs_reparse(vp, cr))
130 130 va.va_type = VLNK;
131 131
132 132 /* overflow error if time or size is out of range */
133 133 error = vattr_to_fattr3(&va, &resp->resok.obj_attributes);
134 134 if (error)
135 135 goto out;
136 136 resp->status = NFS3_OK;
137 137
138 138 DTRACE_NFSV3_5(op__getattr__done, struct svc_req *, req,
139 139 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
140 140 GETATTR3res *, resp);
141 141
142 142 VN_RELE(vp);
143 143
144 144 return;
145 145 }
146 146
147 147 out:
148 148 if (curthread->t_flag & T_WOULDBLOCK) {
149 149 curthread->t_flag &= ~T_WOULDBLOCK;
150 150 resp->status = NFS3ERR_JUKEBOX;
151 151 } else
152 152 resp->status = puterrno3(error);
153 153
154 154 DTRACE_NFSV3_5(op__getattr__done, struct svc_req *, req,
155 155 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
156 156 GETATTR3res *, resp);
157 157
158 158 if (vp != NULL)
159 159 VN_RELE(vp);
160 160 }
161 161
162 162 void *
163 163 rfs3_getattr_getfh(GETATTR3args *args)
164 164 {
165 165
166 166 return (&args->object);
167 167 }
168 168
169 169 void
170 170 rfs3_setattr(SETATTR3args *args, SETATTR3res *resp, struct exportinfo *exi,
171 171 struct svc_req *req, cred_t *cr, bool_t ro)
172 172 {
173 173 int error;
174 174 vnode_t *vp;
175 175 struct vattr *bvap;
176 176 struct vattr bva;
177 177 struct vattr *avap;
178 178 struct vattr ava;
179 179 int flag;
180 180 int in_crit = 0;
181 181 struct flock64 bf;
182 182 caller_context_t ct;
183 183
184 184 bvap = NULL;
185 185 avap = NULL;
186 186
187 187 vp = nfs3_fhtovp(&args->object, exi);
188 188
189 189 DTRACE_NFSV3_5(op__setattr__start, struct svc_req *, req,
190 190 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
191 191 SETATTR3args *, args);
192 192
193 193 if (vp == NULL) {
194 194 error = ESTALE;
195 195 goto out;
196 196 }
197 197
198 198 error = sattr3_to_vattr(&args->new_attributes, &ava);
199 199 if (error)
200 200 goto out;
201 201
202 202 if (is_system_labeled()) {
203 203 bslabel_t *clabel = req->rq_label;
204 204
205 205 ASSERT(clabel != NULL);
206 206 DTRACE_PROBE2(tx__rfs3__log__info__opsetattr__clabel, char *,
207 207 "got client label from request(1)", struct svc_req *, req);
208 208
209 209 if (!blequal(&l_admin_low->tsl_label, clabel)) {
210 210 if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK,
211 211 exi)) {
212 212 resp->status = NFS3ERR_ACCES;
213 213 goto out1;
214 214 }
215 215 }
216 216 }
217 217
218 218 /*
219 219 * We need to specially handle size changes because of
220 220 * possible conflicting NBMAND locks. Get into critical
221 221 * region before VOP_GETATTR, so the size attribute is
222 222 * valid when checking conflicts.
223 223 *
224 224 * Also, check to see if the v4 side of the server has
225 225 * delegated this file. If so, then we return JUKEBOX to
226 226 * allow the client to retrasmit its request.
227 227 */
228 228 if (vp->v_type == VREG && (ava.va_mask & AT_SIZE)) {
229 229 if (nbl_need_check(vp)) {
230 230 nbl_start_crit(vp, RW_READER);
231 231 in_crit = 1;
232 232 }
233 233 }
234 234
235 235 bva.va_mask = AT_ALL;
236 236 error = rfs4_delegated_getattr(vp, &bva, 0, cr);
237 237
238 238 /*
239 239 * If we can't get the attributes, then we can't do the
240 240 * right access checking. So, we'll fail the request.
241 241 */
242 242 if (error)
243 243 goto out;
244 244
245 245 bvap = &bva;
246 246
247 247 if (rdonly(ro, vp)) {
248 248 resp->status = NFS3ERR_ROFS;
249 249 goto out1;
250 250 }
251 251
252 252 if (args->guard.check &&
253 253 (args->guard.obj_ctime.seconds != bva.va_ctime.tv_sec ||
254 254 args->guard.obj_ctime.nseconds != bva.va_ctime.tv_nsec)) {
255 255 resp->status = NFS3ERR_NOT_SYNC;
256 256 goto out1;
257 257 }
258 258
259 259 if (args->new_attributes.mtime.set_it == SET_TO_CLIENT_TIME)
260 260 flag = ATTR_UTIME;
261 261 else
262 262 flag = 0;
263 263
264 264 /*
265 265 * If the filesystem is exported with nosuid, then mask off
266 266 * the setuid and setgid bits.
267 267 */
268 268 if ((ava.va_mask & AT_MODE) && vp->v_type == VREG &&
269 269 (exi->exi_export.ex_flags & EX_NOSUID))
270 270 ava.va_mode &= ~(VSUID | VSGID);
271 271
272 272 ct.cc_sysid = 0;
273 273 ct.cc_pid = 0;
274 274 ct.cc_caller_id = nfs3_srv_caller_id;
275 275 ct.cc_flags = CC_DONTBLOCK;
276 276
277 277 /*
278 278 * We need to specially handle size changes because it is
279 279 * possible for the client to create a file with modes
280 280 * which indicate read-only, but with the file opened for
281 281 * writing. If the client then tries to set the size of
282 282 * the file, then the normal access checking done in
283 283 * VOP_SETATTR would prevent the client from doing so,
284 284 * although it should be legal for it to do so. To get
285 285 * around this, we do the access checking for ourselves
286 286 * and then use VOP_SPACE which doesn't do the access
287 287 * checking which VOP_SETATTR does. VOP_SPACE can only
288 288 * operate on VREG files, let VOP_SETATTR handle the other
289 289 * extremely rare cases.
290 290 * Also the client should not be allowed to change the
291 291 * size of the file if there is a conflicting non-blocking
292 292 * mandatory lock in the region the change.
293 293 */
294 294 if (vp->v_type == VREG && (ava.va_mask & AT_SIZE)) {
295 295 if (in_crit) {
296 296 u_offset_t offset;
297 297 ssize_t length;
298 298
299 299 if (ava.va_size < bva.va_size) {
300 300 offset = ava.va_size;
301 301 length = bva.va_size - ava.va_size;
302 302 } else {
303 303 offset = bva.va_size;
304 304 length = ava.va_size - bva.va_size;
305 305 }
306 306 if (nbl_conflict(vp, NBL_WRITE, offset, length, 0,
307 307 NULL)) {
308 308 error = EACCES;
309 309 goto out;
310 310 }
311 311 }
312 312
313 313 if (crgetuid(cr) == bva.va_uid && ava.va_size != bva.va_size) {
314 314 ava.va_mask &= ~AT_SIZE;
315 315 bf.l_type = F_WRLCK;
316 316 bf.l_whence = 0;
317 317 bf.l_start = (off64_t)ava.va_size;
318 318 bf.l_len = 0;
319 319 bf.l_sysid = 0;
320 320 bf.l_pid = 0;
321 321 error = VOP_SPACE(vp, F_FREESP, &bf, FWRITE,
322 322 (offset_t)ava.va_size, cr, &ct);
323 323 }
324 324 }
325 325
326 326 if (!error && ava.va_mask)
327 327 error = VOP_SETATTR(vp, &ava, flag, cr, &ct);
328 328
329 329 /* check if a monitor detected a delegation conflict */
330 330 if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
331 331 resp->status = NFS3ERR_JUKEBOX;
332 332 goto out1;
333 333 }
334 334
335 335 ava.va_mask = AT_ALL;
336 336 avap = rfs4_delegated_getattr(vp, &ava, 0, cr) ? NULL : &ava;
337 337
338 338 /*
339 339 * Force modified metadata out to stable storage.
340 340 */
341 341 (void) VOP_FSYNC(vp, FNODSYNC, cr, &ct);
342 342
343 343 if (error)
344 344 goto out;
345 345
346 346 if (in_crit)
347 347 nbl_end_crit(vp);
348 348
349 349 resp->status = NFS3_OK;
350 350 vattr_to_wcc_data(bvap, avap, &resp->resok.obj_wcc);
351 351
352 352 DTRACE_NFSV3_5(op__setattr__done, struct svc_req *, req,
353 353 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
354 354 SETATTR3res *, resp);
355 355
356 356 VN_RELE(vp);
357 357
358 358 return;
359 359
360 360 out:
361 361 if (curthread->t_flag & T_WOULDBLOCK) {
362 362 curthread->t_flag &= ~T_WOULDBLOCK;
363 363 resp->status = NFS3ERR_JUKEBOX;
364 364 } else
365 365 resp->status = puterrno3(error);
366 366 out1:
367 367 DTRACE_NFSV3_5(op__setattr__done, struct svc_req *, req,
368 368 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
369 369 SETATTR3res *, resp);
370 370
371 371 if (vp != NULL) {
372 372 if (in_crit)
373 373 nbl_end_crit(vp);
374 374 VN_RELE(vp);
375 375 }
376 376 vattr_to_wcc_data(bvap, avap, &resp->resfail.obj_wcc);
377 377 }
378 378
379 379 void *
380 380 rfs3_setattr_getfh(SETATTR3args *args)
381 381 {
382 382
383 383 return (&args->object);
384 384 }
385 385
386 386 /* ARGSUSED */
387 387 void
388 388 rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
389 389 struct svc_req *req, cred_t *cr, bool_t ro)
390 390 {
391 391 int error;
392 392 vnode_t *vp;
393 393 vnode_t *dvp;
394 394 struct vattr *vap;
395 395 struct vattr va;
396 396 struct vattr *dvap;
397 397 struct vattr dva;
398 398 nfs_fh3 *fhp;
399 399 struct sec_ol sec = {0, 0};
400 400 bool_t publicfh_flag = FALSE, auth_weak = FALSE;
401 401 struct sockaddr *ca;
402 402 char *name = NULL;
403 403
|
↓ open down ↓ |
403 lines elided |
↑ open up ↑ |
404 404 dvap = NULL;
405 405
406 406 if (exi != NULL)
407 407 exi_hold(exi);
408 408
409 409 /*
410 410 * Allow lookups from the root - the default
411 411 * location of the public filehandle.
412 412 */
413 413 if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
414 + ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
414 415 dvp = ZONE_ROOTVP();
415 416 VN_HOLD(dvp);
416 417
417 418 DTRACE_NFSV3_5(op__lookup__start, struct svc_req *, req,
418 419 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
419 420 LOOKUP3args *, args);
420 421 } else {
421 422 dvp = nfs3_fhtovp(&args->what.dir, exi);
422 423
423 424 DTRACE_NFSV3_5(op__lookup__start, struct svc_req *, req,
424 425 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
425 426 LOOKUP3args *, args);
426 427
427 428 if (dvp == NULL) {
428 429 error = ESTALE;
429 430 goto out;
430 431 }
431 432 }
432 433
433 434 dva.va_mask = AT_ALL;
434 435 dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
435 436
436 437 if (args->what.name == nfs3nametoolong) {
437 438 resp->status = NFS3ERR_NAMETOOLONG;
438 439 goto out1;
439 440 }
440 441
441 442 if (args->what.name == NULL || *(args->what.name) == '\0') {
442 443 resp->status = NFS3ERR_ACCES;
443 444 goto out1;
444 445 }
445 446
446 447 fhp = &args->what.dir;
447 448 ASSERT3P(curzone, ==, exi->exi_zone); /* exi is guaranteed non-NULL. */
448 449 if (strcmp(args->what.name, "..") == 0 &&
449 450 EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
450 451 if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
451 452 ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp))) {
452 453 /*
453 454 * special case for ".." and 'nohide'exported root
454 455 */
455 456 if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
456 457 resp->status = NFS3ERR_ACCES;
457 458 goto out1;
458 459 }
459 460 } else {
460 461 resp->status = NFS3ERR_NOENT;
461 462 goto out1;
462 463 }
463 464 }
464 465
465 466 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
466 467 name = nfscmd_convname(ca, exi, args->what.name,
467 468 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
468 469
469 470 if (name == NULL) {
470 471 resp->status = NFS3ERR_ACCES;
471 472 goto out1;
|
↓ open down ↓ |
48 lines elided |
↑ open up ↑ |
472 473 }
473 474
474 475 /*
475 476 * If the public filehandle is used then allow
476 477 * a multi-component lookup
477 478 */
478 479 if (PUBLIC_FH3(&args->what.dir)) {
479 480 publicfh_flag = TRUE;
480 481
481 482 exi_rele(exi);
483 + exi = NULL;
482 484
483 485 error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
484 486 &exi, &sec);
485 487
486 488 /*
487 489 * Since WebNFS may bypass MOUNT, we need to ensure this
488 490 * request didn't come from an unlabeled admin_low client.
489 491 */
490 492 if (is_system_labeled() && error == 0) {
491 493 int addr_type;
492 494 void *ipaddr;
493 495 tsol_tpc_t *tp;
494 496
495 497 if (ca->sa_family == AF_INET) {
496 498 addr_type = IPV4_VERSION;
497 499 ipaddr = &((struct sockaddr_in *)ca)->sin_addr;
498 500 } else if (ca->sa_family == AF_INET6) {
499 501 addr_type = IPV6_VERSION;
500 502 ipaddr = &((struct sockaddr_in6 *)
501 503 ca)->sin6_addr;
502 504 }
503 505 tp = find_tpc(ipaddr, addr_type, B_FALSE);
504 506 if (tp == NULL || tp->tpc_tp.tp_doi !=
505 507 l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
506 508 SUN_CIPSO) {
507 509 VN_RELE(vp);
508 510 error = EACCES;
509 511 }
510 512 if (tp != NULL)
511 513 TPC_RELE(tp);
512 514 }
513 515 } else {
514 516 error = VOP_LOOKUP(dvp, name, &vp,
515 517 NULL, 0, NULL, cr, NULL, NULL, NULL);
516 518 }
517 519
518 520 if (name != args->what.name)
519 521 kmem_free(name, MAXPATHLEN + 1);
520 522
521 523 if (error == 0 && vn_ismntpt(vp)) {
522 524 error = rfs_cross_mnt(&vp, &exi);
523 525 if (error)
524 526 VN_RELE(vp);
525 527 }
526 528
527 529 if (is_system_labeled() && error == 0) {
528 530 bslabel_t *clabel = req->rq_label;
529 531
530 532 ASSERT(clabel != NULL);
531 533 DTRACE_PROBE2(tx__rfs3__log__info__oplookup__clabel, char *,
532 534 "got client label from request(1)", struct svc_req *, req);
533 535
534 536 if (!blequal(&l_admin_low->tsl_label, clabel)) {
535 537 if (!do_rfs_label_check(clabel, dvp,
536 538 DOMINANCE_CHECK, exi)) {
537 539 VN_RELE(vp);
538 540 error = EACCES;
539 541 }
540 542 }
541 543 }
542 544
543 545 dva.va_mask = AT_ALL;
544 546 dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
545 547
546 548 if (error)
547 549 goto out;
548 550
549 551 if (sec.sec_flags & SEC_QUERY) {
550 552 error = makefh3_ol(&resp->resok.object, exi, sec.sec_index);
551 553 } else {
552 554 error = makefh3(&resp->resok.object, vp, exi);
553 555 if (!error && publicfh_flag && !chk_clnt_sec(exi, req))
554 556 auth_weak = TRUE;
|
↓ open down ↓ |
63 lines elided |
↑ open up ↑ |
555 557 }
556 558
557 559 if (error) {
558 560 VN_RELE(vp);
559 561 goto out;
560 562 }
561 563
562 564 va.va_mask = AT_ALL;
563 565 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
564 566
565 - exi_rele(exi);
566 567 VN_RELE(vp);
567 568
568 569 resp->status = NFS3_OK;
569 570 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
570 571 vattr_to_post_op_attr(dvap, &resp->resok.dir_attributes);
571 572
572 573 /*
573 574 * If it's public fh, no 0x81, and client's flavor is
574 575 * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.
575 576 * Then set RPC status to AUTH_TOOWEAK in common_dispatch.
576 577 */
577 578 if (auth_weak)
578 579 resp->status = (enum nfsstat3)WNFSERR_CLNT_FLAVOR;
579 580
580 581 DTRACE_NFSV3_5(op__lookup__done, struct svc_req *, req,
581 582 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
582 583 LOOKUP3res *, resp);
583 584 VN_RELE(dvp);
585 + exi_rele(exi);
584 586
585 587 return;
586 588
587 589 out:
588 590 if (curthread->t_flag & T_WOULDBLOCK) {
589 591 curthread->t_flag &= ~T_WOULDBLOCK;
590 592 resp->status = NFS3ERR_JUKEBOX;
591 593 } else
592 594 resp->status = puterrno3(error);
593 595 out1:
594 596 DTRACE_NFSV3_5(op__lookup__done, struct svc_req *, req,
595 597 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
596 598 LOOKUP3res *, resp);
597 599
598 600 if (exi != NULL)
599 601 exi_rele(exi);
600 602
601 603 if (dvp != NULL)
602 604 VN_RELE(dvp);
603 605 vattr_to_post_op_attr(dvap, &resp->resfail.dir_attributes);
604 606
605 607 }
606 608
607 609 void *
608 610 rfs3_lookup_getfh(LOOKUP3args *args)
609 611 {
610 612
611 613 return (&args->what.dir);
612 614 }
613 615
614 616 /* ARGSUSED */
615 617 void
616 618 rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi,
617 619 struct svc_req *req, cred_t *cr, bool_t ro)
618 620 {
619 621 int error;
620 622 vnode_t *vp;
621 623 struct vattr *vap;
622 624 struct vattr va;
623 625 int checkwriteperm;
624 626 boolean_t dominant_label = B_FALSE;
625 627 boolean_t equal_label = B_FALSE;
626 628 boolean_t admin_low_client;
627 629
628 630 vap = NULL;
629 631
630 632 vp = nfs3_fhtovp(&args->object, exi);
631 633
632 634 DTRACE_NFSV3_5(op__access__start, struct svc_req *, req,
633 635 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
634 636 ACCESS3args *, args);
635 637
636 638 if (vp == NULL) {
637 639 error = ESTALE;
638 640 goto out;
639 641 }
640 642
641 643 /*
642 644 * If the file system is exported read only, it is not appropriate
643 645 * to check write permissions for regular files and directories.
644 646 * Special files are interpreted by the client, so the underlying
645 647 * permissions are sent back to the client for interpretation.
646 648 */
647 649 if (rdonly(ro, vp) && (vp->v_type == VREG || vp->v_type == VDIR))
648 650 checkwriteperm = 0;
649 651 else
650 652 checkwriteperm = 1;
651 653
652 654 /*
653 655 * We need the mode so that we can correctly determine access
654 656 * permissions relative to a mandatory lock file. Access to
655 657 * mandatory lock files is denied on the server, so it might
656 658 * as well be reflected to the server during the open.
657 659 */
658 660 va.va_mask = AT_MODE;
659 661 error = VOP_GETATTR(vp, &va, 0, cr, NULL);
660 662 if (error)
661 663 goto out;
662 664
663 665 vap = &va;
664 666
665 667 resp->resok.access = 0;
666 668
667 669 if (is_system_labeled()) {
668 670 bslabel_t *clabel = req->rq_label;
669 671
670 672 ASSERT(clabel != NULL);
671 673 DTRACE_PROBE2(tx__rfs3__log__info__opaccess__clabel, char *,
672 674 "got client label from request(1)", struct svc_req *, req);
673 675
674 676 if (!blequal(&l_admin_low->tsl_label, clabel)) {
675 677 if ((equal_label = do_rfs_label_check(clabel, vp,
676 678 EQUALITY_CHECK, exi)) == B_FALSE) {
677 679 dominant_label = do_rfs_label_check(clabel,
678 680 vp, DOMINANCE_CHECK, exi);
679 681 } else
680 682 dominant_label = B_TRUE;
681 683 admin_low_client = B_FALSE;
682 684 } else
683 685 admin_low_client = B_TRUE;
684 686 }
685 687
686 688 if (args->access & ACCESS3_READ) {
687 689 error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
688 690 if (error) {
689 691 if (curthread->t_flag & T_WOULDBLOCK)
690 692 goto out;
691 693 } else if (!MANDLOCK(vp, va.va_mode) &&
692 694 (!is_system_labeled() || admin_low_client ||
693 695 dominant_label))
694 696 resp->resok.access |= ACCESS3_READ;
695 697 }
696 698 if ((args->access & ACCESS3_LOOKUP) && vp->v_type == VDIR) {
697 699 error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
698 700 if (error) {
699 701 if (curthread->t_flag & T_WOULDBLOCK)
700 702 goto out;
701 703 } else if (!is_system_labeled() || admin_low_client ||
702 704 dominant_label)
703 705 resp->resok.access |= ACCESS3_LOOKUP;
704 706 }
705 707 if (checkwriteperm &&
706 708 (args->access & (ACCESS3_MODIFY|ACCESS3_EXTEND))) {
707 709 error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
708 710 if (error) {
709 711 if (curthread->t_flag & T_WOULDBLOCK)
710 712 goto out;
711 713 } else if (!MANDLOCK(vp, va.va_mode) &&
712 714 (!is_system_labeled() || admin_low_client || equal_label)) {
713 715 resp->resok.access |=
714 716 (args->access & (ACCESS3_MODIFY|ACCESS3_EXTEND));
715 717 }
716 718 }
717 719 if (checkwriteperm &&
718 720 (args->access & ACCESS3_DELETE) && vp->v_type == VDIR) {
719 721 error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
720 722 if (error) {
721 723 if (curthread->t_flag & T_WOULDBLOCK)
722 724 goto out;
723 725 } else if (!is_system_labeled() || admin_low_client ||
724 726 equal_label)
725 727 resp->resok.access |= ACCESS3_DELETE;
726 728 }
727 729 if (args->access & ACCESS3_EXECUTE) {
728 730 error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
729 731 if (error) {
730 732 if (curthread->t_flag & T_WOULDBLOCK)
731 733 goto out;
732 734 } else if (!MANDLOCK(vp, va.va_mode) &&
733 735 (!is_system_labeled() || admin_low_client ||
734 736 dominant_label))
735 737 resp->resok.access |= ACCESS3_EXECUTE;
736 738 }
737 739
738 740 va.va_mask = AT_ALL;
739 741 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
740 742
741 743 resp->status = NFS3_OK;
742 744 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
743 745
744 746 DTRACE_NFSV3_5(op__access__done, struct svc_req *, req,
745 747 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
746 748 ACCESS3res *, resp);
747 749
748 750 VN_RELE(vp);
749 751
750 752 return;
751 753
752 754 out:
753 755 if (curthread->t_flag & T_WOULDBLOCK) {
754 756 curthread->t_flag &= ~T_WOULDBLOCK;
755 757 resp->status = NFS3ERR_JUKEBOX;
756 758 } else
757 759 resp->status = puterrno3(error);
758 760 DTRACE_NFSV3_5(op__access__done, struct svc_req *, req,
759 761 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
760 762 ACCESS3res *, resp);
761 763 if (vp != NULL)
762 764 VN_RELE(vp);
763 765 vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
764 766 }
765 767
766 768 void *
767 769 rfs3_access_getfh(ACCESS3args *args)
768 770 {
769 771
770 772 return (&args->object);
771 773 }
772 774
773 775 /* ARGSUSED */
774 776 void
775 777 rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi,
776 778 struct svc_req *req, cred_t *cr, bool_t ro)
777 779 {
778 780 int error;
779 781 vnode_t *vp;
780 782 struct vattr *vap;
781 783 struct vattr va;
782 784 struct iovec iov;
783 785 struct uio uio;
784 786 char *data;
785 787 struct sockaddr *ca;
786 788 char *name = NULL;
787 789 int is_referral = 0;
788 790
789 791 vap = NULL;
790 792
791 793 vp = nfs3_fhtovp(&args->symlink, exi);
792 794
793 795 DTRACE_NFSV3_5(op__readlink__start, struct svc_req *, req,
794 796 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
795 797 READLINK3args *, args);
796 798
797 799 if (vp == NULL) {
798 800 error = ESTALE;
799 801 goto out;
800 802 }
801 803
802 804 va.va_mask = AT_ALL;
803 805 error = VOP_GETATTR(vp, &va, 0, cr, NULL);
804 806 if (error)
805 807 goto out;
806 808
807 809 vap = &va;
808 810
809 811 /* We lied about the object type for a referral */
810 812 if (vn_is_nfs_reparse(vp, cr))
811 813 is_referral = 1;
812 814
813 815 if (vp->v_type != VLNK && !is_referral) {
814 816 resp->status = NFS3ERR_INVAL;
815 817 goto out1;
816 818 }
817 819
818 820 if (MANDLOCK(vp, va.va_mode)) {
819 821 resp->status = NFS3ERR_ACCES;
820 822 goto out1;
821 823 }
822 824
823 825 if (is_system_labeled()) {
824 826 bslabel_t *clabel = req->rq_label;
825 827
826 828 ASSERT(clabel != NULL);
827 829 DTRACE_PROBE2(tx__rfs3__log__info__opreadlink__clabel, char *,
828 830 "got client label from request(1)", struct svc_req *, req);
829 831
830 832 if (!blequal(&l_admin_low->tsl_label, clabel)) {
831 833 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
832 834 exi)) {
833 835 resp->status = NFS3ERR_ACCES;
834 836 goto out1;
835 837 }
836 838 }
837 839 }
838 840
839 841 data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP);
840 842
841 843 if (is_referral) {
842 844 char *s;
843 845 size_t strsz;
844 846
845 847 /* Get an artificial symlink based on a referral */
846 848 s = build_symlink(vp, cr, &strsz);
847 849 global_svstat_ptr[3][NFS_REFERLINKS].value.ui64++;
848 850 DTRACE_PROBE2(nfs3serv__func__referral__reflink,
849 851 vnode_t *, vp, char *, s);
850 852 if (s == NULL)
851 853 error = EINVAL;
852 854 else {
853 855 error = 0;
854 856 (void) strlcpy(data, s, MAXPATHLEN + 1);
855 857 kmem_free(s, strsz);
856 858 }
857 859
858 860 } else {
859 861
860 862 iov.iov_base = data;
861 863 iov.iov_len = MAXPATHLEN;
862 864 uio.uio_iov = &iov;
863 865 uio.uio_iovcnt = 1;
864 866 uio.uio_segflg = UIO_SYSSPACE;
865 867 uio.uio_extflg = UIO_COPY_CACHED;
866 868 uio.uio_loffset = 0;
867 869 uio.uio_resid = MAXPATHLEN;
868 870
869 871 error = VOP_READLINK(vp, &uio, cr, NULL);
870 872
871 873 if (!error)
872 874 *(data + MAXPATHLEN - uio.uio_resid) = '\0';
873 875 }
874 876
875 877 va.va_mask = AT_ALL;
876 878 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
877 879
878 880 /* Lie about object type again just to be consistent */
879 881 if (is_referral && vap != NULL)
880 882 vap->va_type = VLNK;
881 883
882 884 #if 0 /* notyet */
883 885 /*
884 886 * Don't do this. It causes local disk writes when just
885 887 * reading the file and the overhead is deemed larger
886 888 * than the benefit.
887 889 */
888 890 /*
889 891 * Force modified metadata out to stable storage.
890 892 */
891 893 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
892 894 #endif
893 895
894 896 if (error) {
895 897 kmem_free(data, MAXPATHLEN + 1);
896 898 goto out;
897 899 }
898 900
899 901 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
900 902 name = nfscmd_convname(ca, exi, data, NFSCMD_CONV_OUTBOUND,
901 903 MAXPATHLEN + 1);
902 904
903 905 if (name == NULL) {
904 906 /*
905 907 * Even though the conversion failed, we return
906 908 * something. We just don't translate it.
907 909 */
908 910 name = data;
909 911 }
910 912
911 913 resp->status = NFS3_OK;
912 914 vattr_to_post_op_attr(vap, &resp->resok.symlink_attributes);
913 915 resp->resok.data = name;
914 916
915 917 DTRACE_NFSV3_5(op__readlink__done, struct svc_req *, req,
916 918 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
917 919 READLINK3res *, resp);
918 920 VN_RELE(vp);
919 921
920 922 if (name != data)
921 923 kmem_free(data, MAXPATHLEN + 1);
922 924
923 925 return;
924 926
925 927 out:
926 928 if (curthread->t_flag & T_WOULDBLOCK) {
927 929 curthread->t_flag &= ~T_WOULDBLOCK;
928 930 resp->status = NFS3ERR_JUKEBOX;
929 931 } else
930 932 resp->status = puterrno3(error);
931 933 out1:
932 934 DTRACE_NFSV3_5(op__readlink__done, struct svc_req *, req,
933 935 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
934 936 READLINK3res *, resp);
935 937 if (vp != NULL)
936 938 VN_RELE(vp);
937 939 vattr_to_post_op_attr(vap, &resp->resfail.symlink_attributes);
938 940 }
939 941
940 942 void *
941 943 rfs3_readlink_getfh(READLINK3args *args)
942 944 {
943 945
944 946 return (&args->symlink);
945 947 }
946 948
947 949 void
948 950 rfs3_readlink_free(READLINK3res *resp)
949 951 {
950 952
951 953 if (resp->status == NFS3_OK)
952 954 kmem_free(resp->resok.data, MAXPATHLEN + 1);
953 955 }
954 956
955 957 /*
956 958 * Server routine to handle read
957 959 * May handle RDMA data as well as mblks
958 960 */
959 961 /* ARGSUSED */
960 962 void
961 963 rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi,
962 964 struct svc_req *req, cred_t *cr, bool_t ro)
963 965 {
964 966 int error;
965 967 vnode_t *vp;
966 968 struct vattr *vap;
967 969 struct vattr va;
968 970 struct iovec iov, *iovp = NULL;
969 971 int iovcnt;
970 972 struct uio uio;
971 973 u_offset_t offset;
972 974 mblk_t *mp = NULL;
973 975 int in_crit = 0;
974 976 int need_rwunlock = 0;
975 977 caller_context_t ct;
976 978 int rdma_used = 0;
977 979 int loaned_buffers;
978 980 struct uio *uiop;
979 981
980 982 vap = NULL;
981 983
982 984 vp = nfs3_fhtovp(&args->file, exi);
983 985
984 986 DTRACE_NFSV3_5(op__read__start, struct svc_req *, req,
985 987 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
986 988 READ3args *, args);
987 989
988 990
989 991 if (vp == NULL) {
990 992 error = ESTALE;
991 993 goto out;
992 994 }
993 995
994 996 if (args->wlist) {
995 997 if (args->count > clist_len(args->wlist)) {
996 998 error = EINVAL;
997 999 goto out;
998 1000 }
999 1001 rdma_used = 1;
1000 1002 }
1001 1003
1002 1004 /* use loaned buffers for TCP */
1003 1005 loaned_buffers = (nfs_loaned_buffers && !rdma_used) ? 1 : 0;
1004 1006
1005 1007 if (is_system_labeled()) {
1006 1008 bslabel_t *clabel = req->rq_label;
1007 1009
1008 1010 ASSERT(clabel != NULL);
1009 1011 DTRACE_PROBE2(tx__rfs3__log__info__opread__clabel, char *,
1010 1012 "got client label from request(1)", struct svc_req *, req);
1011 1013
1012 1014 if (!blequal(&l_admin_low->tsl_label, clabel)) {
1013 1015 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
1014 1016 exi)) {
1015 1017 resp->status = NFS3ERR_ACCES;
1016 1018 goto out1;
1017 1019 }
1018 1020 }
1019 1021 }
1020 1022
1021 1023 ct.cc_sysid = 0;
1022 1024 ct.cc_pid = 0;
1023 1025 ct.cc_caller_id = nfs3_srv_caller_id;
1024 1026 ct.cc_flags = CC_DONTBLOCK;
1025 1027
1026 1028 /*
1027 1029 * Enter the critical region before calling VOP_RWLOCK
1028 1030 * to avoid a deadlock with write requests.
1029 1031 */
1030 1032 if (nbl_need_check(vp)) {
1031 1033 nbl_start_crit(vp, RW_READER);
1032 1034 in_crit = 1;
1033 1035 if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0,
1034 1036 NULL)) {
1035 1037 error = EACCES;
1036 1038 goto out;
1037 1039 }
1038 1040 }
1039 1041
1040 1042 error = VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &ct);
1041 1043
1042 1044 /* check if a monitor detected a delegation conflict */
1043 1045 if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1044 1046 resp->status = NFS3ERR_JUKEBOX;
1045 1047 goto out1;
1046 1048 }
1047 1049
1048 1050 need_rwunlock = 1;
1049 1051
1050 1052 va.va_mask = AT_ALL;
1051 1053 error = VOP_GETATTR(vp, &va, 0, cr, &ct);
1052 1054
1053 1055 /*
1054 1056 * If we can't get the attributes, then we can't do the
1055 1057 * right access checking. So, we'll fail the request.
1056 1058 */
1057 1059 if (error)
1058 1060 goto out;
1059 1061
1060 1062 vap = &va;
1061 1063
1062 1064 if (vp->v_type != VREG) {
1063 1065 resp->status = NFS3ERR_INVAL;
1064 1066 goto out1;
1065 1067 }
1066 1068
1067 1069 if (crgetuid(cr) != va.va_uid) {
1068 1070 error = VOP_ACCESS(vp, VREAD, 0, cr, &ct);
1069 1071 if (error) {
1070 1072 if (curthread->t_flag & T_WOULDBLOCK)
1071 1073 goto out;
1072 1074 error = VOP_ACCESS(vp, VEXEC, 0, cr, &ct);
1073 1075 if (error)
1074 1076 goto out;
1075 1077 }
1076 1078 }
1077 1079
1078 1080 if (MANDLOCK(vp, va.va_mode)) {
1079 1081 resp->status = NFS3ERR_ACCES;
1080 1082 goto out1;
1081 1083 }
1082 1084
1083 1085 offset = args->offset;
1084 1086 if (offset >= va.va_size) {
1085 1087 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1086 1088 if (in_crit)
1087 1089 nbl_end_crit(vp);
1088 1090 resp->status = NFS3_OK;
1089 1091 vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1090 1092 resp->resok.count = 0;
1091 1093 resp->resok.eof = TRUE;
1092 1094 resp->resok.data.data_len = 0;
1093 1095 resp->resok.data.data_val = NULL;
1094 1096 resp->resok.data.mp = NULL;
1095 1097 /* RDMA */
1096 1098 resp->resok.wlist = args->wlist;
1097 1099 resp->resok.wlist_len = resp->resok.count;
1098 1100 if (resp->resok.wlist)
1099 1101 clist_zero_len(resp->resok.wlist);
1100 1102 goto done;
1101 1103 }
1102 1104
1103 1105 if (args->count == 0) {
1104 1106 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1105 1107 if (in_crit)
1106 1108 nbl_end_crit(vp);
1107 1109 resp->status = NFS3_OK;
1108 1110 vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1109 1111 resp->resok.count = 0;
1110 1112 resp->resok.eof = FALSE;
1111 1113 resp->resok.data.data_len = 0;
1112 1114 resp->resok.data.data_val = NULL;
1113 1115 resp->resok.data.mp = NULL;
1114 1116 /* RDMA */
1115 1117 resp->resok.wlist = args->wlist;
1116 1118 resp->resok.wlist_len = resp->resok.count;
1117 1119 if (resp->resok.wlist)
1118 1120 clist_zero_len(resp->resok.wlist);
1119 1121 goto done;
1120 1122 }
1121 1123
1122 1124 /*
1123 1125 * do not allocate memory more the max. allowed
1124 1126 * transfer size
1125 1127 */
1126 1128 if (args->count > rfs3_tsize(req))
1127 1129 args->count = rfs3_tsize(req);
1128 1130
1129 1131 if (loaned_buffers) {
1130 1132 uiop = (uio_t *)rfs_setup_xuio(vp);
1131 1133 ASSERT(uiop != NULL);
1132 1134 uiop->uio_segflg = UIO_SYSSPACE;
1133 1135 uiop->uio_loffset = args->offset;
1134 1136 uiop->uio_resid = args->count;
1135 1137
1136 1138 /* Jump to do the read if successful */
1137 1139 if (VOP_REQZCBUF(vp, UIO_READ, (xuio_t *)uiop, cr, &ct) == 0) {
1138 1140 /*
1139 1141 * Need to hold the vnode until after VOP_RETZCBUF()
1140 1142 * is called.
1141 1143 */
1142 1144 VN_HOLD(vp);
1143 1145 goto doio_read;
1144 1146 }
1145 1147
1146 1148 DTRACE_PROBE2(nfss__i__reqzcbuf_failed, int,
1147 1149 uiop->uio_loffset, int, uiop->uio_resid);
1148 1150
1149 1151 uiop->uio_extflg = 0;
1150 1152 /* failure to setup for zero copy */
1151 1153 rfs_free_xuio((void *)uiop);
1152 1154 loaned_buffers = 0;
1153 1155 }
1154 1156
1155 1157 /*
1156 1158 * If returning data via RDMA Write, then grab the chunk list.
1157 1159 * If we aren't returning READ data w/RDMA_WRITE, then grab
1158 1160 * a mblk.
1159 1161 */
1160 1162 if (rdma_used) {
1161 1163 (void) rdma_get_wchunk(req, &iov, args->wlist);
1162 1164 uio.uio_iov = &iov;
1163 1165 uio.uio_iovcnt = 1;
1164 1166 } else {
1165 1167 /*
1166 1168 * mp will contain the data to be sent out in the read reply.
1167 1169 * For UDP, this will be freed after the reply has been sent
1168 1170 * out by the driver. For TCP, it will be freed after the last
1169 1171 * segment associated with the reply has been ACKed by the
1170 1172 * client.
1171 1173 */
1172 1174 mp = rfs_read_alloc(args->count, &iovp, &iovcnt);
1173 1175 uio.uio_iov = iovp;
1174 1176 uio.uio_iovcnt = iovcnt;
1175 1177 }
1176 1178
1177 1179 uio.uio_segflg = UIO_SYSSPACE;
1178 1180 uio.uio_extflg = UIO_COPY_CACHED;
1179 1181 uio.uio_loffset = args->offset;
1180 1182 uio.uio_resid = args->count;
1181 1183 uiop = &uio;
1182 1184
1183 1185 doio_read:
1184 1186 error = VOP_READ(vp, uiop, 0, cr, &ct);
1185 1187
1186 1188 if (error) {
1187 1189 if (mp)
1188 1190 freemsg(mp);
1189 1191 /* check if a monitor detected a delegation conflict */
1190 1192 if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1191 1193 resp->status = NFS3ERR_JUKEBOX;
1192 1194 goto out1;
1193 1195 }
1194 1196 goto out;
1195 1197 }
1196 1198
1197 1199 /* make mblk using zc buffers */
1198 1200 if (loaned_buffers) {
1199 1201 mp = uio_to_mblk(uiop);
1200 1202 ASSERT(mp != NULL);
1201 1203 }
1202 1204
1203 1205 va.va_mask = AT_ALL;
1204 1206 error = VOP_GETATTR(vp, &va, 0, cr, &ct);
1205 1207
1206 1208 if (error)
1207 1209 vap = NULL;
1208 1210 else
1209 1211 vap = &va;
1210 1212
1211 1213 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1212 1214
1213 1215 if (in_crit)
1214 1216 nbl_end_crit(vp);
1215 1217
1216 1218 resp->status = NFS3_OK;
1217 1219 vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1218 1220 resp->resok.count = args->count - uiop->uio_resid;
1219 1221 if (!error && offset + resp->resok.count == va.va_size)
1220 1222 resp->resok.eof = TRUE;
1221 1223 else
1222 1224 resp->resok.eof = FALSE;
1223 1225 resp->resok.data.data_len = resp->resok.count;
1224 1226
1225 1227 if (mp)
1226 1228 rfs_rndup_mblks(mp, resp->resok.count, loaned_buffers);
1227 1229
1228 1230 resp->resok.data.mp = mp;
1229 1231 resp->resok.size = (uint_t)args->count;
1230 1232
1231 1233 if (rdma_used) {
1232 1234 resp->resok.data.data_val = (caddr_t)iov.iov_base;
1233 1235 if (!rdma_setup_read_data3(args, &(resp->resok))) {
1234 1236 resp->status = NFS3ERR_INVAL;
1235 1237 }
1236 1238 } else {
1237 1239 resp->resok.data.data_val = (caddr_t)mp->b_datap->db_base;
1238 1240 (resp->resok).wlist = NULL;
1239 1241 }
1240 1242
1241 1243 done:
1242 1244 DTRACE_NFSV3_5(op__read__done, struct svc_req *, req,
1243 1245 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
1244 1246 READ3res *, resp);
1245 1247
1246 1248 VN_RELE(vp);
1247 1249
1248 1250 if (iovp != NULL)
1249 1251 kmem_free(iovp, iovcnt * sizeof (struct iovec));
1250 1252
1251 1253 return;
1252 1254
1253 1255 out:
1254 1256 if (curthread->t_flag & T_WOULDBLOCK) {
1255 1257 curthread->t_flag &= ~T_WOULDBLOCK;
1256 1258 resp->status = NFS3ERR_JUKEBOX;
1257 1259 } else
1258 1260 resp->status = puterrno3(error);
1259 1261 out1:
1260 1262 DTRACE_NFSV3_5(op__read__done, struct svc_req *, req,
1261 1263 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
1262 1264 READ3res *, resp);
1263 1265
1264 1266 if (vp != NULL) {
1265 1267 if (need_rwunlock)
1266 1268 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1267 1269 if (in_crit)
1268 1270 nbl_end_crit(vp);
1269 1271 VN_RELE(vp);
1270 1272 }
1271 1273 vattr_to_post_op_attr(vap, &resp->resfail.file_attributes);
1272 1274
1273 1275 if (iovp != NULL)
1274 1276 kmem_free(iovp, iovcnt * sizeof (struct iovec));
1275 1277 }
1276 1278
1277 1279 void
1278 1280 rfs3_read_free(READ3res *resp)
1279 1281 {
1280 1282 mblk_t *mp;
1281 1283
1282 1284 if (resp->status == NFS3_OK) {
1283 1285 mp = resp->resok.data.mp;
1284 1286 if (mp != NULL)
1285 1287 freemsg(mp);
1286 1288 }
1287 1289 }
1288 1290
1289 1291 void *
1290 1292 rfs3_read_getfh(READ3args *args)
1291 1293 {
1292 1294
1293 1295 return (&args->file);
1294 1296 }
1295 1297
1296 1298 #define MAX_IOVECS 12
1297 1299
1298 1300 #ifdef DEBUG
1299 1301 static int rfs3_write_hits = 0;
1300 1302 static int rfs3_write_misses = 0;
1301 1303 #endif
1302 1304
1303 1305 void
1304 1306 rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi,
1305 1307 struct svc_req *req, cred_t *cr, bool_t ro)
1306 1308 {
1307 1309 nfs3_srv_t *ns;
1308 1310 int error;
1309 1311 vnode_t *vp;
1310 1312 struct vattr *bvap = NULL;
1311 1313 struct vattr bva;
1312 1314 struct vattr *avap = NULL;
1313 1315 struct vattr ava;
1314 1316 u_offset_t rlimit;
1315 1317 struct uio uio;
1316 1318 struct iovec iov[MAX_IOVECS];
1317 1319 mblk_t *m;
1318 1320 struct iovec *iovp;
1319 1321 int iovcnt;
1320 1322 int ioflag;
1321 1323 cred_t *savecred;
1322 1324 int in_crit = 0;
1323 1325 int rwlock_ret = -1;
1324 1326 caller_context_t ct;
1325 1327
1326 1328 vp = nfs3_fhtovp(&args->file, exi);
1327 1329
1328 1330 DTRACE_NFSV3_5(op__write__start, struct svc_req *, req,
1329 1331 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
1330 1332 WRITE3args *, args);
1331 1333
1332 1334 if (vp == NULL) {
1333 1335 error = ESTALE;
1334 1336 goto err;
1335 1337 }
1336 1338
1337 1339 ASSERT3P(curzone, ==, exi->exi_zone); /* exi is guaranteed non-NULL. */
1338 1340 ns = nfs3_get_srv();
1339 1341
1340 1342 if (is_system_labeled()) {
1341 1343 bslabel_t *clabel = req->rq_label;
1342 1344
1343 1345 ASSERT(clabel != NULL);
1344 1346 DTRACE_PROBE2(tx__rfs3__log__info__opwrite__clabel, char *,
1345 1347 "got client label from request(1)", struct svc_req *, req);
1346 1348
1347 1349 if (!blequal(&l_admin_low->tsl_label, clabel)) {
1348 1350 if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK,
1349 1351 exi)) {
1350 1352 resp->status = NFS3ERR_ACCES;
1351 1353 goto err1;
1352 1354 }
1353 1355 }
1354 1356 }
1355 1357
1356 1358 ct.cc_sysid = 0;
1357 1359 ct.cc_pid = 0;
1358 1360 ct.cc_caller_id = nfs3_srv_caller_id;
1359 1361 ct.cc_flags = CC_DONTBLOCK;
1360 1362
1361 1363 /*
1362 1364 * We have to enter the critical region before calling VOP_RWLOCK
1363 1365 * to avoid a deadlock with ufs.
1364 1366 */
1365 1367 if (nbl_need_check(vp)) {
1366 1368 nbl_start_crit(vp, RW_READER);
1367 1369 in_crit = 1;
1368 1370 if (nbl_conflict(vp, NBL_WRITE, args->offset, args->count, 0,
1369 1371 NULL)) {
1370 1372 error = EACCES;
1371 1373 goto err;
1372 1374 }
1373 1375 }
1374 1376
1375 1377 rwlock_ret = VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct);
1376 1378
1377 1379 /* check if a monitor detected a delegation conflict */
1378 1380 if (rwlock_ret == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1379 1381 resp->status = NFS3ERR_JUKEBOX;
1380 1382 rwlock_ret = -1;
1381 1383 goto err1;
1382 1384 }
1383 1385
1384 1386
1385 1387 bva.va_mask = AT_ALL;
1386 1388 error = VOP_GETATTR(vp, &bva, 0, cr, &ct);
1387 1389
1388 1390 /*
1389 1391 * If we can't get the attributes, then we can't do the
1390 1392 * right access checking. So, we'll fail the request.
1391 1393 */
1392 1394 if (error)
1393 1395 goto err;
1394 1396
1395 1397 bvap = &bva;
1396 1398 avap = bvap;
1397 1399
1398 1400 if (args->count != args->data.data_len) {
1399 1401 resp->status = NFS3ERR_INVAL;
1400 1402 goto err1;
1401 1403 }
1402 1404
1403 1405 if (rdonly(ro, vp)) {
1404 1406 resp->status = NFS3ERR_ROFS;
1405 1407 goto err1;
1406 1408 }
1407 1409
1408 1410 if (vp->v_type != VREG) {
1409 1411 resp->status = NFS3ERR_INVAL;
1410 1412 goto err1;
1411 1413 }
1412 1414
1413 1415 if (crgetuid(cr) != bva.va_uid &&
1414 1416 (error = VOP_ACCESS(vp, VWRITE, 0, cr, &ct)))
1415 1417 goto err;
1416 1418
1417 1419 if (MANDLOCK(vp, bva.va_mode)) {
1418 1420 resp->status = NFS3ERR_ACCES;
1419 1421 goto err1;
1420 1422 }
1421 1423
1422 1424 if (args->count == 0) {
1423 1425 resp->status = NFS3_OK;
1424 1426 vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
1425 1427 resp->resok.count = 0;
1426 1428 resp->resok.committed = args->stable;
1427 1429 resp->resok.verf = ns->write3verf;
1428 1430 goto out;
1429 1431 }
1430 1432
1431 1433 if (args->mblk != NULL) {
1432 1434 iovcnt = 0;
1433 1435 for (m = args->mblk; m != NULL; m = m->b_cont)
1434 1436 iovcnt++;
1435 1437 if (iovcnt <= MAX_IOVECS) {
1436 1438 #ifdef DEBUG
1437 1439 rfs3_write_hits++;
1438 1440 #endif
1439 1441 iovp = iov;
1440 1442 } else {
1441 1443 #ifdef DEBUG
1442 1444 rfs3_write_misses++;
1443 1445 #endif
1444 1446 iovp = kmem_alloc(sizeof (*iovp) * iovcnt, KM_SLEEP);
1445 1447 }
1446 1448 mblk_to_iov(args->mblk, iovcnt, iovp);
1447 1449
1448 1450 } else if (args->rlist != NULL) {
1449 1451 iovcnt = 1;
1450 1452 iovp = iov;
1451 1453 iovp->iov_base = (char *)((args->rlist)->u.c_daddr3);
1452 1454 iovp->iov_len = args->count;
1453 1455 } else {
1454 1456 iovcnt = 1;
1455 1457 iovp = iov;
1456 1458 iovp->iov_base = args->data.data_val;
1457 1459 iovp->iov_len = args->count;
1458 1460 }
1459 1461
1460 1462 uio.uio_iov = iovp;
1461 1463 uio.uio_iovcnt = iovcnt;
1462 1464
1463 1465 uio.uio_segflg = UIO_SYSSPACE;
1464 1466 uio.uio_extflg = UIO_COPY_DEFAULT;
1465 1467 uio.uio_loffset = args->offset;
1466 1468 uio.uio_resid = args->count;
1467 1469 uio.uio_llimit = curproc->p_fsz_ctl;
1468 1470 rlimit = uio.uio_llimit - args->offset;
1469 1471 if (rlimit < (u_offset_t)uio.uio_resid)
1470 1472 uio.uio_resid = (int)rlimit;
1471 1473
1472 1474 if (args->stable == UNSTABLE)
1473 1475 ioflag = 0;
1474 1476 else if (args->stable == FILE_SYNC)
1475 1477 ioflag = FSYNC;
1476 1478 else if (args->stable == DATA_SYNC)
1477 1479 ioflag = FDSYNC;
1478 1480 else {
1479 1481 if (iovp != iov)
1480 1482 kmem_free(iovp, sizeof (*iovp) * iovcnt);
1481 1483 resp->status = NFS3ERR_INVAL;
1482 1484 goto err1;
1483 1485 }
1484 1486
1485 1487 /*
1486 1488 * We're changing creds because VM may fault and we need
1487 1489 * the cred of the current thread to be used if quota
1488 1490 * checking is enabled.
1489 1491 */
1490 1492 savecred = curthread->t_cred;
1491 1493 curthread->t_cred = cr;
1492 1494 error = VOP_WRITE(vp, &uio, ioflag, cr, &ct);
1493 1495 curthread->t_cred = savecred;
1494 1496
1495 1497 if (iovp != iov)
1496 1498 kmem_free(iovp, sizeof (*iovp) * iovcnt);
1497 1499
1498 1500 /* check if a monitor detected a delegation conflict */
1499 1501 if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1500 1502 resp->status = NFS3ERR_JUKEBOX;
1501 1503 goto err1;
1502 1504 }
1503 1505
1504 1506 ava.va_mask = AT_ALL;
1505 1507 avap = VOP_GETATTR(vp, &ava, 0, cr, &ct) ? NULL : &ava;
1506 1508
1507 1509 if (error)
1508 1510 goto err;
1509 1511
1510 1512 /*
1511 1513 * If we were unable to get the V_WRITELOCK_TRUE, then we
1512 1514 * may not have accurate after attrs, so check if
1513 1515 * we have both attributes, they have a non-zero va_seq, and
1514 1516 * va_seq has changed by exactly one,
1515 1517 * if not, turn off the before attr.
1516 1518 */
1517 1519 if (rwlock_ret != V_WRITELOCK_TRUE) {
1518 1520 if (bvap == NULL || avap == NULL ||
1519 1521 bvap->va_seq == 0 || avap->va_seq == 0 ||
1520 1522 avap->va_seq != (bvap->va_seq + 1)) {
1521 1523 bvap = NULL;
1522 1524 }
1523 1525 }
1524 1526
1525 1527 resp->status = NFS3_OK;
1526 1528 vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
1527 1529 resp->resok.count = args->count - uio.uio_resid;
1528 1530 resp->resok.committed = args->stable;
1529 1531 resp->resok.verf = ns->write3verf;
1530 1532 goto out;
1531 1533
1532 1534 err:
1533 1535 if (curthread->t_flag & T_WOULDBLOCK) {
1534 1536 curthread->t_flag &= ~T_WOULDBLOCK;
1535 1537 resp->status = NFS3ERR_JUKEBOX;
1536 1538 } else
1537 1539 resp->status = puterrno3(error);
1538 1540 err1:
1539 1541 vattr_to_wcc_data(bvap, avap, &resp->resfail.file_wcc);
1540 1542 out:
1541 1543 DTRACE_NFSV3_5(op__write__done, struct svc_req *, req,
1542 1544 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
1543 1545 WRITE3res *, resp);
1544 1546
1545 1547 if (vp != NULL) {
1546 1548 if (rwlock_ret != -1)
1547 1549 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct);
1548 1550 if (in_crit)
1549 1551 nbl_end_crit(vp);
1550 1552 VN_RELE(vp);
1551 1553 }
1552 1554 }
1553 1555
1554 1556 void *
1555 1557 rfs3_write_getfh(WRITE3args *args)
1556 1558 {
1557 1559
1558 1560 return (&args->file);
1559 1561 }
1560 1562
1561 1563 void
1562 1564 rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi,
1563 1565 struct svc_req *req, cred_t *cr, bool_t ro)
1564 1566 {
1565 1567 int error;
1566 1568 int in_crit = 0;
1567 1569 vnode_t *vp;
1568 1570 vnode_t *tvp = NULL;
1569 1571 vnode_t *dvp;
1570 1572 struct vattr *vap;
1571 1573 struct vattr va;
1572 1574 struct vattr *dbvap;
1573 1575 struct vattr dbva;
1574 1576 struct vattr *davap;
1575 1577 struct vattr dava;
1576 1578 enum vcexcl excl;
1577 1579 nfstime3 *mtime;
1578 1580 len_t reqsize;
1579 1581 bool_t trunc;
1580 1582 struct sockaddr *ca;
1581 1583 char *name = NULL;
1582 1584
1583 1585 dbvap = NULL;
1584 1586 davap = NULL;
1585 1587
1586 1588 dvp = nfs3_fhtovp(&args->where.dir, exi);
1587 1589
1588 1590 DTRACE_NFSV3_5(op__create__start, struct svc_req *, req,
1589 1591 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
1590 1592 CREATE3args *, args);
1591 1593
1592 1594 if (dvp == NULL) {
1593 1595 error = ESTALE;
1594 1596 goto out;
1595 1597 }
1596 1598
1597 1599 dbva.va_mask = AT_ALL;
1598 1600 dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1599 1601 davap = dbvap;
1600 1602
1601 1603 if (args->where.name == nfs3nametoolong) {
1602 1604 resp->status = NFS3ERR_NAMETOOLONG;
1603 1605 goto out1;
1604 1606 }
1605 1607
1606 1608 if (args->where.name == NULL || *(args->where.name) == '\0') {
1607 1609 resp->status = NFS3ERR_ACCES;
1608 1610 goto out1;
1609 1611 }
1610 1612
1611 1613 if (rdonly(ro, dvp)) {
1612 1614 resp->status = NFS3ERR_ROFS;
1613 1615 goto out1;
1614 1616 }
1615 1617
1616 1618 if (is_system_labeled()) {
1617 1619 bslabel_t *clabel = req->rq_label;
1618 1620
1619 1621 ASSERT(clabel != NULL);
1620 1622 DTRACE_PROBE2(tx__rfs3__log__info__opcreate__clabel, char *,
1621 1623 "got client label from request(1)", struct svc_req *, req);
1622 1624
1623 1625 if (!blequal(&l_admin_low->tsl_label, clabel)) {
1624 1626 if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK,
1625 1627 exi)) {
1626 1628 resp->status = NFS3ERR_ACCES;
1627 1629 goto out1;
1628 1630 }
1629 1631 }
1630 1632 }
1631 1633
1632 1634 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
1633 1635 name = nfscmd_convname(ca, exi, args->where.name,
1634 1636 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
1635 1637
1636 1638 if (name == NULL) {
1637 1639 /* This is really a Solaris EILSEQ */
1638 1640 resp->status = NFS3ERR_INVAL;
1639 1641 goto out1;
1640 1642 }
1641 1643
1642 1644 if (args->how.mode == EXCLUSIVE) {
1643 1645 va.va_mask = AT_TYPE | AT_MODE | AT_MTIME;
1644 1646 va.va_type = VREG;
1645 1647 va.va_mode = (mode_t)0;
1646 1648 /*
1647 1649 * Ensure no time overflows and that types match
1648 1650 */
1649 1651 mtime = (nfstime3 *)&args->how.createhow3_u.verf;
1650 1652 va.va_mtime.tv_sec = mtime->seconds % INT32_MAX;
1651 1653 va.va_mtime.tv_nsec = mtime->nseconds;
1652 1654 excl = EXCL;
1653 1655 } else {
1654 1656 error = sattr3_to_vattr(&args->how.createhow3_u.obj_attributes,
1655 1657 &va);
1656 1658 if (error)
1657 1659 goto out;
1658 1660 va.va_mask |= AT_TYPE;
1659 1661 va.va_type = VREG;
1660 1662 if (args->how.mode == GUARDED)
1661 1663 excl = EXCL;
1662 1664 else {
1663 1665 excl = NONEXCL;
1664 1666
1665 1667 /*
1666 1668 * During creation of file in non-exclusive mode
1667 1669 * if size of file is being set then make sure
1668 1670 * that if the file already exists that no conflicting
1669 1671 * non-blocking mandatory locks exists in the region
1670 1672 * being modified. If there are conflicting locks fail
1671 1673 * the operation with EACCES.
1672 1674 */
1673 1675 if (va.va_mask & AT_SIZE) {
1674 1676 struct vattr tva;
1675 1677
1676 1678 /*
1677 1679 * Does file already exist?
1678 1680 */
1679 1681 error = VOP_LOOKUP(dvp, name, &tvp,
1680 1682 NULL, 0, NULL, cr, NULL, NULL, NULL);
1681 1683
1682 1684 /*
1683 1685 * Check to see if the file has been delegated
1684 1686 * to a v4 client. If so, then begin recall of
1685 1687 * the delegation and return JUKEBOX to allow
1686 1688 * the client to retrasmit its request.
1687 1689 */
1688 1690
1689 1691 trunc = va.va_size == 0;
1690 1692 if (!error &&
1691 1693 rfs4_check_delegated(FWRITE, tvp, trunc)) {
1692 1694 resp->status = NFS3ERR_JUKEBOX;
1693 1695 goto out1;
1694 1696 }
1695 1697
1696 1698 /*
1697 1699 * Check for NBMAND lock conflicts
1698 1700 */
1699 1701 if (!error && nbl_need_check(tvp)) {
1700 1702 u_offset_t offset;
1701 1703 ssize_t len;
1702 1704
1703 1705 nbl_start_crit(tvp, RW_READER);
1704 1706 in_crit = 1;
1705 1707
1706 1708 tva.va_mask = AT_SIZE;
1707 1709 error = VOP_GETATTR(tvp, &tva, 0, cr,
1708 1710 NULL);
1709 1711 /*
1710 1712 * Can't check for conflicts, so return
1711 1713 * error.
1712 1714 */
1713 1715 if (error)
1714 1716 goto out;
1715 1717
1716 1718 offset = tva.va_size < va.va_size ?
1717 1719 tva.va_size : va.va_size;
1718 1720 len = tva.va_size < va.va_size ?
1719 1721 va.va_size - tva.va_size :
1720 1722 tva.va_size - va.va_size;
1721 1723 if (nbl_conflict(tvp, NBL_WRITE,
1722 1724 offset, len, 0, NULL)) {
1723 1725 error = EACCES;
1724 1726 goto out;
1725 1727 }
1726 1728 } else if (tvp) {
1727 1729 VN_RELE(tvp);
1728 1730 tvp = NULL;
1729 1731 }
1730 1732 }
1731 1733 }
1732 1734 if (va.va_mask & AT_SIZE)
1733 1735 reqsize = va.va_size;
1734 1736 }
1735 1737
1736 1738 /*
1737 1739 * Must specify the mode.
1738 1740 */
1739 1741 if (!(va.va_mask & AT_MODE)) {
1740 1742 resp->status = NFS3ERR_INVAL;
1741 1743 goto out1;
1742 1744 }
1743 1745
1744 1746 /*
1745 1747 * If the filesystem is exported with nosuid, then mask off
1746 1748 * the setuid and setgid bits.
1747 1749 */
1748 1750 if (va.va_type == VREG && (exi->exi_export.ex_flags & EX_NOSUID))
1749 1751 va.va_mode &= ~(VSUID | VSGID);
1750 1752
1751 1753 tryagain:
1752 1754 /*
1753 1755 * The file open mode used is VWRITE. If the client needs
1754 1756 * some other semantic, then it should do the access checking
1755 1757 * itself. It would have been nice to have the file open mode
1756 1758 * passed as part of the arguments.
1757 1759 */
1758 1760 error = VOP_CREATE(dvp, name, &va, excl, VWRITE,
1759 1761 &vp, cr, 0, NULL, NULL);
1760 1762
1761 1763 dava.va_mask = AT_ALL;
1762 1764 davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
1763 1765
1764 1766 if (error) {
1765 1767 /*
1766 1768 * If we got something other than file already exists
1767 1769 * then just return this error. Otherwise, we got
1768 1770 * EEXIST. If we were doing a GUARDED create, then
1769 1771 * just return this error. Otherwise, we need to
1770 1772 * make sure that this wasn't a duplicate of an
1771 1773 * exclusive create request.
1772 1774 *
1773 1775 * The assumption is made that a non-exclusive create
1774 1776 * request will never return EEXIST.
1775 1777 */
1776 1778 if (error != EEXIST || args->how.mode == GUARDED)
1777 1779 goto out;
1778 1780 /*
1779 1781 * Lookup the file so that we can get a vnode for it.
1780 1782 */
1781 1783 error = VOP_LOOKUP(dvp, name, &vp, NULL, 0,
1782 1784 NULL, cr, NULL, NULL, NULL);
1783 1785 if (error) {
1784 1786 /*
1785 1787 * We couldn't find the file that we thought that
1786 1788 * we just created. So, we'll just try creating
1787 1789 * it again.
1788 1790 */
1789 1791 if (error == ENOENT)
1790 1792 goto tryagain;
1791 1793 goto out;
1792 1794 }
1793 1795
1794 1796 /*
1795 1797 * If the file is delegated to a v4 client, go ahead
1796 1798 * and initiate recall, this create is a hint that a
1797 1799 * conflicting v3 open has occurred.
1798 1800 */
1799 1801
1800 1802 if (rfs4_check_delegated(FWRITE, vp, FALSE)) {
1801 1803 VN_RELE(vp);
1802 1804 resp->status = NFS3ERR_JUKEBOX;
1803 1805 goto out1;
1804 1806 }
1805 1807
1806 1808 va.va_mask = AT_ALL;
1807 1809 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1808 1810
1809 1811 mtime = (nfstime3 *)&args->how.createhow3_u.verf;
1810 1812 /* % with INT32_MAX to prevent overflows */
1811 1813 if (args->how.mode == EXCLUSIVE && (vap == NULL ||
1812 1814 vap->va_mtime.tv_sec !=
1813 1815 (mtime->seconds % INT32_MAX) ||
1814 1816 vap->va_mtime.tv_nsec != mtime->nseconds)) {
1815 1817 VN_RELE(vp);
1816 1818 error = EEXIST;
1817 1819 goto out;
1818 1820 }
1819 1821 } else {
1820 1822
1821 1823 if ((args->how.mode == UNCHECKED ||
1822 1824 args->how.mode == GUARDED) &&
1823 1825 args->how.createhow3_u.obj_attributes.size.set_it &&
1824 1826 va.va_size == 0)
1825 1827 trunc = TRUE;
1826 1828 else
1827 1829 trunc = FALSE;
1828 1830
1829 1831 if (rfs4_check_delegated(FWRITE, vp, trunc)) {
1830 1832 VN_RELE(vp);
1831 1833 resp->status = NFS3ERR_JUKEBOX;
1832 1834 goto out1;
1833 1835 }
1834 1836
1835 1837 va.va_mask = AT_ALL;
1836 1838 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1837 1839
1838 1840 /*
1839 1841 * We need to check to make sure that the file got
1840 1842 * created to the indicated size. If not, we do a
1841 1843 * setattr to try to change the size, but we don't
1842 1844 * try too hard. This shouldn't a problem as most
1843 1845 * clients will only specifiy a size of zero which
1844 1846 * local file systems handle. However, even if
1845 1847 * the client does specify a non-zero size, it can
1846 1848 * still recover by checking the size of the file
1847 1849 * after it has created it and then issue a setattr
1848 1850 * request of its own to set the size of the file.
1849 1851 */
1850 1852 if (vap != NULL &&
1851 1853 (args->how.mode == UNCHECKED ||
1852 1854 args->how.mode == GUARDED) &&
1853 1855 args->how.createhow3_u.obj_attributes.size.set_it &&
1854 1856 vap->va_size != reqsize) {
1855 1857 va.va_mask = AT_SIZE;
1856 1858 va.va_size = reqsize;
1857 1859 (void) VOP_SETATTR(vp, &va, 0, cr, NULL);
1858 1860 va.va_mask = AT_ALL;
1859 1861 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1860 1862 }
1861 1863 }
1862 1864
1863 1865 if (name != args->where.name)
1864 1866 kmem_free(name, MAXPATHLEN + 1);
1865 1867
1866 1868 error = makefh3(&resp->resok.obj.handle, vp, exi);
1867 1869 if (error)
1868 1870 resp->resok.obj.handle_follows = FALSE;
1869 1871 else
1870 1872 resp->resok.obj.handle_follows = TRUE;
1871 1873
1872 1874 /*
1873 1875 * Force modified data and metadata out to stable storage.
1874 1876 */
1875 1877 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
1876 1878 (void) VOP_FSYNC(dvp, 0, cr, NULL);
1877 1879
1878 1880 VN_RELE(vp);
1879 1881 if (tvp != NULL) {
1880 1882 if (in_crit)
1881 1883 nbl_end_crit(tvp);
1882 1884 VN_RELE(tvp);
1883 1885 }
1884 1886
1885 1887 resp->status = NFS3_OK;
1886 1888 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
1887 1889 vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
1888 1890
1889 1891 DTRACE_NFSV3_5(op__create__done, struct svc_req *, req,
1890 1892 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
1891 1893 CREATE3res *, resp);
1892 1894
1893 1895 VN_RELE(dvp);
1894 1896 return;
1895 1897
1896 1898 out:
1897 1899 if (curthread->t_flag & T_WOULDBLOCK) {
1898 1900 curthread->t_flag &= ~T_WOULDBLOCK;
1899 1901 resp->status = NFS3ERR_JUKEBOX;
1900 1902 } else
1901 1903 resp->status = puterrno3(error);
1902 1904 out1:
1903 1905 DTRACE_NFSV3_5(op__create__done, struct svc_req *, req,
1904 1906 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
1905 1907 CREATE3res *, resp);
1906 1908
1907 1909 if (name != NULL && name != args->where.name)
1908 1910 kmem_free(name, MAXPATHLEN + 1);
1909 1911
1910 1912 if (tvp != NULL) {
1911 1913 if (in_crit)
1912 1914 nbl_end_crit(tvp);
1913 1915 VN_RELE(tvp);
1914 1916 }
1915 1917 if (dvp != NULL)
1916 1918 VN_RELE(dvp);
1917 1919 vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
1918 1920 }
1919 1921
1920 1922 void *
1921 1923 rfs3_create_getfh(CREATE3args *args)
1922 1924 {
1923 1925
1924 1926 return (&args->where.dir);
1925 1927 }
1926 1928
1927 1929 void
1928 1930 rfs3_mkdir(MKDIR3args *args, MKDIR3res *resp, struct exportinfo *exi,
1929 1931 struct svc_req *req, cred_t *cr, bool_t ro)
1930 1932 {
1931 1933 int error;
1932 1934 vnode_t *vp = NULL;
1933 1935 vnode_t *dvp;
1934 1936 struct vattr *vap;
1935 1937 struct vattr va;
1936 1938 struct vattr *dbvap;
1937 1939 struct vattr dbva;
1938 1940 struct vattr *davap;
1939 1941 struct vattr dava;
1940 1942 struct sockaddr *ca;
1941 1943 char *name = NULL;
1942 1944
1943 1945 dbvap = NULL;
1944 1946 davap = NULL;
1945 1947
1946 1948 dvp = nfs3_fhtovp(&args->where.dir, exi);
1947 1949
1948 1950 DTRACE_NFSV3_5(op__mkdir__start, struct svc_req *, req,
1949 1951 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
1950 1952 MKDIR3args *, args);
1951 1953
1952 1954 if (dvp == NULL) {
1953 1955 error = ESTALE;
1954 1956 goto out;
1955 1957 }
1956 1958
1957 1959 dbva.va_mask = AT_ALL;
1958 1960 dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1959 1961 davap = dbvap;
1960 1962
1961 1963 if (args->where.name == nfs3nametoolong) {
1962 1964 resp->status = NFS3ERR_NAMETOOLONG;
1963 1965 goto out1;
1964 1966 }
1965 1967
1966 1968 if (args->where.name == NULL || *(args->where.name) == '\0') {
1967 1969 resp->status = NFS3ERR_ACCES;
1968 1970 goto out1;
1969 1971 }
1970 1972
1971 1973 if (rdonly(ro, dvp)) {
1972 1974 resp->status = NFS3ERR_ROFS;
1973 1975 goto out1;
1974 1976 }
1975 1977
1976 1978 if (is_system_labeled()) {
1977 1979 bslabel_t *clabel = req->rq_label;
1978 1980
1979 1981 ASSERT(clabel != NULL);
1980 1982 DTRACE_PROBE2(tx__rfs3__log__info__opmkdir__clabel, char *,
1981 1983 "got client label from request(1)", struct svc_req *, req);
1982 1984
1983 1985 if (!blequal(&l_admin_low->tsl_label, clabel)) {
1984 1986 if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK,
1985 1987 exi)) {
1986 1988 resp->status = NFS3ERR_ACCES;
1987 1989 goto out1;
1988 1990 }
1989 1991 }
1990 1992 }
1991 1993
1992 1994 error = sattr3_to_vattr(&args->attributes, &va);
1993 1995 if (error)
1994 1996 goto out;
1995 1997
1996 1998 if (!(va.va_mask & AT_MODE)) {
1997 1999 resp->status = NFS3ERR_INVAL;
1998 2000 goto out1;
1999 2001 }
2000 2002
2001 2003 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2002 2004 name = nfscmd_convname(ca, exi, args->where.name,
2003 2005 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2004 2006
2005 2007 if (name == NULL) {
2006 2008 resp->status = NFS3ERR_INVAL;
2007 2009 goto out1;
2008 2010 }
2009 2011
2010 2012 va.va_mask |= AT_TYPE;
2011 2013 va.va_type = VDIR;
2012 2014
2013 2015 error = VOP_MKDIR(dvp, name, &va, &vp, cr, NULL, 0, NULL);
2014 2016
2015 2017 if (name != args->where.name)
2016 2018 kmem_free(name, MAXPATHLEN + 1);
2017 2019
2018 2020 dava.va_mask = AT_ALL;
2019 2021 davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2020 2022
2021 2023 /*
2022 2024 * Force modified data and metadata out to stable storage.
2023 2025 */
2024 2026 (void) VOP_FSYNC(dvp, 0, cr, NULL);
2025 2027
2026 2028 if (error)
2027 2029 goto out;
2028 2030
2029 2031 error = makefh3(&resp->resok.obj.handle, vp, exi);
2030 2032 if (error)
2031 2033 resp->resok.obj.handle_follows = FALSE;
2032 2034 else
2033 2035 resp->resok.obj.handle_follows = TRUE;
2034 2036
2035 2037 va.va_mask = AT_ALL;
2036 2038 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2037 2039
2038 2040 /*
2039 2041 * Force modified data and metadata out to stable storage.
2040 2042 */
2041 2043 (void) VOP_FSYNC(vp, 0, cr, NULL);
2042 2044
2043 2045 VN_RELE(vp);
2044 2046
2045 2047 resp->status = NFS3_OK;
2046 2048 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
2047 2049 vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2048 2050
2049 2051 DTRACE_NFSV3_5(op__mkdir__done, struct svc_req *, req,
2050 2052 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2051 2053 MKDIR3res *, resp);
2052 2054 VN_RELE(dvp);
2053 2055
2054 2056 return;
2055 2057
2056 2058 out:
2057 2059 if (curthread->t_flag & T_WOULDBLOCK) {
2058 2060 curthread->t_flag &= ~T_WOULDBLOCK;
2059 2061 resp->status = NFS3ERR_JUKEBOX;
2060 2062 } else
2061 2063 resp->status = puterrno3(error);
2062 2064 out1:
2063 2065 DTRACE_NFSV3_5(op__mkdir__done, struct svc_req *, req,
2064 2066 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2065 2067 MKDIR3res *, resp);
2066 2068 if (dvp != NULL)
2067 2069 VN_RELE(dvp);
2068 2070 vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
2069 2071 }
2070 2072
2071 2073 void *
2072 2074 rfs3_mkdir_getfh(MKDIR3args *args)
2073 2075 {
2074 2076
2075 2077 return (&args->where.dir);
2076 2078 }
2077 2079
2078 2080 void
2079 2081 rfs3_symlink(SYMLINK3args *args, SYMLINK3res *resp, struct exportinfo *exi,
2080 2082 struct svc_req *req, cred_t *cr, bool_t ro)
2081 2083 {
2082 2084 int error;
2083 2085 vnode_t *vp;
2084 2086 vnode_t *dvp;
2085 2087 struct vattr *vap;
2086 2088 struct vattr va;
2087 2089 struct vattr *dbvap;
2088 2090 struct vattr dbva;
2089 2091 struct vattr *davap;
2090 2092 struct vattr dava;
2091 2093 struct sockaddr *ca;
2092 2094 char *name = NULL;
2093 2095 char *symdata = NULL;
2094 2096
2095 2097 dbvap = NULL;
2096 2098 davap = NULL;
2097 2099
2098 2100 dvp = nfs3_fhtovp(&args->where.dir, exi);
2099 2101
2100 2102 DTRACE_NFSV3_5(op__symlink__start, struct svc_req *, req,
2101 2103 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2102 2104 SYMLINK3args *, args);
2103 2105
2104 2106 if (dvp == NULL) {
2105 2107 error = ESTALE;
2106 2108 goto err;
2107 2109 }
2108 2110
2109 2111 dbva.va_mask = AT_ALL;
2110 2112 dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2111 2113 davap = dbvap;
2112 2114
2113 2115 if (args->where.name == nfs3nametoolong) {
2114 2116 resp->status = NFS3ERR_NAMETOOLONG;
2115 2117 goto err1;
2116 2118 }
2117 2119
2118 2120 if (args->where.name == NULL || *(args->where.name) == '\0') {
2119 2121 resp->status = NFS3ERR_ACCES;
2120 2122 goto err1;
2121 2123 }
2122 2124
2123 2125 if (rdonly(ro, dvp)) {
2124 2126 resp->status = NFS3ERR_ROFS;
2125 2127 goto err1;
2126 2128 }
2127 2129
2128 2130 if (is_system_labeled()) {
2129 2131 bslabel_t *clabel = req->rq_label;
2130 2132
2131 2133 ASSERT(clabel != NULL);
2132 2134 DTRACE_PROBE2(tx__rfs3__log__info__opsymlink__clabel, char *,
2133 2135 "got client label from request(1)", struct svc_req *, req);
2134 2136
2135 2137 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2136 2138 if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK,
2137 2139 exi)) {
2138 2140 resp->status = NFS3ERR_ACCES;
2139 2141 goto err1;
2140 2142 }
2141 2143 }
2142 2144 }
2143 2145
2144 2146 error = sattr3_to_vattr(&args->symlink.symlink_attributes, &va);
2145 2147 if (error)
2146 2148 goto err;
2147 2149
2148 2150 if (!(va.va_mask & AT_MODE)) {
2149 2151 resp->status = NFS3ERR_INVAL;
2150 2152 goto err1;
2151 2153 }
2152 2154
2153 2155 if (args->symlink.symlink_data == nfs3nametoolong) {
2154 2156 resp->status = NFS3ERR_NAMETOOLONG;
2155 2157 goto err1;
2156 2158 }
2157 2159
2158 2160 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2159 2161 name = nfscmd_convname(ca, exi, args->where.name,
2160 2162 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2161 2163
2162 2164 if (name == NULL) {
2163 2165 /* This is really a Solaris EILSEQ */
2164 2166 resp->status = NFS3ERR_INVAL;
2165 2167 goto err1;
2166 2168 }
2167 2169
2168 2170 symdata = nfscmd_convname(ca, exi, args->symlink.symlink_data,
2169 2171 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2170 2172 if (symdata == NULL) {
2171 2173 /* This is really a Solaris EILSEQ */
2172 2174 resp->status = NFS3ERR_INVAL;
2173 2175 goto err1;
2174 2176 }
2175 2177
2176 2178
2177 2179 va.va_mask |= AT_TYPE;
2178 2180 va.va_type = VLNK;
2179 2181
2180 2182 error = VOP_SYMLINK(dvp, name, &va, symdata, cr, NULL, 0);
2181 2183
2182 2184 dava.va_mask = AT_ALL;
2183 2185 davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2184 2186
2185 2187 if (error)
2186 2188 goto err;
2187 2189
2188 2190 error = VOP_LOOKUP(dvp, name, &vp, NULL, 0, NULL, cr,
2189 2191 NULL, NULL, NULL);
2190 2192
2191 2193 /*
2192 2194 * Force modified data and metadata out to stable storage.
2193 2195 */
2194 2196 (void) VOP_FSYNC(dvp, 0, cr, NULL);
2195 2197
2196 2198
2197 2199 resp->status = NFS3_OK;
2198 2200 if (error) {
2199 2201 resp->resok.obj.handle_follows = FALSE;
2200 2202 vattr_to_post_op_attr(NULL, &resp->resok.obj_attributes);
2201 2203 vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2202 2204 goto out;
2203 2205 }
2204 2206
2205 2207 error = makefh3(&resp->resok.obj.handle, vp, exi);
2206 2208 if (error)
2207 2209 resp->resok.obj.handle_follows = FALSE;
2208 2210 else
2209 2211 resp->resok.obj.handle_follows = TRUE;
2210 2212
2211 2213 va.va_mask = AT_ALL;
2212 2214 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2213 2215
2214 2216 /*
2215 2217 * Force modified data and metadata out to stable storage.
2216 2218 */
2217 2219 (void) VOP_FSYNC(vp, 0, cr, NULL);
2218 2220
2219 2221 VN_RELE(vp);
2220 2222
2221 2223 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
2222 2224 vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2223 2225 goto out;
2224 2226
2225 2227 err:
2226 2228 if (curthread->t_flag & T_WOULDBLOCK) {
2227 2229 curthread->t_flag &= ~T_WOULDBLOCK;
2228 2230 resp->status = NFS3ERR_JUKEBOX;
2229 2231 } else
2230 2232 resp->status = puterrno3(error);
2231 2233 err1:
2232 2234 vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
2233 2235 out:
2234 2236 if (name != NULL && name != args->where.name)
2235 2237 kmem_free(name, MAXPATHLEN + 1);
2236 2238 if (symdata != NULL && symdata != args->symlink.symlink_data)
2237 2239 kmem_free(symdata, MAXPATHLEN + 1);
2238 2240
2239 2241 DTRACE_NFSV3_5(op__symlink__done, struct svc_req *, req,
2240 2242 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2241 2243 SYMLINK3res *, resp);
2242 2244
2243 2245 if (dvp != NULL)
2244 2246 VN_RELE(dvp);
2245 2247 }
2246 2248
2247 2249 void *
2248 2250 rfs3_symlink_getfh(SYMLINK3args *args)
2249 2251 {
2250 2252
2251 2253 return (&args->where.dir);
2252 2254 }
2253 2255
2254 2256 void
2255 2257 rfs3_mknod(MKNOD3args *args, MKNOD3res *resp, struct exportinfo *exi,
2256 2258 struct svc_req *req, cred_t *cr, bool_t ro)
2257 2259 {
2258 2260 int error;
2259 2261 vnode_t *vp;
2260 2262 vnode_t *realvp;
2261 2263 vnode_t *dvp;
2262 2264 struct vattr *vap;
2263 2265 struct vattr va;
2264 2266 struct vattr *dbvap;
2265 2267 struct vattr dbva;
2266 2268 struct vattr *davap;
2267 2269 struct vattr dava;
2268 2270 int mode;
2269 2271 enum vcexcl excl;
2270 2272 struct sockaddr *ca;
2271 2273 char *name = NULL;
2272 2274
2273 2275 dbvap = NULL;
2274 2276 davap = NULL;
2275 2277
2276 2278 dvp = nfs3_fhtovp(&args->where.dir, exi);
2277 2279
2278 2280 DTRACE_NFSV3_5(op__mknod__start, struct svc_req *, req,
2279 2281 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2280 2282 MKNOD3args *, args);
2281 2283
2282 2284 if (dvp == NULL) {
2283 2285 error = ESTALE;
2284 2286 goto out;
2285 2287 }
2286 2288
2287 2289 dbva.va_mask = AT_ALL;
2288 2290 dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2289 2291 davap = dbvap;
2290 2292
2291 2293 if (args->where.name == nfs3nametoolong) {
2292 2294 resp->status = NFS3ERR_NAMETOOLONG;
2293 2295 goto out1;
2294 2296 }
2295 2297
2296 2298 if (args->where.name == NULL || *(args->where.name) == '\0') {
2297 2299 resp->status = NFS3ERR_ACCES;
2298 2300 goto out1;
2299 2301 }
2300 2302
2301 2303 if (rdonly(ro, dvp)) {
2302 2304 resp->status = NFS3ERR_ROFS;
2303 2305 goto out1;
2304 2306 }
2305 2307
2306 2308 if (is_system_labeled()) {
2307 2309 bslabel_t *clabel = req->rq_label;
2308 2310
2309 2311 ASSERT(clabel != NULL);
2310 2312 DTRACE_PROBE2(tx__rfs3__log__info__opmknod__clabel, char *,
2311 2313 "got client label from request(1)", struct svc_req *, req);
2312 2314
2313 2315 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2314 2316 if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK,
2315 2317 exi)) {
2316 2318 resp->status = NFS3ERR_ACCES;
2317 2319 goto out1;
2318 2320 }
2319 2321 }
2320 2322 }
2321 2323
2322 2324 switch (args->what.type) {
2323 2325 case NF3CHR:
2324 2326 case NF3BLK:
2325 2327 error = sattr3_to_vattr(
2326 2328 &args->what.mknoddata3_u.device.dev_attributes, &va);
2327 2329 if (error)
2328 2330 goto out;
2329 2331 if (secpolicy_sys_devices(cr) != 0) {
2330 2332 resp->status = NFS3ERR_PERM;
2331 2333 goto out1;
2332 2334 }
2333 2335 if (args->what.type == NF3CHR)
2334 2336 va.va_type = VCHR;
2335 2337 else
2336 2338 va.va_type = VBLK;
2337 2339 va.va_rdev = makedevice(
2338 2340 args->what.mknoddata3_u.device.spec.specdata1,
2339 2341 args->what.mknoddata3_u.device.spec.specdata2);
2340 2342 va.va_mask |= AT_TYPE | AT_RDEV;
2341 2343 break;
2342 2344 case NF3SOCK:
2343 2345 error = sattr3_to_vattr(
2344 2346 &args->what.mknoddata3_u.pipe_attributes, &va);
2345 2347 if (error)
2346 2348 goto out;
2347 2349 va.va_type = VSOCK;
2348 2350 va.va_mask |= AT_TYPE;
2349 2351 break;
2350 2352 case NF3FIFO:
2351 2353 error = sattr3_to_vattr(
2352 2354 &args->what.mknoddata3_u.pipe_attributes, &va);
2353 2355 if (error)
2354 2356 goto out;
2355 2357 va.va_type = VFIFO;
2356 2358 va.va_mask |= AT_TYPE;
2357 2359 break;
2358 2360 default:
2359 2361 resp->status = NFS3ERR_BADTYPE;
2360 2362 goto out1;
2361 2363 }
2362 2364
2363 2365 /*
2364 2366 * Must specify the mode.
2365 2367 */
2366 2368 if (!(va.va_mask & AT_MODE)) {
2367 2369 resp->status = NFS3ERR_INVAL;
2368 2370 goto out1;
2369 2371 }
2370 2372
2371 2373 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2372 2374 name = nfscmd_convname(ca, exi, args->where.name,
2373 2375 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2374 2376
2375 2377 if (name == NULL) {
2376 2378 resp->status = NFS3ERR_INVAL;
2377 2379 goto out1;
2378 2380 }
2379 2381
2380 2382 excl = EXCL;
2381 2383
2382 2384 mode = 0;
2383 2385
2384 2386 error = VOP_CREATE(dvp, name, &va, excl, mode,
2385 2387 &vp, cr, 0, NULL, NULL);
2386 2388
2387 2389 if (name != args->where.name)
2388 2390 kmem_free(name, MAXPATHLEN + 1);
2389 2391
2390 2392 dava.va_mask = AT_ALL;
2391 2393 davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2392 2394
2393 2395 /*
2394 2396 * Force modified data and metadata out to stable storage.
2395 2397 */
2396 2398 (void) VOP_FSYNC(dvp, 0, cr, NULL);
2397 2399
2398 2400 if (error)
2399 2401 goto out;
2400 2402
2401 2403 resp->status = NFS3_OK;
2402 2404
2403 2405 error = makefh3(&resp->resok.obj.handle, vp, exi);
2404 2406 if (error)
2405 2407 resp->resok.obj.handle_follows = FALSE;
2406 2408 else
2407 2409 resp->resok.obj.handle_follows = TRUE;
2408 2410
2409 2411 va.va_mask = AT_ALL;
2410 2412 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2411 2413
2412 2414 /*
2413 2415 * Force modified metadata out to stable storage.
2414 2416 *
2415 2417 * if a underlying vp exists, pass it to VOP_FSYNC
2416 2418 */
2417 2419 if (VOP_REALVP(vp, &realvp, NULL) == 0)
2418 2420 (void) VOP_FSYNC(realvp, FNODSYNC, cr, NULL);
2419 2421 else
2420 2422 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
2421 2423
2422 2424 VN_RELE(vp);
2423 2425
2424 2426 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
2425 2427 vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2426 2428 DTRACE_NFSV3_5(op__mknod__done, struct svc_req *, req,
2427 2429 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2428 2430 MKNOD3res *, resp);
2429 2431 VN_RELE(dvp);
2430 2432 return;
2431 2433
2432 2434 out:
2433 2435 if (curthread->t_flag & T_WOULDBLOCK) {
2434 2436 curthread->t_flag &= ~T_WOULDBLOCK;
2435 2437 resp->status = NFS3ERR_JUKEBOX;
2436 2438 } else
2437 2439 resp->status = puterrno3(error);
2438 2440 out1:
2439 2441 DTRACE_NFSV3_5(op__mknod__done, struct svc_req *, req,
2440 2442 cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
2441 2443 MKNOD3res *, resp);
2442 2444 if (dvp != NULL)
2443 2445 VN_RELE(dvp);
2444 2446 vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
2445 2447 }
2446 2448
2447 2449 void *
2448 2450 rfs3_mknod_getfh(MKNOD3args *args)
2449 2451 {
2450 2452
2451 2453 return (&args->where.dir);
2452 2454 }
2453 2455
2454 2456 void
2455 2457 rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi,
2456 2458 struct svc_req *req, cred_t *cr, bool_t ro)
2457 2459 {
2458 2460 int error = 0;
2459 2461 vnode_t *vp;
2460 2462 struct vattr *bvap;
2461 2463 struct vattr bva;
2462 2464 struct vattr *avap;
2463 2465 struct vattr ava;
2464 2466 vnode_t *targvp = NULL;
2465 2467 struct sockaddr *ca;
2466 2468 char *name = NULL;
2467 2469
2468 2470 bvap = NULL;
2469 2471 avap = NULL;
2470 2472
2471 2473 vp = nfs3_fhtovp(&args->object.dir, exi);
2472 2474
2473 2475 DTRACE_NFSV3_5(op__remove__start, struct svc_req *, req,
2474 2476 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
2475 2477 REMOVE3args *, args);
2476 2478
2477 2479 if (vp == NULL) {
2478 2480 error = ESTALE;
2479 2481 goto err;
2480 2482 }
2481 2483
2482 2484 bva.va_mask = AT_ALL;
2483 2485 bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2484 2486 avap = bvap;
2485 2487
2486 2488 if (vp->v_type != VDIR) {
2487 2489 resp->status = NFS3ERR_NOTDIR;
2488 2490 goto err1;
2489 2491 }
2490 2492
2491 2493 if (args->object.name == nfs3nametoolong) {
2492 2494 resp->status = NFS3ERR_NAMETOOLONG;
2493 2495 goto err1;
2494 2496 }
2495 2497
2496 2498 if (args->object.name == NULL || *(args->object.name) == '\0') {
2497 2499 resp->status = NFS3ERR_ACCES;
2498 2500 goto err1;
2499 2501 }
2500 2502
2501 2503 if (rdonly(ro, vp)) {
2502 2504 resp->status = NFS3ERR_ROFS;
2503 2505 goto err1;
2504 2506 }
2505 2507
2506 2508 if (is_system_labeled()) {
2507 2509 bslabel_t *clabel = req->rq_label;
2508 2510
2509 2511 ASSERT(clabel != NULL);
2510 2512 DTRACE_PROBE2(tx__rfs3__log__info__opremove__clabel, char *,
2511 2513 "got client label from request(1)", struct svc_req *, req);
2512 2514
2513 2515 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2514 2516 if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK,
2515 2517 exi)) {
2516 2518 resp->status = NFS3ERR_ACCES;
2517 2519 goto err1;
2518 2520 }
2519 2521 }
2520 2522 }
2521 2523
2522 2524 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2523 2525 name = nfscmd_convname(ca, exi, args->object.name,
2524 2526 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2525 2527
2526 2528 if (name == NULL) {
2527 2529 resp->status = NFS3ERR_INVAL;
2528 2530 goto err1;
2529 2531 }
2530 2532
2531 2533 /*
2532 2534 * Check for a conflict with a non-blocking mandatory share
2533 2535 * reservation and V4 delegations
2534 2536 */
2535 2537 error = VOP_LOOKUP(vp, name, &targvp, NULL, 0,
2536 2538 NULL, cr, NULL, NULL, NULL);
2537 2539 if (error != 0)
2538 2540 goto err;
2539 2541
2540 2542 if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
2541 2543 resp->status = NFS3ERR_JUKEBOX;
2542 2544 goto err1;
2543 2545 }
2544 2546
2545 2547 if (!nbl_need_check(targvp)) {
2546 2548 error = VOP_REMOVE(vp, name, cr, NULL, 0);
2547 2549 } else {
2548 2550 nbl_start_crit(targvp, RW_READER);
2549 2551 if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) {
2550 2552 error = EACCES;
2551 2553 } else {
2552 2554 error = VOP_REMOVE(vp, name, cr, NULL, 0);
2553 2555 }
2554 2556 nbl_end_crit(targvp);
2555 2557 }
2556 2558 VN_RELE(targvp);
2557 2559 targvp = NULL;
2558 2560
2559 2561 ava.va_mask = AT_ALL;
2560 2562 avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2561 2563
2562 2564 /*
2563 2565 * Force modified data and metadata out to stable storage.
2564 2566 */
2565 2567 (void) VOP_FSYNC(vp, 0, cr, NULL);
2566 2568
2567 2569 if (error)
2568 2570 goto err;
2569 2571
2570 2572 resp->status = NFS3_OK;
2571 2573 vattr_to_wcc_data(bvap, avap, &resp->resok.dir_wcc);
2572 2574 goto out;
2573 2575
2574 2576 err:
2575 2577 if (curthread->t_flag & T_WOULDBLOCK) {
2576 2578 curthread->t_flag &= ~T_WOULDBLOCK;
2577 2579 resp->status = NFS3ERR_JUKEBOX;
2578 2580 } else
2579 2581 resp->status = puterrno3(error);
2580 2582 err1:
2581 2583 vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
2582 2584 out:
2583 2585 DTRACE_NFSV3_5(op__remove__done, struct svc_req *, req,
2584 2586 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
2585 2587 REMOVE3res *, resp);
2586 2588
2587 2589 if (name != NULL && name != args->object.name)
2588 2590 kmem_free(name, MAXPATHLEN + 1);
2589 2591
2590 2592 if (vp != NULL)
2591 2593 VN_RELE(vp);
2592 2594 }
2593 2595
2594 2596 void *
2595 2597 rfs3_remove_getfh(REMOVE3args *args)
2596 2598 {
2597 2599
2598 2600 return (&args->object.dir);
2599 2601 }
2600 2602
2601 2603 void
2602 2604 rfs3_rmdir(RMDIR3args *args, RMDIR3res *resp, struct exportinfo *exi,
2603 2605 struct svc_req *req, cred_t *cr, bool_t ro)
2604 2606 {
2605 2607 int error;
2606 2608 vnode_t *vp;
2607 2609 struct vattr *bvap;
2608 2610 struct vattr bva;
2609 2611 struct vattr *avap;
2610 2612 struct vattr ava;
2611 2613 struct sockaddr *ca;
2612 2614 char *name = NULL;
2613 2615
2614 2616 bvap = NULL;
2615 2617 avap = NULL;
2616 2618
2617 2619 vp = nfs3_fhtovp(&args->object.dir, exi);
2618 2620
2619 2621 DTRACE_NFSV3_5(op__rmdir__start, struct svc_req *, req,
2620 2622 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
2621 2623 RMDIR3args *, args);
2622 2624
2623 2625 if (vp == NULL) {
2624 2626 error = ESTALE;
2625 2627 goto err;
2626 2628 }
2627 2629
2628 2630 bva.va_mask = AT_ALL;
2629 2631 bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2630 2632 avap = bvap;
2631 2633
2632 2634 if (vp->v_type != VDIR) {
2633 2635 resp->status = NFS3ERR_NOTDIR;
2634 2636 goto err1;
2635 2637 }
2636 2638
2637 2639 if (args->object.name == nfs3nametoolong) {
2638 2640 resp->status = NFS3ERR_NAMETOOLONG;
2639 2641 goto err1;
2640 2642 }
2641 2643
2642 2644 if (args->object.name == NULL || *(args->object.name) == '\0') {
2643 2645 resp->status = NFS3ERR_ACCES;
2644 2646 goto err1;
2645 2647 }
2646 2648
2647 2649 if (rdonly(ro, vp)) {
2648 2650 resp->status = NFS3ERR_ROFS;
2649 2651 goto err1;
2650 2652 }
2651 2653
2652 2654 if (is_system_labeled()) {
2653 2655 bslabel_t *clabel = req->rq_label;
2654 2656
2655 2657 ASSERT(clabel != NULL);
2656 2658 DTRACE_PROBE2(tx__rfs3__log__info__opremovedir__clabel, char *,
2657 2659 "got client label from request(1)", struct svc_req *, req);
2658 2660
2659 2661 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2660 2662 if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK,
2661 2663 exi)) {
2662 2664 resp->status = NFS3ERR_ACCES;
2663 2665 goto err1;
2664 2666 }
2665 2667 }
2666 2668 }
|
↓ open down ↓ |
2073 lines elided |
↑ open up ↑ |
2667 2669
2668 2670 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2669 2671 name = nfscmd_convname(ca, exi, args->object.name,
2670 2672 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2671 2673
2672 2674 if (name == NULL) {
2673 2675 resp->status = NFS3ERR_INVAL;
2674 2676 goto err1;
2675 2677 }
2676 2678
2679 + ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
2677 2680 error = VOP_RMDIR(vp, name, ZONE_ROOTVP(), cr, NULL, 0);
2678 2681
2679 2682 if (name != args->object.name)
2680 2683 kmem_free(name, MAXPATHLEN + 1);
2681 2684
2682 2685 ava.va_mask = AT_ALL;
2683 2686 avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2684 2687
2685 2688 /*
2686 2689 * Force modified data and metadata out to stable storage.
2687 2690 */
2688 2691 (void) VOP_FSYNC(vp, 0, cr, NULL);
2689 2692
2690 2693 if (error) {
2691 2694 /*
2692 2695 * System V defines rmdir to return EEXIST, not ENOTEMPTY,
2693 2696 * if the directory is not empty. A System V NFS server
2694 2697 * needs to map NFS3ERR_EXIST to NFS3ERR_NOTEMPTY to transmit
2695 2698 * over the wire.
2696 2699 */
2697 2700 if (error == EEXIST)
2698 2701 error = ENOTEMPTY;
2699 2702 goto err;
2700 2703 }
2701 2704
2702 2705 resp->status = NFS3_OK;
2703 2706 vattr_to_wcc_data(bvap, avap, &resp->resok.dir_wcc);
2704 2707 goto out;
2705 2708
2706 2709 err:
2707 2710 if (curthread->t_flag & T_WOULDBLOCK) {
2708 2711 curthread->t_flag &= ~T_WOULDBLOCK;
2709 2712 resp->status = NFS3ERR_JUKEBOX;
2710 2713 } else
2711 2714 resp->status = puterrno3(error);
2712 2715 err1:
2713 2716 vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
2714 2717 out:
2715 2718 DTRACE_NFSV3_5(op__rmdir__done, struct svc_req *, req,
2716 2719 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
2717 2720 RMDIR3res *, resp);
2718 2721 if (vp != NULL)
2719 2722 VN_RELE(vp);
2720 2723
2721 2724 }
2722 2725
2723 2726 void *
2724 2727 rfs3_rmdir_getfh(RMDIR3args *args)
2725 2728 {
2726 2729
2727 2730 return (&args->object.dir);
2728 2731 }
2729 2732
2730 2733 void
2731 2734 rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi,
2732 2735 struct svc_req *req, cred_t *cr, bool_t ro)
2733 2736 {
2734 2737 int error = 0;
2735 2738 vnode_t *fvp;
2736 2739 vnode_t *tvp;
2737 2740 vnode_t *targvp;
2738 2741 struct vattr *fbvap;
2739 2742 struct vattr fbva;
2740 2743 struct vattr *favap;
2741 2744 struct vattr fava;
2742 2745 struct vattr *tbvap;
2743 2746 struct vattr tbva;
2744 2747 struct vattr *tavap;
2745 2748 struct vattr tava;
2746 2749 nfs_fh3 *fh3;
2747 2750 struct exportinfo *to_exi;
2748 2751 vnode_t *srcvp = NULL;
2749 2752 bslabel_t *clabel;
2750 2753 struct sockaddr *ca;
2751 2754 char *name = NULL;
2752 2755 char *toname = NULL;
2753 2756
2754 2757 fbvap = NULL;
2755 2758 favap = NULL;
2756 2759 tbvap = NULL;
2757 2760 tavap = NULL;
2758 2761 tvp = NULL;
2759 2762
2760 2763 fvp = nfs3_fhtovp(&args->from.dir, exi);
2761 2764
2762 2765 DTRACE_NFSV3_5(op__rename__start, struct svc_req *, req,
2763 2766 cred_t *, cr, vnode_t *, fvp, struct exportinfo *, exi,
2764 2767 RENAME3args *, args);
2765 2768
2766 2769 if (fvp == NULL) {
2767 2770 error = ESTALE;
2768 2771 goto err;
2769 2772 }
2770 2773
2771 2774 if (is_system_labeled()) {
2772 2775 clabel = req->rq_label;
2773 2776 ASSERT(clabel != NULL);
2774 2777 DTRACE_PROBE2(tx__rfs3__log__info__oprename__clabel, char *,
2775 2778 "got client label from request(1)", struct svc_req *, req);
2776 2779
2777 2780 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2778 2781 if (!do_rfs_label_check(clabel, fvp, EQUALITY_CHECK,
2779 2782 exi)) {
2780 2783 resp->status = NFS3ERR_ACCES;
2781 2784 goto err1;
2782 2785 }
2783 2786 }
2784 2787 }
2785 2788
2786 2789 fbva.va_mask = AT_ALL;
2787 2790 fbvap = VOP_GETATTR(fvp, &fbva, 0, cr, NULL) ? NULL : &fbva;
2788 2791 favap = fbvap;
2789 2792
2790 2793 fh3 = &args->to.dir;
2791 2794 to_exi = checkexport(&fh3->fh3_fsid, FH3TOXFIDP(fh3));
2792 2795 if (to_exi == NULL) {
2793 2796 resp->status = NFS3ERR_ACCES;
2794 2797 goto err1;
2795 2798 }
2796 2799 exi_rele(to_exi);
2797 2800
2798 2801 if (to_exi != exi) {
2799 2802 resp->status = NFS3ERR_XDEV;
2800 2803 goto err1;
2801 2804 }
2802 2805
2803 2806 tvp = nfs3_fhtovp(&args->to.dir, exi);
2804 2807 if (tvp == NULL) {
2805 2808 error = ESTALE;
2806 2809 goto err;
2807 2810 }
2808 2811
2809 2812 tbva.va_mask = AT_ALL;
2810 2813 tbvap = VOP_GETATTR(tvp, &tbva, 0, cr, NULL) ? NULL : &tbva;
2811 2814 tavap = tbvap;
2812 2815
2813 2816 if (fvp->v_type != VDIR || tvp->v_type != VDIR) {
2814 2817 resp->status = NFS3ERR_NOTDIR;
2815 2818 goto err1;
2816 2819 }
2817 2820
2818 2821 if (args->from.name == nfs3nametoolong ||
2819 2822 args->to.name == nfs3nametoolong) {
2820 2823 resp->status = NFS3ERR_NAMETOOLONG;
2821 2824 goto err1;
2822 2825 }
2823 2826 if (args->from.name == NULL || *(args->from.name) == '\0' ||
2824 2827 args->to.name == NULL || *(args->to.name) == '\0') {
2825 2828 resp->status = NFS3ERR_ACCES;
2826 2829 goto err1;
2827 2830 }
2828 2831
2829 2832 if (rdonly(ro, tvp)) {
2830 2833 resp->status = NFS3ERR_ROFS;
2831 2834 goto err1;
2832 2835 }
2833 2836
2834 2837 if (is_system_labeled()) {
2835 2838 if (!blequal(&l_admin_low->tsl_label, clabel)) {
2836 2839 if (!do_rfs_label_check(clabel, tvp, EQUALITY_CHECK,
2837 2840 exi)) {
2838 2841 resp->status = NFS3ERR_ACCES;
2839 2842 goto err1;
2840 2843 }
2841 2844 }
2842 2845 }
2843 2846
2844 2847 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
2845 2848 name = nfscmd_convname(ca, exi, args->from.name,
2846 2849 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2847 2850
2848 2851 if (name == NULL) {
2849 2852 resp->status = NFS3ERR_INVAL;
2850 2853 goto err1;
2851 2854 }
2852 2855
2853 2856 toname = nfscmd_convname(ca, exi, args->to.name,
2854 2857 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
2855 2858
2856 2859 if (toname == NULL) {
2857 2860 resp->status = NFS3ERR_INVAL;
2858 2861 goto err1;
2859 2862 }
2860 2863
2861 2864 /*
2862 2865 * Check for a conflict with a non-blocking mandatory share
2863 2866 * reservation or V4 delegations.
2864 2867 */
2865 2868 error = VOP_LOOKUP(fvp, name, &srcvp, NULL, 0,
2866 2869 NULL, cr, NULL, NULL, NULL);
2867 2870 if (error != 0)
2868 2871 goto err;
2869 2872
2870 2873 /*
2871 2874 * If we rename a delegated file we should recall the
2872 2875 * delegation, since future opens should fail or would
2873 2876 * refer to a new file.
2874 2877 */
2875 2878 if (rfs4_check_delegated(FWRITE, srcvp, FALSE)) {
2876 2879 resp->status = NFS3ERR_JUKEBOX;
2877 2880 goto err1;
2878 2881 }
2879 2882
2880 2883 /*
2881 2884 * Check for renaming over a delegated file. Check nfs4_deleg_policy
2882 2885 * first to avoid VOP_LOOKUP if possible.
2883 2886 */
2884 2887 if (nfs4_get_deleg_policy() != SRV_NEVER_DELEGATE &&
2885 2888 VOP_LOOKUP(tvp, toname, &targvp, NULL, 0, NULL, cr,
2886 2889 NULL, NULL, NULL) == 0) {
2887 2890
2888 2891 if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
2889 2892 VN_RELE(targvp);
2890 2893 resp->status = NFS3ERR_JUKEBOX;
2891 2894 goto err1;
2892 2895 }
2893 2896 VN_RELE(targvp);
2894 2897 }
2895 2898
2896 2899 if (!nbl_need_check(srcvp)) {
2897 2900 error = VOP_RENAME(fvp, name, tvp, toname, cr, NULL, 0);
2898 2901 } else {
2899 2902 nbl_start_crit(srcvp, RW_READER);
2900 2903 if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL))
2901 2904 error = EACCES;
2902 2905 else
2903 2906 error = VOP_RENAME(fvp, name, tvp, toname, cr, NULL, 0);
2904 2907 nbl_end_crit(srcvp);
2905 2908 }
2906 2909 if (error == 0)
2907 2910 vn_renamepath(tvp, srcvp, args->to.name,
2908 2911 strlen(args->to.name));
2909 2912 VN_RELE(srcvp);
2910 2913 srcvp = NULL;
2911 2914
2912 2915 fava.va_mask = AT_ALL;
2913 2916 favap = VOP_GETATTR(fvp, &fava, 0, cr, NULL) ? NULL : &fava;
2914 2917 tava.va_mask = AT_ALL;
2915 2918 tavap = VOP_GETATTR(tvp, &tava, 0, cr, NULL) ? NULL : &tava;
2916 2919
2917 2920 /*
2918 2921 * Force modified data and metadata out to stable storage.
2919 2922 */
2920 2923 (void) VOP_FSYNC(fvp, 0, cr, NULL);
2921 2924 (void) VOP_FSYNC(tvp, 0, cr, NULL);
2922 2925
2923 2926 if (error)
2924 2927 goto err;
2925 2928
2926 2929 resp->status = NFS3_OK;
2927 2930 vattr_to_wcc_data(fbvap, favap, &resp->resok.fromdir_wcc);
2928 2931 vattr_to_wcc_data(tbvap, tavap, &resp->resok.todir_wcc);
2929 2932 goto out;
2930 2933
2931 2934 err:
2932 2935 if (curthread->t_flag & T_WOULDBLOCK) {
2933 2936 curthread->t_flag &= ~T_WOULDBLOCK;
2934 2937 resp->status = NFS3ERR_JUKEBOX;
2935 2938 } else {
2936 2939 resp->status = puterrno3(error);
2937 2940 }
2938 2941 err1:
2939 2942 vattr_to_wcc_data(fbvap, favap, &resp->resfail.fromdir_wcc);
2940 2943 vattr_to_wcc_data(tbvap, tavap, &resp->resfail.todir_wcc);
2941 2944
2942 2945 out:
2943 2946 if (name != NULL && name != args->from.name)
2944 2947 kmem_free(name, MAXPATHLEN + 1);
2945 2948 if (toname != NULL && toname != args->to.name)
2946 2949 kmem_free(toname, MAXPATHLEN + 1);
2947 2950
2948 2951 DTRACE_NFSV3_5(op__rename__done, struct svc_req *, req,
2949 2952 cred_t *, cr, vnode_t *, fvp, struct exportinfo *, exi,
2950 2953 RENAME3res *, resp);
2951 2954 if (fvp != NULL)
2952 2955 VN_RELE(fvp);
2953 2956 if (tvp != NULL)
2954 2957 VN_RELE(tvp);
2955 2958 }
2956 2959
2957 2960 void *
2958 2961 rfs3_rename_getfh(RENAME3args *args)
2959 2962 {
2960 2963
2961 2964 return (&args->from.dir);
2962 2965 }
2963 2966
2964 2967 void
2965 2968 rfs3_link(LINK3args *args, LINK3res *resp, struct exportinfo *exi,
2966 2969 struct svc_req *req, cred_t *cr, bool_t ro)
2967 2970 {
2968 2971 int error;
2969 2972 vnode_t *vp;
2970 2973 vnode_t *dvp;
2971 2974 struct vattr *vap;
2972 2975 struct vattr va;
2973 2976 struct vattr *bvap;
2974 2977 struct vattr bva;
2975 2978 struct vattr *avap;
2976 2979 struct vattr ava;
2977 2980 nfs_fh3 *fh3;
2978 2981 struct exportinfo *to_exi;
2979 2982 bslabel_t *clabel;
2980 2983 struct sockaddr *ca;
2981 2984 char *name = NULL;
2982 2985
2983 2986 vap = NULL;
2984 2987 bvap = NULL;
2985 2988 avap = NULL;
2986 2989 dvp = NULL;
2987 2990
2988 2991 vp = nfs3_fhtovp(&args->file, exi);
2989 2992
2990 2993 DTRACE_NFSV3_5(op__link__start, struct svc_req *, req,
2991 2994 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
2992 2995 LINK3args *, args);
2993 2996
2994 2997 if (vp == NULL) {
2995 2998 error = ESTALE;
2996 2999 goto out;
2997 3000 }
2998 3001
2999 3002 va.va_mask = AT_ALL;
3000 3003 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3001 3004
3002 3005 fh3 = &args->link.dir;
3003 3006 to_exi = checkexport(&fh3->fh3_fsid, FH3TOXFIDP(fh3));
3004 3007 if (to_exi == NULL) {
3005 3008 resp->status = NFS3ERR_ACCES;
3006 3009 goto out1;
3007 3010 }
3008 3011 exi_rele(to_exi);
3009 3012
3010 3013 if (to_exi != exi) {
3011 3014 resp->status = NFS3ERR_XDEV;
3012 3015 goto out1;
3013 3016 }
3014 3017
3015 3018 if (is_system_labeled()) {
3016 3019 clabel = req->rq_label;
3017 3020
3018 3021 ASSERT(clabel != NULL);
3019 3022 DTRACE_PROBE2(tx__rfs3__log__info__oplink__clabel, char *,
3020 3023 "got client label from request(1)", struct svc_req *, req);
3021 3024
3022 3025 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3023 3026 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
3024 3027 exi)) {
3025 3028 resp->status = NFS3ERR_ACCES;
3026 3029 goto out1;
3027 3030 }
3028 3031 }
3029 3032 }
3030 3033
3031 3034 dvp = nfs3_fhtovp(&args->link.dir, exi);
3032 3035 if (dvp == NULL) {
3033 3036 error = ESTALE;
3034 3037 goto out;
3035 3038 }
3036 3039
3037 3040 bva.va_mask = AT_ALL;
3038 3041 bvap = VOP_GETATTR(dvp, &bva, 0, cr, NULL) ? NULL : &bva;
3039 3042
3040 3043 if (dvp->v_type != VDIR) {
3041 3044 resp->status = NFS3ERR_NOTDIR;
3042 3045 goto out1;
3043 3046 }
3044 3047
3045 3048 if (args->link.name == nfs3nametoolong) {
3046 3049 resp->status = NFS3ERR_NAMETOOLONG;
3047 3050 goto out1;
3048 3051 }
3049 3052
3050 3053 if (args->link.name == NULL || *(args->link.name) == '\0') {
3051 3054 resp->status = NFS3ERR_ACCES;
3052 3055 goto out1;
3053 3056 }
3054 3057
3055 3058 if (rdonly(ro, dvp)) {
3056 3059 resp->status = NFS3ERR_ROFS;
3057 3060 goto out1;
3058 3061 }
3059 3062
3060 3063 if (is_system_labeled()) {
3061 3064 DTRACE_PROBE2(tx__rfs3__log__info__oplinkdir__clabel, char *,
3062 3065 "got client label from request(1)", struct svc_req *, req);
3063 3066
3064 3067 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3065 3068 if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK,
3066 3069 exi)) {
3067 3070 resp->status = NFS3ERR_ACCES;
3068 3071 goto out1;
3069 3072 }
3070 3073 }
3071 3074 }
3072 3075
3073 3076 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3074 3077 name = nfscmd_convname(ca, exi, args->link.name,
3075 3078 NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
3076 3079
3077 3080 if (name == NULL) {
3078 3081 resp->status = NFS3ERR_SERVERFAULT;
3079 3082 goto out1;
3080 3083 }
3081 3084
3082 3085 error = VOP_LINK(dvp, vp, name, cr, NULL, 0);
3083 3086
3084 3087 va.va_mask = AT_ALL;
3085 3088 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3086 3089 ava.va_mask = AT_ALL;
3087 3090 avap = VOP_GETATTR(dvp, &ava, 0, cr, NULL) ? NULL : &ava;
3088 3091
3089 3092 /*
3090 3093 * Force modified data and metadata out to stable storage.
3091 3094 */
3092 3095 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3093 3096 (void) VOP_FSYNC(dvp, 0, cr, NULL);
3094 3097
3095 3098 if (error)
3096 3099 goto out;
3097 3100
3098 3101 VN_RELE(dvp);
3099 3102
3100 3103 resp->status = NFS3_OK;
3101 3104 vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
3102 3105 vattr_to_wcc_data(bvap, avap, &resp->resok.linkdir_wcc);
3103 3106
3104 3107 DTRACE_NFSV3_5(op__link__done, struct svc_req *, req,
3105 3108 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3106 3109 LINK3res *, resp);
3107 3110
3108 3111 VN_RELE(vp);
3109 3112
3110 3113 return;
3111 3114
3112 3115 out:
3113 3116 if (curthread->t_flag & T_WOULDBLOCK) {
3114 3117 curthread->t_flag &= ~T_WOULDBLOCK;
3115 3118 resp->status = NFS3ERR_JUKEBOX;
3116 3119 } else
3117 3120 resp->status = puterrno3(error);
3118 3121 out1:
3119 3122 if (name != NULL && name != args->link.name)
3120 3123 kmem_free(name, MAXPATHLEN + 1);
3121 3124
3122 3125 DTRACE_NFSV3_5(op__link__done, struct svc_req *, req,
3123 3126 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3124 3127 LINK3res *, resp);
3125 3128
3126 3129 if (vp != NULL)
3127 3130 VN_RELE(vp);
3128 3131 if (dvp != NULL)
3129 3132 VN_RELE(dvp);
3130 3133 vattr_to_post_op_attr(vap, &resp->resfail.file_attributes);
3131 3134 vattr_to_wcc_data(bvap, avap, &resp->resfail.linkdir_wcc);
3132 3135 }
3133 3136
3134 3137 void *
3135 3138 rfs3_link_getfh(LINK3args *args)
3136 3139 {
3137 3140
3138 3141 return (&args->file);
3139 3142 }
3140 3143
3141 3144 /*
3142 3145 * This macro defines the size of a response which contains attribute
3143 3146 * information and one directory entry (whose length is specified by
3144 3147 * the macro parameter). If the incoming request is larger than this,
3145 3148 * then we are guaranteed to be able to return at one directory entry
3146 3149 * if one exists. Therefore, we do not need to check for
3147 3150 * NFS3ERR_TOOSMALL if the requested size is larger then this. If it
3148 3151 * is not, then we need to check to make sure that this error does not
3149 3152 * need to be returned.
3150 3153 *
3151 3154 * NFS3_READDIR_MIN_COUNT is comprised of following :
3152 3155 *
3153 3156 * status - 1 * BYTES_PER_XDR_UNIT
3154 3157 * attr. flag - 1 * BYTES_PER_XDR_UNIT
3155 3158 * cookie verifier - 2 * BYTES_PER_XDR_UNIT
3156 3159 * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
3157 3160 * boolean - 1 * BYTES_PER_XDR_UNIT
3158 3161 * file id - 2 * BYTES_PER_XDR_UNIT
3159 3162 * directory name length - 1 * BYTES_PER_XDR_UNIT
3160 3163 * cookie - 2 * BYTES_PER_XDR_UNIT
3161 3164 * end of list - 1 * BYTES_PER_XDR_UNIT
3162 3165 * end of file - 1 * BYTES_PER_XDR_UNIT
3163 3166 * Name length of directory to the nearest byte
3164 3167 */
3165 3168
3166 3169 #define NFS3_READDIR_MIN_COUNT(length) \
3167 3170 ((1 + 1 + 2 + NFS3_SIZEOF_FATTR3 + 1 + 2 + 1 + 2 + 1 + 1) * \
3168 3171 BYTES_PER_XDR_UNIT + roundup((length), BYTES_PER_XDR_UNIT))
3169 3172
3170 3173 /* ARGSUSED */
3171 3174 void
3172 3175 rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi,
3173 3176 struct svc_req *req, cred_t *cr, bool_t ro)
3174 3177 {
3175 3178 int error;
3176 3179 vnode_t *vp;
3177 3180 struct vattr *vap;
3178 3181 struct vattr va;
3179 3182 struct iovec iov;
3180 3183 struct uio uio;
3181 3184 char *data;
3182 3185 int iseof;
3183 3186 int bufsize;
3184 3187 int namlen;
3185 3188 uint_t count;
3186 3189 struct sockaddr *ca;
3187 3190
3188 3191 vap = NULL;
3189 3192
3190 3193 vp = nfs3_fhtovp(&args->dir, exi);
3191 3194
3192 3195 DTRACE_NFSV3_5(op__readdir__start, struct svc_req *, req,
3193 3196 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3194 3197 READDIR3args *, args);
3195 3198
3196 3199 if (vp == NULL) {
3197 3200 error = ESTALE;
3198 3201 goto out;
3199 3202 }
3200 3203
3201 3204 if (is_system_labeled()) {
3202 3205 bslabel_t *clabel = req->rq_label;
3203 3206
3204 3207 ASSERT(clabel != NULL);
3205 3208 DTRACE_PROBE2(tx__rfs3__log__info__opreaddir__clabel, char *,
3206 3209 "got client label from request(1)", struct svc_req *, req);
3207 3210
3208 3211 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3209 3212 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
3210 3213 exi)) {
3211 3214 resp->status = NFS3ERR_ACCES;
3212 3215 goto out1;
3213 3216 }
3214 3217 }
3215 3218 }
3216 3219
3217 3220 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
3218 3221
3219 3222 va.va_mask = AT_ALL;
3220 3223 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3221 3224
3222 3225 if (vp->v_type != VDIR) {
3223 3226 resp->status = NFS3ERR_NOTDIR;
3224 3227 goto out1;
3225 3228 }
3226 3229
3227 3230 error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
3228 3231 if (error)
3229 3232 goto out;
3230 3233
3231 3234 /*
3232 3235 * Now don't allow arbitrary count to alloc;
3233 3236 * allow the maximum not to exceed rfs3_tsize()
3234 3237 */
3235 3238 if (args->count > rfs3_tsize(req))
3236 3239 args->count = rfs3_tsize(req);
3237 3240
3238 3241 /*
3239 3242 * Make sure that there is room to read at least one entry
3240 3243 * if any are available.
3241 3244 */
3242 3245 if (args->count < DIRENT64_RECLEN(MAXNAMELEN))
3243 3246 count = DIRENT64_RECLEN(MAXNAMELEN);
3244 3247 else
3245 3248 count = args->count;
3246 3249
3247 3250 data = kmem_alloc(count, KM_SLEEP);
3248 3251
3249 3252 iov.iov_base = data;
3250 3253 iov.iov_len = count;
3251 3254 uio.uio_iov = &iov;
3252 3255 uio.uio_iovcnt = 1;
3253 3256 uio.uio_segflg = UIO_SYSSPACE;
3254 3257 uio.uio_extflg = UIO_COPY_CACHED;
3255 3258 uio.uio_loffset = (offset_t)args->cookie;
3256 3259 uio.uio_resid = count;
3257 3260
3258 3261 error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
3259 3262
3260 3263 va.va_mask = AT_ALL;
3261 3264 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3262 3265
3263 3266 if (error) {
3264 3267 kmem_free(data, count);
3265 3268 goto out;
3266 3269 }
3267 3270
3268 3271 /*
3269 3272 * If the count was not large enough to be able to guarantee
3270 3273 * to be able to return at least one entry, then need to
3271 3274 * check to see if NFS3ERR_TOOSMALL should be returned.
3272 3275 */
3273 3276 if (args->count < NFS3_READDIR_MIN_COUNT(MAXNAMELEN)) {
3274 3277 /*
3275 3278 * bufsize is used to keep track of the size of the response.
3276 3279 * It is primed with:
3277 3280 * 1 for the status +
3278 3281 * 1 for the dir_attributes.attributes boolean +
3279 3282 * 2 for the cookie verifier
3280 3283 * all times BYTES_PER_XDR_UNIT to convert from XDR units
3281 3284 * to bytes. If there are directory attributes to be
3282 3285 * returned, then:
3283 3286 * NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
3284 3287 * time BYTES_PER_XDR_UNIT is added to account for them.
3285 3288 */
3286 3289 bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
3287 3290 if (vap != NULL)
3288 3291 bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
3289 3292 /*
3290 3293 * An entry is composed of:
3291 3294 * 1 for the true/false list indicator +
3292 3295 * 2 for the fileid +
3293 3296 * 1 for the length of the name +
3294 3297 * 2 for the cookie +
3295 3298 * all times BYTES_PER_XDR_UNIT to convert from
3296 3299 * XDR units to bytes, plus the length of the name
3297 3300 * rounded up to the nearest BYTES_PER_XDR_UNIT.
3298 3301 */
3299 3302 if (count != uio.uio_resid) {
3300 3303 namlen = strlen(((struct dirent64 *)data)->d_name);
3301 3304 bufsize += (1 + 2 + 1 + 2) * BYTES_PER_XDR_UNIT +
3302 3305 roundup(namlen, BYTES_PER_XDR_UNIT);
3303 3306 }
3304 3307 /*
3305 3308 * We need to check to see if the number of bytes left
3306 3309 * to go into the buffer will actually fit into the
3307 3310 * buffer. This is calculated as the size of this
3308 3311 * entry plus:
3309 3312 * 1 for the true/false list indicator +
3310 3313 * 1 for the eof indicator
3311 3314 * times BYTES_PER_XDR_UNIT to convert from from
3312 3315 * XDR units to bytes.
3313 3316 */
3314 3317 bufsize += (1 + 1) * BYTES_PER_XDR_UNIT;
3315 3318 if (bufsize > args->count) {
3316 3319 kmem_free(data, count);
3317 3320 resp->status = NFS3ERR_TOOSMALL;
3318 3321 goto out1;
3319 3322 }
3320 3323 }
3321 3324
3322 3325 /*
3323 3326 * Have a valid readir buffer for the native character
3324 3327 * set. Need to check if a conversion is necessary and
3325 3328 * potentially rewrite the whole buffer. Note that if the
3326 3329 * conversion expands names enough, the structure may not
3327 3330 * fit. In this case, we need to drop entries until if fits
3328 3331 * and patch the counts in order that the next readdir will
3329 3332 * get the correct entries.
3330 3333 */
3331 3334 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3332 3335 data = nfscmd_convdirent(ca, exi, data, count, &resp->status);
3333 3336
3334 3337
3335 3338 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3336 3339
3337 3340 #if 0 /* notyet */
3338 3341 /*
3339 3342 * Don't do this. It causes local disk writes when just
3340 3343 * reading the file and the overhead is deemed larger
3341 3344 * than the benefit.
3342 3345 */
3343 3346 /*
3344 3347 * Force modified metadata out to stable storage.
3345 3348 */
3346 3349 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3347 3350 #endif
3348 3351
3349 3352 resp->status = NFS3_OK;
3350 3353 vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
3351 3354 resp->resok.cookieverf = 0;
3352 3355 resp->resok.reply.entries = (entry3 *)data;
3353 3356 resp->resok.reply.eof = iseof;
3354 3357 resp->resok.size = count - uio.uio_resid;
3355 3358 resp->resok.count = args->count;
3356 3359 resp->resok.freecount = count;
3357 3360
3358 3361 DTRACE_NFSV3_5(op__readdir__done, struct svc_req *, req,
3359 3362 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3360 3363 READDIR3res *, resp);
3361 3364
3362 3365 VN_RELE(vp);
3363 3366
3364 3367 return;
3365 3368
3366 3369 out:
3367 3370 if (curthread->t_flag & T_WOULDBLOCK) {
3368 3371 curthread->t_flag &= ~T_WOULDBLOCK;
3369 3372 resp->status = NFS3ERR_JUKEBOX;
3370 3373 } else
3371 3374 resp->status = puterrno3(error);
3372 3375 out1:
3373 3376 vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3374 3377
3375 3378 DTRACE_NFSV3_5(op__readdir__done, struct svc_req *, req,
3376 3379 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3377 3380 READDIR3res *, resp);
3378 3381
3379 3382 if (vp != NULL) {
3380 3383 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3381 3384 VN_RELE(vp);
3382 3385 }
3383 3386 vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3384 3387 }
3385 3388
3386 3389 void *
3387 3390 rfs3_readdir_getfh(READDIR3args *args)
3388 3391 {
3389 3392
3390 3393 return (&args->dir);
3391 3394 }
3392 3395
3393 3396 void
3394 3397 rfs3_readdir_free(READDIR3res *resp)
3395 3398 {
3396 3399
3397 3400 if (resp->status == NFS3_OK)
3398 3401 kmem_free(resp->resok.reply.entries, resp->resok.freecount);
3399 3402 }
3400 3403
3401 3404 #ifdef nextdp
3402 3405 #undef nextdp
3403 3406 #endif
3404 3407 #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
3405 3408
3406 3409 /*
3407 3410 * This macro computes the size of a response which contains
3408 3411 * one directory entry including the attributes as well as file handle.
3409 3412 * If the incoming request is larger than this, then we are guaranteed to be
3410 3413 * able to return at least one more directory entry if one exists.
3411 3414 *
3412 3415 * NFS3_READDIRPLUS_ENTRY is made up of the following:
3413 3416 *
3414 3417 * boolean - 1 * BYTES_PER_XDR_UNIT
3415 3418 * file id - 2 * BYTES_PER_XDR_UNIT
3416 3419 * directory name length - 1 * BYTES_PER_XDR_UNIT
3417 3420 * cookie - 2 * BYTES_PER_XDR_UNIT
3418 3421 * attribute flag - 1 * BYTES_PER_XDR_UNIT
3419 3422 * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
3420 3423 * status byte for file handle - 1 * BYTES_PER_XDR_UNIT
3421 3424 * length of a file handle - 1 * BYTES_PER_XDR_UNIT
3422 3425 * Maximum length of a file handle (NFS3_MAXFHSIZE)
3423 3426 * name length of the entry to the nearest bytes
3424 3427 */
3425 3428 #define NFS3_READDIRPLUS_ENTRY(namelen) \
3426 3429 ((1 + 2 + 1 + 2 + 1 + NFS3_SIZEOF_FATTR3 + 1 + 1) * \
3427 3430 BYTES_PER_XDR_UNIT + \
3428 3431 NFS3_MAXFHSIZE + roundup(namelen, BYTES_PER_XDR_UNIT))
3429 3432
3430 3433 static int rfs3_readdir_unit = MAXBSIZE;
3431 3434
3432 3435 /* ARGSUSED */
3433 3436 void
3434 3437 rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp,
3435 3438 struct exportinfo *exi, struct svc_req *req, cred_t *cr, bool_t ro)
3436 3439 {
3437 3440 int error;
3438 3441 vnode_t *vp;
3439 3442 struct vattr *vap;
3440 3443 struct vattr va;
3441 3444 struct iovec iov;
3442 3445 struct uio uio;
3443 3446 char *data;
3444 3447 int iseof;
3445 3448 struct dirent64 *dp;
3446 3449 vnode_t *nvp;
3447 3450 struct vattr *nvap;
3448 3451 struct vattr nva;
3449 3452 entryplus3_info *infop = NULL;
3450 3453 int size = 0;
3451 3454 int nents = 0;
3452 3455 int bufsize = 0;
3453 3456 int entrysize = 0;
3454 3457 int tofit = 0;
3455 3458 int rd_unit = rfs3_readdir_unit;
3456 3459 int prev_len;
3457 3460 int space_left;
3458 3461 int i;
3459 3462 uint_t *namlen = NULL;
3460 3463 char *ndata = NULL;
3461 3464 struct sockaddr *ca;
3462 3465 size_t ret;
3463 3466
3464 3467 vap = NULL;
3465 3468
3466 3469 vp = nfs3_fhtovp(&args->dir, exi);
3467 3470
3468 3471 DTRACE_NFSV3_5(op__readdirplus__start, struct svc_req *, req,
3469 3472 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3470 3473 READDIRPLUS3args *, args);
3471 3474
3472 3475 if (vp == NULL) {
3473 3476 error = ESTALE;
3474 3477 goto out;
3475 3478 }
3476 3479
3477 3480 if (is_system_labeled()) {
3478 3481 bslabel_t *clabel = req->rq_label;
3479 3482
3480 3483 ASSERT(clabel != NULL);
3481 3484 DTRACE_PROBE2(tx__rfs3__log__info__opreaddirplus__clabel,
3482 3485 char *, "got client label from request(1)",
3483 3486 struct svc_req *, req);
3484 3487
3485 3488 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3486 3489 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
3487 3490 exi)) {
3488 3491 resp->status = NFS3ERR_ACCES;
3489 3492 goto out1;
3490 3493 }
3491 3494 }
3492 3495 }
3493 3496
3494 3497 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
3495 3498
3496 3499 va.va_mask = AT_ALL;
3497 3500 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3498 3501
3499 3502 if (vp->v_type != VDIR) {
3500 3503 error = ENOTDIR;
3501 3504 goto out;
3502 3505 }
3503 3506
3504 3507 error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
3505 3508 if (error)
3506 3509 goto out;
3507 3510
3508 3511 /*
3509 3512 * Don't allow arbitrary counts for allocation
3510 3513 */
3511 3514 if (args->maxcount > rfs3_tsize(req))
3512 3515 args->maxcount = rfs3_tsize(req);
3513 3516
3514 3517 /*
3515 3518 * Make sure that there is room to read at least one entry
3516 3519 * if any are available
3517 3520 */
3518 3521 args->dircount = MIN(args->dircount, args->maxcount);
3519 3522
3520 3523 if (args->dircount < DIRENT64_RECLEN(MAXNAMELEN))
3521 3524 args->dircount = DIRENT64_RECLEN(MAXNAMELEN);
3522 3525
3523 3526 /*
3524 3527 * This allocation relies on a minimum directory entry
3525 3528 * being roughly 24 bytes. Therefore, the namlen array
3526 3529 * will have enough space based on the maximum number of
3527 3530 * entries to read.
3528 3531 */
3529 3532 namlen = kmem_alloc(args->dircount, KM_SLEEP);
3530 3533
3531 3534 space_left = args->dircount;
3532 3535 data = kmem_alloc(args->dircount, KM_SLEEP);
3533 3536 dp = (struct dirent64 *)data;
3534 3537 uio.uio_iov = &iov;
3535 3538 uio.uio_iovcnt = 1;
3536 3539 uio.uio_segflg = UIO_SYSSPACE;
3537 3540 uio.uio_extflg = UIO_COPY_CACHED;
3538 3541 uio.uio_loffset = (offset_t)args->cookie;
3539 3542
3540 3543 /*
3541 3544 * bufsize is used to keep track of the size of the response as we
3542 3545 * get post op attributes and filehandles for each entry. This is
3543 3546 * an optimization as the server may have read more entries than will
3544 3547 * fit in the buffer specified by maxcount. We stop calculating
3545 3548 * post op attributes and filehandles once we have exceeded maxcount.
3546 3549 * This will minimize the effect of truncation.
3547 3550 *
3548 3551 * It is primed with:
3549 3552 * 1 for the status +
3550 3553 * 1 for the dir_attributes.attributes boolean +
3551 3554 * 2 for the cookie verifier
3552 3555 * all times BYTES_PER_XDR_UNIT to convert from XDR units
3553 3556 * to bytes. If there are directory attributes to be
3554 3557 * returned, then:
3555 3558 * NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
3556 3559 * time BYTES_PER_XDR_UNIT is added to account for them.
3557 3560 */
3558 3561 bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
3559 3562 if (vap != NULL)
3560 3563 bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
3561 3564
3562 3565 getmoredents:
3563 3566 /*
3564 3567 * Here we make a check so that our read unit is not larger than
3565 3568 * the space left in the buffer.
3566 3569 */
3567 3570 rd_unit = MIN(rd_unit, space_left);
3568 3571 iov.iov_base = (char *)dp;
3569 3572 iov.iov_len = rd_unit;
3570 3573 uio.uio_resid = rd_unit;
3571 3574 prev_len = rd_unit;
3572 3575
3573 3576 error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
3574 3577
3575 3578 if (error) {
3576 3579 kmem_free(data, args->dircount);
3577 3580 goto out;
3578 3581 }
3579 3582
3580 3583 if (uio.uio_resid == prev_len && !iseof) {
3581 3584 if (nents == 0) {
3582 3585 kmem_free(data, args->dircount);
3583 3586 resp->status = NFS3ERR_TOOSMALL;
3584 3587 goto out1;
3585 3588 }
3586 3589
3587 3590 /*
3588 3591 * We could not get any more entries, so get the attributes
3589 3592 * and filehandle for the entries already obtained.
3590 3593 */
3591 3594 goto good;
3592 3595 }
3593 3596
3594 3597 /*
3595 3598 * We estimate the size of the response by assuming the
3596 3599 * entry exists and attributes and filehandle are also valid
3597 3600 */
3598 3601 for (size = prev_len - uio.uio_resid;
3599 3602 size > 0;
3600 3603 size -= dp->d_reclen, dp = nextdp(dp)) {
3601 3604
3602 3605 if (dp->d_ino == 0) {
3603 3606 nents++;
3604 3607 continue;
3605 3608 }
3606 3609
3607 3610 namlen[nents] = strlen(dp->d_name);
3608 3611 entrysize = NFS3_READDIRPLUS_ENTRY(namlen[nents]);
3609 3612
3610 3613 /*
3611 3614 * We need to check to see if the number of bytes left
3612 3615 * to go into the buffer will actually fit into the
3613 3616 * buffer. This is calculated as the size of this
3614 3617 * entry plus:
3615 3618 * 1 for the true/false list indicator +
3616 3619 * 1 for the eof indicator
3617 3620 * times BYTES_PER_XDR_UNIT to convert from XDR units
3618 3621 * to bytes.
3619 3622 *
3620 3623 * Also check the dircount limit against the first entry read
3621 3624 *
3622 3625 */
3623 3626 tofit = entrysize + (1 + 1) * BYTES_PER_XDR_UNIT;
3624 3627 if (bufsize + tofit > args->maxcount) {
3625 3628 /*
3626 3629 * We make a check here to see if this was the
3627 3630 * first entry being measured. If so, then maxcount
3628 3631 * was too small to begin with and so we need to
3629 3632 * return with NFS3ERR_TOOSMALL.
3630 3633 */
3631 3634 if (nents == 0) {
3632 3635 kmem_free(data, args->dircount);
3633 3636 resp->status = NFS3ERR_TOOSMALL;
3634 3637 goto out1;
3635 3638 }
3636 3639 iseof = FALSE;
3637 3640 goto good;
3638 3641 }
3639 3642 bufsize += entrysize;
3640 3643 nents++;
3641 3644 }
3642 3645
3643 3646 /*
3644 3647 * If there is enough room to fit at least 1 more entry including
3645 3648 * post op attributes and filehandle in the buffer AND that we haven't
3646 3649 * exceeded dircount then go back and get some more.
3647 3650 */
3648 3651 if (!iseof &&
3649 3652 (args->maxcount - bufsize) >= NFS3_READDIRPLUS_ENTRY(MAXNAMELEN)) {
3650 3653 space_left -= (prev_len - uio.uio_resid);
3651 3654 if (space_left >= DIRENT64_RECLEN(MAXNAMELEN))
3652 3655 goto getmoredents;
3653 3656
3654 3657 /* else, fall through */
3655 3658 }
3656 3659 good:
3657 3660 va.va_mask = AT_ALL;
3658 3661 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3659 3662
3660 3663 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3661 3664
3662 3665 infop = kmem_alloc(nents * sizeof (struct entryplus3_info), KM_SLEEP);
3663 3666 resp->resok.infop = infop;
3664 3667
3665 3668 dp = (struct dirent64 *)data;
3666 3669 for (i = 0; i < nents; i++) {
3667 3670
3668 3671 if (dp->d_ino == 0) {
3669 3672 infop[i].attr.attributes = FALSE;
3670 3673 infop[i].fh.handle_follows = FALSE;
3671 3674 dp = nextdp(dp);
3672 3675 continue;
3673 3676 }
3674 3677
3675 3678 infop[i].namelen = namlen[i];
3676 3679
3677 3680 error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
3678 3681 NULL, NULL, NULL);
3679 3682 if (error) {
3680 3683 infop[i].attr.attributes = FALSE;
3681 3684 infop[i].fh.handle_follows = FALSE;
3682 3685 dp = nextdp(dp);
3683 3686 continue;
3684 3687 }
3685 3688
3686 3689 nva.va_mask = AT_ALL;
3687 3690 nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
3688 3691
3689 3692 /* Lie about the object type for a referral */
3690 3693 if (vn_is_nfs_reparse(nvp, cr))
3691 3694 nvap->va_type = VLNK;
3692 3695
3693 3696 if (vn_ismntpt(nvp)) {
3694 3697 infop[i].attr.attributes = FALSE;
3695 3698 infop[i].fh.handle_follows = FALSE;
3696 3699 } else {
3697 3700 vattr_to_post_op_attr(nvap, &infop[i].attr);
3698 3701
3699 3702 error = makefh3(&infop[i].fh.handle, nvp, exi);
3700 3703 if (!error)
3701 3704 infop[i].fh.handle_follows = TRUE;
3702 3705 else
3703 3706 infop[i].fh.handle_follows = FALSE;
3704 3707 }
3705 3708
3706 3709 VN_RELE(nvp);
3707 3710 dp = nextdp(dp);
3708 3711 }
3709 3712
3710 3713 ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3711 3714 ret = nfscmd_convdirplus(ca, exi, data, nents, args->dircount, &ndata);
3712 3715 if (ndata == NULL)
3713 3716 ndata = data;
3714 3717
3715 3718 if (ret > 0) {
3716 3719 /*
3717 3720 * We had to drop one or more entries in order to fit
3718 3721 * during the character conversion. We need to patch
3719 3722 * up the size and eof info.
3720 3723 */
3721 3724 if (iseof)
3722 3725 iseof = FALSE;
3723 3726
3724 3727 ret = nfscmd_dropped_entrysize((struct dirent64 *)data,
3725 3728 nents, ret);
3726 3729 }
3727 3730
3728 3731
3729 3732 #if 0 /* notyet */
3730 3733 /*
3731 3734 * Don't do this. It causes local disk writes when just
3732 3735 * reading the file and the overhead is deemed larger
3733 3736 * than the benefit.
3734 3737 */
3735 3738 /*
3736 3739 * Force modified metadata out to stable storage.
3737 3740 */
3738 3741 (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3739 3742 #endif
3740 3743
3741 3744 kmem_free(namlen, args->dircount);
3742 3745
3743 3746 resp->status = NFS3_OK;
3744 3747 vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
3745 3748 resp->resok.cookieverf = 0;
3746 3749 resp->resok.reply.entries = (entryplus3 *)ndata;
3747 3750 resp->resok.reply.eof = iseof;
3748 3751 resp->resok.size = nents;
3749 3752 resp->resok.count = args->dircount - ret;
3750 3753 resp->resok.maxcount = args->maxcount;
3751 3754
3752 3755 DTRACE_NFSV3_5(op__readdirplus__done, struct svc_req *, req,
3753 3756 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3754 3757 READDIRPLUS3res *, resp);
3755 3758
3756 3759 VN_RELE(vp);
3757 3760
3758 3761 return;
3759 3762
3760 3763 out:
3761 3764 if (curthread->t_flag & T_WOULDBLOCK) {
3762 3765 curthread->t_flag &= ~T_WOULDBLOCK;
3763 3766 resp->status = NFS3ERR_JUKEBOX;
3764 3767 } else {
3765 3768 resp->status = puterrno3(error);
3766 3769 }
3767 3770 out1:
3768 3771 vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3769 3772
3770 3773 DTRACE_NFSV3_5(op__readdirplus__done, struct svc_req *, req,
3771 3774 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3772 3775 READDIRPLUS3res *, resp);
3773 3776
3774 3777 if (vp != NULL) {
3775 3778 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3776 3779 VN_RELE(vp);
3777 3780 }
3778 3781
3779 3782 if (namlen != NULL)
3780 3783 kmem_free(namlen, args->dircount);
3781 3784
3782 3785 vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3783 3786 }
3784 3787
3785 3788 void *
3786 3789 rfs3_readdirplus_getfh(READDIRPLUS3args *args)
3787 3790 {
3788 3791
3789 3792 return (&args->dir);
3790 3793 }
3791 3794
3792 3795 void
3793 3796 rfs3_readdirplus_free(READDIRPLUS3res *resp)
3794 3797 {
3795 3798
3796 3799 if (resp->status == NFS3_OK) {
3797 3800 kmem_free(resp->resok.reply.entries, resp->resok.count);
3798 3801 kmem_free(resp->resok.infop,
3799 3802 resp->resok.size * sizeof (struct entryplus3_info));
3800 3803 }
3801 3804 }
3802 3805
3803 3806 /* ARGSUSED */
3804 3807 void
3805 3808 rfs3_fsstat(FSSTAT3args *args, FSSTAT3res *resp, struct exportinfo *exi,
3806 3809 struct svc_req *req, cred_t *cr, bool_t ro)
3807 3810 {
3808 3811 int error;
3809 3812 vnode_t *vp;
3810 3813 struct vattr *vap;
3811 3814 struct vattr va;
3812 3815 struct statvfs64 sb;
3813 3816
3814 3817 vap = NULL;
3815 3818
3816 3819 vp = nfs3_fhtovp(&args->fsroot, exi);
3817 3820
3818 3821 DTRACE_NFSV3_5(op__fsstat__start, struct svc_req *, req,
3819 3822 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3820 3823 FSSTAT3args *, args);
3821 3824
3822 3825 if (vp == NULL) {
3823 3826 error = ESTALE;
3824 3827 goto out;
3825 3828 }
3826 3829
3827 3830 if (is_system_labeled()) {
3828 3831 bslabel_t *clabel = req->rq_label;
3829 3832
3830 3833 ASSERT(clabel != NULL);
3831 3834 DTRACE_PROBE2(tx__rfs3__log__info__opfsstat__clabel, char *,
3832 3835 "got client label from request(1)", struct svc_req *, req);
3833 3836
3834 3837 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3835 3838 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
3836 3839 exi)) {
3837 3840 resp->status = NFS3ERR_ACCES;
3838 3841 goto out1;
3839 3842 }
3840 3843 }
3841 3844 }
3842 3845
3843 3846 error = VFS_STATVFS(vp->v_vfsp, &sb);
3844 3847
3845 3848 va.va_mask = AT_ALL;
3846 3849 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3847 3850
3848 3851 if (error)
3849 3852 goto out;
3850 3853
3851 3854 resp->status = NFS3_OK;
3852 3855 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
3853 3856 if (sb.f_blocks != (fsblkcnt64_t)-1)
3854 3857 resp->resok.tbytes = (size3)sb.f_frsize * (size3)sb.f_blocks;
3855 3858 else
3856 3859 resp->resok.tbytes = (size3)sb.f_blocks;
3857 3860 if (sb.f_bfree != (fsblkcnt64_t)-1)
3858 3861 resp->resok.fbytes = (size3)sb.f_frsize * (size3)sb.f_bfree;
3859 3862 else
3860 3863 resp->resok.fbytes = (size3)sb.f_bfree;
3861 3864 if (sb.f_bavail != (fsblkcnt64_t)-1)
3862 3865 resp->resok.abytes = (size3)sb.f_frsize * (size3)sb.f_bavail;
3863 3866 else
3864 3867 resp->resok.abytes = (size3)sb.f_bavail;
3865 3868 resp->resok.tfiles = (size3)sb.f_files;
3866 3869 resp->resok.ffiles = (size3)sb.f_ffree;
3867 3870 resp->resok.afiles = (size3)sb.f_favail;
3868 3871 resp->resok.invarsec = 0;
3869 3872
3870 3873 DTRACE_NFSV3_5(op__fsstat__done, struct svc_req *, req,
3871 3874 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3872 3875 FSSTAT3res *, resp);
3873 3876 VN_RELE(vp);
3874 3877
3875 3878 return;
3876 3879
3877 3880 out:
3878 3881 if (curthread->t_flag & T_WOULDBLOCK) {
3879 3882 curthread->t_flag &= ~T_WOULDBLOCK;
3880 3883 resp->status = NFS3ERR_JUKEBOX;
3881 3884 } else
3882 3885 resp->status = puterrno3(error);
3883 3886 out1:
3884 3887 DTRACE_NFSV3_5(op__fsstat__done, struct svc_req *, req,
3885 3888 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3886 3889 FSSTAT3res *, resp);
3887 3890
3888 3891 if (vp != NULL)
3889 3892 VN_RELE(vp);
3890 3893 vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
3891 3894 }
3892 3895
3893 3896 void *
3894 3897 rfs3_fsstat_getfh(FSSTAT3args *args)
3895 3898 {
3896 3899
3897 3900 return (&args->fsroot);
3898 3901 }
3899 3902
3900 3903 /* ARGSUSED */
3901 3904 void
3902 3905 rfs3_fsinfo(FSINFO3args *args, FSINFO3res *resp, struct exportinfo *exi,
3903 3906 struct svc_req *req, cred_t *cr, bool_t ro)
3904 3907 {
3905 3908 vnode_t *vp;
3906 3909 struct vattr *vap;
3907 3910 struct vattr va;
3908 3911 uint32_t xfer_size;
3909 3912 ulong_t l = 0;
3910 3913 int error;
3911 3914
3912 3915 vp = nfs3_fhtovp(&args->fsroot, exi);
3913 3916
3914 3917 DTRACE_NFSV3_5(op__fsinfo__start, struct svc_req *, req,
3915 3918 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3916 3919 FSINFO3args *, args);
3917 3920
3918 3921 if (vp == NULL) {
3919 3922 if (curthread->t_flag & T_WOULDBLOCK) {
3920 3923 curthread->t_flag &= ~T_WOULDBLOCK;
3921 3924 resp->status = NFS3ERR_JUKEBOX;
3922 3925 } else
3923 3926 resp->status = NFS3ERR_STALE;
3924 3927 vattr_to_post_op_attr(NULL, &resp->resfail.obj_attributes);
3925 3928 goto out;
3926 3929 }
3927 3930
3928 3931 if (is_system_labeled()) {
3929 3932 bslabel_t *clabel = req->rq_label;
3930 3933
3931 3934 ASSERT(clabel != NULL);
3932 3935 DTRACE_PROBE2(tx__rfs3__log__info__opfsinfo__clabel, char *,
3933 3936 "got client label from request(1)", struct svc_req *, req);
3934 3937
3935 3938 if (!blequal(&l_admin_low->tsl_label, clabel)) {
3936 3939 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
3937 3940 exi)) {
3938 3941 resp->status = NFS3ERR_STALE;
3939 3942 vattr_to_post_op_attr(NULL,
3940 3943 &resp->resfail.obj_attributes);
3941 3944 goto out;
3942 3945 }
3943 3946 }
3944 3947 }
3945 3948
3946 3949 va.va_mask = AT_ALL;
3947 3950 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3948 3951
3949 3952 resp->status = NFS3_OK;
3950 3953 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
3951 3954 xfer_size = rfs3_tsize(req);
3952 3955 resp->resok.rtmax = xfer_size;
3953 3956 resp->resok.rtpref = xfer_size;
3954 3957 resp->resok.rtmult = DEV_BSIZE;
3955 3958 resp->resok.wtmax = xfer_size;
3956 3959 resp->resok.wtpref = xfer_size;
3957 3960 resp->resok.wtmult = DEV_BSIZE;
3958 3961 resp->resok.dtpref = MAXBSIZE;
3959 3962
3960 3963 /*
3961 3964 * Large file spec: want maxfilesize based on limit of
3962 3965 * underlying filesystem. We can guess 2^31-1 if need be.
3963 3966 */
3964 3967 error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &l, cr, NULL);
3965 3968 if (error) {
3966 3969 resp->status = puterrno3(error);
3967 3970 goto out;
3968 3971 }
3969 3972
3970 3973 /*
3971 3974 * If the underlying file system does not support _PC_FILESIZEBITS,
3972 3975 * return a reasonable default. Note that error code on VOP_PATHCONF
3973 3976 * will be 0, even if the underlying file system does not support
3974 3977 * _PC_FILESIZEBITS.
3975 3978 */
3976 3979 if (l == (ulong_t)-1) {
3977 3980 resp->resok.maxfilesize = MAXOFF32_T;
3978 3981 } else {
3979 3982 if (l >= (sizeof (uint64_t) * 8))
3980 3983 resp->resok.maxfilesize = INT64_MAX;
3981 3984 else
3982 3985 resp->resok.maxfilesize = (1LL << (l-1)) - 1;
3983 3986 }
3984 3987
3985 3988 resp->resok.time_delta.seconds = 0;
3986 3989 resp->resok.time_delta.nseconds = 1000;
3987 3990 resp->resok.properties = FSF3_LINK | FSF3_SYMLINK |
3988 3991 FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
3989 3992
3990 3993 DTRACE_NFSV3_5(op__fsinfo__done, struct svc_req *, req,
3991 3994 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
3992 3995 FSINFO3res *, resp);
3993 3996
3994 3997 VN_RELE(vp);
3995 3998
3996 3999 return;
3997 4000
3998 4001 out:
3999 4002 DTRACE_NFSV3_5(op__fsinfo__done, struct svc_req *, req,
4000 4003 cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi,
4001 4004 FSINFO3res *, resp);
4002 4005 if (vp != NULL)
4003 4006 VN_RELE(vp);
4004 4007 }
4005 4008
4006 4009 void *
4007 4010 rfs3_fsinfo_getfh(FSINFO3args *args)
4008 4011 {
4009 4012 return (&args->fsroot);
4010 4013 }
4011 4014
4012 4015 /* ARGSUSED */
4013 4016 void
4014 4017 rfs3_pathconf(PATHCONF3args *args, PATHCONF3res *resp, struct exportinfo *exi,
4015 4018 struct svc_req *req, cred_t *cr, bool_t ro)
4016 4019 {
4017 4020 int error;
4018 4021 vnode_t *vp;
4019 4022 struct vattr *vap;
4020 4023 struct vattr va;
4021 4024 ulong_t val;
4022 4025
4023 4026 vap = NULL;
4024 4027
4025 4028 vp = nfs3_fhtovp(&args->object, exi);
4026 4029
4027 4030 DTRACE_NFSV3_5(op__pathconf__start, struct svc_req *, req,
4028 4031 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4029 4032 PATHCONF3args *, args);
4030 4033
4031 4034 if (vp == NULL) {
4032 4035 error = ESTALE;
4033 4036 goto out;
4034 4037 }
4035 4038
4036 4039 if (is_system_labeled()) {
4037 4040 bslabel_t *clabel = req->rq_label;
4038 4041
4039 4042 ASSERT(clabel != NULL);
4040 4043 DTRACE_PROBE2(tx__rfs3__log__info__oppathconf__clabel, char *,
4041 4044 "got client label from request(1)", struct svc_req *, req);
4042 4045
4043 4046 if (!blequal(&l_admin_low->tsl_label, clabel)) {
4044 4047 if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK,
4045 4048 exi)) {
4046 4049 resp->status = NFS3ERR_ACCES;
4047 4050 goto out1;
4048 4051 }
4049 4052 }
4050 4053 }
4051 4054
4052 4055 va.va_mask = AT_ALL;
4053 4056 vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
4054 4057
4055 4058 error = VOP_PATHCONF(vp, _PC_LINK_MAX, &val, cr, NULL);
4056 4059 if (error)
4057 4060 goto out;
4058 4061 resp->resok.info.link_max = (uint32)val;
4059 4062
4060 4063 error = VOP_PATHCONF(vp, _PC_NAME_MAX, &val, cr, NULL);
4061 4064 if (error)
4062 4065 goto out;
4063 4066 resp->resok.info.name_max = (uint32)val;
4064 4067
4065 4068 error = VOP_PATHCONF(vp, _PC_NO_TRUNC, &val, cr, NULL);
4066 4069 if (error)
4067 4070 goto out;
4068 4071 if (val == 1)
4069 4072 resp->resok.info.no_trunc = TRUE;
4070 4073 else
4071 4074 resp->resok.info.no_trunc = FALSE;
4072 4075
4073 4076 error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &val, cr, NULL);
4074 4077 if (error)
4075 4078 goto out;
4076 4079 if (val == 1)
4077 4080 resp->resok.info.chown_restricted = TRUE;
4078 4081 else
4079 4082 resp->resok.info.chown_restricted = FALSE;
4080 4083
4081 4084 resp->status = NFS3_OK;
4082 4085 vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
4083 4086 resp->resok.info.case_insensitive = FALSE;
4084 4087 resp->resok.info.case_preserving = TRUE;
4085 4088 DTRACE_NFSV3_5(op__pathconf__done, struct svc_req *, req,
4086 4089 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4087 4090 PATHCONF3res *, resp);
4088 4091 VN_RELE(vp);
4089 4092 return;
4090 4093
4091 4094 out:
4092 4095 if (curthread->t_flag & T_WOULDBLOCK) {
4093 4096 curthread->t_flag &= ~T_WOULDBLOCK;
4094 4097 resp->status = NFS3ERR_JUKEBOX;
4095 4098 } else
4096 4099 resp->status = puterrno3(error);
4097 4100 out1:
4098 4101 DTRACE_NFSV3_5(op__pathconf__done, struct svc_req *, req,
4099 4102 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4100 4103 PATHCONF3res *, resp);
4101 4104 if (vp != NULL)
4102 4105 VN_RELE(vp);
4103 4106 vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
4104 4107 }
4105 4108
4106 4109 void *
4107 4110 rfs3_pathconf_getfh(PATHCONF3args *args)
4108 4111 {
4109 4112
4110 4113 return (&args->object);
4111 4114 }
4112 4115
4113 4116 void
4114 4117 rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi,
4115 4118 struct svc_req *req, cred_t *cr, bool_t ro)
4116 4119 {
4117 4120 nfs3_srv_t *ns;
4118 4121 int error;
4119 4122 vnode_t *vp;
4120 4123 struct vattr *bvap;
4121 4124 struct vattr bva;
4122 4125 struct vattr *avap;
4123 4126 struct vattr ava;
4124 4127
4125 4128 bvap = NULL;
4126 4129 avap = NULL;
4127 4130
4128 4131 vp = nfs3_fhtovp(&args->file, exi);
4129 4132
4130 4133 DTRACE_NFSV3_5(op__commit__start, struct svc_req *, req,
4131 4134 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4132 4135 COMMIT3args *, args);
4133 4136
4134 4137 if (vp == NULL) {
4135 4138 error = ESTALE;
4136 4139 goto out;
4137 4140 }
4138 4141
4139 4142 ASSERT3P(curzone, ==, exi->exi_zone); /* exi is guaranteed non-NULL. */
4140 4143 ns = nfs3_get_srv();
4141 4144 bva.va_mask = AT_ALL;
4142 4145 error = VOP_GETATTR(vp, &bva, 0, cr, NULL);
4143 4146
4144 4147 /*
4145 4148 * If we can't get the attributes, then we can't do the
4146 4149 * right access checking. So, we'll fail the request.
4147 4150 */
4148 4151 if (error)
4149 4152 goto out;
4150 4153
4151 4154 bvap = &bva;
4152 4155
4153 4156 if (rdonly(ro, vp)) {
4154 4157 resp->status = NFS3ERR_ROFS;
4155 4158 goto out1;
4156 4159 }
4157 4160
4158 4161 if (vp->v_type != VREG) {
4159 4162 resp->status = NFS3ERR_INVAL;
4160 4163 goto out1;
4161 4164 }
4162 4165
4163 4166 if (is_system_labeled()) {
4164 4167 bslabel_t *clabel = req->rq_label;
4165 4168
4166 4169 ASSERT(clabel != NULL);
4167 4170 DTRACE_PROBE2(tx__rfs3__log__info__opcommit__clabel, char *,
4168 4171 "got client label from request(1)", struct svc_req *, req);
4169 4172
4170 4173 if (!blequal(&l_admin_low->tsl_label, clabel)) {
4171 4174 if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK,
4172 4175 exi)) {
4173 4176 resp->status = NFS3ERR_ACCES;
4174 4177 goto out1;
4175 4178 }
4176 4179 }
4177 4180 }
4178 4181
4179 4182 if (crgetuid(cr) != bva.va_uid &&
4180 4183 (error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL)))
4181 4184 goto out;
4182 4185
4183 4186 error = VOP_FSYNC(vp, FSYNC, cr, NULL);
4184 4187
4185 4188 ava.va_mask = AT_ALL;
4186 4189 avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
4187 4190
4188 4191 if (error)
4189 4192 goto out;
4190 4193
4191 4194 resp->status = NFS3_OK;
4192 4195 vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
4193 4196 resp->resok.verf = ns->write3verf;
4194 4197
4195 4198 DTRACE_NFSV3_5(op__commit__done, struct svc_req *, req,
4196 4199 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4197 4200 COMMIT3res *, resp);
4198 4201
4199 4202 VN_RELE(vp);
4200 4203
4201 4204 return;
4202 4205
4203 4206 out:
4204 4207 if (curthread->t_flag & T_WOULDBLOCK) {
4205 4208 curthread->t_flag &= ~T_WOULDBLOCK;
4206 4209 resp->status = NFS3ERR_JUKEBOX;
4207 4210 } else
4208 4211 resp->status = puterrno3(error);
4209 4212 out1:
4210 4213 DTRACE_NFSV3_5(op__commit__done, struct svc_req *, req,
4211 4214 cred_t *, cr, vnode_t *, vp, struct exportinfo *, exi,
4212 4215 COMMIT3res *, resp);
4213 4216
4214 4217 if (vp != NULL)
4215 4218 VN_RELE(vp);
4216 4219 vattr_to_wcc_data(bvap, avap, &resp->resfail.file_wcc);
4217 4220 }
4218 4221
4219 4222 void *
4220 4223 rfs3_commit_getfh(COMMIT3args *args)
4221 4224 {
4222 4225
4223 4226 return (&args->file);
4224 4227 }
4225 4228
4226 4229 static int
4227 4230 sattr3_to_vattr(sattr3 *sap, struct vattr *vap)
4228 4231 {
4229 4232
4230 4233 vap->va_mask = 0;
4231 4234
4232 4235 if (sap->mode.set_it) {
4233 4236 vap->va_mode = (mode_t)sap->mode.mode;
4234 4237 vap->va_mask |= AT_MODE;
4235 4238 }
4236 4239 if (sap->uid.set_it) {
4237 4240 vap->va_uid = (uid_t)sap->uid.uid;
4238 4241 vap->va_mask |= AT_UID;
4239 4242 }
4240 4243 if (sap->gid.set_it) {
4241 4244 vap->va_gid = (gid_t)sap->gid.gid;
4242 4245 vap->va_mask |= AT_GID;
4243 4246 }
4244 4247 if (sap->size.set_it) {
4245 4248 if (sap->size.size > (size3)((u_longlong_t)-1))
4246 4249 return (EINVAL);
4247 4250 vap->va_size = sap->size.size;
4248 4251 vap->va_mask |= AT_SIZE;
4249 4252 }
4250 4253 if (sap->atime.set_it == SET_TO_CLIENT_TIME) {
4251 4254 #ifndef _LP64
4252 4255 /* check time validity */
4253 4256 if (!NFS3_TIME_OK(sap->atime.atime.seconds))
4254 4257 return (EOVERFLOW);
4255 4258 #endif
4256 4259 /*
4257 4260 * nfs protocol defines times as unsigned so don't extend sign,
4258 4261 * unless sysadmin set nfs_allow_preepoch_time.
4259 4262 */
4260 4263 NFS_TIME_T_CONVERT(vap->va_atime.tv_sec,
4261 4264 sap->atime.atime.seconds);
4262 4265 vap->va_atime.tv_nsec = (uint32_t)sap->atime.atime.nseconds;
4263 4266 vap->va_mask |= AT_ATIME;
4264 4267 } else if (sap->atime.set_it == SET_TO_SERVER_TIME) {
4265 4268 gethrestime(&vap->va_atime);
4266 4269 vap->va_mask |= AT_ATIME;
4267 4270 }
4268 4271 if (sap->mtime.set_it == SET_TO_CLIENT_TIME) {
4269 4272 #ifndef _LP64
4270 4273 /* check time validity */
4271 4274 if (!NFS3_TIME_OK(sap->mtime.mtime.seconds))
4272 4275 return (EOVERFLOW);
4273 4276 #endif
4274 4277 /*
4275 4278 * nfs protocol defines times as unsigned so don't extend sign,
4276 4279 * unless sysadmin set nfs_allow_preepoch_time.
4277 4280 */
4278 4281 NFS_TIME_T_CONVERT(vap->va_mtime.tv_sec,
4279 4282 sap->mtime.mtime.seconds);
4280 4283 vap->va_mtime.tv_nsec = (uint32_t)sap->mtime.mtime.nseconds;
4281 4284 vap->va_mask |= AT_MTIME;
4282 4285 } else if (sap->mtime.set_it == SET_TO_SERVER_TIME) {
4283 4286 gethrestime(&vap->va_mtime);
4284 4287 vap->va_mask |= AT_MTIME;
4285 4288 }
4286 4289
4287 4290 return (0);
4288 4291 }
4289 4292
4290 4293 static const ftype3 vt_to_nf3[] = {
4291 4294 0, NF3REG, NF3DIR, NF3BLK, NF3CHR, NF3LNK, NF3FIFO, 0, 0, NF3SOCK, 0
4292 4295 };
4293 4296
4294 4297 static int
4295 4298 vattr_to_fattr3(struct vattr *vap, fattr3 *fap)
4296 4299 {
4297 4300
4298 4301 ASSERT(vap->va_type >= VNON && vap->va_type <= VBAD);
4299 4302 /* Return error if time or size overflow */
4300 4303 if (! (NFS_VAP_TIME_OK(vap) && NFS3_SIZE_OK(vap->va_size))) {
4301 4304 return (EOVERFLOW);
4302 4305 }
4303 4306 fap->type = vt_to_nf3[vap->va_type];
4304 4307 fap->mode = (mode3)(vap->va_mode & MODEMASK);
4305 4308 fap->nlink = (uint32)vap->va_nlink;
4306 4309 if (vap->va_uid == UID_NOBODY)
4307 4310 fap->uid = (uid3)NFS_UID_NOBODY;
4308 4311 else
4309 4312 fap->uid = (uid3)vap->va_uid;
4310 4313 if (vap->va_gid == GID_NOBODY)
4311 4314 fap->gid = (gid3)NFS_GID_NOBODY;
4312 4315 else
4313 4316 fap->gid = (gid3)vap->va_gid;
4314 4317 fap->size = (size3)vap->va_size;
4315 4318 fap->used = (size3)DEV_BSIZE * (size3)vap->va_nblocks;
4316 4319 fap->rdev.specdata1 = (uint32)getmajor(vap->va_rdev);
4317 4320 fap->rdev.specdata2 = (uint32)getminor(vap->va_rdev);
4318 4321 fap->fsid = (uint64)vap->va_fsid;
4319 4322 fap->fileid = (fileid3)vap->va_nodeid;
4320 4323 fap->atime.seconds = vap->va_atime.tv_sec;
4321 4324 fap->atime.nseconds = vap->va_atime.tv_nsec;
4322 4325 fap->mtime.seconds = vap->va_mtime.tv_sec;
4323 4326 fap->mtime.nseconds = vap->va_mtime.tv_nsec;
4324 4327 fap->ctime.seconds = vap->va_ctime.tv_sec;
4325 4328 fap->ctime.nseconds = vap->va_ctime.tv_nsec;
4326 4329 return (0);
4327 4330 }
4328 4331
4329 4332 static int
4330 4333 vattr_to_wcc_attr(struct vattr *vap, wcc_attr *wccap)
4331 4334 {
4332 4335
4333 4336 /* Return error if time or size overflow */
4334 4337 if (!(NFS_TIME_T_OK(vap->va_mtime.tv_sec) &&
4335 4338 NFS_TIME_T_OK(vap->va_ctime.tv_sec) &&
4336 4339 NFS3_SIZE_OK(vap->va_size))) {
4337 4340 return (EOVERFLOW);
4338 4341 }
4339 4342 wccap->size = (size3)vap->va_size;
4340 4343 wccap->mtime.seconds = vap->va_mtime.tv_sec;
4341 4344 wccap->mtime.nseconds = vap->va_mtime.tv_nsec;
4342 4345 wccap->ctime.seconds = vap->va_ctime.tv_sec;
4343 4346 wccap->ctime.nseconds = vap->va_ctime.tv_nsec;
4344 4347 return (0);
4345 4348 }
4346 4349
4347 4350 static void
4348 4351 vattr_to_pre_op_attr(struct vattr *vap, pre_op_attr *poap)
4349 4352 {
4350 4353
4351 4354 /* don't return attrs if time overflow */
4352 4355 if ((vap != NULL) && !vattr_to_wcc_attr(vap, &poap->attr)) {
4353 4356 poap->attributes = TRUE;
4354 4357 } else
4355 4358 poap->attributes = FALSE;
4356 4359 }
4357 4360
4358 4361 void
4359 4362 vattr_to_post_op_attr(struct vattr *vap, post_op_attr *poap)
4360 4363 {
4361 4364
4362 4365 /* don't return attrs if time overflow */
4363 4366 if ((vap != NULL) && !vattr_to_fattr3(vap, &poap->attr)) {
4364 4367 poap->attributes = TRUE;
4365 4368 } else
4366 4369 poap->attributes = FALSE;
4367 4370 }
4368 4371
4369 4372 static void
4370 4373 vattr_to_wcc_data(struct vattr *bvap, struct vattr *avap, wcc_data *wccp)
4371 4374 {
4372 4375 vattr_to_pre_op_attr(bvap, &wccp->before);
4373 4376 vattr_to_post_op_attr(avap, &wccp->after);
4374 4377 }
4375 4378
4376 4379 static int
4377 4380 rdma_setup_read_data3(READ3args *args, READ3resok *rok)
4378 4381 {
4379 4382 struct clist *wcl;
4380 4383 int wlist_len;
4381 4384 count3 count = rok->count;
4382 4385
4383 4386 wcl = args->wlist;
4384 4387 if (rdma_setup_read_chunks(wcl, count, &wlist_len) == FALSE)
4385 4388 return (FALSE);
4386 4389
4387 4390 wcl = args->wlist;
4388 4391 rok->wlist_len = wlist_len;
4389 4392 rok->wlist = wcl;
4390 4393 return (TRUE);
4391 4394 }
4392 4395
4393 4396 void
4394 4397 rfs3_srv_zone_init(nfs_globals_t *ng)
4395 4398 {
4396 4399 nfs3_srv_t *ns;
4397 4400 struct rfs3_verf_overlay {
4398 4401 uint_t id; /* a "unique" identifier */
4399 4402 int ts; /* a unique timestamp */
4400 4403 } *verfp;
4401 4404 timestruc_t now;
4402 4405
4403 4406 ns = kmem_zalloc(sizeof (*ns), KM_SLEEP);
4404 4407
4405 4408 /*
4406 4409 * The following algorithm attempts to find a unique verifier
4407 4410 * to be used as the write verifier returned from the server
4408 4411 * to the client. It is important that this verifier change
4409 4412 * whenever the server reboots. Of secondary importance, it
4410 4413 * is important for the verifier to be unique between two
4411 4414 * different servers.
4412 4415 *
4413 4416 * Thus, an attempt is made to use the system hostid and the
4414 4417 * current time in seconds when the nfssrv kernel module is
4415 4418 * loaded. It is assumed that an NFS server will not be able
4416 4419 * to boot and then to reboot in less than a second. If the
4417 4420 * hostid has not been set, then the current high resolution
4418 4421 * time is used. This will ensure different verifiers each
4419 4422 * time the server reboots and minimize the chances that two
4420 4423 * different servers will have the same verifier.
4421 4424 */
4422 4425
4423 4426 #ifndef lint
4424 4427 /*
4425 4428 * We ASSERT that this constant logic expression is
4426 4429 * always true because in the past, it wasn't.
4427 4430 */
4428 4431 ASSERT(sizeof (*verfp) <= sizeof (ns->write3verf));
4429 4432 #endif
4430 4433
4431 4434 gethrestime(&now);
4432 4435 verfp = (struct rfs3_verf_overlay *)&ns->write3verf;
4433 4436 verfp->ts = (int)now.tv_sec;
4434 4437 verfp->id = zone_get_hostid(NULL);
4435 4438
4436 4439 if (verfp->id == 0)
4437 4440 verfp->id = (uint_t)now.tv_nsec;
4438 4441
4439 4442 ng->nfs3_srv = ns;
4440 4443 }
4441 4444
4442 4445 void
4443 4446 rfs3_srv_zone_fini(nfs_globals_t *ng)
4444 4447 {
4445 4448 nfs3_srv_t *ns = ng->nfs3_srv;
4446 4449
4447 4450 ng->nfs3_srv = NULL;
4448 4451
4449 4452 kmem_free(ns, sizeof (*ns));
4450 4453 }
4451 4454
4452 4455 void
4453 4456 rfs3_srvrinit(void)
4454 4457 {
4455 4458 nfs3_srv_caller_id = fs_new_caller_id();
4456 4459 }
4457 4460
4458 4461 void
4459 4462 rfs3_srvrfini(void)
4460 4463 {
4461 4464 /* Nothing to do */
4462 4465 }
|
↓ open down ↓ |
1776 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX