1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Simple nfs V4 ops 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <rpc/types.h> 32 #include <rpc/auth.h> 33 #include <sys/t_lock.h> 34 #include "clnt.h" 35 #include <sys/fcntl.h> 36 #include <sys/vfs.h> 37 #include <errno.h> 38 #include <sys/promif.h> 39 #include <rpc/xdr.h> 40 #include "nfs_inet.h" 41 #include <sys/stat.h> 42 #include <sys/bootvfs.h> 43 #include <sys/bootdebug.h> 44 #include <sys/salib.h> 45 #include <sys/sacache.h> 46 #include <rpc/rpc.h> 47 #include "brpc.h" 48 #include <rpcsvc/nfs4_prot.h> 49 50 #define dprintf if (boothowto & RB_DEBUG) printf 51 52 static struct timeval zero_timeout = {0, 0}; /* default */ 53 54 /* 55 * NFS Version 4 specific functions 56 */ 57 58 ssize_t 59 nfs4read(struct nfs_file *filep, char *buf, size_t size) 60 { 61 enum clnt_stat status; 62 read4arg_t readargs; 63 read4res_t readres; 64 char *buf_offset; 65 uint_t count = 0; 66 uint_t readcnt = 0; 67 bool_t done = FALSE; 68 struct timeval timeout; 69 int framing_errs = 0; 70 #ifndef i386 71 static uint_t pos; 72 static char ind[] = "|/-\\"; 73 static int blks_read; 74 #endif 75 utf8string str; 76 char tagname[] = "inetboot read"; 77 78 bzero(&readres, sizeof (readres)); 79 80 str.utf8string_len = sizeof (tagname) - 1; 81 str.utf8string_val = tagname; 82 83 /* 84 * read 85 */ 86 buf_offset = buf; 87 88 if (nfs_readsize == 0) 89 nfs_readsize = READ4_SIZE; 90 91 if (size < nfs_readsize) 92 readargs.r_count = size; 93 else 94 readargs.r_count = nfs_readsize; 95 96 if (filep->fh.fh4.len > 0) 97 compound_init(&readargs.r_arg, &str, 0, 2, &filep->fh.fh4); 98 else 99 compound_init(&readargs.r_arg, &str, 0, 2, NULL); 100 101 readargs.r_opread = OP_READ; 102 /* 103 * zero out the stateid field 104 */ 105 bzero(&readargs.r_stateid, sizeof (readargs.r_stateid)); 106 readargs.r_offset = filep->offset; 107 108 do { 109 readres.r_data_val = buf_offset; 110 111 if ((count + readargs.r_count) > size) 112 readargs.r_count = size - count; 113 114 timeout.tv_sec = NFS_REXMIT_MIN; 115 timeout.tv_usec = 0; 116 117 do { 118 status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, 119 xdr_read4_args, (caddr_t)&readargs, 120 xdr_read4_res, (caddr_t)&readres, 121 timeout); 122 123 if (status == RPC_TIMEDOUT) { 124 dprintf("NFS read(%d) timed out. Retrying...\n", readargs.r_count); 125 if (errno == ETIMEDOUT) 126 framing_errs++; 127 128 if (framing_errs > NFS_MAX_FERRS && 129 readargs.r_count > NFS_READ_DECR) { 130 readargs.r_count /= 2; 131 nfs_readsize /= 2; 132 dprintf("NFS read size now %d.\n", 133 nfs_readsize); 134 timeout.tv_sec = NFS_REXMIT_MIN; 135 framing_errs = 0; 136 } else { 137 if (timeout.tv_sec < NFS_REXMIT_MAX) 138 timeout.tv_sec++; 139 else 140 timeout.tv_sec = 0; 141 } 142 } 143 } while (status == RPC_TIMEDOUT); 144 145 if (status != RPC_SUCCESS) 146 return (-1); 147 148 if (readres.r_status != NFS4_OK) { 149 nfs4_error(readres.r_status); 150 return (-1); 151 } 152 153 readcnt = readres.r_data_len; 154 155 if (readres.r_eof == TRUE) 156 done = TRUE; 157 158 if (readcnt < readargs.r_count) { 159 #ifdef NFS_OPS_DEBUG 160 if ((boothowto & DBFLAGS) == DBFLAGS) 161 printf("nfs4read: partial read %d instead of %d\n", readcnt, 162 readargs.count); 163 #endif 164 done = TRUE; 165 } 166 167 count += readcnt; 168 filep->offset += readcnt; 169 buf_offset += readcnt; 170 readargs.r_offset += readcnt; 171 #ifndef i386 172 if ((blks_read++ & 0x3) == 0) 173 printf("%c\b", ind[pos++ & 3]); 174 #endif 175 } while (count < size && !done); 176 177 return (count); 178 } 179 180 181 static vtype_t nf4_to_vt[] = { 182 VBAD, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO 183 }; 184 185 int 186 nfs4getattr(struct nfs_file *nfp, struct vattr *vap) 187 { 188 enum clnt_stat status; 189 attr4_bitmap1_t bitmap1; 190 attr4_bitmap2_t bitmap2; 191 getattr4arg_t getattrargs; 192 getattr4res_t getattrres; 193 b_fattr4_t *bfattr4; 194 utf8string str; 195 char tagname[] = "inetboot getattr"; 196 197 bzero(&getattrres, sizeof (getattrres)); 198 /* 199 * Putfh 200 */ 201 str.utf8string_len = sizeof (tagname) - 1; 202 str.utf8string_val = tagname; 203 204 if (nfp->fh.fh4.len > 0) 205 compound_init(&getattrargs.ga_arg, &str, 0, 2, &nfp->fh.fh4); 206 else 207 compound_init(&getattrargs.ga_arg, &str, 0, 2, NULL); 208 209 /* 210 * getattr 211 */ 212 getattrargs.ga_opgetattr = OP_GETATTR; 213 /* 214 * Set up the attribute bitmap. We pretty much need everything 215 * except for the filehandle and supported attrs. 216 */ 217 bitmap1.word = 0; 218 bitmap1.bm_fattr4_type = 1; 219 bitmap1.bm_fattr4_size = 1; 220 bitmap1.bm_fattr4_fileid = 1; 221 bitmap2.word = 0; 222 bitmap2.bm_fattr4_mode = 1; 223 bitmap2.bm_fattr4_time_access = 1; 224 bitmap2.bm_fattr4_time_metadata = 1; 225 bitmap2.bm_fattr4_time_modify = 1; 226 227 getattrargs.ga_attr_req.b_bitmap_len = NFS4_MAX_BITWORDS; 228 getattrargs.ga_attr_req.b_bitmap_val[0] = bitmap1.word; 229 getattrargs.ga_attr_req.b_bitmap_val[1] = bitmap2.word; 230 231 status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_getattr4_args, 232 (caddr_t)&getattrargs, xdr_getattr4_res, 233 (caddr_t)&getattrres, zero_timeout); 234 235 if (status != RPC_SUCCESS) { 236 dprintf("nfs4getattr: RPC error %d\n", status); 237 return (-1); 238 } 239 240 if (getattrres.gr_attr_status != NFS4_OK) { 241 nfs4_error(getattrres.gr_attr_status); 242 return (getattrres.gr_attr_status); 243 } 244 245 bfattr4 = &getattrres.gr_attrs; 246 if (vap->va_mask & AT_TYPE) { 247 if (bfattr4->b_fattr4_type < NF4REG || 248 bfattr4->b_fattr4_type > NF4FIFO) 249 vap->va_type = VBAD; 250 else 251 vap->va_type = nf4_to_vt[bfattr4->b_fattr4_type]; 252 } 253 if (vap->va_mask & AT_MODE) 254 vap->va_mode = (mode_t)bfattr4->b_fattr4_mode; 255 if (vap->va_mask & AT_SIZE) 256 vap->va_size = (u_offset_t)bfattr4->b_fattr4_size; 257 if (vap->va_mask & AT_NODEID) 258 vap->va_nodeid = (uint64_t)bfattr4->b_fattr4_fileid; 259 /* 260 * XXX - may need to do something more here. 261 */ 262 if (vap->va_mask & AT_ATIME) { 263 vap->va_atime.tv_sec = bfattr4->b_fattr4_time_access.seconds; 264 vap->va_atime.tv_nsec = bfattr4->b_fattr4_time_access.nseconds; 265 } 266 if (vap->va_mask & AT_CTIME) { 267 vap->va_ctime.tv_sec = bfattr4->b_fattr4_time_metadata.seconds; 268 vap->va_ctime.tv_nsec = 269 bfattr4->b_fattr4_time_metadata.nseconds; 270 } 271 if (vap->va_mask & AT_MTIME) { 272 vap->va_mtime.tv_sec = bfattr4->b_fattr4_time_modify.seconds; 273 vap->va_mtime.tv_nsec = bfattr4->b_fattr4_time_modify.nseconds; 274 } 275 276 return (NFS4_OK); 277 } 278 279 /* 280 * Display nfs error messages. 281 */ 282 /*ARGSUSED*/ 283 void 284 nfs4_error(enum nfsstat4 status) 285 { 286 if (!(boothowto & RB_DEBUG)) 287 return; 288 289 switch (status) { 290 case NFS4_OK: 291 printf("NFS: No error.\n"); 292 break; 293 case NFS4ERR_PERM: 294 printf("NFS: Not owner.\n"); 295 break; 296 case NFS4ERR_NOENT: 297 #ifdef NFS_OPS_DEBUG 298 printf("NFS: No such file or directory.\n"); 299 #endif /* NFS_OPS_DEBUG */ 300 break; 301 case NFS4ERR_IO: 302 printf("NFS: IO ERROR occurred on NFS server.\n"); 303 break; 304 case NFS4ERR_NXIO: 305 printf("NFS: No such device or address.\n"); 306 break; 307 case NFS4ERR_ACCESS: 308 printf("NFS: Permission denied.\n"); 309 break; 310 case NFS4ERR_EXIST: 311 printf("NFS: File exists.\n"); 312 break; 313 case NFS4ERR_XDEV: 314 printf("NFS: Cross device hard link.\n"); 315 break; 316 case NFS4ERR_NOTDIR: 317 printf("NFS: Not a directory.\n"); 318 break; 319 case NFS4ERR_ISDIR: 320 printf("NFS: Is a directory.\n"); 321 break; 322 case NFS4ERR_INVAL: 323 printf("NFS: Invalid argument.\n"); 324 break; 325 case NFS4ERR_FBIG: 326 printf("NFS: File too large.\n"); 327 break; 328 case NFS4ERR_NOSPC: 329 printf("NFS: No space left on device.\n"); 330 break; 331 case NFS4ERR_ROFS: 332 printf("NFS: Read-only filesystem.\n"); 333 break; 334 case NFS4ERR_MLINK: 335 printf("NFS: Too many hard links.\n"); 336 break; 337 case NFS4ERR_NAMETOOLONG: 338 printf("NFS: File name too long.\n"); 339 break; 340 case NFS4ERR_NOTEMPTY: 341 printf("NFS: Directory not empty.\n"); 342 break; 343 case NFS4ERR_DQUOT: 344 printf("NFS: Disk quota exceeded.\n"); 345 break; 346 case NFS4ERR_STALE: 347 printf("NFS: Stale file handle.\n"); 348 break; 349 case NFS4ERR_BADHANDLE: 350 printf("NFS: Illegal NFS file handle.\n"); 351 break; 352 case NFS4ERR_BAD_COOKIE: 353 printf("NFS: Stale Cookie.\n"); 354 break; 355 case NFS4ERR_NOTSUPP: 356 printf("NFS: Operation is not supported.\n"); 357 break; 358 case NFS4ERR_TOOSMALL: 359 printf("NFS: Buffer too small.\n"); 360 break; 361 case NFS4ERR_SERVERFAULT: 362 printf("NFS: Server fault.\n"); 363 break; 364 case NFS4ERR_BADTYPE: 365 printf("NFS: Unsupported object type.\n"); 366 break; 367 case NFS4ERR_BAD_STATEID: 368 printf("NFS: Bad stateid\n"); 369 break; 370 case NFS4ERR_BAD_SEQID: 371 printf("NFS: Bad seqid\n"); 372 break; 373 default: 374 printf("NFS: unknown error.\n"); 375 break; 376 } 377 } 378 379 /* 380 * lookup one component. for multicomponent lookup use a driver like lookup(). 381 */ 382 struct nfs_file * 383 nfs4lookup(struct nfs_file *dir, char *name, int *nstat) 384 { 385 static struct nfs_file cd; 386 attr4_bitmap1_t bitmap1; 387 lookup4arg_t lookupargs; 388 lookup4res_t lookupres; 389 enum clnt_stat status; 390 utf8string str; 391 char tagname[] = "inetboot lookup"; 392 393 /* 394 * NFSv4 uses a special LOOKUPP op 395 * for looking up the parent directory. 396 */ 397 if (strcmp(name, "..") == 0) 398 return (nfs4lookupp(dir, nstat, NULL)); 399 400 *nstat = (int)NFS4_OK; 401 402 bzero(&lookupres, sizeof (lookupres)); 403 404 /* 405 * Check if we have a filehandle and initialize the compound 406 * with putfh or putrootfh appropriately. 407 */ 408 str.utf8string_len = sizeof (tagname) - 1; 409 str.utf8string_val = tagname; 410 411 if (dir->fh.fh4.len > 0) 412 compound_init(&lookupargs.la_arg, &str, 0, 3, &dir->fh.fh4); 413 else 414 compound_init(&lookupargs.la_arg, &str, 0, 3, NULL); 415 416 /* 417 * lookup 418 */ 419 lookupargs.la_oplookup = OP_LOOKUP; 420 /* 421 * convert the pathname from char * to utf8string 422 */ 423 lookupargs.la_pathname.utf8string_len = strlen(name); 424 lookupargs.la_pathname.utf8string_val = 425 bkmem_alloc(lookupargs.la_pathname.utf8string_len); 426 if (lookupargs.la_pathname.utf8string_val == NULL) { 427 dprintf("nfs4lookup: bkmem_alloc failed\n"); 428 return (NULL); 429 } 430 bcopy(name, lookupargs.la_pathname.utf8string_val, 431 lookupargs.la_pathname.utf8string_len); 432 433 /* 434 * Setup the attr bitmap. All we need is the type and filehandle info 435 */ 436 lookupargs.la_opgetattr = OP_GETATTR; 437 bitmap1.word = 0; 438 bitmap1.bm_fattr4_type = 1; 439 bitmap1.bm_fattr4_filehandle = 1; 440 lookupargs.la_attr_req.b_bitmap_len = 1; 441 lookupargs.la_attr_req.b_bitmap_val[0] = bitmap1.word; 442 lookupargs.la_attr_req.b_bitmap_val[1] = 0; 443 444 status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_lookup4_args, 445 (caddr_t)&lookupargs, xdr_lookup4_res, 446 (caddr_t)&lookupres, zero_timeout); 447 448 if (status != RPC_SUCCESS) { 449 dprintf("nfs4lookup: RPC error. status %d\n", status); 450 return (NULL); 451 } 452 453 if (lookupres.lr_lookup_status != NFS4_OK) { 454 #ifdef DEBUG 455 dprintf("nfs4lookup: lookup status = %d\n", 456 lookupres.lr_lookup_status); 457 #endif 458 nfs4_error(lookupres.lr_lookup_status); 459 *nstat = (int)lookupres.lr_lookup_status; 460 if (lookupargs.la_pathname.utf8string_val != NULL) 461 bkmem_free(lookupargs.la_pathname.utf8string_val, 462 lookupargs.la_pathname.utf8string_len); 463 return (NULL); 464 } 465 466 if (lookupres.lr_attr_status != NFS4_OK) { 467 #ifdef DEBUG 468 dprintf("nfs4lookup: getattr status = %d\n", 469 lookupres.lr_attr_status); 470 #endif 471 nfs4_error(lookupres.lr_attr_status); 472 *nstat = (int)lookupres.lr_attr_status; 473 if (lookupargs.la_pathname.utf8string_val != NULL) 474 bkmem_free(lookupargs.la_pathname.utf8string_val, 475 lookupargs.la_pathname.utf8string_len); 476 return (NULL); 477 } 478 479 /* 480 * We have all the information we need to update the file pointer 481 */ 482 bzero((caddr_t)&cd, sizeof (struct nfs_file)); 483 cd.version = NFS_V4; 484 cd.ftype.type4 = lookupres.lr_attrs.b_fattr4_type; 485 cd.fh.fh4.len = lookupres.lr_attrs.b_fattr4_filehandle.len; 486 bcopy(lookupres.lr_attrs.b_fattr4_filehandle.data, cd.fh.fh4.data, 487 cd.fh.fh4.len); 488 489 /* 490 * Free the arg string 491 */ 492 if (lookupargs.la_pathname.utf8string_val != NULL) 493 bkmem_free(lookupargs.la_pathname.utf8string_val, 494 lookupargs.la_pathname.utf8string_len); 495 496 return (&cd); 497 } 498 499 /* 500 * lookup parent directory. 501 */ 502 struct nfs_file * 503 nfs4lookupp(struct nfs_file *dir, int *nstat, uint64_t *fileid) 504 { 505 static struct nfs_file cd; 506 attr4_bitmap1_t bitmap1; 507 lookupp4arg_t lookuppargs; 508 lookup4res_t lookupres; 509 enum clnt_stat status; 510 utf8string str; 511 char tagname[] = "inetboot lookupp"; 512 513 *nstat = (int)NFS4_OK; 514 515 bzero(&lookupres, sizeof (lookupres)); 516 517 /* 518 * Check if we have a filehandle and initialize the compound 519 * with putfh or putrootfh appropriately. 520 */ 521 str.utf8string_len = sizeof (tagname) - 1; 522 str.utf8string_val = tagname; 523 524 if (dir->fh.fh4.len > 0) 525 compound_init(&lookuppargs.la_arg, &str, 0, 3, &dir->fh.fh4); 526 else 527 compound_init(&lookuppargs.la_arg, &str, 0, 3, NULL); 528 529 /* 530 * lookupp 531 */ 532 lookuppargs.la_oplookupp = OP_LOOKUPP; 533 /* 534 * Setup the attr bitmap. Normally, all we need is the type and 535 * filehandle info, but getdents might require the fileid of the 536 * parent. 537 */ 538 lookuppargs.la_opgetattr = OP_GETATTR; 539 bitmap1.word = 0; 540 bitmap1.bm_fattr4_type = 1; 541 bitmap1.bm_fattr4_filehandle = 1; 542 if (fileid != NULL) 543 bitmap1.bm_fattr4_fileid = 1; 544 lookuppargs.la_attr_req.b_bitmap_len = 1; 545 lookuppargs.la_attr_req.b_bitmap_val[0] = bitmap1.word; 546 lookuppargs.la_attr_req.b_bitmap_val[1] = 0; 547 548 status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_lookupp4_args, 549 (caddr_t)&lookuppargs, xdr_lookup4_res, 550 (caddr_t)&lookupres, zero_timeout); 551 552 if (status != RPC_SUCCESS) { 553 dprintf("nfs4lookupp: RPC error. status %d\n", status); 554 return (NULL); 555 } 556 557 if (lookupres.lr_lookup_status != NFS4_OK) { 558 #ifdef DEBUG 559 dprintf("nfs4lookupp: lookupp status = %d\n", 560 lookupres.lr_lookup_status); 561 #endif 562 nfs4_error(lookupres.lr_lookup_status); 563 *nstat = (int)lookupres.lr_lookup_status; 564 return (NULL); 565 } 566 567 if (lookupres.lr_attr_status != NFS4_OK) { 568 #ifdef DEBUG 569 dprintf("nfs4lookupp: getattr status = %d\n", 570 lookupres.lr_attr_status); 571 #endif 572 nfs4_error(lookupres.lr_attr_status); 573 *nstat = (int)lookupres.lr_attr_status; 574 return (NULL); 575 } 576 577 /* 578 * We have all the information we need to update the file pointer 579 */ 580 bzero((caddr_t)&cd, sizeof (struct nfs_file)); 581 cd.version = NFS_V4; 582 cd.ftype.type4 = lookupres.lr_attrs.b_fattr4_type; 583 cd.fh.fh4.len = lookupres.lr_attrs.b_fattr4_filehandle.len; 584 bcopy(lookupres.lr_attrs.b_fattr4_filehandle.data, cd.fh.fh4.data, 585 cd.fh.fh4.len); 586 587 /* 588 * Fill in the fileid if the user passed in one 589 */ 590 if (fileid != NULL) 591 *fileid = lookupres.lr_attrs.b_fattr4_fileid; 592 593 return (&cd); 594 } 595 596 /* 597 * Gets symbolic link into pathname. 598 */ 599 int 600 nfs4getsymlink(struct nfs_file *cfile, char **path) 601 { 602 enum clnt_stat status; 603 readlink4arg_t readlinkargs; 604 readlink4res_t readlinkres; 605 static char symlink_path[NFS_MAXPATHLEN]; 606 int spathlen; 607 utf8string str; 608 char tagname[] = "inetboot getsymlink"; 609 int error = NFS4_OK; 610 611 bzero(&readlinkres, sizeof (readlinkres)); 612 613 /* 614 * readlink 615 */ 616 str.utf8string_len = sizeof (tagname) - 1; 617 str.utf8string_val = tagname; 618 619 if (cfile->fh.fh4.len > 0) 620 compound_init(&readlinkargs.rl_arg, &str, 0, 2, 621 &cfile->fh.fh4); 622 else 623 compound_init(&readlinkargs.rl_arg, &str, 0, 2, NULL); 624 625 readlinkargs.rl_opreadlink = OP_READLINK; 626 status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_readlink4_args, 627 (caddr_t)&readlinkargs, xdr_readlink4_res, 628 (caddr_t)&readlinkres, zero_timeout); 629 630 if (status != RPC_SUCCESS) { 631 dprintf("nfs4getsymlink: RPC readlink error %d\n", status); 632 error = -1; 633 goto out; 634 } 635 636 if (readlinkres.rl_status != NFS4_OK) { 637 nfs4_error(readlinkres.rl_status); 638 error = readlinkres.rl_status; 639 goto out; 640 } 641 642 /* 643 * Convert the utf8string to a normal character string 644 */ 645 spathlen = readlinkres.rl_link.utf8string_len; 646 if (spathlen <= 0 || readlinkres.rl_link.utf8string_val == NULL) { 647 *path = NULL; 648 error = readlinkres.rl_status; 649 goto out; 650 } 651 652 bcopy(readlinkres.rl_link.utf8string_val, symlink_path, spathlen); 653 symlink_path[spathlen] = '\0'; 654 *path = symlink_path; 655 656 out: 657 /* 658 * Free the results 659 */ 660 if (readlinkres.rl_link.utf8string_val != NULL) 661 bkmem_free(readlinkres.rl_link.utf8string_val, spathlen); 662 663 return (error); 664 } 665 666 /* 667 * Should just forget about the tag, but will leave in support for the time 668 * being. 669 */ 670 void 671 compound_init(b_compound_t *cp, utf8string *str, uint_t mvers, uint_t arglen, 672 struct nfs_bfh4 *pfh) 673 { 674 if (str == NULL || str->utf8string_len == 0) { 675 cp->ca_tag.utf8string_len = 0; 676 cp->ca_tag.utf8string_val = NULL; 677 } else { 678 cp->ca_tag.utf8string_len = str->utf8string_len; 679 cp->ca_tag.utf8string_val = str->utf8string_val; 680 } 681 cp->ca_minorversion = mvers; 682 cp->ca_argarray_len = arglen; 683 if (pfh == NULL) { 684 cp->ca_isputrootfh = TRUE; 685 cp->ca_opputfh.pf_opnum = OP_PUTROOTFH; 686 } else { 687 cp->ca_isputrootfh = FALSE; 688 cp->ca_opputfh.pf_opnum = OP_PUTFH; 689 cp->ca_opputfh.pf_filehandle.len = pfh->len; 690 bcopy(pfh->data, cp->ca_opputfh.pf_filehandle.data, pfh->len); 691 } 692 } 693