2021-10-11 18:20:23 -03:00

486 lines
9.8 KiB
C

#ifndef lint
static char *sccsid = "@(#)umount.c 1.1 92/07/30 SMI"; /* from UCB 4.8 */
#endif
/*
* umount
*/
#include <locale.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <stdio.h>
#include <mntent.h>
#include <errno.h>
#include <sys/time.h>
#include <rpc/rpc.h>
#include <nfs/nfs.h>
#include <rpcsvc/mount.h>
#include <sys/socket.h>
#include <netdb.h>
/*
* This structure is used to build a list of mntent structures
* in reverse order from /etc/mtab.
*/
struct mntlist {
struct mntent *mntl_mnt;
struct mntlist *mntl_next;
};
int all = 0;
int verbose = 0;
int host = 0;
int type = 0;
int forked = 0;
char *typestr;
char *hoststr;
char *xmalloc();
char *index();
char *realpath() ;
struct mntlist *mkmntlist();
struct mntent *mntdup();
int eachreply();
extern int errno;
main(argc, argv)
int argc;
char **argv;
{
char *options;
argc--, argv++;
sync();
umask(0);
/* load the correct version of the ctype table for the
* library functions that internally use the ctype macros.
* This ensures corerct code-set testing therein
*/
setlocale(LC_CTYPE, "");
while (argc && *argv[0] == '-') {
options = &argv[0][1];
while (*options) {
switch (*options) {
case 'a':
all++;
break;
case 'd': /* For RFS SVID compatibility */
break;
case 'h':
all++;
host++;
hoststr = argv[1];
argc--;
argv++;
break;
case 't':
all++;
type++;
typestr = argv[1];
argc--;
argv++;
break;
case 'v':
verbose++;
break;
default:
fprintf(stderr, "umount: unknown option '%c'\n",
*options);
usage();
}
options++;
}
argv++;
argc--;
}
if ((all && argc) || (!all && !argc)){
usage();
}
if (geteuid() != 0) {
fprintf(stderr, "Must be root to use umount\n");
exit(1);
}
umountlist(argc, argv);
exit(0) ;
/* NOTREACHED */
}
umountlist(argc, argv)
int argc;
char *argv[];
{
int i, pid;
struct mntent *mnt;
struct mntlist *mntl;
struct mntlist *m, *newlist;
struct mntlist *mntcur = NULL;
struct mntlist *mntrev = NULL;
int tmpfd;
char *colon;
FILE *tmpmnt;
char *tmpname = "/etc/umountXXXXXX";
mktemp(tmpname);
if ((tmpfd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
perror(tmpname);
exit(1);
}
close(tmpfd);
tmpmnt = setmntent(tmpname, "w");
if (tmpmnt == NULL) {
perror(tmpname);
exit(1);
}
if (all) {
if (!host &&
(!type || (type && strcmp(typestr, MNTTYPE_NFS) == 0))) {
pid = fork();
if (pid < 0)
perror("umount: fork");
if (pid == 0) {
endmntent(tmpmnt);
clnt_broadcast(MOUNTPROG,
MOUNTVERS, MOUNTPROC_UMNTALL,
xdr_void, NULL, xdr_void, NULL, eachreply);
exit(0);
}
}
}
/*
* get a last first list of mounted stuff, reverse list and
* null out entries that get unmounted.
*/
for (mntl = mkmntlist(); mntl != NULL;
mntcur = mntl, mntl = mntl->mntl_next,
mntcur->mntl_next = mntrev, mntrev = mntcur) {
mnt = mntl->mntl_mnt;
if (strcmp(mnt->mnt_dir, "/") == 0) {
continue;
}
if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) {
continue;
}
if (all) {
if (type && strcmp(typestr, mnt->mnt_type)) {
continue;
}
if (host) {
if (strcmp(MNTTYPE_NFS, mnt->mnt_type)) {
continue;
}
colon = index(mnt->mnt_fsname, ':');
if (colon) {
*colon = '\0';
if (strcmp(hoststr, mnt->mnt_fsname)) {
*colon = ':';
continue;
}
*colon = ':';
} else {
continue;
}
}
if (umountmnt(mnt)) {
mntl->mntl_mnt = NULL;
}
continue;
}
for (i=0; i<argc; i++) {
if (*argv[i]) {
if ((strcmp(mnt->mnt_dir, argv[i]) == 0) ||
(strcmp(mnt->mnt_fsname, argv[i]) == 0)) {
if (umountmnt(mnt)) {
mntl->mntl_mnt = NULL;
}
*argv[i] = '\0';
break;
}
}
}
}
for (i = 0 ; i < argc ; i++) {
if (*argv[i]) break ;
}
if (i < argc) {
struct mntlist *temp, *previous;
int oldi ;
oldi = i ;
/*
* Now find those arguments which are links, resolve them
* and then umount them. This is done separately because it
* "stats" the arg, and it may hang if the server is down.
*/
previous = NULL ;
for (; mntcur; temp = mntcur, mntcur = mntcur->mntl_next,
temp->mntl_next = previous, previous = temp) ;
mntrev = NULL ;
for (mntl = previous; mntl != NULL;
mntcur = mntl, mntl = mntl->mntl_next,
mntcur->mntl_next = mntrev, mntrev = mntcur) {
if ((mnt = mntl->mntl_mnt)==NULL)
continue; /* Already unmounted */
if (strcmp(mnt->mnt_dir, "/") == 0)
continue;
if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0)
continue;
for (i=oldi; i<argc; i++) {
char resolvebuf[MAXPATHLEN] ;
char *resolve = resolvebuf ;
if (*argv[i]) {
if (realpath(argv[i], resolve) == 0) {
fprintf(stderr, "umount: ") ;
perror(resolve) ;
*argv[i] = NULL ;
continue ;
}
if ((strcmp(mnt->mnt_dir, resolve) == 0)) {
if (umountmnt(mnt)) {
mntl->mntl_mnt = NULL;
}
*argv[i] = '\0';
break;
}
}
}
}
/*
* For all the remaining ones including the ones which have
* no corresponding entry in /etc/mtab file.
*/
for (i=oldi; i<argc; i++) {
if (*argv[i] && *argv[i] != '/') {
fprintf(stderr,
"umount: must use full path, %s not unmounted\n",
argv[i]);
continue;
}
if (*argv[i]) {
struct mntent tmpmnt;
tmpmnt.mnt_fsname = NULL;
tmpmnt.mnt_dir = argv[i];
tmpmnt.mnt_type = MNTTYPE_42;
(void) umountmnt(&tmpmnt);
}
}
}
/*
* Build new temporary mtab by walking mnt list.
* If a umount_xxx command was forked-off for a special
* filesystem then make sure that each entry written
* back is in the newlist in case the command removed it
* (see umount_tfs).
*/
newlist = forked ? mkmntlist() : NULL; /* another snapshot of mtab */
for (; mntcur != NULL; mntcur = mntcur->mntl_next) {
if (mntcur->mntl_mnt) {
for (m = newlist; m; m = m->mntl_next) {
if (strcmp(mntcur->mntl_mnt->mnt_dir,
m->mntl_mnt->mnt_dir) == 0)
break;
}
if (!forked || m)
addmntent(tmpmnt, mntcur->mntl_mnt);
}
}
endmntent(tmpmnt);
/*
* Move tmp mtab to real mtab
*/
if (rename(tmpname, MOUNTED) < 0) {
perror(MOUNTED);
exit(1);
}
}
umountmnt(mnt)
struct mntent *mnt;
{
if (unmount(mnt->mnt_dir) < 0) {
if (errno != EINVAL) {
perror(mnt->mnt_dir);
return(0);
}
fprintf(stderr, "%s not mounted\n", mnt->mnt_dir);
return(1);
} else {
if (strcmp(mnt->mnt_type, MNTTYPE_NFS) == 0) {
rpctoserver(mnt);
} else if (strcmp(mnt->mnt_type, MNTTYPE_42) == 0) {
#ifdef PCFS
} else if (strcmp(mnt->mnt_type, MNTTYPE_PC) == 0) {
#endif
} else {
char umountcmd[128];
int pid;
union wait status;
/*
* user-level filesystem...attempt to exec
* external umount_FS program.
*/
pid = fork();
switch (pid) {
case -1:
fprintf(stderr, "umount: ");
perror(mnt->mnt_fsname);
break;
case 0:
sprintf(umountcmd, "umount_%s", mnt->mnt_type);
(void) execlp(umountcmd, umountcmd,
mnt->mnt_fsname, mnt->mnt_dir,
mnt->mnt_type, mnt->mnt_opts, 0);
/*
* Don't worry if there is no user-written
* umount_FS program.
*/
exit(1);
default:
while (wait(&status) != pid);
forked++;
/*
* Ignore errors in user-written umount_FS.
*/
}
}
if (verbose) {
fprintf(stderr, "%s: Unmounted\n", mnt->mnt_dir);
}
return(1);
}
}
usage()
{
fprintf(stderr, "usage: umount -a[v] [-t <type>] [-h <host>]\n");
fprintf(stderr, " umount [-v] <path> | <dev> ...\n");
exit(1);
}
rpctoserver(mnt)
struct mntent *mnt;
{
char *p;
struct sockaddr_in sin;
struct hostent *hp;
int s;
struct timeval timeout;
CLIENT *client;
enum clnt_stat rpc_stat;
if ((p = index(mnt->mnt_fsname, ':')) == NULL)
return;
*p++ = 0;
if ((hp = gethostbyname(mnt->mnt_fsname)) == NULL) {
fprintf(stderr, "%s not in hosts database\n", mnt->mnt_fsname);
return;
}
bzero(&sin, sizeof(sin));
bcopy(hp->h_addr, (char *) & sin.sin_addr, hp->h_length);
sin.sin_family = AF_INET;
s = RPC_ANYSOCK;
timeout.tv_usec = 0;
timeout.tv_sec = 10;
if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS,
timeout, &s)) == NULL) {
clnt_pcreateerror("Warning: umount");
return;
}
client->cl_auth = authunix_create_default();
timeout.tv_usec = 0;
timeout.tv_sec = 25;
rpc_stat = clnt_call(client, MOUNTPROC_UMNT, xdr_path, &p,
xdr_void, NULL, timeout);
if (rpc_stat != RPC_SUCCESS) {
clnt_perror(client, "Warning: umount");
return;
}
}
/*ARGSUSED*/
eachreply(resultsp, addrp)
char *resultsp;
struct sockaddr_in *addrp;
{
return (1);
}
char *
xmalloc(size)
int size;
{
char *ret;
if ((ret = (char *)malloc(size)) == NULL) {
fprintf(stderr, "umount: ran out of memory!\n");
exit(1);
}
return (ret);
}
struct mntent *
mntdup(mnt)
struct mntent *mnt;
{
struct mntent *new;
new = (struct mntent *)xmalloc(sizeof(*new));
new->mnt_fsname = (char *)xmalloc(strlen(mnt->mnt_fsname) + 1);
strcpy(new->mnt_fsname, mnt->mnt_fsname);
new->mnt_dir = (char *)xmalloc(strlen(mnt->mnt_dir) + 1);
strcpy(new->mnt_dir, mnt->mnt_dir);
new->mnt_type = (char *)xmalloc(strlen(mnt->mnt_type) + 1);
strcpy(new->mnt_type, mnt->mnt_type);
new->mnt_opts = (char *)xmalloc(strlen(mnt->mnt_opts) + 1);
strcpy(new->mnt_opts, mnt->mnt_opts);
new->mnt_freq = mnt->mnt_freq;
new->mnt_passno = mnt->mnt_passno;
return (new);
}
struct mntlist *
mkmntlist()
{
FILE *mounted;
struct mntlist *mntl;
struct mntlist *mntst = NULL;
struct mntent *mnt;
mounted = setmntent(MOUNTED, "r");
if (mounted == NULL) {
perror(MOUNTED);
exit(1);
}
while ((mnt = getmntent(mounted)) != NULL) {
mntl = (struct mntlist *)xmalloc(sizeof(*mntl));
mntl->mntl_mnt = mntdup(mnt);
mntl->mntl_next = mntst;
mntst = mntl;
}
endmntent(mounted);
return(mntst);
}