int main(int argc, char *argv[]) { int all, errs, ch, mntsize; char **typelist = NULL, *mntonname, *mntfromname; char *type, *mntfromnamerev, *mntonnamerev; struct statfs *mntbuf; /* Start disks transferring immediately. */ sync(); all = errs = 0; while ((ch = getopt(argc, argv, "Aafh:t:v")) != -1) switch (ch) { case 'A': all = 2; break; case 'a': all = 1; break; case 'f': fflag = MNT_FORCE; break; case 'h': /* -h implies -A. */ all = 2; nfshost = optarg; break; case 't': if (typelist != NULL) err(1, "only one -t option may be specified"); typelist = makevfslist(optarg); break; case 'v': vflag = 1; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if ((argc == 0 && !all) || (argc != 0 && all)) usage(); /* -h implies "-t nfs" if no -t flag. */ if ((nfshost != NULL) && (typelist == NULL)) typelist = makevfslist("nfs"); switch (all) { case 2: if ((mntsize = mntinfo(&mntbuf)) <= 0) break; /* * We unmount the nfs-mounts in the reverse order * that they were mounted. */ for (errs = 0, mntsize--; mntsize > 0; mntsize--) { if (checkvfsname(mntbuf[mntsize].f_fstypename, typelist)) continue; /* * Check if a mountpoint is laid over by another mount. * A warning will be printed to stderr if this is * the case. The laid over mount remains unmounted. */ mntonname = mntbuf[mntsize].f_mntonname; mntfromname = mntbuf[mntsize].f_mntfromname; mntonnamerev = getmntname(getmntname(mntonname, NULL, MNTFROM, &type, NAME), NULL, MNTON, &type, NAME); mntfromnamerev = getmntname(mntonnamerev, NULL, MNTFROM, &type, NAME); if (strcmp(mntonnamerev, mntonname) == 0 && strcmp(mntfromnamerev, mntfromname ) != 0) warnx("cannot umount %s, %s\n " "is mounted there, umount it first", mntonname, mntfromnamerev); if (umountfs(mntbuf[mntsize].f_mntonname, typelist) != 0) errs = 1; } free(mntbuf); break; case 1: if (setfsent() == 0) err(1, "%s", _PATH_FSTAB); errs = umountall(typelist); break; case 0: for (errs = 0; *argv != NULL; ++argv) if (umountfs(*argv, typelist) != 0) errs = 1; break; } (void)getmntname(NULL, NULL, NOTHING, NULL, FREE); exit(errs); }
int umountfs(char *oname) { enum clnt_stat clnt_stat; struct hostent *hp; struct sockaddr_in saddr; struct stat sb; struct timeval pertry, try; CLIENT *clp; int so; char *delimp, *hostp, *mntpt; char *name, *newname, rname[MAXPATHLEN], type[MFSNAMELEN]; if (realpath(oname, rname) == NULL) mntpt = name = oname; else mntpt = name = rname; newname = NULL; /* If we can stat the file, check to see if it is a device or non-dir */ if (stat(name, &sb) == 0) { if (S_ISBLK(sb.st_mode)) { if ((mntpt = getmntname(name, MNTON, type)) == NULL) { warnx("%s: not currently mounted", name); return (1); } } else if (!S_ISDIR(sb.st_mode)) { warnx("%s: not a directory or special device", name); return (1); } } /* * Look up the name in the mount table. * 99.9% of the time the path in the kernel is the one * realpath() returns but check the original just in case... */ if (!(newname = getmntname(name, MNTFROM, type)) && !(mntpt = getmntname(name, MNTON, type)) ) { mntpt = oname; if (!(newname = getmntname(oname, MNTFROM, type)) && !(mntpt = getmntname(oname, MNTON, type))) { warnx("%s: not currently mounted", oname); return (1); } } if (newname) name = newname; if (!selected(type)) return (1); if (!strncmp(type, MOUNT_NFS, MFSNAMELEN)) { if ((delimp = strchr(name, '@')) != NULL) { hostp = delimp + 1; *delimp = '\0'; hp = gethostbyname(hostp); *delimp = '@'; } else if ((delimp = strchr(name, ':')) != NULL) { *delimp = '\0'; hostp = name; hp = gethostbyname(hostp); name = delimp + 1; *delimp = ':'; } else hp = NULL; if (!namematch(hp)) return (1); } if (verbose) (void)printf("%s: unmount from %s\n", name, mntpt); if (unmount(mntpt, fflag) < 0) { warn("%s", mntpt); return (1); } if (!strncmp(type, MOUNT_NFS, MFSNAMELEN) && (hp != NULL) && !(fflag & MNT_FORCE)) { *delimp = '\0'; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = 0; memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); pertry.tv_sec = 3; pertry.tv_usec = 0; so = RPC_ANYSOCK; if ((clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { clnt_pcreateerror("Cannot MNT PRC"); return (1); } clp->cl_auth = authunix_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { clnt_perror(clp, "Bad MNT RPC"); return (1); } auth_destroy(clp->cl_auth); clnt_destroy(clp); }
int umountfs(char *name, char **typelist) { enum clnt_stat clnt_stat; struct hostent *hp; struct mtablist *mtab; struct sockaddr_in saddr; struct timeval pertry, try; CLIENT *clp; size_t len; int so, speclen, do_rpc; char *mntonname, *mntfromname; char *mntfromnamerev; char *nfsdirname, *orignfsdirname; char *resolved, realname[MAXPATHLEN]; char *type, *delimp, *hostp, *origname; len = 0; mtab = NULL; mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL; /* * 1. Check if the name exists in the mounttable. */ (void)checkmntlist(name, &mntfromname, &mntonname, &type); /* * 2. Remove trailing slashes if there are any. After that * we look up the name in the mounttable again. */ if (mntfromname == NULL && mntonname == NULL) { speclen = strlen(name); for (speclen = strlen(name); speclen > 1 && name[speclen - 1] == '/'; speclen--) name[speclen - 1] = '\0'; (void)checkmntlist(name, &mntfromname, &mntonname, &type); resolved = name; /* Save off original name in origname */ if ((origname = strdup(name)) == NULL) err(1, "strdup"); /* * 3. Check if the deprecated nfs-syntax with an '@' * has been used and translate it to the ':' syntax. * Look up the name in the mounttable again. */ if (mntfromname == NULL && mntonname == NULL) { if ((delimp = strrchr(name, '@')) != NULL) { hostp = delimp + 1; if (*hostp != '\0') { /* * Make both '@' and ':' * notations equal */ char *host = strdup(hostp); len = strlen(hostp); if (host == NULL) err(1, "strdup"); memmove(name + len + 1, name, (size_t)(delimp - name)); name[len] = ':'; memmove(name, host, len); free(host); } for (speclen = strlen(name); speclen > 1 && name[speclen - 1] == '/'; speclen--) name[speclen - 1] = '\0'; name[len + speclen + 1] = '\0'; (void)checkmntlist(name, &mntfromname, &mntonname, &type); resolved = name; } /* * 4. Check if a relative mountpoint has been * specified. This should happen as last check, * the order is important. To prevent possible * nfs-hangs, we just call realpath(3) on the * basedir of mountpoint and add the dirname again. * Check the name in mounttable one last time. */ if (mntfromname == NULL && mntonname == NULL) { (void)strcpy(name, origname); if ((getrealname(name, realname)) != NULL) { (void)checkmntlist(realname, &mntfromname, &mntonname, &type); resolved = realname; } /* * All tests failed, return to main() */ if (mntfromname == NULL && mntonname == NULL) { (void)strcpy(name, origname); warnx("%s: not currently mounted", origname); free(origname); return (1); } } } free(origname); } else resolved = name; if (checkvfsname(type, typelist)) return (1); hp = NULL; nfsdirname = NULL; if (!strcmp(type, "nfs")) { if ((nfsdirname = strdup(mntfromname)) == NULL) err(1, "strdup"); orignfsdirname = nfsdirname; if ((delimp = strchr(nfsdirname, ':')) != NULL) { *delimp = '\0'; hostp = nfsdirname; if ((hp = gethostbyname(hostp)) == NULL) { warnx("can't get net id for host"); } nfsdirname = delimp + 1; } } /* * Check if the reverse entrys of the mounttable are really the * same as the normal ones. */ if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname, NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL) err(1, "strdup"); /* * Mark the uppermost mount as unmounted. */ (void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK); /* * If several equal mounts are in the mounttable, check the order * and warn the user if necessary. */ if (strcmp(mntfromnamerev, mntfromname ) != 0 && strcmp(resolved, mntonname) != 0) { warnx("cannot umount %s, %s\n " "is mounted there, umount it first", mntonname, mntfromnamerev); /* call getmntname again to set mntcheck[i] to 0 */ (void)getmntname(mntfromname, mntonname, NOTHING, &type, UNMARK); return (1); } free(mntfromnamerev); /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, * we skip the rpc-call. Obviously this has to * happen before unmount(2), but it should happen * after the previous namecheck. */ if (strcmp(type, "nfs") == 0 && getmntname(mntfromname, NULL, NOTHING, &type, COUNT) != NULL) do_rpc = 1; else do_rpc = 0; if (!namematch(hp)) return (1); if (unmount(mntonname, fflag) != 0 ) { warn("unmount of %s failed", mntonname); return (1); } if (vflag) (void)printf("%s: unmount from %s\n", mntfromname, mntonname); /* * Report to mountd-server which nfsname * has been unmounted. */ if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) { memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = 0; memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, sizeof(saddr.sin_addr))); pertry.tv_sec = 3; pertry.tv_usec = 0; so = RPC_ANYSOCK; if ((clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { clnt_pcreateerror("Cannot MNT PRC"); return (1); } clp->cl_auth = authunix_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, nfsdirname, xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { clnt_perror(clp, "Bad MNT RPC"); return (1); } /* * Remove the unmounted entry from /var/db/mounttab. */ if (read_mtab(mtab)) { mtab = mtabhead; clean_mtab(hostp, nfsdirname); if(!write_mtab()) warnx("cannot remove entry %s:%s", hostp, nfsdirname); free_mtab(); } free(orignfsdirname); auth_destroy(clp->cl_auth); clnt_destroy(clp); }