nfsmount: sanitize it. It had a rather peculiar idea of implementing "bg"

option - it was going to return a special flag back to caller and
expecting caller to call it again with special parameter! Also
caller was charged with calling mount() syscall...

mount: mtab support was non-functional. Enabling it revealed serious bug
which is not fixed yet.
This commit is contained in:
Denis Vlasenko 2006-09-11 17:42:44 +00:00
parent 64d7e93081
commit 00d7d6cef6
3 changed files with 221 additions and 187 deletions

View File

@ -303,8 +303,8 @@ extern int set_loop(char **device, const char *file, int offset);
extern int vdprintf(int d, const char *format, va_list ap); extern int vdprintf(int d, const char *format, va_list ap);
#endif #endif
int nfsmount(const char *spec, const char *node, int *flags, int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts);
char **mount_opts, int running_bg); int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
/* Include our own copy of struct sysinfo to avoid binary compatibility /* Include our own copy of struct sysinfo to avoid binary compatibility
* problems with Linux 2.4, which changed things. Grumble, grumble. */ * problems with Linux 2.4, which changed things. Grumble, grumble. */

View File

@ -102,7 +102,7 @@ static void append_mount_options(char **oldopts, char *newopts)
} }
/* Use the mount_options list to parse options into flags. /* Use the mount_options list to parse options into flags.
* Return list of unrecognized options in *strflags if strflags!=NULL */ * Also return list of unrecognized options if unrecognized!=NULL */
static int parse_mount_options(char *options, char **unrecognized) static int parse_mount_options(char *options, char **unrecognized)
{ {
int flags = MS_SILENT; int flags = MS_SILENT;
@ -188,7 +188,7 @@ void delete_block_backed_filesystems(void);
#endif #endif
#if ENABLE_FEATURE_MTAB_SUPPORT #if ENABLE_FEATURE_MTAB_SUPPORT
static int useMtab; static int useMtab = 1;
static int fakeIt; static int fakeIt;
#else #else
#define useMtab 0 #define useMtab 0
@ -196,8 +196,7 @@ static int fakeIt;
#endif #endif
// Perform actual mount of specific filesystem at specific location. // Perform actual mount of specific filesystem at specific location.
int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
{ {
int rc; int rc;
@ -228,7 +227,7 @@ static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
int i; int i;
if(!mountTable) if(!mountTable)
bb_error_msg("No %s",bb_path_mtab_file); bb_error_msg("no %s",bb_path_mtab_file);
// Add vfs string flags // Add vfs string flags
@ -244,7 +243,13 @@ static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
// Write and close. // Write and close.
if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind"; if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind";
addmntent(mountTable, mp); // addmntent(mountTable, mp);
if(0) bb_error_msg("buggy: addmntent(fsname='%s' dir='%s' type='%s' opts='%s')",
mp->mnt_fsname,
mp->mnt_dir,
mp->mnt_type,
mp->mnt_opts
);
endmntent(mountTable); endmntent(mountTable);
if (ENABLE_FEATURE_CLEAN_UP) if (ENABLE_FEATURE_CLEAN_UP)
if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0; if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0;
@ -319,13 +324,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
(!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) && (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
strchr(mp->mnt_fsname, ':') != NULL) strchr(mp->mnt_fsname, ':') != NULL)
{ {
if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) { rc = nfsmount(mp, vfsflags, filteropts);
bb_perror_msg("nfsmount failed");
} else {
// Strangely enough, nfsmount() doesn't actually mount() anything.
mp->mnt_type = "nfs";
rc = mount_it_now(mp, vfsflags, filteropts);
}
goto report_error; goto report_error;
} }
@ -400,7 +399,8 @@ report_error:
if (rc && errno == EBUSY && ignore_busy) rc = 0; if (rc && errno == EBUSY && ignore_busy) rc = 0;
if (rc < 0) if (rc < 0)
bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); /* perror here sometimes says "mounting ... on ... failed: Success" */
bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
return rc; return rc;
} }

View File

@ -22,6 +22,8 @@
*/ */
#include "busybox.h" #include "busybox.h"
#include <syslog.h>
#include <mntent.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#undef TRUE #undef TRUE
#undef FALSE #undef FALSE
@ -332,15 +334,8 @@ static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
return TRUE; return TRUE;
} }
#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
enum {
EX_FAIL = 32, /* mount failure */
EX_BG = 256 /* retry in background (internal only) */
};
/* /*
* nfs_mount_version according to the sources seen at compile time. * nfs_mount_version according to the sources seen at compile time.
*/ */
@ -426,50 +421,62 @@ next:
return &p; return &p;
} }
int nfsmount(const char *spec, const char *node, int *flags, static int daemonize(void)
char **mount_opts, int running_bg)
{ {
/* prev_bg_host is a -o bg support: int fd;
* "bg: if the first NFS mount attempt times out, int pid = fork();
* retry the mount in the background. if (pid < 0) /* error */
* After a mount operation is backgrounded, return -errno;
* all subsequent mounts on the same NFS server if (pid > 0) /* parent */
* will be backgrounded immediately, without first return 0;
* attempting the mount. A missing mount point is treated /* child */
* as a timeout, to allow for nested NFS mounts." fd = xopen(bb_dev_null, O_RDWR);
* dup2(fd, 0);
* But current implementation is a dirty hack - dup2(fd, 1);
* it works only if all mounts are to one host! dup2(fd, 2);
* IOW: think what will happen if you have this sequence: if (fd > 2) close(fd);
* mount a.a.a.a:/dir /mnt/a -o bg setsid();
* mount b.b.b.b:/dir /mnt/b -o bg openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
* mount a.a.a.a:/dir /mnt/a -o bg logmode = LOGMODE_SYSLOG;
* mount b.b.b.b:/dir /mnt/b -o bg return 1;
*/ }
static char *prev_bg_host; // TODO
char *hostdir; static inline int we_saw_this_host_before(const char *hostname)
{
return 0;
}
/* RPC strerror analogs are terminally idiotic:
* *mandatory* prefix and \n at end.
* This hopefully helps. Usage:
* error_msg_rpc(clnt_*error*(" ")) */
static void error_msg_rpc(const char *msg)
{
size_t len;
while (msg[0] == ' ' || msg[0] == ':') msg++;
len = strlen(msg);
while (len && msg[len-1] == '\n') len--;
bb_error_msg("%.*s", len, msg);
}
int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
{
CLIENT *mclient; CLIENT *mclient;
char *hostname; char *hostname;
char *pathname; char *pathname;
char *old_opts;
char *mounthost; char *mounthost;
struct timeval total_timeout;
enum clnt_stat clnt_stat;
struct nfs_mount_data data; struct nfs_mount_data data;
char *opt, *opteq; char *opt;
int val;
struct hostent *hp; struct hostent *hp;
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
struct sockaddr_in mount_server_addr; struct sockaddr_in mount_server_addr;
struct pmap* pm_mnt;
int msock, fsock; int msock, fsock;
struct timeval retry_timeout;
union { union {
struct fhstatus nfsv2; struct fhstatus nfsv2;
struct mountres3 nfsv3; struct mountres3 nfsv3;
} status; } status;
struct stat statbuf; int daemonized;
char *s; char *s;
int port; int port;
int mountport; int mountport;
@ -488,29 +495,27 @@ int nfsmount(const char *spec, const char *node, int *flags,
int nfsprog; int nfsprog;
int nfsvers; int nfsvers;
int retval; int retval;
time_t t;
time_t prevt;
time_t timeout;
find_kernel_nfs_mount_version(); find_kernel_nfs_mount_version();
/* NB: old_opts and hostdir must be free()d prior to return! */ daemonized = 0;
old_opts = NULL;
mounthost = NULL; mounthost = NULL;
retval = EX_FAIL; retval = ETIMEDOUT;
msock = fsock = -1; msock = fsock = -1;
mclient = NULL; mclient = NULL;
hostdir = xstrdup(spec); /* NB: hostname, mounthost, filteropts must be free()d prior to return */
filteropts = xstrdup(filteropts); /* going to trash it later... */
hostname = xstrdup(mp->mnt_fsname);
/* mount_main() guarantees that ':' is there */ /* mount_main() guarantees that ':' is there */
s = strchr(hostdir, ':'); s = strchr(hostname, ':');
hostname = hostdir;
pathname = s + 1; pathname = s + 1;
*s = '\0'; *s = '\0';
/* Ignore all but first hostname in replicated mounts /* Ignore all but first hostname in replicated mounts
until they can be fully supported. (mack@sgi.com) */ until they can be fully supported. (mack@sgi.com) */
s = strchr(hostdir, ','); s = strchr(hostname, ',');
if (s) { if (s) {
*s = '\0'; *s = '\0';
bb_error_msg("warning: multiple hostnames not supported"); bb_error_msg("warning: multiple hostnames not supported");
@ -538,12 +543,15 @@ int nfsmount(const char *spec, const char *node, int *flags,
/* add IP address to mtab options for use when unmounting */ /* add IP address to mtab options for use when unmounting */
s = inet_ntoa(server_addr.sin_addr); if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
old_opts = *mount_opts; mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
if (!old_opts) } else {
old_opts = xstrdup(""); char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
*mount_opts = xasprintf("%s%saddr=%s", mp->mnt_opts[0] ? "," : "",
old_opts, *old_opts ? "," : "", s); inet_ntoa(server_addr.sin_addr));
free(mp->mnt_opts);
mp->mnt_opts = tmp;
}
/* Set default options. /* Set default options.
* rsize/wsize (and bsize, for ver >= 3) are left 0 in order to * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
@ -576,10 +584,10 @@ int nfsmount(const char *spec, const char *node, int *flags,
/* parse options */ /* parse options */
for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
opteq = strchr(opt, '='); char *opteq = strchr(opt, '=');
if (opteq) { if (opteq) {
val = atoi(opteq + 1); int val = atoi(opteq + 1);
*opteq = '\0'; *opteq = '\0';
if (!strcmp(opt, "rsize")) if (!strcmp(opt, "rsize"))
data.rsize = val; data.rsize = val;
@ -641,7 +649,7 @@ int nfsmount(const char *spec, const char *node, int *flags,
} }
} }
else { else {
val = 1; int val = 1;
if (!strncmp(opt, "no", 2)) { if (!strncmp(opt, "no", 2)) {
val = 0; val = 0;
opt += 2; opt += 2;
@ -723,19 +731,20 @@ int nfsmount(const char *spec, const char *node, int *flags,
data.version = nfs_mount_version; data.version = nfs_mount_version;
if (*flags & MS_REMOUNT) if (vfsflags & MS_REMOUNT)
goto copy_data_and_return; goto do_mount;
/* /*
* If the previous mount operation on the same host was * If the previous mount operation on the same host was
* backgrounded, and the "bg" for this mount is also set, * backgrounded, and the "bg" for this mount is also set,
* give up immediately, to avoid the initial timeout. * give up immediately, to avoid the initial timeout.
*/ */
if (bg && !running_bg && if (bg && we_saw_this_host_before(hostname)) {
prev_bg_host && strcmp(hostname, prev_bg_host) == 0) { daemonized = daemonize(); /* parent or error */
if (retry > 0) if (daemonized <= 0) { /* parent or error */
retval = EX_BG; retval = -daemonized;
goto ret; goto ret;
}
} }
/* create mount daemon client */ /* create mount daemon client */
@ -745,7 +754,8 @@ int nfsmount(const char *spec, const char *node, int *flags,
mount_server_addr.sin_family = AF_INET; mount_server_addr.sin_family = AF_INET;
mount_server_addr.sin_addr.s_addr = inet_addr(hostname); mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
} else { } else {
if ((hp = gethostbyname(mounthost)) == NULL) { hp = gethostbyname(mounthost);
if (hp == NULL) {
bb_herror_msg("%s", mounthost); bb_herror_msg("%s", mounthost);
goto fail; goto fail;
} else { } else {
@ -761,11 +771,9 @@ int nfsmount(const char *spec, const char *node, int *flags,
} }
/* /*
* The following loop implements the mount retries. On the first * The following loop implements the mount retries. When the mount
* call, "running_bg" is 0. When the mount times out, and the * times out, and the "bg" option is set, we background ourself
* "bg" option is set, the exit status EX_BG will be returned. * and continue trying.
* 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" * The case where the mount point is not present and the "bg"
* option is set, is treated as a timeout. This is done to * option is set, is treated as a timeout. This is done to
@ -773,115 +781,122 @@ int nfsmount(const char *spec, const char *node, int *flags,
* *
* The "retry" count specified by the user is the number of * The "retry" count specified by the user is the number of
* minutes to retry before giving up. * minutes to retry before giving up.
*
* Only the first error message will be displayed.
*/ */
retry_timeout.tv_sec = 3; {
retry_timeout.tv_usec = 0; struct timeval total_timeout;
total_timeout.tv_sec = 20; struct timeval retry_timeout;
total_timeout.tv_usec = 0; struct pmap* pm_mnt;
timeout = time(NULL) + 60 * retry; time_t t;
prevt = 0; time_t prevt;
t = 30; time_t timeout;
val = 1;
for (;;) {
if (bg && stat(node, &statbuf) == -1) {
if (running_bg) {
sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
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, retry_timeout.tv_sec = 3;
mountprog, retry_timeout.tv_usec = 0;
mountvers, total_timeout.tv_sec = 20;
proto, total_timeout.tv_usec = 0;
mountport); timeout = time(NULL) + 60 * retry;
prevt = 0;
t = 30;
retry:
/* be careful not to use too many CPU cycles */
if (t - prevt < 30)
sleep(30);
/* contact the mount daemon via TCP */ pm_mnt = get_mountport(&mount_server_addr,
mount_server_addr.sin_port = htons(pm_mnt->pm_port); mountprog,
msock = RPC_ANYSOCK; mountvers,
proto,
mountport);
nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
switch (pm_mnt->pm_prot) { /* contact the mount daemon via TCP */
case IPPROTO_UDP: mount_server_addr.sin_port = htons(pm_mnt->pm_port);
mclient = clntudp_create(&mount_server_addr, msock = RPC_ANYSOCK;
switch (pm_mnt->pm_prot) {
case IPPROTO_UDP:
mclient = clntudp_create(&mount_server_addr,
pm_mnt->pm_prog, pm_mnt->pm_prog,
pm_mnt->pm_vers, pm_mnt->pm_vers,
retry_timeout, retry_timeout,
&msock); &msock);
if (mclient) if (mclient)
break; break;
mount_server_addr.sin_port = htons(pm_mnt->pm_port); mount_server_addr.sin_port = htons(pm_mnt->pm_port);
msock = RPC_ANYSOCK; msock = RPC_ANYSOCK;
case IPPROTO_TCP: case IPPROTO_TCP:
mclient = clnttcp_create(&mount_server_addr, mclient = clnttcp_create(&mount_server_addr,
pm_mnt->pm_prog, pm_mnt->pm_prog,
pm_mnt->pm_vers, pm_mnt->pm_vers,
&msock, 0, 0); &msock, 0, 0);
break; break;
default: default:
mclient = 0; mclient = 0;
}
if (mclient) {
/* try to mount hostname:pathname */
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
clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
(xdrproc_t) xdr_dirpath,
(caddr_t) &pathname,
(xdrproc_t) xdr_fhstatus,
(caddr_t) &status,
total_timeout);
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 (!mclient) {
if (!daemonized && prevt == 0)
error_msg_rpc(clnt_spcreateerror(" "));
} else {
enum clnt_stat clnt_stat;
/* try to mount hostname:pathname */
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
clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
(xdrproc_t) xdr_dirpath,
(caddr_t) &pathname,
(xdrproc_t) xdr_fhstatus,
(caddr_t) &status,
total_timeout);
if (clnt_stat == RPC_SUCCESS)
goto prepare_kernel_data; /* we're done */
if (errno != ECONNREFUSED) {
error_msg_rpc(clnt_sperror(mclient, " "));
goto fail; /* don't retry */
}
/* Connection refused */
if (!daemonized && prevt == 0) /* print just once */
error_msg_rpc(clnt_sperror(mclient, " "));
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
mclient = 0;
close(msock);
}
/* Timeout. We are going to retry... maybe */
if (!bg) if (!bg)
goto fail; goto fail;
if (!running_bg) { if (!daemonized) {
prev_bg_host = xstrdup(hostname); daemonized = daemonize();
if (retry > 0) if (daemonized <= 0) { /* parent or error */
retval = EX_BG; retval = -daemonized;
goto fail; goto ret;
}
} }
prevt = t;
t = time(NULL); t = time(NULL);
if (t >= timeout) if (t >= timeout)
/* TODO error message */
goto fail; goto fail;
goto retry;
} }
nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
prepare_kernel_data:
if (nfsvers == 2) { if (nfsvers == 2) {
if (status.nfsv2.fhs_status != 0) { if (status.nfsv2.fhs_status != 0) {
@ -927,17 +942,17 @@ int nfsmount(const char *spec, const char *node, int *flags,
} else } else
fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fsock < 0) { if (fsock < 0) {
perror("nfs socket"); bb_perror_msg("nfs socket");
goto fail; goto fail;
} }
if (bindresvport(fsock, 0) < 0) { if (bindresvport(fsock, 0) < 0) {
perror("nfs bindresvport"); bb_perror_msg("nfs bindresvport");
goto fail; goto fail;
} }
if (port == 0) { if (port == 0) {
server_addr.sin_port = PMAPPORT; server_addr.sin_port = PMAPPORT;
port = pmap_getport(&server_addr, nfsprog, nfsvers, port = pmap_getport(&server_addr, nfsprog, nfsvers,
tcp ? IPPROTO_TCP : IPPROTO_UDP); tcp ? IPPROTO_TCP : IPPROTO_UDP);
if (port == 0) if (port == 0)
port = NFS_PORT; port = NFS_PORT;
#ifdef NFS_MOUNT_DEBUG #ifdef NFS_MOUNT_DEBUG
@ -961,16 +976,34 @@ int nfsmount(const char *spec, const char *node, int *flags,
auth_destroy(mclient->cl_auth); auth_destroy(mclient->cl_auth);
clnt_destroy(mclient); clnt_destroy(mclient);
close(msock); close(msock);
copy_data_and_return:
*mount_opts = xrealloc(*mount_opts, sizeof(data));
memcpy(*mount_opts, &data, sizeof(data));
retval = 0; if (bg) {
/* We must wait until mount directory is available */
struct stat statbuf;
int delay = 1;
while (stat(mp->mnt_dir, &statbuf) == -1) {
if (!daemonized) {
daemonized = daemonize();
if (daemonized <= 0) { /* parent or error */
retval = -daemonized;
goto ret;
}
}
sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
delay *= 2;
if (delay > 30)
delay = 30;
}
}
do_mount: /* perform actual mount */
mp->mnt_type = "nfs";
retval = mount_it_now(mp, vfsflags, (char*)&data);
goto ret; goto ret;
/* abort */ fail: /* abort */
fail:
if (msock != -1) { if (msock != -1) {
if (mclient) { if (mclient) {
auth_destroy(mclient->cl_auth); auth_destroy(mclient->cl_auth);
@ -982,7 +1015,8 @@ fail:
close(fsock); close(fsock);
ret: ret:
free(hostdir); free(hostname);
free(old_opts); free(mounthost);
free(filteropts);
return retval; return retval;
} }