1
0
Files
irix-657m-src/eoe/cmd/sun/automount/auto_mount.c
2022-09-29 17:59:04 +03:00

1396 lines
32 KiB
C

#ifndef lint
static char sccsid[] = "@(#)auto_mount.c 1.5 90/07/24 4.1NFSSRC Copyr 1990 Sun Micro";
#endif
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/fs/export.h>
#include <sys/fs/bds.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <mntent.h>
#include <netdb.h>
#include <errno.h>
#include <sat.h>
#include "nfs_prot.h"
typedef nfs_fh fhandle_t;
#define NFS3_FHSIZE 64
struct nfs_fh3 {
u_int fh3_length;
union nfs_fh3_u {
struct nfs_fh3_i {
fhandle_t fh3_i;
} nfs_fh3_i;
char data[NFS3_FHSIZE];
} fh3_u;
};
#define fh3_fsid fh3_u.nfs_fh3_i.fh3_i.fh_fsid
#define fh3_len fh3_u.nfs_fh3_i.fh3_i.fh_len /* fid length */
#define fh3_data fh3_u.nfs_fh3_i.fh3_i.fh_data /* fid bytes */
#define fh3_xlen fh3_u.nfs_fh3_i.fh3_i.fh_xlen
#define fh3_xdata fh3_u.nfs_fh3_i.fh3_i.fh_xdata
typedef struct nfs_fh3 nfs_fh3;
#include <rpcsvc/mount.h>
#define NFSCLIENT
#include <sys/mount.h>
#include "automount.h"
#define MAXHOSTS 20
struct mapfs *find_server();
struct filsys *already_mounted();
void addtomtab();
char *inet_ntoa();
extern int verbose, trace;
static long last_mtab_time = 0;
prunenohide(me)
struct mapent *me;
{
struct mapent *n, **nextme;
char *slash;
int mfslen;
mfslen = strlen(me->map_fs->mfs_dir);
nextme = &me->map_next;
while (n = *nextme) {
if (n->map_exflags == EX_NOHIDE) {
slash = strrchr(n->map_fs->mfs_dir, '/');
if ((mfslen == 1 && slash == n->map_fs->mfs_dir)
|| (slash == &n->map_fs->mfs_dir[mfslen]
&& !strncmp(me->map_fs->mfs_dir,
n->map_fs->mfs_dir,
mfslen))) {
/* prune this guy's kids */
prunenohide(n);
/* remove from chain */
*nextme = n->map_next;
n->map_next = NULL;
free_mapent(n);
continue;
}
}
nextme = &n->map_next;
}
}
nfsstat
do_mount(dir, me, rootfs, linkpath)
struct autodir *dir;
struct mapent *me;
struct filsys **rootfs;
char **linkpath;
{
char mntpnt[MAXPATHLEN];
static char linkbuf[MAXPATHLEN];
enum clnt_stat pingmount();
struct filsys *fs, *tfs;
struct mapfs *mfs;
struct mapent *m, *mapnext;
nfsstat status = NFSERR_NOENT;
struct in_addr prevhost;
int imadeit;
struct stat stbuf;
extern dev_t tmpdev;
enum nfs_level nfs_level = NFS2;
*rootfs = NULL;
*linkpath = "";
prevhost.s_addr = (u_long)0;
for (m = me ; m ; m = mapnext) {
mapnext = m->map_next;
(void) sprintf(mntpnt, "%s%s%s%s", tmpdir,
dir->dir_name, me->map_root, m->map_mntpnt);
/* check whether the mntpnt is already mounted on */
for (fs = HEAD(struct filsys, fs_q); fs;
fs = NEXT(struct filsys, fs)) {
if ((m == me || fs->fs_rootfs == fs) &&
(strcmp(mntpnt, fs->fs_mntpnt) == 0)) {
(void) sprintf(linkbuf, "%s%s%s%s",
tmpdir,
dir->dir_name,
me->map_root,
me->map_fs->mfs_subdir);
if (trace > 1)
(void) fprintf(stderr,
"renew link for %s\n",
linkbuf);
*linkpath = linkbuf;
*rootfs = fs->fs_rootfs;
return (NFS_OK);
}
}
tfs = NULL;
mfs = find_server(m, &tfs, m == me, prevhost);
if (mfs == NULL)
continue;
/*
* It may be possible to return a symlink
* to an existing mount point without
* actually having to do a mount.
*/
if (me->map_next == NULL && *me->map_mntpnt == '\0') {
/* Is it my host ? */
if ((mfs->mfs_addr.s_addr == my_addr.s_addr ||
mfs->mfs_addr.s_addr == htonl(INADDR_LOOPBACK))) {
(void) strcpy(linkbuf, mfs->mfs_dir);
(void) strcat(linkbuf, mfs->mfs_subdir);
*linkpath = linkbuf;
if (trace > 1)
(void) fprintf(stderr,
"It's on my host\n");
return (NFS_OK);
}
/*
* An existing mount point ?
* XXX Note: this is a bit risky - the
* mount may belong to another automount
* daemon - it could unmount it anytime and
* this symlink would then point to an empty
* or non-existent mount point.
*/
if (tfs != NULL) {
if (trace > 1)
(void) fprintf(stderr,
"already mounted %s:%s on %s (%s)\n",
tfs->fs_host, tfs->fs_dir,
tfs->fs_mntpnt, tfs->fs_opts);
(void) strcpy(linkbuf, tfs->fs_mntpnt);
(void) strcat(linkbuf, mfs->mfs_subdir);
*linkpath = linkbuf;
*rootfs = tfs;
return (NFS_OK);
}
}
if (nomounts)
return (NFSERR_PERM);
/* Create the mount point if necessary */
imadeit = 0;
if (stat(mntpnt, &stbuf) != 0) {
if (mkdir_r(mntpnt) == 0) {
imadeit = 1;
if (stat(mntpnt, &stbuf) < 0) {
syslog(LOG_ERR,
"Couldn't stat created mountpoint %s: %m",
mntpnt);
continue;
}
} else {
if (verbose)
syslog(LOG_ERR,
"Couldn't create mountpoint %s: %m",
mntpnt);
if (trace > 1)
(void) fprintf(stderr,
"couldn't create mntpnt %s\n",
mntpnt);
continue;
}
}
if (verbose && *rootfs == NULL && tmpdev != stbuf.st_dev)
syslog(LOG_ERR, "WARNING: %s already mounted on",
mntpnt);
/* Now do the mount */
tfs = NULL;
nfs_level = (((me->map_nfs_level == NFS3) ||
(dir->dir_nfs_level == NFS3)) &&
(me->map_nfs_level != NFS2_OVERRIDE)) ? NFS3 : NFS2;
#ifdef never
syslog(LOG_INFO, "do_mount: %s %s %s",
mfs->mfs_host, mfs->mfs_dir,
(nfs_level == NFS2) ? "NFS2" : "NFS3");
#endif /* never */
if (nfs_level == NFS2) {
status = nfsmount(mfs->mfs_host, mfs->mfs_addr,
mfs->mfs_dir, mntpnt, m->map_mntopts, &tfs,
MOUNTVERS, MOUNTVERS_SGI_ORIG);
} else {
status = nfsmount(mfs->mfs_host, mfs->mfs_addr,
mfs->mfs_dir, mntpnt, m->map_mntopts, &tfs,
MOUNTVERS3, MOUNTVERS_SGI_ORIG_3);
}
if (status == NFS_OK) {
if (*rootfs == NULL) {
*rootfs = tfs;
(void) sprintf(linkbuf, "%s%s%s%s",
tmpdir, dir->dir_name,
me->map_root,
mfs->mfs_subdir);
*linkpath = linkbuf;
}
tfs->fs_rootfs = *rootfs;
tfs->fs_mntpntdev = stbuf.st_dev;
if (stat(mntpnt, &stbuf) < 0) {
syslog(LOG_ERR, "Couldn't stat: %s: %m",
mntpnt);
} else {
tfs->fs_mountdev = stbuf.st_dev;
}
prevhost.s_addr = mfs->mfs_addr.s_addr;
prunenohide(m);
mapnext = m->map_next;
} else {
if (imadeit)
safe_rmdir(mntpnt);
mfs->mfs_ignore = 1;
prevhost.s_addr = (u_long)0;
mapnext = m;
continue; /* try another server */
}
}
if (*rootfs != NULL) {
addtomtab(*rootfs);
return (NFS_OK);
}
return (status);
}
struct mapfs *
find_server(me, fsp, rootmount, preferred)
struct mapent *me;
struct filsys **fsp;
int rootmount;
struct in_addr preferred;
{
int entrycount, localcount, serverok;
struct mapfs *mfs, *mfs_one;
struct hostent *hp;
struct in_addr addrs[MAXHOSTS], addr, *addrp, trymany();
/*
* get addresses & see if any are myself
* or were mounted from previously in a
* hierarchical mount.
*/
entrycount = localcount = 0;
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) {
if (mfs->mfs_addr.s_addr == 0) {
hp = gethostbyname(mfs->mfs_host);
if (hp)
bcopy(hp->h_addr, (char *)&mfs->mfs_addr,
hp->h_length);
}
if (mfs->mfs_addr.s_addr && !mfs->mfs_ignore) {
entrycount++;
mfs_one = mfs;
if (mfs->mfs_addr.s_addr == my_addr.s_addr ||
mfs->mfs_addr.s_addr == htonl(INADDR_LOOPBACK) ||
mfs->mfs_addr.s_addr == preferred.s_addr) {
return (mfs);
}
}
}
if (entrycount == 0)
return (NULL);
/* see if any already mounted */
if (rootmount)
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) {
if (mfs->mfs_ignore)
continue;
*fsp = already_mounted(mfs->mfs_host, mfs->mfs_dir,
me->map_mntopts, mfs->mfs_addr,
&serverok);
if (*fsp)
return (mfs);
/* fail if couldn't ping server */
if (!serverok)
return (NULL);
}
if (entrycount == 1) /* no replication involved */
return (mfs_one);
/* try local net */
addrp = addrs;
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) {
if (mfs->mfs_ignore)
continue;
if (inet_netof(mfs->mfs_addr) == inet_netof(my_addr)) {
localcount++;
*addrp++ = mfs->mfs_addr;
}
}
if (localcount > 0) { /* got some */
(*addrp).s_addr = 0; /* terminate list */
addr = trymany(addrs, mount_timeout / 2);
if (addr.s_addr) { /* got one */
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
if (addr.s_addr == mfs->mfs_addr.s_addr)
return (mfs);
}
}
if (entrycount == localcount)
return (NULL);
/* now try them all */
addrp = addrs;
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
if (!mfs->mfs_ignore)
*addrp++ = mfs->mfs_addr;
(*addrp).s_addr = 0; /* terminate list */
addr = trymany(addrs, mount_timeout / 2);
if (addr.s_addr) { /* got one */
for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
if (addr.s_addr == mfs->mfs_addr.s_addr)
return (mfs);
}
return (NULL);
}
/*
* Read the mtab and correlate with internal fs info
*/
void
read_mtab()
{
struct stat stbuf;
struct filsys *fs, *fsnext;
FILE *mtab;
register struct mntent *mt;
int found, c;
long mtab_size;
struct stat st;
struct hostent *hp;
char *tmphost, *tmppath, *p, tmpc;
/* reset the present flags */
for (fs = HEAD(struct filsys, fs_q); fs;
fs = NEXT(struct filsys, fs))
fs->fs_present = 0;
/* now see what's been mounted */
mtab = setmntent(MOUNTED, "r");
if (fstat(fileno(mtab), &st) < 0) {
syslog(LOG_ERR, "couldn't stat %s: %m", MOUNTED);
return;
}
mtab_size = st.st_size;
for (c = 1 ;; c++) {
mt = getmntent(mtab);
if (mt == NULL) {
if (ftell(mtab) >= mtab_size)
break; /* it's really EOF */
syslog(LOG_ERR, "WARNING: %s: line %d: bad entry",
MOUNTED, c);
continue;
}
if (strcmp(mt->mnt_type, MNTTYPE_NFS) != 0)
continue;
p = index(mt->mnt_fsname, ':');
if (p == NULL)
continue;
tmpc = *p;
*p = '\0';
tmphost = mt->mnt_fsname;
tmppath = p+1;
if (tmppath[0] != '/')
continue;
found = 0;
for (fs = HEAD(struct filsys, fs_q); fs;
fs = NEXT(struct filsys, fs)) {
if (strcmp(mt->mnt_dir, fs->fs_mntpnt) == 0 &&
strcmp(tmphost, fs->fs_host) == 0 &&
strcmp(tmppath, fs->fs_dir) == 0 &&
(fs->fs_mine ||
strcmp(mt->mnt_opts, fs->fs_opts) == 0)) {
fs->fs_present = 1;
found++;
break;
}
}
if (!found) {
fs = alloc_fs(tmphost, tmppath,
mt->mnt_dir, mt->mnt_opts);
if (fs == NULL)
break;
fs->fs_present = 1;
hp = gethostbyname(tmphost);
if (hp != NULL) {
bcopy(hp->h_addr, &fs->fs_addr.sin_addr, hp->h_length);
}
}
*p = tmpc;
}
(void) endmntent(mtab);
/* free fs's that are no longer present */
for (fs = HEAD(struct filsys, fs_q); fs; fs = fsnext) {
fsnext = NEXT(struct filsys, fs);
if (!fs->fs_present) {
if (fs->fs_mine)
syslog(LOG_ERR,
"%s:%s no longer mounted",
fs->fs_host, fs->fs_dir);
if (trace > 1)
(void) fprintf(stderr,
"%s:%s no longer mounted\n",
fs->fs_host, fs->fs_dir);
flush_links(fs);
free_filsys(fs);
}
}
if (stat(MOUNTED, &stbuf) != 0) {
syslog(LOG_ERR, "Cannot stat %s: %m", MOUNTED);
return;
}
last_mtab_time = stbuf.st_mtime;
}
/*
* If mtab has changed update internal fs info
*/
void
check_mtab()
{
struct stat stbuf;
int omask;
if (stat(MOUNTED, &stbuf) != 0) {
syslog(LOG_ERR, "Cannot stat %s: %m", MOUNTED);
return;
}
if (stbuf.st_mtime != last_mtab_time) {
omask = sigblock(sigmask(SIGHUP));
read_mtab();
(void)sigsetmask(omask);
}
}
/*
* Search the mount table to see if the given file system is already
* mounted.
*/
struct filsys *
already_mounted(host, fsname, opts, hostaddr, serverok)
char *host;
char *fsname;
char *opts;
struct in_addr hostaddr;
int *serverok; /* flag (in)ability to ping server */
{
struct filsys *fs;
struct stat stbuf;
struct mntent m1, m2;
int has1, has2;
int fd;
struct autodir *dir;
int mydir;
extern int verbose;
check_mtab();
m1.mnt_opts = opts;
*serverok = TRUE;
for (fs = HEAD(struct filsys, fs_q); fs; fs = NEXT(struct filsys, fs)){
if (strcmp(fsname, fs->fs_dir) != 0)
continue;
if (hostaddr.s_addr != fs->fs_addr.sin_addr.s_addr)
continue;
/*
* Check it's not on one of my mount points.
* I might be mounted on top of a previous
* mount of the same file system.
*/
for (mydir = 0, dir = HEAD(struct autodir, dir_q); dir;
dir = NEXT(struct autodir, dir)) {
if (strcmp(dir->dir_name, fs->fs_mntpnt) == 0) {
mydir = 1;
if (verbose)
syslog(LOG_ERR,
"%s:%s already mounted on %s",
host, fsname, fs->fs_mntpnt);
break;
}
}
if (mydir)
continue;
m2.mnt_opts = fs->fs_opts;
has1 = hasmntopt(&m1, MNTOPT_RO) != NULL;
has2 = hasmntopt(&m2, MNTOPT_RO) != NULL;
if (has1 != has2)
continue;
has1 = hasmntopt(&m1, MNTOPT_NOSUID) != NULL;
has2 = hasmntopt(&m2, MNTOPT_NOSUID) != NULL;
if (has1 != has2)
continue;
has1 = hasmntopt(&m1, MNTOPT_SOFT) != NULL;
has2 = hasmntopt(&m2, MNTOPT_SOFT) != NULL;
if (has1 != has2)
continue;
has1 = hasmntopt(&m1, MNTOPT_NOINTR) != NULL;
has2 = hasmntopt(&m2, MNTOPT_NOINTR) != NULL;
if (has1 != has2)
continue;
/*
* Use pingmount to avoid hanging in open().
* dir under mountpoint may have been removed by other means
* If so, we get a stale file handle error here if we fsync
* the dir to remove the attr cache info
*/
if (pingmount(fs->fs_addr.sin_addr) != RPC_SUCCESS) {
if (trace > 3)
(void) fprintf(stderr, "dead server");
*serverok = FALSE;
return(0);
}
fd = open(fs->fs_mntpnt, 0);
if (fd < 0)
continue;
if (fsync(fd) != 0 || fstat(fd, &stbuf) != 0) {
(void) close(fd);
continue;
}
(void) close(fd);
return (fs);
}
return(0);
}
nfsunmount(fs)
struct filsys *fs;
{
struct sockaddr_in sin;
struct timeval timeout;
CLIENT *cl;
enum clnt_stat rpc_stat;
int s;
sin = fs->fs_addr;
if (pingmount(sin.sin_addr) != RPC_SUCCESS)
return;
/*
* Port number of "fs->fs_addr" is NFS port number; make port
* number 0, so "clntudp_create" finds port number of mount
* service.
*/
sin.sin_port = 0;
s = RPC_ANYSOCK;
timeout.tv_sec = 2; /* retry interval */
timeout.tv_usec = 0;
if ((cl = clntudp_create(&sin, MOUNTPROG, 1,
timeout, &s)) == NULL) {
syslog(LOG_WARNING, "%s:%s %s", fs->fs_host, fs->fs_dir,
clnt_spcreateerror("server not responding"));
return;
}
cl->cl_auth = authunix_create_default();
timeout.tv_sec = 10;
rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_path, &fs->fs_dir,
xdr_void, (char *)NULL, timeout);
(void) close(s);
AUTH_DESTROY(cl->cl_auth);
clnt_destroy(cl);
if (rpc_stat != RPC_SUCCESS)
syslog(LOG_WARNING, "%s", clnt_sperror(cl, "unmount"));
}
enum clnt_stat
pingmount(hostaddr)
struct in_addr hostaddr;
{
struct timeval timeout;
struct sockaddr_in server_addr;
enum clnt_stat clnt_stat = RPC_TIMEDOUT;
CLIENT *cl;
static struct in_addr goodhost, deadhost;
static time_t goodtime, deadtime;
int cache_time = ping_cache_time; /* sec */
int sock = RPC_ANYSOCK;
if (goodtime > time_now && hostaddr.s_addr == goodhost.s_addr)
return (RPC_SUCCESS);
if (deadtime > time_now && hostaddr.s_addr == deadhost.s_addr)
return (RPC_TIMEDOUT);
if (trace > 1)
(void) fprintf(stderr, "ping %s ", inet_ntoa(hostaddr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr = hostaddr;
server_addr.sin_port = htons(NFS_PORT);
timeout.tv_sec = 2; /* retry interval */
timeout.tv_usec = 0;
cl = clntudp_create(&server_addr, NFS_PROGRAM, NFS_VERSION,
timeout, &sock);
if (cl) {
timeout.tv_sec = 10;
clnt_stat = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void, 0,
timeout);
clnt_destroy(cl);
}
if (clnt_stat == RPC_SUCCESS) {
goodhost = hostaddr;
goodtime = time_now + cache_time;
} else {
deadhost = hostaddr;
deadtime = time_now + cache_time;
}
if (trace > 1)
(void) fprintf(stderr, "%s\n", clnt_stat == RPC_SUCCESS ?
"OK" : "NO RESPONSE");
return (clnt_stat);
}
struct in_addr gotaddr;
/* ARGSUSED */
catchfirst(raddr)
struct sockaddr_in *raddr;
{
gotaddr = raddr->sin_addr;
return (1); /* first one ends search */
}
/*
* ping a bunch of hosts at once and find out who
* responds first
*/
struct in_addr
trymany(addrs, timeout)
struct in_addr *addrs;
int timeout;
{
enum clnt_stat nfs_cast();
enum clnt_stat clnt_stat;
if (trace > 1) {
register struct in_addr *a;
(void) fprintf(stderr, "nfs_cast: ");
for (a = addrs ; a->s_addr ; a++)
(void) fprintf(stderr, "%s ", inet_ntoa(*a));
(void) fprintf(stderr, "\n");
}
gotaddr.s_addr = 0;
clnt_stat = nfs_cast(addrs, catchfirst, timeout);
if (trace > 1) {
(void) fprintf(stderr, "nfs_cast: got %s\n",
(int) clnt_stat ? "no response" : inet_ntoa(gotaddr));
}
if (clnt_stat)
syslog(LOG_ERR, "trymany: servers not responding: %s",
clnt_sperrno(clnt_stat));
return (gotaddr);
}
/*
* Return 1 if the path s2 is a subdirectory of s1.
*/
subdir(s1, s2)
char *s1, *s2;
{
while (*s1 == *s2)
s1++, s2++;
return (*s1 == '\0' && *s2 == '/');
}
nfsstat
nfsmount(host, hostaddr, dir, mntpnt, opts, fsp, mnt_vers, sgi_vers)
char *host, *dir, *mntpnt, *opts;
struct in_addr hostaddr;
struct filsys **fsp;
{
struct filsys *fs;
char netname[MAXNETNAMELEN+1];
char remname[MAXPATHLEN];
struct mntent m;
static struct mountres3 mountres3;
static nfs_fh3 fh3;
struct nfs_args args;
int flags;
static struct sockaddr_in sin;
static struct fhstatus fhs;
static int s = -1;
struct timeval timeout;
static CLIENT *cl = NULL;
static time_t time_valid;
int cache_time = 60; /* sec */
enum clnt_stat rpc_stat;
enum clnt_stat pingmount();
u_short port;
nfsstat status;
struct stat stbuf;
static u_long prev_vers = 0;
u_long vers;
static u_long prev_prog = 0;
extern short nfstype;
int mount_pid, child_status;
/* Make sure the mountpoint is safe to mount on */
if (lstat(mntpnt, &stbuf) < 0) {
syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
return (NFSERR_NOENT);
}
/* A symbolic link could be dangerous e.g. -> /usr */
if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
if (realpath(mntpnt, remname) == NULL) {
syslog(LOG_ERR, "%s: realpath failed: %m",
mntpnt);
return (NFSERR_NOENT);
}
if (!subdir(tmpdir, remname)) {
syslog(LOG_ERR,
"%s:%s -> %s : dangerous symbolic link",
host, dir, remname);
return (NFSERR_NOENT);
}
}
if (pingmount(hostaddr) != RPC_SUCCESS) {
syslog(LOG_ERR, "host %s not responding", host);
return (NFSERR_NOENT);
}
(void) sprintf(remname, "%s:%s", host, dir);
m.mnt_opts = opts;
vers = mnt_vers;
/*
* Get a new client handle if the host is different
*/
if (hostaddr.s_addr != sin.sin_addr.s_addr ||
vers != prev_vers || time_now > time_valid || !cl) {
prev_vers = vers;
if (cl) {
if (cl->cl_auth)
AUTH_DESTROY(cl->cl_auth);
clnt_destroy(cl);
}
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = hostaddr.s_addr;
timeout.tv_usec = 0;
timeout.tv_sec = 2; /* retry interval */
s = RPC_ANYSOCK;
/*
* Try to get SGI private mount proc first so that
* statvfs will work.
*/
if ((cl = clntudp_create(&sin, prev_prog = MOUNTPROG_SGI,
(u_long)sgi_vers, timeout, &s)) == NULL &&
(rpc_createerr.cf_stat == RPC_TIMEDOUT ||
(cl = clntudp_create(&sin, prev_prog = MOUNTPROG,
(u_long)vers, timeout, &s)) == NULL)) {
syslog(LOG_ERR, "%s: %s", remname,
clnt_spcreateerror("server not responding"));
return (NFSERR_NOENT);
}
cl->cl_auth = authunix_create_default();
time_valid = time_now + cache_time;
}
callmount:
/*
* Get fhandle of remote path from server's mountd.
*/
timeout.tv_usec = 0;
timeout.tv_sec = mount_timeout;
if (vers == MOUNTVERS) {
rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_path, &dir,
xdr_fhstatus, &fhs, timeout);
} else {
rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_path, &dir,
xdr_mountres3, (caddr_t)&mountres3, timeout);
}
if (rpc_stat != RPC_SUCCESS) {
if (prev_prog == MOUNTPROG_SGI) {
CLIENT *nclient;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = hostaddr.s_addr;
timeout.tv_usec = 0;
timeout.tv_sec = 2;
s = RPC_ANYSOCK;
if (nclient = clntudp_create(&sin, prev_prog = (u_long)MOUNTPROG,
(u_long)vers, timeout, &s)) {
clnt_destroy(cl);
cl = nclient;
goto callmount;
}
}
/*
* Given the way "clnt_sperror" works, the "%s" immediately
* following the "not responding" is correct.
*/
syslog(LOG_ERR, "%s: server not responding%s", remname,
clnt_sperror(cl, ""));
return (NFSERR_NOENT);
}
if (errno = fhs.fhs_status) {
if (errno == EACCES) {
if (verbose)
syslog(LOG_ERR,
"can't mount %s: no permission", remname);
status = NFSERR_ACCES;
} else {
syslog(LOG_ERR, "can't mount %s: %m", remname);
status = NFSERR_IO;
}
return (status);
}
/*
* set mount args
*/
if (vers == MOUNTVERS3) {
args.fh_len =
(u_int)mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
args.fh =
(fhandle_t *)mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val;
} else {
args.fh = (fhandle_t *)&fhs.fhs_fh;
}
args.hostname = host;
args.flags = 0;
args.flags |= NFSMNT_HOSTNAME;
args.addr = &sin;
if (hasmntopt(&m, MNTOPT_SOFT) != NULL) {
args.flags |= NFSMNT_SOFT;
}
if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) {
args.flags |= NFSMNT_NOINT;
}
if (hasmntopt(&m, MNTOPT_NOAC) != NULL) {
args.flags |= NFSMNT_NOAC;
}
if (hasmntopt(&m, MNTOPT_PRIVATE)) {
args.flags |= NFSMNT_PRIVATE;
}
if (hasmntopt(&m, MNTOPT_SHORTUID)) {
args.flags |= NFSMNT_SHORTUID;
}
if (hasmntopt(&m, "bds")) {
args.flags |= NFSMNT_BDS;
if (args.bdsauto = nopt(&m, "bdsauto")) {
args.flags |= NFSMNT_BDSAUTO;
}
if (args.bdswindow = nopt(&m, "bdswindow")) {
args.flags |= NFSMNT_BDSWINDOW;
}
if (args.bdsbuflen = nopt(&m, "bdsbuffer")) {
args.flags |= NFSMNT_BDSBUFLEN;
}
}
if (port = nopt(&m, MNTOPT_PORT)) {
sin.sin_port = htons(port);
} else {
sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */
}
if (args.rsize = nopt(&m, MNTOPT_RSIZE)) {
args.flags |= NFSMNT_RSIZE;
}
if (args.wsize = nopt(&m, MNTOPT_WSIZE)) {
args.flags |= NFSMNT_WSIZE;
}
if (args.timeo = nopt(&m, MNTOPT_TIMEO)) {
args.flags |= NFSMNT_TIMEO;
}
if (args.retrans = nopt(&m, MNTOPT_RETRANS)) {
args.flags |= NFSMNT_RETRANS;
}
if (args.acregmax = nopt(&m, MNTOPT_ACTIMEO)) {
args.acdirmax = args.acregmax;
args.flags |= NFSMNT_ACREGMAX;
args.flags |= NFSMNT_ACDIRMAX;
} else {
if (args.acregmin = nopt(&m, MNTOPT_ACREGMIN)) {
args.flags |= NFSMNT_ACREGMIN;
}
if (args.acregmax = nopt(&m, MNTOPT_ACREGMAX)) {
args.flags |= NFSMNT_ACREGMAX;
}
if (args.acdirmin = nopt(&m, MNTOPT_ACDIRMIN)) {
args.flags |= NFSMNT_ACDIRMIN;
}
if (args.acdirmax = nopt(&m, MNTOPT_ACDIRMAX)) {
args.flags |= NFSMNT_ACDIRMAX;
}
}
/*
* Get statvfs from remote mount point to set static values
* that aren't supported by the nfs protocol.
*/
if (prev_prog == MOUNTPROG_SGI) {
struct mntrpc_statvfs mntstatvfs;
rpc_stat = clnt_call(cl, MOUNTPROC_STATVFS, xdr_path, &dir,
xdr_statvfs, &mntstatvfs, timeout);
if (rpc_stat == RPC_SUCCESS) {
args.flags |= NFSMNT_BASETYPE|NFSMNT_NAMEMAX;
strncpy(args.base, mntstatvfs.f_basetype,
sizeof args.base);
args.namemax = mntstatvfs.f_namemax;
}
}
flags = MS_DATA;
if (hasmntopt(&m, MNTOPT_RO) != NULL)
flags |= MS_RDONLY;
if (hasmntopt(&m, MNTOPT_NOSUID) != NULL)
flags |= MS_NOSUID;
if (hasmntopt(&m, MNTOPT_NODEV) != NULL)
flags |= MS_NODEV;
if (hasmntopt(&m, MNTOPT_GRPID) != NULL)
flags |= MS_GRPID;
if (trace > 1) {
timestamp();
(void) fprintf(stderr, "mount %s %s (%s)\n",
remname, mntpnt, opts);
}
if (mount_pid = fork()) { /* parent */
waitpid (mount_pid, &child_status, 0);
if (child_status) {
syslog(LOG_ERR, "Mount of %s on %s status 0x%x",
remname, mntpnt, child_status);
return (NFSERR_IO);
}
} else { /* child */
if (mount(mntpnt, mntpnt, flags, ((vers == MOUNTVERS3) ?
sysfs(GETFSIND, FSID_NFS3) : sysfs(GETFSIND, FSID_NFS)),
&args, sizeof args)) {
syslog(LOG_ERR, "Mount of %s on %s: %m", remname, mntpnt);
satvwrite(SAT_AE_MOUNT, SAT_FAILURE,
"automount: failed mount of %s on %s: %s", remname,
mntpnt, strerror(errno));
exit (1);
}
exit (0);
}
satvwrite(SAT_AE_MOUNT, SAT_SUCCESS, "automount: mounted %s on %s",
remname, mntpnt);
if (trace > 1) {
timestamp();
(void) fprintf(stderr, "mount %s OK\n", remname);
}
if (*fsp)
fs = *fsp;
else {
fs = alloc_fs(host, dir, mntpnt, opts);
if (fs == NULL)
return (NFSERR_NOSPC);
}
fs->fs_type = MNTTYPE_NFS;
fs->fs_mine = 1;
fs->fs_nfsargs = args;
fs->fs_mflags = flags;
fs->fs_nfsargs.hostname = fs->fs_host;
fs->fs_nfsargs.addr = &fs->fs_addr;
fs->fs_nfsargs.fh = (fhandle_t *)&fs->fs_rootfh;
fs->fs_addr = sin;
bcopy(&fhs.fhs_fh, &fs->fs_rootfh, sizeof fs->fs_rootfh);
*fsp = fs;
return (NFS_OK);
}
nfsstat
remount(fs)
struct filsys *fs;
{
char remname[1024];
struct stat stbuf;
if (fs->fs_nfsargs.fh == 0)
return nfsmount(fs->fs_host, fs->fs_addr.sin_addr, fs->fs_dir,
fs->fs_mntpnt, fs->fs_opts, &fs,
MOUNTVERS, MOUNTVERS_SGI_ORIG);
(void) sprintf(remname, "%s:%s", fs->fs_host, fs->fs_dir);
if (trace > 1) {
(void) fprintf(stderr, "remount %s %s (%s)\n",
remname, fs->fs_mntpnt, fs->fs_opts);
}
if (pingmount(fs->fs_addr.sin_addr) != RPC_SUCCESS) {
if (verbose || fs->fs_unmounted++ < 5)
syslog(LOG_ERR,
"remount %s on %s: server not responding",
remname, fs->fs_mntpnt);
if (trace > 1)
(void) fprintf(stderr, "remount FAILED: server not responding\n");
return (NFSERR_IO);
}
if (stat(fs->fs_mntpnt, &stbuf) < 0) {
syslog(LOG_ERR, "remount: couldn't stat: %s: %m", fs->fs_mntpnt);
return (NFSERR_IO);
}
if (mount(fs->fs_mntpnt, fs->fs_mntpnt, fs->fs_mflags|MS_DATA, nfstype,
&fs->fs_nfsargs, sizeof fs->fs_nfsargs)) {
if (verbose || fs->fs_unmounted++ < 5)
syslog(LOG_ERR, "remount of %s on %s: %m", remname,
fs->fs_mntpnt);
if (trace > 1)
perror("remount FAILED ");
satvwrite(SAT_AE_MOUNT, SAT_SUCCESS,
"automount: failed remount of %s on %s: %s", remname,
fs->fs_mntpnt, strerror(errno));
return (NFSERR_IO);
}
satvwrite(SAT_AE_MOUNT, SAT_SUCCESS, "automount: remounted %s on %s",
remname, fs->fs_mntpnt);
fs->fs_mntpntdev = stbuf.st_dev;
if (stat(fs->fs_mntpnt, &stbuf) < 0) {
syslog(LOG_ERR, "remount: couldn't stat: %s: %m", fs->fs_mntpnt);
return (NFSERR_IO);
}
fs->fs_mountdev = stbuf.st_dev;
if (trace > 1)
(void) fprintf(stderr, "remount OK\n");
return (NFS_OK);
}
/*
* Add one or more entries to /etc/mtab
*/
void
addtomtab(rootfs)
struct filsys *rootfs;
{
FILE *fp;
struct filsys *fs;
struct stat stbuf;
struct mntent mnt;
char remname[MAXPATHLEN];
char opts[1024];
int forked;
/*
* In SunOs 4.0 only mount and automount flock the mtab.
* In releases prior to 4.0 any programs that browse mtab
* (df, find etc) also lock the mtab and deadlock with
* the automounter is a distinct possibility. Avoid
* deadlock here by backgrounding the update.
*/
forked = 0;
if (islocked(MOUNTED)) {
if (fork())
return;
forked++;
}
fp = setmntent(MOUNTED, "r+");
if (fp == NULL) {
syslog(LOG_ERR, "%s: %m", MOUNTED);
return;
}
for (fs = TAIL(struct filsys, fs_q); fs; fs = PREV(struct filsys, fs)) {
if (fs->fs_rootfs != rootfs)
continue;
(void) sprintf(remname, "%s:%s", fs->fs_host, fs->fs_dir);
mnt.mnt_fsname = remname;
mnt.mnt_dir = fs->fs_mntpnt;
mnt.mnt_type = MNTTYPE_NFS;
mnt.mnt_freq = mnt.mnt_passno = 0;
(void) sprintf(opts, "%s,%s=%04x", fs->fs_opts,
MNTINFO_DEV, fs->fs_mountdev);
mnt.mnt_opts = opts;
if (addmntent(fp, &mnt)) {
(void) endmntent(fp);
syslog(LOG_ERR, "%s: %m", MOUNTED);
return;
}
}
(void) endmntent(fp);
if (stat(MOUNTED, &stbuf) < 0)
syslog(LOG_ERR, "%s: %m", MOUNTED);
else
last_mtab_time = stbuf.st_mtime;
if (forked)
_exit(0);
}
#include <sys/file.h>
#include <sys/fcntl.h>
/*
* Check whether the file is flock'ed.
*/
int
islocked(file)
char *file;
{
int f;
f = open(file, O_RDONLY);
if (f < 0) {
syslog(LOG_ERR, "%s: %m", MOUNTED);
return 1;
}
if (flock(f, LOCK_EX | LOCK_NB) < 0) {
sleep(1);
if (flock(f, LOCK_EX | LOCK_NB) < 0) {
(void) close(f);
return 1;
}
}
(void) close(f); /* close and release the lock */
return 0;
}
/*
* This structure is used to build a list of
* mntent structures from /etc/mtab.
*/
struct mntlist {
struct mntent *mntl_mnt;
struct mntlist *mntl_next;
};
/*
* Duplicate a mntent structure
*/
struct mntent *
dupmntent(mnt)
struct mntent *mnt;
{
struct mntent *new;
void freemntent();
new = (struct mntent *)malloc(sizeof(*new));
if (new == NULL)
goto alloc_failed;
bzero((char *)new, sizeof(*new));
new->mnt_fsname = strdup(mnt->mnt_fsname);
if (new->mnt_fsname == NULL)
goto alloc_failed;
new->mnt_dir = strdup(mnt->mnt_dir);
if (new->mnt_dir == NULL)
goto alloc_failed;
new->mnt_type = strdup(mnt->mnt_type);
if (new->mnt_type == NULL)
goto alloc_failed;
new->mnt_opts = strdup(mnt->mnt_opts);
if (new->mnt_opts == NULL)
goto alloc_failed;
new->mnt_freq = mnt->mnt_freq;
new->mnt_passno = mnt->mnt_passno;
return (new);
alloc_failed:
syslog(LOG_ERR, "dupmntent: memory allocation failed");
freemntent(new);
return NULL;
}
/*
* Free a single mntent structure
*/
void
freemntent(mnt)
struct mntent *mnt;
{
if (mnt) {
if (mnt->mnt_fsname)
free(mnt->mnt_fsname);
if (mnt->mnt_dir)
free(mnt->mnt_dir);
if (mnt->mnt_type)
free(mnt->mnt_type);
if (mnt->mnt_opts)
free(mnt->mnt_opts);
free(mnt);
}
}
/*
* Free a list of mntent structures
*/
void
freemntlist(mntl)
struct mntlist *mntl;
{
register struct mntlist *mntl_tmp;
while (mntl) {
freemntent(mntl->mntl_mnt);
mntl_tmp = mntl;
mntl = mntl->mntl_next;
free(mntl_tmp);
}
}
/*
* Remove one or more entries from the mount table.
* If mntpnt is non-null then remove the entry
* for that mount point.
* Otherwise use rootfs - it is the root fs of
* a mounted hierarchy. Remove all entries for
* the hierarchy.
*/
void
clean_mtab(mntpnt, rootfs)
char *mntpnt;
struct filsys *rootfs;
{
FILE *mtab;
struct mntent *mnt;
struct stat stbuf;
struct filsys *fs;
struct mntlist *mntl_head = NULL;
struct mntlist *mntl_prev, *mntl;
long mtab_size;
int delete, c;
mtab = setmntent(MOUNTED, "r+");
if (mtab == NULL) {
syslog(LOG_ERR, "%s: %m", MOUNTED);
return;
}
/*
* Read the entire mtab into memory except for the
* entries we're trying to delete.
*/
if (fstat(fileno(mtab), &stbuf) < 0) {
syslog(LOG_ERR, "couldn't stat %s: %m", MOUNTED);
return;
}
mtab_size = stbuf.st_size;
for (c = 1 ;; c++) {
mnt = getmntent(mtab);
if (mnt == NULL) {
if (ftell(mtab) >= mtab_size)
break; /* it's really EOF */
syslog(LOG_ERR, "WARNING: %s: line %d: bad entry removed",
MOUNTED, c);
continue;
}
if (mntpnt) {
if (strcmp(mnt->mnt_dir, mntpnt) == 0)
continue;
} else {
delete = 0;
for (fs = rootfs ; fs ; fs = NEXT(struct filsys, fs)) {
if (strcmp(mnt->mnt_dir, fs->fs_mntpnt) == 0) {
delete = 1;
break;
}
}
if (delete)
continue;
}
mntl = (struct mntlist *)malloc(sizeof(*mntl));
if (mntl == NULL)
goto alloc_failed;
if (mntl_head == NULL)
mntl_head = mntl;
else
mntl_prev->mntl_next = mntl;
mntl_prev = mntl;
mntl->mntl_next = NULL;
mntl->mntl_mnt = dupmntent(mnt);
if (mntl->mntl_mnt == NULL)
goto alloc_failed;
}
/* now truncate the mtab and write almost all of it back */
rewind(mtab);
if (ftruncate(fileno(mtab), 0) < 0) {
syslog(LOG_ERR, "truncate %s: %m", MOUNTED);
(void) endmntent(mtab);
return;
}
for (mntl = mntl_head ; mntl ; mntl = mntl->mntl_next) {
if (addmntent(mtab, mntl->mntl_mnt)) {
syslog(LOG_ERR, "addmntent: %m");
(void) endmntent(mtab);
return;
}
}
(void) endmntent(mtab);
freemntlist(mntl_head);
if (stat(MOUNTED, &stbuf) < 0)
syslog(LOG_ERR, "%s: %m", MOUNTED);
else
last_mtab_time = stbuf.st_mtime;
return;
alloc_failed:
(void) endmntent(mtab);
freemntlist(mntl_head);
return;
}
/*
* Return the value of a numeric option of the form foo=x, if
* option is not found or is malformed, return 0.
*/
nopt(mnt, opt)
struct mntent *mnt;
char *opt;
{
int val = 0;
char *equal;
char *str;
if (str = hasmntopt(mnt, opt)) {
if (equal = index(str, '=')) {
val = atoi(&equal[1]);
} else {
syslog(LOG_ERR, "Bad numeric option '%s'", str);
}
}
return (val);
}