xref: /illumos-gate/usr/src/stand/lib/fs/nfs/nfs4ops.c (revision 1fa2a66491e7d8ae0be84e7da4da8e812480c710)
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
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
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
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 *
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 *
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
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
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