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