hush/util-linux/nfsmount.c

1016 lines
26 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
1999-11-18 07:58:07 +00:00
/*
* nfsmount.c -- Linux NFS mount
* Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
* numbers to be specified on the command line.
*
* Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
* Omit the call to connect() for Linux version 1.3.11 or later.
*
* Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
* Implemented the "bg", "fg" and "retry" mount options for NFS.
*
* 1999-02-22 Arkadiusz Mi<EFBFBD>kiewicz <misiek@misiek.eu.org>
* - added Native Language Support
*
* Modified by Olaf Kirch and Trond Myklebust for new NFS code,
* plus NFSv3 stuff.
1999-11-18 07:58:07 +00:00
*/
/*
* nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <time.h>
1999-11-18 07:58:07 +00:00
#include <sys/utsname.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include "busybox.h"
#undef TRUE
#undef FALSE
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include "nfsmount.h"
/* This is just a warning of a common mistake. Possibly this should be a
* uclibc faq entry rather than in busybox... */
#if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
#endif
/*
* NFS stats. The good thing with these values is that NFSv3 errors are
* a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
* no-one uses anyway), so we can happily mix code as long as we make sure
* no NFSv3 errors are returned to NFSv2 clients.
* Error codes that have a `--' in the v2 column are not part of the
* standard, but seem to be widely used nevertheless.
*/
enum nfs_stat {
NFS_OK = 0, /* v2 v3 */
NFSERR_PERM = 1, /* v2 v3 */
NFSERR_NOENT = 2, /* v2 v3 */
NFSERR_IO = 5, /* v2 v3 */
NFSERR_NXIO = 6, /* v2 v3 */
NFSERR_EAGAIN = 11, /* v2 v3 */
NFSERR_ACCES = 13, /* v2 v3 */
NFSERR_EXIST = 17, /* v2 v3 */
NFSERR_XDEV = 18, /* v3 */
NFSERR_NODEV = 19, /* v2 v3 */
NFSERR_NOTDIR = 20, /* v2 v3 */
NFSERR_ISDIR = 21, /* v2 v3 */
NFSERR_INVAL = 22, /* v2 v3 that Sun forgot */
NFSERR_FBIG = 27, /* v2 v3 */
NFSERR_NOSPC = 28, /* v2 v3 */
NFSERR_ROFS = 30, /* v2 v3 */
NFSERR_MLINK = 31, /* v3 */
NFSERR_OPNOTSUPP = 45, /* v2 v3 */
NFSERR_NAMETOOLONG = 63, /* v2 v3 */
NFSERR_NOTEMPTY = 66, /* v2 v3 */
NFSERR_DQUOT = 69, /* v2 v3 */
NFSERR_STALE = 70, /* v2 v3 */
NFSERR_REMOTE = 71, /* v2 v3 */
NFSERR_WFLUSH = 99, /* v2 */
NFSERR_BADHANDLE = 10001, /* v3 */
NFSERR_NOT_SYNC = 10002, /* v3 */
NFSERR_BAD_COOKIE = 10003, /* v3 */
NFSERR_NOTSUPP = 10004, /* v3 */
NFSERR_TOOSMALL = 10005, /* v3 */
NFSERR_SERVERFAULT = 10006, /* v3 */
NFSERR_BADTYPE = 10007, /* v3 */
NFSERR_JUKEBOX = 10008 /* v3 */
};
#define NFS_PROGRAM 100003
enum {
#ifndef NFS_FHSIZE
NFS_FHSIZE = 32,
#endif
#ifndef NFS_PORT
NFS_PORT = 2049
#endif
};
/* Disable the nls stuff */
# undef bindtextdomain
# define bindtextdomain(Domain, Directory) /* empty */
# undef textdomain
# define textdomain(Domain) /* empty */
enum {
MS_MGC_VAL = 0xc0ed0000, /* Magic number indicatng "new" flags */
MS_RDONLY = 1, /* Mount read-only */
MS_NOSUID = 2, /* Ignore suid and sgid bits */
MS_NODEV = 4, /* Disallow access to device special files */
MS_NOEXEC = 8, /* Disallow program execution */
MS_SYNCHRONOUS = 16, /* Writes are synced at once */
MS_REMOUNT = 32, /* Alter flags of a mounted FS */
MS_MANDLOCK = 64, /* Allow mandatory locks on an FS */
S_QUOTA = 128, /* Quota initialized for file/directory/symlink */
S_APPEND = 256, /* Append-only file */
S_IMMUTABLE = 512, /* Immutable file */
MS_NOATIME = 1024, /* Do not update access times. */
MS_NODIRATIME = 2048 /* Do not update directory access times */
};
1999-11-18 07:58:07 +00:00
/*
* We want to be able to compile mount on old kernels in such a way
* that the binary will work well on more recent kernels.
* Thus, if necessary we teach nfsmount.c the structure of new fields
* that will come later.
*
* Moreover, the new kernel includes conflict with glibc includes
* so it is easiest to ignore the kernel altogether (at compile time).
*/
/* NOTE: Do not make this into a 'static const int' because the pre-processor
* needs to test this value in some #if statements. */
#define NFS_MOUNT_VERSION 4
1999-11-18 07:58:07 +00:00
struct nfs2_fh {
2006-01-25 00:08:53 +00:00
char data[32];
};
struct nfs3_fh {
2006-01-25 00:08:53 +00:00
unsigned short size;
unsigned char data[64];
};
struct nfs_mount_data {
int version; /* 1 */
int fd; /* 1 */
struct nfs2_fh old_root; /* 1 */
int flags; /* 1 */
int rsize; /* 1 */
int wsize; /* 1 */
int timeo; /* 1 */
int retrans; /* 1 */
int acregmin; /* 1 */
int acregmax; /* 1 */
int acdirmin; /* 1 */
int acdirmax; /* 1 */
struct sockaddr_in addr; /* 1 */
char hostname[256]; /* 1 */
int namlen; /* 2 */
unsigned int bsize; /* 3 */
struct nfs3_fh root; /* 4 */
};
1999-11-18 07:58:07 +00:00
/* bits in the flags field */
enum {
NFS_MOUNT_SOFT = 0x0001, /* 1 */
NFS_MOUNT_INTR = 0x0002, /* 1 */
NFS_MOUNT_SECURE = 0x0004, /* 1 */
NFS_MOUNT_POSIX = 0x0008, /* 1 */
NFS_MOUNT_NOCTO = 0x0010, /* 1 */
NFS_MOUNT_NOAC = 0x0020, /* 1 */
NFS_MOUNT_TCP = 0x0040, /* 2 */
NFS_MOUNT_VER3 = 0x0080, /* 3 */
NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
NFS_MOUNT_NONLM = 0x0200 /* 3 */
};
#define UTIL_LINUX_VERSION "2.10m"
#define util_linux_version "util-linux-2.10m"
#define HAVE_inet_aton
#define HAVE_scsi_h
#define HAVE_blkpg_h
#define HAVE_kd_h
#define HAVE_termcap
#define HAVE_locale_h
#define HAVE_libintl_h
#define ENABLE_NLS
#define HAVE_langinfo_h
#define HAVE_progname
#define HAVE_openpty
#define HAVE_nanosleep
#define HAVE_personality
#define HAVE_tm_gmtoff
static char *nfs_strerror(int status);
1999-11-18 07:58:07 +00:00
#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1999-11-18 07:58:07 +00:00
enum {
EX_FAIL = 32, /* mount failure */
EX_BG = 256 /* retry in background (internal only) */
};
1999-11-18 07:58:07 +00:00
/*
* nfs_mount_version according to the sources seen at compile time.
1999-11-18 07:58:07 +00:00
*/
static int nfs_mount_version;
1999-11-18 07:58:07 +00:00
/*
* Unfortunately, the kernel prints annoying console messages
* in case of an unexpected nfs mount version (instead of
* just returning some error). Therefore we'll have to try
* and figure out what version the kernel expects.
*
* Variables:
* KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
* NFS_MOUNT_VERSION: these nfsmount sources at compile time
* nfs_mount_version: version this source and running kernel can handle
*/
static void
find_kernel_nfs_mount_version(void)
{
static int kernel_version = 0;
if (kernel_version)
return;
nfs_mount_version = NFS_MOUNT_VERSION; /* default */
1999-11-18 07:58:07 +00:00
kernel_version = get_linux_version_code();
1999-11-18 07:58:07 +00:00
if (kernel_version) {
if (kernel_version < KERNEL_VERSION(2,1,32))
nfs_mount_version = 1;
else if (kernel_version < KERNEL_VERSION(2,2,18) ||
(kernel_version >= KERNEL_VERSION(2,3,0) &&
kernel_version < KERNEL_VERSION(2,3,99)))
nfs_mount_version = 3;
else
nfs_mount_version = 4; /* since 2.3.99pre4 */
1999-11-18 07:58:07 +00:00
}
if (nfs_mount_version > NFS_MOUNT_VERSION)
nfs_mount_version = NFS_MOUNT_VERSION;
1999-11-18 07:58:07 +00:00
}
static struct pmap *
get_mountport(struct sockaddr_in *server_addr,
long unsigned prog,
long unsigned version,
long unsigned proto,
long unsigned port)
{
struct pmaplist *pmap;
static struct pmap p = {0, 0, 0, 0};
server_addr->sin_port = PMAPPORT;
pmap = pmap_getmaps(server_addr);
if (version > MAX_NFSPROT)
version = MAX_NFSPROT;
if (!prog)
prog = MOUNTPROG;
p.pm_prog = prog;
p.pm_vers = version;
p.pm_prot = proto;
p.pm_port = port;
while (pmap) {
if (pmap->pml_map.pm_prog != prog)
goto next;
if (!version && p.pm_vers > pmap->pml_map.pm_vers)
goto next;
if (version > 2 && pmap->pml_map.pm_vers != version)
goto next;
if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
goto next;
if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
(proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
(port && pmap->pml_map.pm_port != port))
goto next;
memcpy(&p, &pmap->pml_map, sizeof(p));
next:
pmap = pmap->pml_next;
}
if (!p.pm_vers)
p.pm_vers = MOUNTVERS;
if (!p.pm_port)
p.pm_port = MOUNTPORT;
if (!p.pm_prot)
p.pm_prot = IPPROTO_TCP;
return &p;
}
int nfsmount(const char *spec, const char *node, int *flags,
Major rewrite of mount, umount, losetup. Untangled lots of code, shrunk things down a bit, fixed a number of funky corner cases, added support for several new features (things like mount --move, mount --bind, lazy unounts, automatic detection of loop mounts, and so on). Probably broke several other things, but it's fixable. (Bang on it, tell me what doesn't work for you...) Note: you no longer need to say "-o loop". It does that for you when necessary. Still need to add "user mount" support, which involves making mount suid. Not too hard to do under the new infrastructure, just haven't done it yet... The previous code had the following notes, that belong in the version control comments: - * 3/21/1999 Charles P. Wright <cpwright@cpwright.com> - * searches through fstab when -a is passed - * will try mounting stuff with all fses when passed -t auto - * - * 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. - * - * 1999-10-07 Erik Andersen <andersen@codepoet.org>. - * Rewrite of a lot of code. Removed mtab usage (I plan on - * putting it back as a compile-time option some time), - * major adjustments to option parsing, and some serious - * dieting all around. - * - * 1999-11-06 mtab support is back - andersee - * - * 2000-01-12 Ben Collins <bcollins@debian.org>, Borrowed utils-linux's - * mount to add loop support. - * - * 2000-04-30 Dave Cinege <dcinege@psychosis.com> - * Rewrote fstab while loop and lower mount section. Can now do - * single mounts from fstab. Can override fstab options for single - * mount. Common mount_one call for single mounts and 'all'. Fixed - * mtab updating and stale entries. Removed 'remount' default. - *
2005-08-10 20:35:54 +00:00
char **mount_opts, int running_bg)
1999-11-18 07:58:07 +00:00
{
static char *prev_bg_host;
char hostdir[1024];
CLIENT *mclient;
char *hostname;
char *pathname;
1999-11-18 07:58:07 +00:00
char *old_opts;
char *mounthost=NULL;
1999-11-18 07:58:07 +00:00
char new_opts[1024];
struct timeval total_timeout;
enum clnt_stat clnt_stat;
struct nfs_mount_data data;
1999-11-18 07:58:07 +00:00
char *opt, *opteq;
int val;
struct hostent *hp;
struct sockaddr_in server_addr;
struct sockaddr_in mount_server_addr;
struct pmap* pm_mnt;
1999-11-18 07:58:07 +00:00
int msock, fsock;
struct timeval retry_timeout;
union {
struct fhstatus nfsv2;
struct mountres3 nfsv3;
} status;
1999-11-18 07:58:07 +00:00
struct stat statbuf;
char *s;
int port;
int mountport;
int proto;
1999-11-18 07:58:07 +00:00
int bg;
int soft;
int intr;
int posix;
int nocto;
int noac;
int nolock;
int retry;
int tcp;
int mountprog;
int mountvers;
int nfsprog;
int nfsvers;
int retval;
time_t t;
time_t prevt;
time_t timeout;
find_kernel_nfs_mount_version();
retval = EX_FAIL;
msock = fsock = -1;
mclient = NULL;
if (strlen(spec) >= sizeof(hostdir)) {
2003-03-19 09:13:01 +00:00
bb_error_msg("excessively long host:dir argument");
1999-11-18 07:58:07 +00:00
goto fail;
}
strcpy(hostdir, spec);
if ((s = strchr(hostdir, ':'))) {
hostname = hostdir;
pathname = s + 1;
1999-11-18 07:58:07 +00:00
*s = '\0';
/* Ignore all but first hostname in replicated mounts
until they can be fully supported. (mack@sgi.com) */
if ((s = strchr(hostdir, ','))) {
*s = '\0';
2003-03-19 09:13:01 +00:00
bb_error_msg("warning: multiple hostnames not supported");
1999-11-18 07:58:07 +00:00
}
} else {
2003-03-19 09:13:01 +00:00
bb_error_msg("directory to mount not in host:dir format");
1999-11-18 07:58:07 +00:00
goto fail;
}
server_addr.sin_family = AF_INET;
#ifdef HAVE_inet_aton
if (!inet_aton(hostname, &server_addr.sin_addr))
#endif
{
if ((hp = gethostbyname(hostname)) == NULL) {
2003-03-19 09:13:01 +00:00
bb_herror_msg("%s", hostname);
1999-11-18 07:58:07 +00:00
goto fail;
} else {
if (hp->h_length > sizeof(struct in_addr)) {
2003-03-19 09:13:01 +00:00
bb_error_msg("got bad hp->h_length");
1999-11-18 07:58:07 +00:00
hp->h_length = sizeof(struct in_addr);
}
memcpy(&server_addr.sin_addr,
hp->h_addr, hp->h_length);
1999-11-18 07:58:07 +00:00
}
}
memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
1999-11-18 07:58:07 +00:00
/* add IP address to mtab options for use when unmounting */
s = inet_ntoa(server_addr.sin_addr);
Major rewrite of mount, umount, losetup. Untangled lots of code, shrunk things down a bit, fixed a number of funky corner cases, added support for several new features (things like mount --move, mount --bind, lazy unounts, automatic detection of loop mounts, and so on). Probably broke several other things, but it's fixable. (Bang on it, tell me what doesn't work for you...) Note: you no longer need to say "-o loop". It does that for you when necessary. Still need to add "user mount" support, which involves making mount suid. Not too hard to do under the new infrastructure, just haven't done it yet... The previous code had the following notes, that belong in the version control comments: - * 3/21/1999 Charles P. Wright <cpwright@cpwright.com> - * searches through fstab when -a is passed - * will try mounting stuff with all fses when passed -t auto - * - * 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. - * - * 1999-10-07 Erik Andersen <andersen@codepoet.org>. - * Rewrite of a lot of code. Removed mtab usage (I plan on - * putting it back as a compile-time option some time), - * major adjustments to option parsing, and some serious - * dieting all around. - * - * 1999-11-06 mtab support is back - andersee - * - * 2000-01-12 Ben Collins <bcollins@debian.org>, Borrowed utils-linux's - * mount to add loop support. - * - * 2000-04-30 Dave Cinege <dcinege@psychosis.com> - * Rewrote fstab while loop and lower mount section. Can now do - * single mounts from fstab. Can override fstab options for single - * mount. Common mount_one call for single mounts and 'all'. Fixed - * mtab updating and stale entries. Removed 'remount' default. - *
2005-08-10 20:35:54 +00:00
old_opts = *mount_opts;
1999-11-18 07:58:07 +00:00
if (!old_opts)
old_opts = "";
if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
2003-03-19 09:13:01 +00:00
bb_error_msg("excessively long option argument");
1999-11-18 07:58:07 +00:00
goto fail;
}
sprintf(new_opts, "%s%saddr=%s",
old_opts, *old_opts ? "," : "", s);
Major rewrite of mount, umount, losetup. Untangled lots of code, shrunk things down a bit, fixed a number of funky corner cases, added support for several new features (things like mount --move, mount --bind, lazy unounts, automatic detection of loop mounts, and so on). Probably broke several other things, but it's fixable. (Bang on it, tell me what doesn't work for you...) Note: you no longer need to say "-o loop". It does that for you when necessary. Still need to add "user mount" support, which involves making mount suid. Not too hard to do under the new infrastructure, just haven't done it yet... The previous code had the following notes, that belong in the version control comments: - * 3/21/1999 Charles P. Wright <cpwright@cpwright.com> - * searches through fstab when -a is passed - * will try mounting stuff with all fses when passed -t auto - * - * 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. - * - * 1999-10-07 Erik Andersen <andersen@codepoet.org>. - * Rewrite of a lot of code. Removed mtab usage (I plan on - * putting it back as a compile-time option some time), - * major adjustments to option parsing, and some serious - * dieting all around. - * - * 1999-11-06 mtab support is back - andersee - * - * 2000-01-12 Ben Collins <bcollins@debian.org>, Borrowed utils-linux's - * mount to add loop support. - * - * 2000-04-30 Dave Cinege <dcinege@psychosis.com> - * Rewrote fstab while loop and lower mount section. Can now do - * single mounts from fstab. Can override fstab options for single - * mount. Common mount_one call for single mounts and 'all'. Fixed - * mtab updating and stale entries. Removed 'remount' default. - *
2005-08-10 20:35:54 +00:00
*mount_opts = bb_xstrdup(new_opts);
1999-11-18 07:58:07 +00:00
/* Set default options.
* rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
* let the kernel decide.
* timeo is filled in after we know whether it'll be TCP or UDP. */
memset(&data, 0, sizeof(data));
data.retrans = 3;
data.acregmin = 3;
data.acregmax = 60;
data.acdirmin = 30;
data.acdirmax = 60;
1999-11-18 07:58:07 +00:00
#if NFS_MOUNT_VERSION >= 2
data.namlen = NAME_MAX;
1999-11-18 07:58:07 +00:00
#endif
bg = 0;
soft = 0;
intr = 0;
posix = 0;
nocto = 0;
nolock = 0;
noac = 0;
retry = 10000; /* 10000 minutes ~ 1 week */
1999-11-18 07:58:07 +00:00
tcp = 0;
mountprog = MOUNTPROG;
mountvers = 0;
1999-11-18 07:58:07 +00:00
port = 0;
mountport = 0;
nfsprog = NFS_PROGRAM;
nfsvers = 0;
1999-11-18 07:58:07 +00:00
/* parse options */
for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
if ((opteq = strchr(opt, '='))) {
val = atoi(opteq + 1);
1999-11-18 07:58:07 +00:00
*opteq = '\0';
if (!strcmp(opt, "rsize"))
data.rsize = val;
else if (!strcmp(opt, "wsize"))
data.wsize = val;
else if (!strcmp(opt, "timeo"))
data.timeo = val;
else if (!strcmp(opt, "retrans"))
data.retrans = val;
else if (!strcmp(opt, "acregmin"))
data.acregmin = val;
else if (!strcmp(opt, "acregmax"))
data.acregmax = val;
else if (!strcmp(opt, "acdirmin"))
data.acdirmin = val;
else if (!strcmp(opt, "acdirmax"))
data.acdirmax = val;
else if (!strcmp(opt, "actimeo")) {
data.acregmin = val;
data.acregmax = val;
data.acdirmin = val;
data.acdirmax = val;
}
else if (!strcmp(opt, "retry"))
1999-11-18 07:58:07 +00:00
retry = val;
else if (!strcmp(opt, "port"))
port = val;
else if (!strcmp(opt, "mountport"))
mountport = val;
1999-11-18 07:58:07 +00:00
else if (!strcmp(opt, "mounthost"))
2003-03-19 09:13:01 +00:00
mounthost=bb_xstrndup(opteq+1,
strcspn(opteq+1," \t\n\r,"));
1999-11-18 07:58:07 +00:00
else if (!strcmp(opt, "mountprog"))
mountprog = val;
else if (!strcmp(opt, "mountvers"))
mountvers = val;
else if (!strcmp(opt, "nfsprog"))
nfsprog = val;
else if (!strcmp(opt, "nfsvers") ||
!strcmp(opt, "vers"))
1999-11-18 07:58:07 +00:00
nfsvers = val;
else if (!strcmp(opt, "proto")) {
if (!strncmp(opteq+1, "tcp", 3))
1999-11-18 07:58:07 +00:00
tcp = 1;
else if (!strncmp(opteq+1, "udp", 3))
1999-11-18 07:58:07 +00:00
tcp = 0;
else
2006-05-29 04:49:55 +00:00
printf("Warning: Unrecognized proto= option.\n");
1999-11-18 07:58:07 +00:00
} else if (!strcmp(opt, "namlen")) {
#if NFS_MOUNT_VERSION >= 2
if (nfs_mount_version >= 2)
data.namlen = val;
else
#endif
2006-05-29 04:49:55 +00:00
printf("Warning: Option namlen is not supported.\n");
1999-11-18 07:58:07 +00:00
} else if (!strcmp(opt, "addr"))
/* ignore */;
1999-11-18 07:58:07 +00:00
else {
2006-05-29 04:49:55 +00:00
printf("unknown nfs mount parameter: %s=%d\n", opt, val);
1999-11-18 07:58:07 +00:00
goto fail;
}
}
else {
1999-11-18 07:58:07 +00:00
val = 1;
if (!strncmp(opt, "no", 2)) {
val = 0;
opt += 2;
}
if (!strcmp(opt, "bg"))
1999-11-18 07:58:07 +00:00
bg = val;
else if (!strcmp(opt, "fg"))
1999-11-18 07:58:07 +00:00
bg = !val;
else if (!strcmp(opt, "soft"))
soft = val;
else if (!strcmp(opt, "hard"))
soft = !val;
else if (!strcmp(opt, "intr"))
intr = val;
else if (!strcmp(opt, "posix"))
posix = val;
else if (!strcmp(opt, "cto"))
nocto = !val;
else if (!strcmp(opt, "ac"))
noac = !val;
else if (!strcmp(opt, "tcp"))
tcp = val;
else if (!strcmp(opt, "udp"))
tcp = !val;
else if (!strcmp(opt, "lock")) {
if (nfs_mount_version >= 3)
nolock = !val;
else
2006-05-29 04:49:55 +00:00
printf("Warning: option nolock is not supported.\n");
1999-11-18 07:58:07 +00:00
} else {
2006-05-29 04:49:55 +00:00
printf("unknown nfs mount option: %s%s\n", val ? "" : "no", opt);
goto fail;
1999-11-18 07:58:07 +00:00
}
}
}
proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1999-11-18 07:58:07 +00:00
data.flags = (soft ? NFS_MOUNT_SOFT : 0)
| (intr ? NFS_MOUNT_INTR : 0)
| (posix ? NFS_MOUNT_POSIX : 0)
| (nocto ? NFS_MOUNT_NOCTO : 0)
| (noac ? NFS_MOUNT_NOAC : 0);
#if NFS_MOUNT_VERSION >= 2
if (nfs_mount_version >= 2)
data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
#endif
#if NFS_MOUNT_VERSION >= 3
if (nfs_mount_version >= 3)
data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
#endif
if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
2003-03-19 09:13:01 +00:00
bb_error_msg("NFSv%d not supported!", nfsvers);
return 1;
}
if (nfsvers && !mountvers)
mountvers = (nfsvers < 3) ? 1 : nfsvers;
if (nfsvers && nfsvers < mountvers) {
mountvers = nfsvers;
}
1999-11-18 07:58:07 +00:00
/* Adjust options if none specified */
if (!data.timeo)
data.timeo = tcp ? 70 : 7;
#ifdef NFS_MOUNT_DEBUG
printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
data.rsize, data.wsize, data.timeo, data.retrans);
1999-11-18 07:58:07 +00:00
printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
1999-11-18 07:58:07 +00:00
printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
port, bg, retry, data.flags);
1999-11-18 07:58:07 +00:00
printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
mountprog, mountvers, nfsprog, nfsvers);
1999-11-18 07:58:07 +00:00
printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
(data.flags & NFS_MOUNT_SOFT) != 0,
(data.flags & NFS_MOUNT_INTR) != 0,
(data.flags & NFS_MOUNT_POSIX) != 0,
(data.flags & NFS_MOUNT_NOCTO) != 0,
(data.flags & NFS_MOUNT_NOAC) != 0);
1999-11-18 07:58:07 +00:00
#if NFS_MOUNT_VERSION >= 2
printf("tcp = %d\n",
(data.flags & NFS_MOUNT_TCP) != 0);
1999-11-18 07:58:07 +00:00
#endif
#endif
data.version = nfs_mount_version;
if (*flags & MS_REMOUNT)
goto copy_data_and_return;
1999-11-18 07:58:07 +00:00
/*
* If the previous mount operation on the same host was
* backgrounded, and the "bg" for this mount is also set,
* give up immediately, to avoid the initial timeout.
*/
if (bg && !running_bg &&
prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
1999-11-18 07:58:07 +00:00
if (retry > 0)
retval = EX_BG;
return retval;
}
2006-05-26 01:48:17 +00:00
/* create mount daemon client */
1999-11-18 07:58:07 +00:00
/* See if the nfs host = mount host. */
if (mounthost) {
if (mounthost[0] >= '0' && mounthost[0] <= '9') {
mount_server_addr.sin_family = AF_INET;
mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
} else {
if ((hp = gethostbyname(mounthost)) == NULL) {
2003-03-19 09:13:01 +00:00
bb_herror_msg("%s", mounthost);
goto fail;
} else {
if (hp->h_length > sizeof(struct in_addr)) {
2003-03-19 09:13:01 +00:00
bb_error_msg("got bad hp->h_length?");
hp->h_length = sizeof(struct in_addr);
}
mount_server_addr.sin_family = AF_INET;
memcpy(&mount_server_addr.sin_addr,
hp->h_addr, hp->h_length);
}
}
1999-11-18 07:58:07 +00:00
}
/*
* The following loop implements the mount retries. On the first
* call, "running_bg" is 0. When the mount times out, and the
* "bg" option is set, the exit status EX_BG will be returned.
* For a backgrounded mount, there will be a second call by the
* child process with "running_bg" set to 1.
*
* The case where the mount point is not present and the "bg"
* option is set, is treated as a timeout. This is done to
* support nested mounts.
*
* The "retry" count specified by the user is the number of
* minutes to retry before giving up.
*
* Only the first error message will be displayed.
*/
retry_timeout.tv_sec = 3;
retry_timeout.tv_usec = 0;
total_timeout.tv_sec = 20;
total_timeout.tv_usec = 0;
timeout = time(NULL) + 60 * retry;
prevt = 0;
t = 30;
val = 1;
for (;;) {
if (bg && stat(node, &statbuf) == -1) {
if (running_bg) {
sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
1999-11-18 07:58:07 +00:00
val *= 2;
if (val > 30)
val = 30;
}
} else {
/* be careful not to use too many CPU cycles */
if (t - prevt < 30)
sleep(30);
pm_mnt = get_mountport(&mount_server_addr,
mountprog,
mountvers,
proto,
2006-01-25 00:08:53 +00:00
mountport);
1999-11-18 07:58:07 +00:00
/* contact the mount daemon via TCP */
mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1999-11-18 07:58:07 +00:00
msock = RPC_ANYSOCK;
switch (pm_mnt->pm_prot) {
case IPPROTO_UDP:
1999-11-18 07:58:07 +00:00
mclient = clntudp_create(&mount_server_addr,
pm_mnt->pm_prog,
pm_mnt->pm_vers,
retry_timeout,
&msock);
if (mclient)
break;
mount_server_addr.sin_port = htons(pm_mnt->pm_port);
msock = RPC_ANYSOCK;
case IPPROTO_TCP:
mclient = clnttcp_create(&mount_server_addr,
pm_mnt->pm_prog,
pm_mnt->pm_vers,
&msock, 0, 0);
break;
default:
mclient = 0;
1999-11-18 07:58:07 +00:00
}
if (mclient) {
/* try to mount hostname:pathname */
1999-11-18 07:58:07 +00:00
mclient->cl_auth = authunix_create_default();
/* make pointers in xdr_mountres3 NULL so
* that xdr_array allocates memory for us
*/
memset(&status, 0, sizeof(status));
if (pm_mnt->pm_vers == 3)
clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
(xdrproc_t) xdr_dirpath,
(caddr_t) &pathname,
(xdrproc_t) xdr_mountres3,
(caddr_t) &status,
total_timeout);
else
1999-11-18 07:58:07 +00:00
clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
(xdrproc_t) xdr_dirpath,
(caddr_t) &pathname,
(xdrproc_t) xdr_fhstatus,
(caddr_t) &status,
total_timeout);
1999-11-18 07:58:07 +00:00
if (clnt_stat == RPC_SUCCESS)
break; /* we're done */
if (errno != ECONNREFUSED) {
clnt_perror(mclient, "mount");
goto fail; /* don't retry */
}
if (!running_bg && prevt == 0)
clnt_perror(mclient, "mount");
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
mclient = 0;
close(msock);
} else {
if (!running_bg && prevt == 0)
clnt_pcreateerror("mount");
}
prevt = t;
}
if (!bg)
goto fail;
1999-11-18 07:58:07 +00:00
if (!running_bg) {
2003-03-19 09:13:01 +00:00
prev_bg_host = bb_xstrdup(hostname);
1999-11-18 07:58:07 +00:00
if (retry > 0)
retval = EX_BG;
goto fail;
}
t = time(NULL);
if (t >= timeout)
goto fail;
}
nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1999-11-18 07:58:07 +00:00
if (nfsvers == 2) {
if (status.nfsv2.fhs_status != 0) {
2003-03-19 09:13:01 +00:00
bb_error_msg("%s:%s failed, reason given by server: %s",
hostname, pathname,
nfs_strerror(status.nfsv2.fhs_status));
goto fail;
}
memcpy(data.root.data,
(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
NFS_FHSIZE);
#if NFS_MOUNT_VERSION >= 4
data.root.size = NFS_FHSIZE;
memcpy(data.old_root.data,
(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
NFS_FHSIZE);
#endif
} else {
#if NFS_MOUNT_VERSION >= 4
fhandle3 *my_fhandle;
if (status.nfsv3.fhs_status != 0) {
2003-03-19 09:13:01 +00:00
bb_error_msg("%s:%s failed, reason given by server: %s",
hostname, pathname,
nfs_strerror(status.nfsv3.fhs_status));
goto fail;
}
my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
memset(data.old_root.data, 0, NFS_FHSIZE);
memset(&data.root, 0, sizeof(data.root));
data.root.size = my_fhandle->fhandle3_len;
memcpy(data.root.data,
(char *) my_fhandle->fhandle3_val,
my_fhandle->fhandle3_len);
data.flags |= NFS_MOUNT_VER3;
#endif
1999-11-18 07:58:07 +00:00
}
/* create nfs socket for kernel */
if (tcp) {
if (nfs_mount_version < 3) {
2006-05-29 04:49:55 +00:00
printf("NFS over TCP is not supported.\n");
1999-11-18 07:58:07 +00:00
goto fail;
}
fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
} else
fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fsock < 0) {
2006-05-29 04:49:55 +00:00
perror("nfs socket");
1999-11-18 07:58:07 +00:00
goto fail;
}
if (bindresvport(fsock, 0) < 0) {
2006-05-29 04:49:55 +00:00
perror("nfs bindresvport");
1999-11-18 07:58:07 +00:00
goto fail;
}
if (port == 0) {
server_addr.sin_port = PMAPPORT;
port = pmap_getport(&server_addr, nfsprog, nfsvers,
tcp ? IPPROTO_TCP : IPPROTO_UDP);
1999-11-18 07:58:07 +00:00
if (port == 0)
port = NFS_PORT;
#ifdef NFS_MOUNT_DEBUG
else
2006-05-29 04:49:55 +00:00
printf("used portmapper to find NFS port\n");
1999-11-18 07:58:07 +00:00
#endif
}
#ifdef NFS_MOUNT_DEBUG
2006-05-29 04:49:55 +00:00
printf("using port %d for nfs daemon\n", port);
1999-11-18 07:58:07 +00:00
#endif
server_addr.sin_port = htons(port);
/*
* connect() the socket for kernels 1.3.10 and below only,
* to avoid problems with multihomed hosts.
* --Swen
*/
if (get_linux_version_code() <= KERNEL_VERSION(2,3,10)
&& connect(fsock, (struct sockaddr *) &server_addr,
sizeof (server_addr)) < 0) {
2006-05-29 04:49:55 +00:00
perror("nfs connect");
1999-11-18 07:58:07 +00:00
goto fail;
}
/* prepare data structure for kernel */
data.fd = fsock;
memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
strncpy(data.hostname, hostname, sizeof(data.hostname));
/* clean up */
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
close(msock);
copy_data_and_return:
*mount_opts = xrealloc(*mount_opts, sizeof(data));
memcpy(*mount_opts, &data, sizeof(data));
1999-11-18 07:58:07 +00:00
return 0;
/* abort */
fail:
1999-11-18 07:58:07 +00:00
if (msock != -1) {
if (mclient) {
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
}
close(msock);
}
if (fsock != -1)
close(fsock);
return retval;
}
1999-11-18 07:58:07 +00:00
/*
* We need to translate between nfs status return values and
* the local errno values which may not be the same.
*
* Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
* "after #include <errno.h> the symbol errno is reserved for any use,
* it cannot even be used as a struct tag or field name".
*/
#ifndef EDQUOT
#define EDQUOT ENOSPC
#endif
2005-10-20 11:17:48 +00:00
static const struct {
1999-11-18 07:58:07 +00:00
enum nfs_stat stat;
int errnum;
} nfs_errtbl[] = {
{ NFS_OK, 0 },
{ NFSERR_PERM, EPERM },
{ NFSERR_NOENT, ENOENT },
{ NFSERR_IO, EIO },
{ NFSERR_NXIO, ENXIO },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
1999-11-18 07:58:07 +00:00
#ifdef NFSERR_INVAL
{ NFSERR_INVAL, EINVAL }, /* that Sun forgot */
1999-11-18 07:58:07 +00:00
#endif
{ NFSERR_FBIG, EFBIG },
{ NFSERR_NOSPC, ENOSPC },
{ NFSERR_ROFS, EROFS },
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
{ NFSERR_NOTEMPTY, ENOTEMPTY },
{ NFSERR_DQUOT, EDQUOT },
{ NFSERR_STALE, ESTALE },
1999-11-18 07:58:07 +00:00
#ifdef EWFLUSH
{ NFSERR_WFLUSH, EWFLUSH },
1999-11-18 07:58:07 +00:00
#endif
/* Throw in some NFSv3 values for even more fun (HP returns these) */
{ 71, EREMOTE },
{ -1, EIO }
1999-11-18 07:58:07 +00:00
};
static char *nfs_strerror(int status)
1999-11-18 07:58:07 +00:00
{
int i;
static char buf[256];
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
if (nfs_errtbl[i].stat == status)
1999-11-18 07:58:07 +00:00
return strerror(nfs_errtbl[i].errnum);
}
2006-05-29 04:49:55 +00:00
sprintf(buf, "unknown nfs status return value: %d", status);
1999-11-18 07:58:07 +00:00
return buf;
}
static bool_t
xdr_fhandle (XDR *xdrs, fhandle objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_opaque (xdrs, objp, FHSIZE))
return FALSE;
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_fhstatus (XDR *xdrs, fhstatus *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_u_int (xdrs, &objp->fhs_status))
return FALSE;
1999-11-18 07:58:07 +00:00
switch (objp->fhs_status) {
case 0:
if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
return FALSE;
1999-11-18 07:58:07 +00:00
break;
default:
break;
}
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_dirpath (XDR *xdrs, dirpath *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_string (xdrs, objp, MNTPATHLEN))
return FALSE;
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
return FALSE;
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_fhandle3 (xdrs, &objp->fhandle))
return FALSE;
if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0,
sizeof (int), (xdrproc_t) xdr_int))
return FALSE;
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_enum (xdrs, (enum_t *) objp))
return FALSE;
return TRUE;
1999-11-18 07:58:07 +00:00
}
bool_t
xdr_mountres3 (XDR *xdrs, mountres3 *objp)
1999-11-18 07:58:07 +00:00
{
if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
return FALSE;
switch (objp->fhs_status) {
case MNT_OK:
if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
return FALSE;
break;
default:
break;
1999-11-18 07:58:07 +00:00
}
return TRUE;
1999-11-18 07:58:07 +00:00
}