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