mount.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <netdb.h>
#include "clnt.h"
#include "brpc.h"
#include "auth_inet.h"
#include "pmap.h"
#include <rpcsvc/nfs_prot.h>
#include <rpcsvc/nfs4_prot.h>
#include "nfs_inet.h"
#include <rpcsvc/bootparam.h>
#include <dhcp_impl.h>
#include "socket_inet.h"
#include "ipv4.h"
#include "mac.h"
#include <sys/bootdebug.h>
#include <errno.h>
#include "dhcpv4.h"
static char my_hostname[MAXHOSTNAMELEN];
/* in network order */
char rootopts[MAX_PATH_LEN];
extern void set_default_filename(char *); /* boot.c */
/*
* xdr routines used by mount.
*/
{
return (FALSE);
if (fhsp->fhs_status == 0) {
}
return (TRUE);
}
{
}
{
}
{
}
{
}
{
return (FALSE);
}
{
return (FALSE);
return (TRUE);
}
static int
{
int rexmit;
int resp_wait;
/*
* Wait up to 16 secs for first response, retransmitting expon.
*/
rexmit = 0; /* default retransmission interval */
resp_wait = 16;
do {
if (status == RPC_TIMEDOUT) {
dprintf("boot: %s:%s mount server not responding.\n",
}
resp_wait = 0; /* use default wait time. */
} while (status == RPC_TIMEDOUT);
return (-1);
}
/*
* Since the mount succeeded, we'll mark the filep's
* status as NFS_OK, and its type as NFDIR. If these
* points aren't the case, then we wouldn't be here.
*/
/*
* Set a reasonable lower limit on readsize
*/
512 : nfs_readsize;
return (0);
}
int
setup_root_vars(void)
{
/*
* Root server name. Required.
*/
buflen = sizeof (root_hostname);
root_hostname, &buflen)) {
} else {
dprintf("BOUND: Missing Root Server Name Option\n");
return (-1);
}
/*
* Root server IP. Required.
*/
dprintf("BOUND: Missing Root Server IP Option\n");
return (-1);
}
/*
* Root path Required.
*/
buflen = sizeof (root_pathbuf);
root_pathbuf, &buflen)) {
} else {
dprintf("BOUND: Missing Root Path Option\n");
return (-1);
}
/*
* Optional Bootfile path.
*/
buflen = sizeof (root_boot_file);
root_boot_file, &buflen)) {
}
/* if we got a boot file name, use it as the default */
if (root_boot_file[0] != '\0')
/*
* Set the NFS read size. The mount code will adjust it to
* the maximum size.
*/
if (boothowto & RB_VERBOSE) {
}
}
/*
* Optional rootopts.
*/
}
return (0);
}
static void
{
return;
switch (status) {
case MNT_OK:
printf("Mount: No error.\n");
break;
case MNT3ERR_PERM:
printf("Mount: Not owner.\n");
break;
case MNT3ERR_NOENT:
printf("Mount: No such file or directory.\n");
break;
case MNT3ERR_IO:
printf("Mount: I/O error.\n");
break;
case MNT3ERR_ACCES:
printf("Mount: Permission denied.\n");
break;
case MNT3ERR_NOTDIR:
printf("Mount: Not a directory.\n");
break;
case MNT3ERR_INVAL:
printf("Mount: Invalid argument.\n");
break;
case MNT3ERR_NAMETOOLONG:
printf("Mount: File name too long.\n");
break;
case MNT3ERR_NOTSUPP:
printf("Mount: Operation not supported.\n");
break;
case MNT3ERR_SERVERFAULT:
printf("Mount: Server fault.\n");
break;
default:
printf("Mount: unknown error.\n");
break;
}
}
static int
{
int rexmit;
int resp_wait;
/*
* Wait up to 16 secs for first response, retransmitting expon.
*/
rexmit = 0; /* default retransmission interval */
resp_wait = 16;
/*
* Try to mount using V3
*/
do {
if (status != RPC_TIMEDOUT)
break;
dprintf("boot: %s:%s mount server not responding.\n",
resp_wait = 0; /* use default wait time. */
} while (status == RPC_TIMEDOUT);
return (-1);
}
/*
* Since the mount succeeded, we'll mark the filep's
* status as NFS_OK, and its type as NF3DIR. If these
* points aren't the case, then we wouldn't be here.
*/
/*
* Hardwire in a known reasonable upper limit of 32K
*/
/*
* Set a reasonable lower limit on readsize
*/
512 : nfs_readsize;
return (0);
}
/*
* Setup v4 client for inetboot
*/
static int
{
int fd = -1;
int error = 0;
/*
* If we haven't explicitly set the port number, set to the standard
* 2049 and don't cause a rpcbind request.
*/
if (nfs_port == 0)
nfs_port = 2049;
/*
* Support TCP only
*/
if (root_CLIENT == NULL) {
return (-1);
}
/*
* Send NULL proc the server first to see if V4 exists
*/
if (rpc_stat != RPC_SUCCESS) {
dprintf("boot: NULL proc failed NFSv4 service not available\n");
return (-1);
}
/*
* Do a lookup to get to the root_path. This is nice since it can
* handle multicomponent lookups.
*/
if (error) {
return (-1);
}
/*
* Hardwire in a known reasonable upper limit of 32K
*/
/*
* Set a reasonable lower limit on readsize
*/
512 : nfs_readsize;
return (0);
}
static int
atoi(const char *p)
{
int n;
int c, neg = 0;
if (!isdigit(c = *p)) {
while (c == ' ' || c == '\t' || c == '\n')
c = *++p;
switch (c) {
case '-':
neg++;
/* FALLTHROUGH */
case '+':
c = *++p;
}
if (!isdigit(c))
return (0);
}
for (n = '0' - c; isdigit(c = *++p); ) {
n *= 10; /* two steps to avoid unnecessary overflow */
n += '0' - c; /* accum neg to avoid surprises at MAX */
}
return (neg ? n : -n);
}
/*
* Parse suboptions from a string.
* Same as getsubopt(3C).
*/
static int
{
char *s = *optionsp, *p;
int i;
if (*s == '\0')
return (-1);
if (p == NULL) {
p = s + strlen(s);
} else {
*p++ = '\0'; /* mark end and point to next */
}
*optionsp = p; /* point to next option */
if (p == NULL) {
} else {
optlen = p - s;
*valuep = ++p;
}
return (i);
}
/* no match, point value at option and return error */
*valuep = s;
return (-1);
}
/*
* The only interesting NFS mount options for initiating the kernel
* all others are ignored.
*/
static char *optlist[] = {
#define OPT_RSIZE 0
#define OPT_TIMEO 1
#define OPT_VERS 2
#define OPT_PROTO 3
#define OPT_PORT 4
};
/*
* This routine will open a device as it is known by the V2 OBP. It
* then goes thru the stuff necessary to initialize the network device,
* get our network parameters, (using DHCP or rarp/bootparams), and
* finally actually go and get the root filehandle. Sound like fun?
* Suuurrrree. Take a look.
*
* Returns 0 if things worked. -1 if we crashed and burned.
*/
int
boot_nfs_mountroot(char *str)
{
int status;
int fd;
int bufsize;
int nfs_version = 0;
int istcp = 1;
int nfs_port = 0; /* Cause pmap to get port */
if (root_CLIENT != NULL) {
root_CLIENT = NULL;
}
(void) ipv4_setpromiscuous(TRUE);
if (get_netconfig_strategy() == NCT_BOOTP_DHCP) {
if (boothowto & RB_VERBOSE)
if (dhcp() != 0 || setup_root_vars() != 0) {
(void) ipv4_setpromiscuous(FALSE);
if (boothowto & RB_VERBOSE)
return (-1);
}
/* now that we have an IP address, turn off promiscuous mode */
(void) ipv4_setpromiscuous(FALSE);
} else {
/* Use RARP/BOOTPARAMS. RARP will try forever... */
if (boothowto & RB_VERBOSE)
printf("Using RARP/BOOTPARAMS...\n");
/*
* Since there is no way to determine our netmask, and therefore
* figure out if the router we got is useful, we assume all
* services are local. Use DHCP if this bothers you.
*/
/* now that we have an IP address, turn off promiscuous mode */
(void) ipv4_setpromiscuous(FALSE);
/* get our hostname */
return (-1);
/* get our bootparams. */
root_pathbuf) == FALSE)
return (-1);
/* get our rootopts. */
rootopts);
}
/* mount root */
if (boothowto & RB_VERBOSE) {
}
/*
* Assumes we've configured the stack and thus know our
* IP address/hostname, either by using DHCP or rarp/bootparams.
*/
/*
* Parse out the interesting root options, if an invalid
* or unknown option is provided, silently ignore it and
* use the defaults.
*/
while (*opts) {
int ival;
case OPT_RSIZE:
break;
break;
case OPT_TIMEO:
break;
break;
case OPT_VERS:
break;
break;
case OPT_PROTO:
break;
istcp = 0;
else
break;
case OPT_PORT:
break;
/*
* Currently nfs_dlinet.c doesn't support setting
* the root NFS port. Delete this when it does.
*/
nfs_port = 0;
break;
default:
/*
* Unknown options are silently ignored
*/
break;
}
}
/*
* If version is set, then try that version first.
*/
switch (nfs_version) {
case NFS_VERSION:
goto domount;
break;
case NFS_V3:
goto domount;
break;
case NFS_V4:
/*
* With v4 we skip the mount and go straight to
* setting the root filehandle. Because of this we
* do things slightly differently and obtain our
* client handle first.
*/
/*
* If v4 init succeeded then we are done. Just return.
*/
return (0);
}
}
/*
* If there was no chosen version or the chosen version failed
* try all versions in order, this may still fail to boot
* at the kernel level if the options are not right, but be
* generous at this early stage.
*/
/*
* If v4 init succeeded then we are done. Just return.
*/
return (0);
}
goto domount;
return (status);
/*
* Only v2 and v3 go on from here.
*/
/*
* Create the CLIENT handle for NFS operations
*/
else
/*
* First try TCP then UDP (unless UDP asked for explicitly), if mountd
* alows this version but neither transport is available we are stuck.
*/
if (istcp) {
fd = -1;
if (root_CLIENT != NULL) {
/*
* Send NULL proc, check if the server really exists
*/
if (rpc_stat == RPC_SUCCESS)
return (0);
root_CLIENT = NULL;
}
/* Fall through to UDP case */
}
fd = -1;
if (root_CLIENT == NULL)
return (-1);
/*
* Send NULL proc, check if the server really exists
*/
if (rpc_stat == RPC_SUCCESS)
return (0);
root_CLIENT = NULL;
return (-1);
}