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