1 /* $OpenBSD: umount.c,v 1.27 2016/12/16 17:44:59 krw Exp $ */ 2 /* $NetBSD: umount.c,v 1.16 1996/05/11 14:13:55 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/stat.h> 34 #include <sys/mount.h> 35 #include <sys/time.h> 36 #include <sys/socket.h> 37 #include <sys/socketvar.h> 38 39 #include <netdb.h> 40 #include <rpc/rpc.h> 41 #include <rpc/pmap_clnt.h> 42 #include <rpc/pmap_prot.h> 43 #include <nfs/rpcv2.h> 44 45 #include <err.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <limits.h> 51 #include <util.h> 52 53 typedef enum { MNTON, MNTFROM } mntwhat; 54 55 int fflag, verbose; 56 char **typelist = NULL; 57 char *nfshost; 58 59 char *getmntname(char *, mntwhat, char *); 60 void maketypelist(char *); 61 int selected(const char *); 62 int namematch(struct hostent *); 63 int umountall(void); 64 int umountfs(char *); 65 void usage(void); 66 int xdr_dir(XDR *, char *); 67 68 int 69 main(int argc, char *argv[]) 70 { 71 int all, ch, errs; 72 73 /* Start disks transferring immediately. */ 74 sync(); 75 76 all = 0; 77 while ((ch = getopt(argc, argv, "afh:t:v")) != -1) 78 switch (ch) { 79 case 'a': 80 all = 1; 81 break; 82 case 'f': 83 fflag = MNT_FORCE; 84 break; 85 case 'h': /* -h implies -a. */ 86 all = 1; 87 nfshost = optarg; 88 break; 89 case 't': 90 if (typelist != NULL) 91 errx(1, "only one -t option may be specified."); 92 maketypelist(optarg); 93 break; 94 case 'v': 95 verbose = 1; 96 break; 97 default: 98 usage(); 99 /* NOTREACHED */ 100 } 101 argc -= optind; 102 argv += optind; 103 104 if ((argc == 0 && !all) || (argc != 0 && all)) 105 usage(); 106 107 /* -h implies "-t nfs" if no -t flag. */ 108 if ((nfshost != NULL) && (typelist == NULL)) 109 maketypelist(MOUNT_NFS); 110 111 if (all) 112 errs = umountall(); 113 else 114 for (errs = 0; *argv != NULL; ++argv) 115 if (umountfs(*argv) != 0) 116 errs = 1; 117 return (errs); 118 } 119 120 int 121 umountall(void) 122 { 123 struct statfs *fs; 124 int n; 125 int rval; 126 127 n = getmntinfo(&fs, MNT_NOWAIT); 128 if (n == 0) 129 err(1, NULL); 130 131 rval = 0; 132 while (--n >= 0) { 133 /* Ignore the root. */ 134 if (strncmp(fs[n].f_mntonname, "/", MNAMELEN) == 0) 135 continue; 136 if (!selected(fs[n].f_fstypename)) 137 continue; 138 if (umountfs(fs[n].f_mntonname)) 139 rval = 1; 140 } 141 return (rval); 142 } 143 144 int 145 umountfs(char *oname) 146 { 147 struct hostent *hp; 148 #ifndef NO_NFS 149 struct sockaddr_in saddr; 150 struct timeval pertry, try; 151 CLIENT *clp; 152 int so; 153 #endif 154 struct stat sb; 155 char *delimp, *hostp, *mntpt; 156 char *name, *newname, rname[PATH_MAX], type[MFSNAMELEN]; 157 158 if (isduid(oname, 0) || realpath(oname, rname) == NULL) 159 mntpt = name = oname; 160 else 161 mntpt = name = rname; 162 newname = NULL; 163 164 /* If we can stat the file, check to see if it is a device or non-dir */ 165 if (stat(name, &sb) == 0) { 166 if (S_ISBLK(sb.st_mode)) { 167 if ((mntpt = getmntname(name, MNTON, type)) == NULL) { 168 warnx("%s: not currently mounted", name); 169 return (1); 170 } 171 } else if (!S_ISDIR(sb.st_mode)) { 172 warnx("%s: not a directory or special device", name); 173 return (1); 174 } 175 } 176 177 /* 178 * Look up the name in the mount table. 179 * 99.9% of the time the path in the kernel is the one 180 * realpath() returns but check the original just in case... 181 */ 182 if (!(newname = getmntname(name, MNTFROM, type)) && 183 !(mntpt = getmntname(name, MNTON, type)) ) { 184 mntpt = oname; 185 if (!(newname = getmntname(oname, MNTFROM, type)) && 186 !(mntpt = getmntname(oname, MNTON, type))) { 187 warnx("%s: not currently mounted", oname); 188 return (1); 189 } 190 } 191 if (newname) 192 name = newname; 193 194 if (!selected(type)) 195 return (1); 196 197 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN)) { 198 if ((delimp = strchr(name, '@')) != NULL) { 199 hostp = delimp + 1; 200 *delimp = '\0'; 201 hp = gethostbyname(hostp); 202 *delimp = '@'; 203 } else if ((delimp = strchr(name, ':')) != NULL) { 204 *delimp = '\0'; 205 hostp = name; 206 hp = gethostbyname(hostp); 207 name = delimp + 1; 208 *delimp = ':'; 209 } else 210 hp = NULL; 211 if (!namematch(hp)) 212 return (1); 213 } 214 215 if (verbose) 216 printf("%s: unmount from %s\n", name, mntpt); 217 218 if (unmount(mntpt, fflag) < 0) { 219 warn("%s", mntpt); 220 return (1); 221 } 222 223 #ifndef NO_NFS 224 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN) && 225 (hp != NULL) && !(fflag & MNT_FORCE)) { 226 enum clnt_stat clnt_stat; 227 228 *delimp = '\0'; 229 memset(&saddr, 0, sizeof(saddr)); 230 saddr.sin_family = AF_INET; 231 saddr.sin_port = 0; 232 memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); 233 pertry.tv_sec = 3; 234 pertry.tv_usec = 0; 235 so = RPC_ANYSOCK; 236 if ((clp = clntudp_create(&saddr, 237 RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { 238 clnt_pcreateerror("Cannot MNT RPC"); 239 return (1); 240 } 241 clp->cl_auth = authunix_create_default(); 242 try.tv_sec = 20; 243 try.tv_usec = 0; 244 clnt_stat = clnt_call(clp, 245 RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try); 246 if (clnt_stat != RPC_SUCCESS) { 247 clnt_perror(clp, "Bad MNT RPC"); 248 return (1); 249 } 250 auth_destroy(clp->cl_auth); 251 clnt_destroy(clp); 252 } 253 #endif 254 return (0); 255 } 256 257 char * 258 getmntname(char *name, mntwhat what, char *type) 259 { 260 struct statfs *mntbuf; 261 int n; 262 263 if ((n = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 264 warn("getmntinfo"); 265 return (NULL); 266 } 267 while (--n >= 0) { 268 if ((what == MNTON) && 269 (strncmp(mntbuf[n].f_mntfromname, name, MNAMELEN) == 0 || 270 strncmp(mntbuf[n].f_mntfromspec, name, MNAMELEN) == 0)) { 271 if (type) 272 memcpy(type, mntbuf[n].f_fstypename, 273 sizeof(mntbuf[n].f_fstypename)); 274 return (mntbuf[n].f_mntonname); 275 } 276 if ((what == MNTFROM) && 277 (strncmp(mntbuf[n].f_mntonname, name, MNAMELEN) == 0)) { 278 if (type) 279 memcpy(type, mntbuf[n].f_fstypename, 280 sizeof(mntbuf[n].f_fstypename)); 281 return (mntbuf[n].f_mntfromname); 282 } 283 } 284 return (NULL); 285 } 286 287 static enum { IN_LIST, NOT_IN_LIST } which; 288 289 int 290 selected(const char *type) 291 { 292 char **av; 293 294 /* If no type specified, it's always selected. */ 295 if (typelist == NULL) 296 return (1); 297 for (av = typelist; *av != NULL; ++av) 298 if (!strncmp(type, *av, MFSNAMELEN)) 299 return (which == IN_LIST ? 1 : 0); 300 return (which == IN_LIST ? 0 : 1); 301 } 302 303 void 304 maketypelist(char *fslist) 305 { 306 int i; 307 char *nextcp, **av; 308 309 if ((fslist == NULL) || (fslist[0] == '\0')) 310 errx(1, "empty type list"); 311 312 /* 313 * XXX 314 * Note: the syntax is "noxxx,yyy" for no xxx's and 315 * no yyy's, not the more intuitive "noxxx,noyyy". 316 */ 317 if (fslist[0] == 'n' && fslist[1] == 'o') { 318 fslist += 2; 319 which = NOT_IN_LIST; 320 } else 321 which = IN_LIST; 322 323 /* Count the number of types. */ 324 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) 325 ++nextcp; 326 327 /* Build an array of that many types. */ 328 if ((av = typelist = calloc(i + 1, sizeof(char *))) == NULL) 329 err(1, NULL); 330 av[0] = fslist; 331 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) { 332 *nextcp = '\0'; 333 av[i] = ++nextcp; 334 } 335 /* Terminate the array. */ 336 av[i] = NULL; 337 } 338 339 int 340 namematch(struct hostent *hp) 341 { 342 char *cp, **np; 343 344 if ((hp == NULL) || (nfshost == NULL)) 345 return (1); 346 347 if (strcasecmp(nfshost, hp->h_name) == 0) 348 return (1); 349 350 if ((cp = strchr(hp->h_name, '.')) != NULL) { 351 *cp = '\0'; 352 if (strcasecmp(nfshost, hp->h_name) == 0) 353 return (1); 354 } 355 for (np = hp->h_aliases; *np; np++) { 356 if (strcasecmp(nfshost, *np) == 0) 357 return (1); 358 if ((cp = strchr(*np, '.')) != NULL) { 359 *cp = '\0'; 360 if (strcasecmp(nfshost, *np) == 0) 361 return (1); 362 } 363 } 364 return (0); 365 } 366 367 #ifndef NO_NFS 368 /* 369 * xdr routines for mount rpc's 370 */ 371 int 372 xdr_dir(XDR *xdrsp, char *dirp) 373 { 374 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 375 } 376 #endif 377 378 void 379 usage(void) 380 { 381 fprintf(stderr, 382 "usage: %s\n %s\n", 383 "umount [-fv] special | node", 384 "umount -a [-fv] [-h host] [-t type]"); 385 exit(1); 386 } 387