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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <rpc/types.h>
32 #include <sys/t_lock.h>
34 #include <sys/fcntl.h>
37 #include <sys/promif.h>
41 #include <sys/bootvfs.h>
42 #include <sys/bootdebug.h>
43 #include <sys/salib.h>
44 #include <sys/sacache.h>
47 #include <rpcsvc/nfs4_prot.h>
49 #define dprintf if (boothowto & RB_DEBUG) printf
51 static struct timeval zero_timeout
= {0, 0}; /* default */
54 * NFS Version 4 specific functions
58 nfs4read(struct nfs_file
*filep
, char *buf
, size_t size
)
60 enum clnt_stat status
;
67 struct timeval timeout
;
70 static char ind
[] = "|/-\\";
73 char tagname
[] = "inetboot read";
75 bzero(&readres
, sizeof (readres
));
77 str
.utf8string_len
= sizeof (tagname
) - 1;
78 str
.utf8string_val
= tagname
;
85 if (nfs_readsize
== 0)
86 nfs_readsize
= READ4_SIZE
;
88 if (size
< nfs_readsize
)
89 readargs
.r_count
= size
;
91 readargs
.r_count
= nfs_readsize
;
93 if (filep
->fh
.fh4
.len
> 0)
94 compound_init(&readargs
.r_arg
, &str
, 0, 2, &filep
->fh
.fh4
);
96 compound_init(&readargs
.r_arg
, &str
, 0, 2, NULL
);
98 readargs
.r_opread
= OP_READ
;
100 * zero out the stateid field
102 bzero(&readargs
.r_stateid
, sizeof (readargs
.r_stateid
));
103 readargs
.r_offset
= filep
->offset
;
106 readres
.r_data_val
= buf_offset
;
108 if ((count
+ readargs
.r_count
) > size
)
109 readargs
.r_count
= size
- count
;
111 timeout
.tv_sec
= NFS_REXMIT_MIN
;
115 status
= CLNT_CALL(root_CLIENT
, NFSPROC4_COMPOUND
,
116 xdr_read4_args
, (caddr_t
)&readargs
,
117 xdr_read4_res
, (caddr_t
)&readres
,
120 if (status
== RPC_TIMEDOUT
) {
121 dprintf("NFS read(%d) timed out. Retrying...\n", readargs
.r_count
);
122 if (errno
== ETIMEDOUT
)
125 if (framing_errs
> NFS_MAX_FERRS
&&
126 readargs
.r_count
> NFS_READ_DECR
) {
127 readargs
.r_count
/= 2;
129 dprintf("NFS read size now %d.\n",
131 timeout
.tv_sec
= NFS_REXMIT_MIN
;
134 if (timeout
.tv_sec
< NFS_REXMIT_MAX
)
140 } while (status
== RPC_TIMEDOUT
);
142 if (status
!= RPC_SUCCESS
)
145 if (readres
.r_status
!= NFS4_OK
) {
146 nfs4_error(readres
.r_status
);
150 readcnt
= readres
.r_data_len
;
152 if (readres
.r_eof
== TRUE
)
155 if (readcnt
< readargs
.r_count
) {
157 if ((boothowto
& DBFLAGS
) == DBFLAGS
)
158 printf("nfs4read: partial read %d instead "
159 "of %d\n", readcnt
, readargs
.count
);
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
);
176 static vtype_t nf4_to_vt
[] = {
177 VBAD
, VREG
, VDIR
, VBLK
, VCHR
, VLNK
, VSOCK
, VFIFO
181 nfs4getattr(struct nfs_file
*nfp
, struct vattr
*vap
)
183 enum clnt_stat status
;
184 attr4_bitmap1_t bitmap1
;
185 attr4_bitmap2_t bitmap2
;
186 getattr4arg_t getattrargs
;
187 getattr4res_t getattrres
;
190 char tagname
[] = "inetboot getattr";
192 bzero(&getattrres
, sizeof (getattrres
));
196 str
.utf8string_len
= sizeof (tagname
) - 1;
197 str
.utf8string_val
= tagname
;
199 if (nfp
->fh
.fh4
.len
> 0)
200 compound_init(&getattrargs
.ga_arg
, &str
, 0, 2, &nfp
->fh
.fh4
);
202 compound_init(&getattrargs
.ga_arg
, &str
, 0, 2, NULL
);
207 getattrargs
.ga_opgetattr
= OP_GETATTR
;
209 * Set up the attribute bitmap. We pretty much need everything
210 * except for the filehandle and supported attrs.
213 bitmap1
.bm_fattr4_type
= 1;
214 bitmap1
.bm_fattr4_size
= 1;
215 bitmap1
.bm_fattr4_fileid
= 1;
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;
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
;
226 status
= CLNT_CALL(root_CLIENT
, NFSPROC4_COMPOUND
, xdr_getattr4_args
,
227 (caddr_t
)&getattrargs
, xdr_getattr4_res
,
228 (caddr_t
)&getattrres
, zero_timeout
);
230 if (status
!= RPC_SUCCESS
) {
231 dprintf("nfs4getattr: RPC error %d\n", status
);
235 if (getattrres
.gr_attr_status
!= NFS4_OK
) {
236 nfs4_error(getattrres
.gr_attr_status
);
237 return (getattrres
.gr_attr_status
);
240 bfattr4
= &getattrres
.gr_attrs
;
241 if (vap
->va_mask
& VATTR_TYPE
) {
242 if (bfattr4
->b_fattr4_type
< NF4REG
||
243 bfattr4
->b_fattr4_type
> NF4FIFO
)
246 vap
->va_type
= nf4_to_vt
[bfattr4
->b_fattr4_type
];
248 if (vap
->va_mask
& VATTR_MODE
)
249 vap
->va_mode
= (mode_t
)bfattr4
->b_fattr4_mode
;
250 if (vap
->va_mask
& VATTR_SIZE
)
251 vap
->va_size
= (uoff_t
)bfattr4
->b_fattr4_size
;
252 if (vap
->va_mask
& VATTR_NODEID
)
253 vap
->va_nodeid
= (uint64_t)bfattr4
->b_fattr4_fileid
;
255 * XXX - may need to do something more here.
257 if (vap
->va_mask
& VATTR_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
;
261 if (vap
->va_mask
& VATTR_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
;
266 if (vap
->va_mask
& VATTR_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
;
275 * Display nfs error messages.
279 nfs4_error(enum nfsstat4 status
)
281 if (!(boothowto
& RB_DEBUG
))
286 printf("NFS: No error.\n");
289 printf("NFS: Not owner.\n");
293 printf("NFS: No such file or directory.\n");
294 #endif /* NFS_OPS_DEBUG */
297 printf("NFS: IO ERROR occurred on NFS server.\n");
300 printf("NFS: No such device or address.\n");
303 printf("NFS: Permission denied.\n");
306 printf("NFS: File exists.\n");
309 printf("NFS: Cross device hard link.\n");
312 printf("NFS: Not a directory.\n");
315 printf("NFS: Is a directory.\n");
318 printf("NFS: Invalid argument.\n");
321 printf("NFS: File too large.\n");
324 printf("NFS: No space left on device.\n");
327 printf("NFS: Read-only filesystem.\n");
330 printf("NFS: Too many hard links.\n");
332 case NFS4ERR_NAMETOOLONG
:
333 printf("NFS: File name too long.\n");
335 case NFS4ERR_NOTEMPTY
:
336 printf("NFS: Directory not empty.\n");
339 printf("NFS: Disk quota exceeded.\n");
342 printf("NFS: Stale file handle.\n");
344 case NFS4ERR_BADHANDLE
:
345 printf("NFS: Illegal NFS file handle.\n");
347 case NFS4ERR_BAD_COOKIE
:
348 printf("NFS: Stale Cookie.\n");
350 case NFS4ERR_NOTSUPP
:
351 printf("NFS: Operation is not supported.\n");
353 case NFS4ERR_TOOSMALL
:
354 printf("NFS: Buffer too small.\n");
356 case NFS4ERR_SERVERFAULT
:
357 printf("NFS: Server fault.\n");
359 case NFS4ERR_BADTYPE
:
360 printf("NFS: Unsupported object type.\n");
362 case NFS4ERR_BAD_STATEID
:
363 printf("NFS: Bad stateid\n");
365 case NFS4ERR_BAD_SEQID
:
366 printf("NFS: Bad seqid\n");
369 printf("NFS: unknown error.\n");
375 * lookup one component. for multicomponent lookup use a driver like lookup().
378 nfs4lookup(struct nfs_file
*dir
, char *name
, int *nstat
)
380 static struct nfs_file cd
;
381 attr4_bitmap1_t bitmap1
;
382 lookup4arg_t lookupargs
;
383 lookup4res_t lookupres
;
384 enum clnt_stat status
;
386 char tagname
[] = "inetboot lookup";
389 * NFSv4 uses a special LOOKUPP op
390 * for looking up the parent directory.
392 if (strcmp(name
, "..") == 0)
393 return (nfs4lookupp(dir
, nstat
, NULL
));
395 *nstat
= (int)NFS4_OK
;
397 bzero(&lookupres
, sizeof (lookupres
));
400 * Check if we have a filehandle and initialize the compound
401 * with putfh or putrootfh appropriately.
403 str
.utf8string_len
= sizeof (tagname
) - 1;
404 str
.utf8string_val
= tagname
;
406 if (dir
->fh
.fh4
.len
> 0)
407 compound_init(&lookupargs
.la_arg
, &str
, 0, 3, &dir
->fh
.fh4
);
409 compound_init(&lookupargs
.la_arg
, &str
, 0, 3, NULL
);
414 lookupargs
.la_oplookup
= OP_LOOKUP
;
416 * convert the pathname from char * to utf8string
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");
425 bcopy(name
, lookupargs
.la_pathname
.utf8string_val
,
426 lookupargs
.la_pathname
.utf8string_len
);
429 * Setup the attr bitmap. All we need is the type and filehandle info
431 lookupargs
.la_opgetattr
= OP_GETATTR
;
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;
439 status
= CLNT_CALL(root_CLIENT
, NFSPROC4_COMPOUND
, xdr_lookup4_args
,
440 (caddr_t
)&lookupargs
, xdr_lookup4_res
,
441 (caddr_t
)&lookupres
, zero_timeout
);
443 if (status
!= RPC_SUCCESS
) {
444 dprintf("nfs4lookup: RPC error. status %d\n", status
);
448 if (lookupres
.lr_lookup_status
!= NFS4_OK
) {
450 dprintf("nfs4lookup: lookup status = %d\n",
451 lookupres
.lr_lookup_status
);
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
);
461 if (lookupres
.lr_attr_status
!= NFS4_OK
) {
463 dprintf("nfs4lookup: getattr status = %d\n",
464 lookupres
.lr_attr_status
);
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
);
475 * We have all the information we need to update the file pointer
477 bzero((caddr_t
)&cd
, sizeof (struct nfs_file
));
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
,
485 * Free the arg string
487 if (lookupargs
.la_pathname
.utf8string_val
!= NULL
)
488 bkmem_free(lookupargs
.la_pathname
.utf8string_val
,
489 lookupargs
.la_pathname
.utf8string_len
);
495 * lookup parent directory.
498 nfs4lookupp(struct nfs_file
*dir
, int *nstat
, uint64_t *fileid
)
500 static struct nfs_file cd
;
501 attr4_bitmap1_t bitmap1
;
502 lookupp4arg_t lookuppargs
;
503 lookup4res_t lookupres
;
504 enum clnt_stat status
;
506 char tagname
[] = "inetboot lookupp";
508 *nstat
= (int)NFS4_OK
;
510 bzero(&lookupres
, sizeof (lookupres
));
513 * Check if we have a filehandle and initialize the compound
514 * with putfh or putrootfh appropriately.
516 str
.utf8string_len
= sizeof (tagname
) - 1;
517 str
.utf8string_val
= tagname
;
519 if (dir
->fh
.fh4
.len
> 0)
520 compound_init(&lookuppargs
.la_arg
, &str
, 0, 3, &dir
->fh
.fh4
);
522 compound_init(&lookuppargs
.la_arg
, &str
, 0, 3, NULL
);
527 lookuppargs
.la_oplookupp
= OP_LOOKUPP
;
529 * Setup the attr bitmap. Normally, all we need is the type and
530 * filehandle info, but getdents might require the fileid of the
533 lookuppargs
.la_opgetattr
= OP_GETATTR
;
535 bitmap1
.bm_fattr4_type
= 1;
536 bitmap1
.bm_fattr4_filehandle
= 1;
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;
543 status
= CLNT_CALL(root_CLIENT
, NFSPROC4_COMPOUND
, xdr_lookupp4_args
,
544 (caddr_t
)&lookuppargs
, xdr_lookup4_res
,
545 (caddr_t
)&lookupres
, zero_timeout
);
547 if (status
!= RPC_SUCCESS
) {
548 dprintf("nfs4lookupp: RPC error. status %d\n", status
);
552 if (lookupres
.lr_lookup_status
!= NFS4_OK
) {
554 dprintf("nfs4lookupp: lookupp status = %d\n",
555 lookupres
.lr_lookup_status
);
557 nfs4_error(lookupres
.lr_lookup_status
);
558 *nstat
= (int)lookupres
.lr_lookup_status
;
562 if (lookupres
.lr_attr_status
!= NFS4_OK
) {
564 dprintf("nfs4lookupp: getattr status = %d\n",
565 lookupres
.lr_attr_status
);
567 nfs4_error(lookupres
.lr_attr_status
);
568 *nstat
= (int)lookupres
.lr_attr_status
;
573 * We have all the information we need to update the file pointer
575 bzero((caddr_t
)&cd
, sizeof (struct nfs_file
));
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
,
583 * Fill in the fileid if the user passed in one
586 *fileid
= lookupres
.lr_attrs
.b_fattr4_fileid
;
592 * Gets symbolic link into pathname.
595 nfs4getsymlink(struct nfs_file
*cfile
, char **path
)
597 enum clnt_stat status
;
598 readlink4arg_t readlinkargs
;
599 readlink4res_t readlinkres
;
600 static char symlink_path
[NFS_MAXPATHLEN
];
603 char tagname
[] = "inetboot getsymlink";
606 bzero(&readlinkres
, sizeof (readlinkres
));
611 str
.utf8string_len
= sizeof (tagname
) - 1;
612 str
.utf8string_val
= tagname
;
614 if (cfile
->fh
.fh4
.len
> 0)
615 compound_init(&readlinkargs
.rl_arg
, &str
, 0, 2,
618 compound_init(&readlinkargs
.rl_arg
, &str
, 0, 2, NULL
);
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
);
625 if (status
!= RPC_SUCCESS
) {
626 dprintf("nfs4getsymlink: RPC readlink error %d\n", status
);
631 if (readlinkres
.rl_status
!= NFS4_OK
) {
632 nfs4_error(readlinkres
.rl_status
);
633 error
= readlinkres
.rl_status
;
638 * Convert the utf8string to a normal character string
640 spathlen
= readlinkres
.rl_link
.utf8string_len
;
641 if (spathlen
<= 0 || readlinkres
.rl_link
.utf8string_val
== NULL
) {
643 error
= readlinkres
.rl_status
;
647 bcopy(readlinkres
.rl_link
.utf8string_val
, symlink_path
, spathlen
);
648 symlink_path
[spathlen
] = '\0';
649 *path
= symlink_path
;
655 if (readlinkres
.rl_link
.utf8string_val
!= NULL
)
656 bkmem_free(readlinkres
.rl_link
.utf8string_val
, spathlen
);
662 * Should just forget about the tag, but will leave in support for the time
666 compound_init(b_compound_t
*cp
, utf8string
*str
, uint_t mvers
, uint_t arglen
,
667 struct nfs_bfh4
*pfh
)
669 if (str
== NULL
|| str
->utf8string_len
== 0) {
670 cp
->ca_tag
.utf8string_len
= 0;
671 cp
->ca_tag
.utf8string_val
= NULL
;
673 cp
->ca_tag
.utf8string_len
= str
->utf8string_len
;
674 cp
->ca_tag
.utf8string_val
= str
->utf8string_val
;
676 cp
->ca_minorversion
= mvers
;
677 cp
->ca_argarray_len
= arglen
;
679 cp
->ca_isputrootfh
= TRUE
;
680 cp
->ca_opputfh
.pf_opnum
= OP_PUTROOTFH
;
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
);