1 /* $OpenBSD: umount.c,v 1.29 2019/06/28 13:32:46 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
38 #include <netdb.h>
39 #include <rpc/rpc.h>
40 #include <rpc/pmap_clnt.h>
41 #include <rpc/pmap_prot.h>
42 #include <nfs/rpcv2.h>
43
44 #include <err.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <limits.h>
50 #include <util.h>
51
52 typedef enum { MNTON, MNTFROM } mntwhat;
53
54 int fflag, verbose;
55 char **typelist = NULL;
56 char *nfshost;
57
58 char *getmntname(char *, mntwhat, char *);
59 void maketypelist(char *);
60 int selected(const char *);
61 int namematch(struct hostent *);
62 int umountall(void);
63 int umountfs(char *);
64 void usage(void);
65 int xdr_dir(XDR *, char *);
66
67 int
main(int argc,char * argv[])68 main(int argc, char *argv[])
69 {
70 int all, ch, errs;
71
72 /* Start disks transferring immediately. */
73 sync();
74
75 all = 0;
76 while ((ch = getopt(argc, argv, "afh:t:v")) != -1)
77 switch (ch) {
78 case 'a':
79 all = 1;
80 break;
81 case 'f':
82 fflag = MNT_FORCE;
83 break;
84 case 'h': /* -h implies -a. */
85 all = 1;
86 nfshost = optarg;
87 break;
88 case 't':
89 if (typelist != NULL)
90 errx(1, "only one -t option may be specified.");
91 maketypelist(optarg);
92 break;
93 case 'v':
94 verbose = 1;
95 break;
96 default:
97 usage();
98 /* NOTREACHED */
99 }
100 argc -= optind;
101 argv += optind;
102
103 if ((argc == 0 && !all) || (argc != 0 && all))
104 usage();
105
106 /* -h implies "-t nfs" if no -t flag. */
107 if ((nfshost != NULL) && (typelist == NULL))
108 maketypelist(MOUNT_NFS);
109
110 if (all)
111 errs = umountall();
112 else
113 for (errs = 0; *argv != NULL; ++argv)
114 if (umountfs(*argv) != 0)
115 errs = 1;
116 return (errs);
117 }
118
119 int
umountall(void)120 umountall(void)
121 {
122 struct statfs *fs;
123 int n;
124 int rval;
125
126 n = getmntinfo(&fs, MNT_NOWAIT);
127 if (n == 0)
128 err(1, NULL);
129
130 rval = 0;
131 while (--n >= 0) {
132 /* Ignore the root. */
133 if (strncmp(fs[n].f_mntonname, "/", MNAMELEN) == 0)
134 continue;
135 if (!selected(fs[n].f_fstypename))
136 continue;
137 if (umountfs(fs[n].f_mntonname))
138 rval = 1;
139 }
140 return (rval);
141 }
142
143 int
umountfs(char * oname)144 umountfs(char *oname)
145 {
146 struct hostent *hp;
147 #ifndef NO_NFS
148 struct sockaddr_in saddr;
149 struct timeval pertry, try;
150 CLIENT *clp;
151 int so;
152 #endif
153 struct stat sb;
154 char *delimp, *hostp, *mntpt;
155 char *name, *newname, rname[PATH_MAX], type[MFSNAMELEN];
156
157 if (isduid(oname, 0) || realpath(oname, rname) == NULL)
158 mntpt = name = oname;
159 else
160 mntpt = name = rname;
161 newname = NULL;
162
163 /* If we can stat the file, check to see if it is a device or non-dir */
164 if (stat(name, &sb) == 0) {
165 if (S_ISBLK(sb.st_mode)) {
166 if ((mntpt = getmntname(name, MNTON, type)) == NULL) {
167 warnx("%s: not currently mounted", name);
168 return (1);
169 }
170 } else if (!S_ISDIR(sb.st_mode)) {
171 warnx("%s: not a directory or special device", name);
172 return (1);
173 }
174 }
175
176 /*
177 * Look up the name in the mount table.
178 * 99.9% of the time the path in the kernel is the one
179 * realpath() returns but check the original just in case...
180 */
181 if (!(newname = getmntname(name, MNTFROM, type)) &&
182 !(mntpt = getmntname(name, MNTON, type)) ) {
183 mntpt = oname;
184 if (!(newname = getmntname(oname, MNTFROM, type)) &&
185 !(mntpt = getmntname(oname, MNTON, type))) {
186 warnx("%s: not currently mounted", oname);
187 return (1);
188 }
189 }
190 if (newname)
191 name = newname;
192
193 if (!selected(type))
194 return (1);
195
196 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN)) {
197 if ((delimp = strchr(name, '@')) != NULL) {
198 hostp = delimp + 1;
199 *delimp = '\0';
200 hp = gethostbyname(hostp);
201 *delimp = '@';
202 } else if ((delimp = strchr(name, ':')) != NULL) {
203 *delimp = '\0';
204 hostp = name;
205 hp = gethostbyname(hostp);
206 name = delimp + 1;
207 *delimp = ':';
208 } else
209 hp = NULL;
210 if (!namematch(hp))
211 return (1);
212 }
213
214 if (verbose)
215 printf("%s: unmount from %s\n", name, mntpt);
216
217 if (unmount(mntpt, fflag) == -1) {
218 warn("%s", mntpt);
219 return (1);
220 }
221
222 #ifndef NO_NFS
223 if (!strncmp(type, MOUNT_NFS, MFSNAMELEN) &&
224 (hp != NULL) && !(fflag & MNT_FORCE)) {
225 enum clnt_stat clnt_stat;
226
227 *delimp = '\0';
228 memset(&saddr, 0, sizeof(saddr));
229 saddr.sin_family = AF_INET;
230 saddr.sin_port = 0;
231 memmove(&saddr.sin_addr, hp->h_addr, hp->h_length);
232 pertry.tv_sec = 3;
233 pertry.tv_usec = 0;
234 so = RPC_ANYSOCK;
235 if ((clp = clntudp_create(&saddr,
236 RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) {
237 clnt_pcreateerror("Cannot MNT RPC");
238 return (1);
239 }
240 clp->cl_auth = authunix_create_default();
241 try.tv_sec = 20;
242 try.tv_usec = 0;
243 clnt_stat = clnt_call(clp,
244 RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try);
245 if (clnt_stat != RPC_SUCCESS) {
246 clnt_perror(clp, "Bad MNT RPC");
247 return (1);
248 }
249 auth_destroy(clp->cl_auth);
250 clnt_destroy(clp);
251 }
252 #endif
253 return (0);
254 }
255
256 char *
getmntname(char * name,mntwhat what,char * type)257 getmntname(char *name, mntwhat what, char *type)
258 {
259 struct statfs *mntbuf;
260 int n;
261
262 if ((n = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
263 warn("getmntinfo");
264 return (NULL);
265 }
266 while (--n >= 0) {
267 if ((what == MNTON) &&
268 (strncmp(mntbuf[n].f_mntfromname, name, MNAMELEN) == 0 ||
269 strncmp(mntbuf[n].f_mntfromspec, name, MNAMELEN) == 0)) {
270 if (type)
271 memcpy(type, mntbuf[n].f_fstypename,
272 sizeof(mntbuf[n].f_fstypename));
273 return (mntbuf[n].f_mntonname);
274 }
275 if ((what == MNTFROM) &&
276 (strncmp(mntbuf[n].f_mntonname, name, MNAMELEN) == 0)) {
277 if (type)
278 memcpy(type, mntbuf[n].f_fstypename,
279 sizeof(mntbuf[n].f_fstypename));
280 return (mntbuf[n].f_mntfromname);
281 }
282 }
283 return (NULL);
284 }
285
286 static enum { IN_LIST, NOT_IN_LIST } which;
287
288 int
selected(const char * type)289 selected(const char *type)
290 {
291 char **av;
292
293 /* If no type specified, it's always selected. */
294 if (typelist == NULL)
295 return (1);
296 for (av = typelist; *av != NULL; ++av)
297 if (!strncmp(type, *av, MFSNAMELEN))
298 return (which == IN_LIST ? 1 : 0);
299 return (which == IN_LIST ? 0 : 1);
300 }
301
302 void
maketypelist(char * fslist)303 maketypelist(char *fslist)
304 {
305 int i;
306 char *nextcp, **av;
307
308 if ((fslist == NULL) || (fslist[0] == '\0'))
309 errx(1, "empty type list");
310
311 /*
312 * XXX
313 * Note: the syntax is "noxxx,yyy" for no xxx's and
314 * no yyy's, not the more intuitive "noxxx,noyyy".
315 */
316 if (fslist[0] == 'n' && fslist[1] == 'o') {
317 fslist += 2;
318 which = NOT_IN_LIST;
319 } else
320 which = IN_LIST;
321
322 /* Count the number of types. */
323 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++)
324 ++nextcp;
325
326 /* Build an array of that many types. */
327 if ((av = typelist = calloc(i + 1, sizeof(char *))) == NULL)
328 err(1, NULL);
329 av[0] = fslist;
330 for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) {
331 *nextcp = '\0';
332 av[i] = ++nextcp;
333 }
334 /* Terminate the array. */
335 av[i] = NULL;
336 }
337
338 int
namematch(struct hostent * hp)339 namematch(struct hostent *hp)
340 {
341 char *cp, **np;
342
343 if ((hp == NULL) || (nfshost == NULL))
344 return (1);
345
346 if (strcasecmp(nfshost, hp->h_name) == 0)
347 return (1);
348
349 if ((cp = strchr(hp->h_name, '.')) != NULL) {
350 *cp = '\0';
351 if (strcasecmp(nfshost, hp->h_name) == 0)
352 return (1);
353 }
354 for (np = hp->h_aliases; *np; np++) {
355 if (strcasecmp(nfshost, *np) == 0)
356 return (1);
357 if ((cp = strchr(*np, '.')) != NULL) {
358 *cp = '\0';
359 if (strcasecmp(nfshost, *np) == 0)
360 return (1);
361 }
362 }
363 return (0);
364 }
365
366 #ifndef NO_NFS
367 /*
368 * xdr routines for mount rpc's
369 */
370 int
xdr_dir(XDR * xdrsp,char * dirp)371 xdr_dir(XDR *xdrsp, char *dirp)
372 {
373 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
374 }
375 #endif
376
377 void
usage(void)378 usage(void)
379 {
380 fprintf(stderr,
381 "usage: %s\n %s\n",
382 "umount [-fv] special | node",
383 "umount -a [-fv] [-h host] [-t type]");
384 exit(1);
385 }
386