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