1 /* $OpenBSD: umount.c,v 1.26 2015/01/16 06:40:01 deraadt 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 struct sockaddr_in saddr; 149 struct stat sb; 150 struct timeval pertry, try; 151 CLIENT *clp; 152 int so; 153 char *delimp, *hostp, *mntpt; 154 char *name, *newname, rname[PATH_MAX], type[MFSNAMELEN]; 155 156 if (isduid(oname, 0) || realpath(oname, rname) == NULL) 157 mntpt = name = oname; 158 else 159 mntpt = name = rname; 160 newname = NULL; 161 162 /* If we can stat the file, check to see if it is a device or non-dir */ 163 if (stat(name, &sb) == 0) { 164 if (S_ISBLK(sb.st_mode)) { 165 if ((mntpt = getmntname(name, MNTON, type)) == NULL) { 166 warnx("%s: not currently mounted", name); 167 return (1); 168 } 169 } else if (!S_ISDIR(sb.st_mode)) { 170 warnx("%s: not a directory or special device", name); 171 return (1); 172 } 173 } 174 175 /* 176 * Look up the name in the mount table. 177 * 99.9% of the time the path in the kernel is the one 178 * realpath() returns but check the original just in case... 179 */ 180 if (!(newname = getmntname(name, MNTFROM, type)) && 181 !(mntpt = getmntname(name, MNTON, type)) ) { 182 mntpt = oname; 183 if (!(newname = getmntname(oname, MNTFROM, type)) && 184 !(mntpt = getmntname(oname, MNTON, type))) { 185 warnx("%s: not currently mounted", oname); 186 return (1); 187 } 188 } 189 if (newname) 190 name = newname; 191 192 if (!selected(type)) 193 return (1); 194 195 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN)) { 196 if ((delimp = strchr(name, '@')) != NULL) { 197 hostp = delimp + 1; 198 *delimp = '\0'; 199 hp = gethostbyname(hostp); 200 *delimp = '@'; 201 } else if ((delimp = strchr(name, ':')) != NULL) { 202 *delimp = '\0'; 203 hostp = name; 204 hp = gethostbyname(hostp); 205 name = delimp + 1; 206 *delimp = ':'; 207 } else 208 hp = NULL; 209 if (!namematch(hp)) 210 return (1); 211 } 212 213 if (verbose) 214 printf("%s: unmount from %s\n", name, mntpt); 215 216 if (unmount(mntpt, fflag) < 0) { 217 warn("%s", mntpt); 218 return (1); 219 } 220 221 #ifndef NO_NFS 222 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN) && 223 (hp != NULL) && !(fflag & MNT_FORCE)) { 224 enum clnt_stat clnt_stat; 225 226 *delimp = '\0'; 227 memset(&saddr, 0, sizeof(saddr)); 228 saddr.sin_family = AF_INET; 229 saddr.sin_port = 0; 230 memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); 231 pertry.tv_sec = 3; 232 pertry.tv_usec = 0; 233 so = RPC_ANYSOCK; 234 if ((clp = clntudp_create(&saddr, 235 RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { 236 clnt_pcreateerror("Cannot MNT RPC"); 237 return (1); 238 } 239 clp->cl_auth = authunix_create_default(); 240 try.tv_sec = 20; 241 try.tv_usec = 0; 242 clnt_stat = clnt_call(clp, 243 RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try); 244 if (clnt_stat != RPC_SUCCESS) { 245 clnt_perror(clp, "Bad MNT RPC"); 246 return (1); 247 } 248 auth_destroy(clp->cl_auth); 249 clnt_destroy(clp); 250 } 251 #endif 252 return (0); 253 } 254 255 char * 256 getmntname(char *name, mntwhat what, char *type) 257 { 258 struct statfs *mntbuf; 259 int n; 260 261 if ((n = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 262 warn("getmntinfo"); 263 return (NULL); 264 } 265 while (--n >= 0) { 266 if ((what == MNTON) && 267 (strncmp(mntbuf[n].f_mntfromname, name, MNAMELEN) == 0 || 268 strncmp(mntbuf[n].f_mntfromspec, name, MNAMELEN) == 0)) { 269 if (type) 270 memcpy(type, mntbuf[n].f_fstypename, 271 sizeof(mntbuf[n].f_fstypename)); 272 return (mntbuf[n].f_mntonname); 273 } 274 if ((what == MNTFROM) && 275 (strncmp(mntbuf[n].f_mntonname, name, MNAMELEN) == 0)) { 276 if (type) 277 memcpy(type, mntbuf[n].f_fstypename, 278 sizeof(mntbuf[n].f_fstypename)); 279 return (mntbuf[n].f_mntfromname); 280 } 281 } 282 return (NULL); 283 } 284 285 static enum { IN_LIST, NOT_IN_LIST } which; 286 287 int 288 selected(const char *type) 289 { 290 char **av; 291 292 /* If no type specified, it's always selected. */ 293 if (typelist == NULL) 294 return (1); 295 for (av = typelist; *av != NULL; ++av) 296 if (!strncmp(type, *av, MFSNAMELEN)) 297 return (which == IN_LIST ? 1 : 0); 298 return (which == IN_LIST ? 0 : 1); 299 } 300 301 void 302 maketypelist(char *fslist) 303 { 304 int i; 305 char *nextcp, **av; 306 307 if ((fslist == NULL) || (fslist[0] == '\0')) 308 errx(1, "empty type list"); 309 310 /* 311 * XXX 312 * Note: the syntax is "noxxx,yyy" for no xxx's and 313 * no yyy's, not the more intuitive "noxxx,noyyy". 314 */ 315 if (fslist[0] == 'n' && fslist[1] == 'o') { 316 fslist += 2; 317 which = NOT_IN_LIST; 318 } else 319 which = IN_LIST; 320 321 /* Count the number of types. */ 322 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) 323 ++nextcp; 324 325 /* Build an array of that many types. */ 326 if ((av = typelist = calloc(i + 1, sizeof(char *))) == NULL) 327 err(1, NULL); 328 av[0] = fslist; 329 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) { 330 *nextcp = '\0'; 331 av[i] = ++nextcp; 332 } 333 /* Terminate the array. */ 334 av[i] = NULL; 335 } 336 337 int 338 namematch(struct hostent *hp) 339 { 340 char *cp, **np; 341 342 if ((hp == NULL) || (nfshost == NULL)) 343 return (1); 344 345 if (strcasecmp(nfshost, hp->h_name) == 0) 346 return (1); 347 348 if ((cp = strchr(hp->h_name, '.')) != NULL) { 349 *cp = '\0'; 350 if (strcasecmp(nfshost, hp->h_name) == 0) 351 return (1); 352 } 353 for (np = hp->h_aliases; *np; np++) { 354 if (strcasecmp(nfshost, *np) == 0) 355 return (1); 356 if ((cp = strchr(*np, '.')) != NULL) { 357 *cp = '\0'; 358 if (strcasecmp(nfshost, *np) == 0) 359 return (1); 360 } 361 } 362 return (0); 363 } 364 365 #ifndef NO_NFS 366 /* 367 * xdr routines for mount rpc's 368 */ 369 int 370 xdr_dir(XDR *xdrsp, char *dirp) 371 { 372 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 373 } 374 #endif 375 376 void 377 usage(void) 378 { 379 fprintf(stderr, 380 "usage: %s\n %s\n", 381 "umount [-fv] special | node", 382 "umount -a [-fv] [-h host] [-t type]"); 383 exit(1); 384 } 385