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