xref: /illumos-gate/usr/src/lib/libvolmgt/common/volmgt_on_private.c (revision d67944fbe3fa0b31893a7116a09b0718eecf6078)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * routines in this module are meant to be called by other libvolmgt
28  * routines only
29  */
30 
31 #include	<stdio.h>
32 #include	<string.h>
33 #include	<dirent.h>
34 #include	<fcntl.h>
35 #include	<string.h>
36 #ifdef	DEBUG
37 #include	<errno.h>
38 #endif
39 #include	<libintl.h>
40 #include	<limits.h>
41 #include	<unistd.h>
42 #include	<stdlib.h>
43 #include	<volmgt.h>
44 #include	<sys/types.h>
45 #include	<sys/mkdev.h>
46 #include	<sys/stat.h>
47 #include	<sys/dkio.h>
48 #include	<sys/param.h>
49 #include	<sys/wait.h>
50 #include	<sys/mnttab.h>
51 #include	"volmgt_private.h"
52 
53 
54 #define	NULL_PATH		"/dev/null"
55 
56 
57 static int	vol_getmntdev(FILE *, struct mnttab *, dev_t,
58 			    struct dk_cinfo *);
59 
60 /*
61  * This is an ON Consolidation Private interface.
62  *
63  * Is the specified path mounted?
64  *
65  * This function is really inadequate for ejection testing.  For example,
66  * I could have /dev/fd0a mounted and eject /dev/fd0c, and it would be
67  * ejected.  There needs to be some better way to make this check, although
68  * short of looking up the mounted dev_t in the kernel mount table and
69  * building in all kinds of knowledge into this function,  I'm not sure
70  * how to do it.
71  */
72 int
73 _dev_mounted(char *path)
74 {
75 	int		fd = -1;
76 	struct dk_cinfo	info;
77 	static FILE 	*fp = NULL;		/* mnttab file pointer */
78 	struct mnttab	mnt;			/* set bug not used */
79 	char		*cn = NULL;		/* char spcl pathname */
80 	struct stat64	sb;
81 	int		ret_val = 0;
82 
83 
84 	/* ensure we have the block spcl pathname */
85 	if ((cn = (char *)volmgt_getfullrawname(path)) == NULL) {
86 		goto dun;
87 	}
88 
89 	if ((fp = fopen(MNTTAB, "rF")) == NULL) {
90 		/* mtab is gone... let him go */
91 		goto dun;
92 	}
93 
94 	if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
95 		goto dun;
96 	}
97 
98 	if (fstat64(fd, &sb) < 0) {
99 		goto dun;
100 	}
101 
102 	if (ioctl(fd, DKIOCINFO, &info) != 0) {
103 		goto dun;
104 	}
105 
106 	if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
107 		ret_val = 1;			/* match found! */
108 	}
109 
110 dun:
111 	if (cn != NULL) {
112 		free(cn);
113 	}
114 	if (fp != NULL) {
115 		(void) fclose(fp);
116 	}
117 	if (fd >= 0) {
118 		(void) close(fd);
119 	}
120 	return (ret_val);
121 }
122 
123 
124 static int	call_unmount_prog(int, int, char *, int, char *,
125 			    char *);
126 static int	get_media_info(char *, char **, int *, char **);
127 /*
128  * This is an ON Consolidation Private interface.
129  *
130  * Forks off rmmount and (in essence) returns the result
131  *
132  * a return value of 0 (FALSE) means failure, non-zero (TRUE) means success
133  */
134 int
135 _dev_unmount(char *path)
136 {
137 	char		*bn = NULL;		/* block name */
138 	char		*mtype = NULL;		/* media type */
139 	char		*spcl = NULL;		/* special dev. path */
140 	char		*spcl_failed = NULL;	/* spcl that failed */
141 	int		ret_val = FALSE;	/* what we return */
142 	char		*vr;			/* volmgt root dir */
143 	int		media_info_gotten = 0;
144 	int		mnum = 0;
145 	int		volume_is_not_managed;
146 	char		*pathbuf, *absname;
147 
148 
149 	if ((bn = (char *)volmgt_getfullblkname(path)) == NULL) {
150 		goto dun;
151 	}
152 
153 	if ((pathbuf = malloc(PATH_MAX+1)) == NULL)
154 		goto dun;
155 
156 	absname = bn;
157 	if (realpath(bn, pathbuf) != NULL)
158 		absname = pathbuf;
159 
160 	volume_is_not_managed = !volmgt_running() ||
161 	    (!volmgt_ownspath(absname) && volmgt_symname(bn) == NULL);
162 
163 	free(pathbuf);
164 
165 	/* decide of we should use rmmount to unmount the media */
166 	if (!volume_is_not_managed) {
167 		int		use_rmm = FALSE;	/* use rmmount??  */
168 
169 		/* at least volmgt is running */
170 		vr = (char *)volmgt_root();
171 		if (strncmp(bn, vr, strlen(vr)) == 0) {
172 			/* the block path is rooted in /vol */
173 			use_rmm = TRUE;
174 		}
175 
176 		/* try to get info about media */
177 		media_info_gotten = get_media_info(bn, &mtype, &mnum, &spcl);
178 
179 		ret_val = call_unmount_prog(media_info_gotten, use_rmm, mtype,
180 		    mnum, spcl, bn);
181 
182 	} else {
183 
184 		/* volmgt is *not* running */
185 
186 		if (get_media_info(bn, &mtype, &mnum, &spcl)) {
187 
188 			/*
189 			 * volmgt is off and get_media_info() has returned
190 			 * info on the media -- soo (this is kinda' a hack)
191 			 * ... we iterate, looking for multiple slices
192 			 * of (say) a floppy being mounted
193 			 *
194 			 * note: if an unmount fails we don't want to try
195 			 * to unmount the same device on the next try, so
196 			 * we try to watch for that
197 			 */
198 
199 			do {
200 				/*
201 				 * don't call the unmount program is we're just
202 				 * trying to unmount the same device that
203 				 * failed last time -- if that's the case,
204 				 * then bail
205 				 */
206 				if (spcl_failed != NULL) {
207 					if (strcmp(spcl, spcl_failed) == 0) {
208 						break;
209 					}
210 				}
211 				ret_val = call_unmount_prog(TRUE, FALSE,
212 				    mtype, mnum, spcl, bn);
213 
214 				if (!ret_val) {
215 					/* save spcl device name that failed */
216 					spcl_failed = strdup(spcl);
217 				} else {
218 					/*
219 					 * unmount succeeded, so clean up
220 					 */
221 					if (spcl_failed != NULL) {
222 						free(spcl_failed);
223 						spcl_failed = NULL;
224 					}
225 				}
226 
227 			} while (get_media_info(bn, &mtype, &mnum, &spcl));
228 
229 		} else {
230 
231 			/* just do the unmmount cycle once */
232 			ret_val = call_unmount_prog(FALSE, FALSE, NULL, 0,
233 			    NULL, bn);
234 		}
235 
236 	}
237 
238 	if (mtype != NULL) {
239 		free(mtype);
240 	}
241 	if (spcl != NULL) {
242 		free(spcl);
243 	}
244 	if (spcl_failed != NULL) {
245 		free(spcl_failed);
246 	}
247 	if (bn != NULL) {
248 		free(bn);
249 	}
250 
251 dun:
252 
253 	return (ret_val);
254 }
255 
256 
257 /*
258  * find a mnttab entry that has the same dev as the supplied dev,
259  *  returning it and a non-zero value if found, else returning 0
260  *
261  * this is just like getmntany(), except that it scans based on st_rdev,
262  * and it even finds different slices on the same device/unit (thanx to
263  * code copied from format.c)
264  */
265 static int
266 vol_getmntdev(FILE *fp, struct mnttab *mp, dev_t dev, struct dk_cinfo *ip)
267 {
268 	int		fd;		/* dev-in-question fd */
269 	struct stat64	sb;		/* dev-in-question stat struct */
270 	int		ret_val = 0;	/* default value: no match found */
271 	char		*cn;		/* char pathname */
272 	struct dk_cinfo	dkinfo;		/* for testing for slices */
273 
274 
275 #ifdef	DEBUG
276 	denter(
277 	    "vol_getmntdev: entering for %d.%d, ctype/cnum/unit = %d/%d/%d\n",
278 	    (int)major(dev), (int)minor(dev), ip->dki_ctype, ip->dki_cnum,
279 	    ip->dki_unit);
280 #endif
281 
282 	/* reset the mnttab -- just in case */
283 	rewind(fp);
284 
285 	/* scan each entry in mnttab */
286 	while (getmntent(fp, mp) == 0) {
287 
288 		/* don't even try unless it's a local pathname */
289 		if (mp->mnt_special[0] != '/') {
290 			continue;
291 		}
292 
293 		/* get char pathname */
294 		if ((cn = volmgt_getfullrawname(mp->mnt_special)) == NULL) {
295 			continue;
296 		}
297 		if (cn[0] == NULLC) {
298 			free(cn);
299 			continue;	/* couldn't get raw name */
300 		}
301 
302 		/* open the device */
303 		if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
304 			/* if we can't open it *assume* it's not a match */
305 			free(cn);
306 			continue;
307 		}
308 
309 		/* stat the device */
310 		if (fstat64(fd, &sb) < 0) {
311 			free(cn);
312 			(void) close(fd);
313 			continue;	/* ain't there: can't be a match */
314 		}
315 
316 		/* ensure we have a spcl device (a double check) */
317 		if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode)) {
318 			free(cn);
319 			(void) close(fd);
320 			continue;
321 		}
322 
323 		/* (almost) finally -- check the dev_t for equality */
324 		if (sb.st_rdev == dev) {
325 			ret_val = 1;		/* match found! */
326 			free(cn);
327 			(void) close(fd);
328 			break;
329 		}
330 
331 		/*
332 		 * check that the major numbers match, since if they
333 		 * don't then there's no reason to use the DKIOCINFO
334 		 * ioctl to see if we have to major/minor pairs that
335 		 * really point to the same device
336 		 */
337 		if (major(sb.st_rdev) != major(dev)) {
338 			/* no use continuing, since major devs are different */
339 			free(cn);
340 			(void) close(fd);
341 			continue;
342 		}
343 
344 		/* one last check -- for diff. slices of the same dev/unit */
345 		if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
346 			free(cn);
347 			(void) close(fd);
348 			continue;
349 		}
350 
351 		free(cn);		/* all done with raw pathname */
352 		(void) close(fd);	/* all done with file descriptor */
353 
354 		/* if ctrler type/number and unit match, it's a match */
355 		if ((ip->dki_ctype == dkinfo.dki_ctype) &&
356 		    (ip->dki_cnum == dkinfo.dki_cnum) &&
357 		    (ip->dki_unit == dkinfo.dki_unit)) {
358 			/*
359 			 * even though minor numbers differ we have a
360 			 * match
361 			 */
362 			ret_val = 1;
363 			break;
364 		}
365 
366 		/* go around again */
367 	}
368 
369 	return (ret_val);
370 }
371 
372 
373 char *
374 vol_basename(char *path)
375 {
376 	char	*cp;
377 
378 
379 	/* check for the degenerate case */
380 	if (strcmp(path, "/") == 0) {
381 		return (path);
382 	}
383 
384 	/* look for the last slash in the name */
385 	if ((cp = strrchr(path, '/')) == NULL) {
386 		/* no slash */
387 		return (path);
388 	}
389 
390 	/* ensure something is after the slash */
391 	if (*++cp != NULLC) {
392 		return (cp);
393 	}
394 
395 	/* a name that ends in slash -- back up until previous slash */
396 	while (cp != path) {
397 		if (*--cp == '/') {
398 			return (--cp);
399 		}
400 	}
401 
402 	/* the only slash is the end of the name */
403 	return (path);
404 }
405 
406 static int	vol_getmntdev(FILE *, struct mnttab *, dev_t,
407 			    struct dk_cinfo *);
408 
409 static int
410 get_media_info(char *path, char **mtypep, int *mnump, char **spclp)
411 {
412 	FILE		*fp = NULL;
413 	int		fd = -1;
414 	char		*cn = NULL;		/* char spcl pathname */
415 	struct stat64	sb;
416 	struct dk_cinfo	info;
417 	struct mnttab	mnt;
418 	int		ret_val = FALSE;
419 
420 	if ((fp = fopen(MNTTAB, "rF")) == NULL) {
421 		/* mtab is gone... let him go */
422 		goto dun;
423 	}
424 
425 	/* get char spcl pathname */
426 	if ((cn = volmgt_getfullrawname(path)) == NULL) {
427 		goto dun;
428 	}
429 	if (cn[0] == NULLC) {
430 		goto dun;
431 	}
432 
433 	if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) {
434 		goto dun;
435 	}
436 
437 	if (fstat64(fd, &sb) < 0) {
438 		goto dun;
439 	}
440 
441 	if (ioctl(fd, DKIOCINFO, &info) != 0) {
442 		goto dun;
443 	}
444 
445 	/* if we found the entry then disect it */
446 	if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) {
447 		char		*cp;
448 		char		*mtype;
449 		char		*mnt_dir;
450 		int		mtype_len;
451 		DIR		*dirp = NULL;
452 		struct dirent64	*dp;
453 		char		*volname;
454 
455 
456 		/* return the spcl device name found */
457 		*spclp = strdup(mnt.mnt_special);
458 
459 		/*
460 		 * try to get the media type (e.g. "floppy") from the mount
461 		 * point (e.g. "/floppy/NAME") if vold is running
462 		 */
463 
464 		if (!volmgt_running() ||
465 		    (!volmgt_ownspath(*spclp) &&
466 		    volmgt_symname(*spclp) == NULL)) {
467 			ret_val = TRUE;		/* success (if limited) */
468 			goto dun;
469 		}
470 
471 		/* get the first part of the mount point (e.g. "floppy") */
472 		cp = mnt.mnt_mountp;
473 		if (*cp++ != '/') {
474 			goto dun;
475 		}
476 		mtype = cp;
477 		if ((cp = strchr(mtype, '/')) == NULL) {
478 			goto dun;
479 		}
480 		*cp++ = NULLC;
481 		mnt_dir = mnt.mnt_mountp;	/* save dir path */
482 
483 		/* get the volume name (e.g. "unnamed_floppy") */
484 		volname = cp;
485 
486 		/* scan for the symlink that points to our volname */
487 		if ((dirp = opendir(mnt_dir)) == NULL) {
488 			goto dun;
489 		}
490 		mtype_len = strlen(mtype);
491 		while ((dp = readdir64(dirp)) != NULL) {
492 			char		lpath[2 * (MAXNAMELEN+1)];
493 			char		linkbuf[MAXPATHLEN+4];
494 			int		lb_len;
495 			struct stat64	sb;
496 
497 
498 			if (strncmp(dp->d_name, mtype, mtype_len) != 0) {
499 				continue;	/* not even close */
500 			}
501 
502 			(void) sprintf(lpath, "%s/%s", mnt_dir,
503 			    dp->d_name);
504 			if (lstat64(lpath, &sb) < 0) {
505 				continue;	/* what? */
506 			}
507 			if (!S_ISLNK(sb.st_mode)) {
508 				continue;	/* not our baby */
509 			}
510 			if ((lb_len = readlink(lpath, linkbuf,
511 			    sizeof (linkbuf))) < 0) {
512 				continue;
513 			}
514 			linkbuf[lb_len] = NULLC; /* null terminate */
515 			if ((cp = vol_basename(linkbuf)) == NULL) {
516 				continue;
517 			}
518 			/* now we have the name! */
519 			if (strcmp(cp, volname) == 0) {
520 				/* found it !! */
521 				if (sscanf(dp->d_name + mtype_len, "%d",
522 				    mnump) == 1) {
523 					*mtypep = strdup(mtype);
524 					ret_val = TRUE;
525 				}
526 				break;
527 			}
528 		}
529 		(void) closedir(dirp);
530 	}
531 
532 dun:
533 	if (fp != NULL) {
534 		(void) fclose(fp);
535 	}
536 	if (fd >= 0) {
537 		(void) close(fd);
538 	}
539 	if (cn != NULL) {
540 		free(cn);
541 	}
542 #ifdef	DEBUG
543 	if (ret_val) {
544 		dexit("get_media_info: returning mtype=%s, mnum=%d, spcl=%s\n",
545 		    *mtypep == NULL ? "<null ptr>" : *mtypep,
546 		    *mnump,
547 		    *spclp == NULL ? "<null ptr>" : *spclp);
548 	} else {
549 		dexit("get_media_info: FAILED\n");
550 	}
551 #endif
552 	return (ret_val);
553 }
554 
555 
556 /*
557  * call the appropriate unmount program, returning its success (TRUE)
558  * or failure (FALSE)
559  */
560 static int
561 call_unmount_prog(int mi_gotten, int use_rmm, char *mtype, int mnum,
562     char *spcl, char *bn)
563 {
564 	pid_t		pid;			/* forked proc's pid */
565 	int		ret_val = FALSE;
566 	const char	*etc_umount = "/etc/umount";
567 	const char	*rmm = "/usr/sbin/rmmount";
568 	int		rval;			/* proc's return value */
569 
570 
571 #ifdef	DEBUG
572 	denter(
573 	"call_unmount_prog(%s, %s, \"%s\", %d, \"%s\", \"%s\"): entering\n",
574 	    mi_gotten ? "TRUE" : "FALSE", use_rmm ? "TRUE" : "FALSE",
575 	    mtype ? mtype : "<null ptr>", mnum, spcl ? spcl : "<null ptr>",
576 	    bn);
577 #endif
578 	/* create a child to unmount the path */
579 	if ((pid = fork()) < 0) {
580 		goto dun;
581 	}
582 
583 	if (pid == 0) {
584 		/* the child */
585 #ifndef	DEBUG
586 		int		xfd;
587 #endif
588 		char		env_buf[MAXPATHLEN];
589 
590 #ifndef	DEBUG
591 		/* get rid of those nasty err messages */
592 		if ((xfd = open(NULL_PATH, O_RDWR)) >= 0) {
593 			(void) dup2(xfd, fileno(stdin));
594 			(void) dup2(xfd, fileno(stdout));
595 			(void) dup2(xfd, fileno(stderr));
596 		}
597 #endif
598 
599 		if (use_rmm) {
600 			/* set up environment vars */
601 			(void) putenv("VOLUME_ACTION=eject");
602 			(void) putenv(strdup(env_buf));
603 			if (mi_gotten) {
604 				(void) sprintf(env_buf,
605 				    "VOLUME_MEDIATYPE=%s", mtype);
606 				(void) putenv(strdup(env_buf));
607 				(void) sprintf(env_buf, "VOLUME_SYMDEV=%s%d",
608 				    mtype, mnum);
609 				(void) putenv(strdup(env_buf));
610 				(void) sprintf(env_buf, "VOLUME_PATH=%s",
611 				    spcl);
612 				(void) putenv(strdup(env_buf));
613 				(void) sprintf(env_buf, "VOLUME_NAME=%s",
614 				    vol_basename(spcl));
615 				(void) putenv(strdup(env_buf));
616 			} else {
617 				(void) sprintf(env_buf, "VOLUME_PATH=%s", bn);
618 				(void) putenv(strdup(env_buf));
619 				(void) sprintf(env_buf, "VOLUME_NAME=%s",
620 				    vol_basename(bn));
621 				(void) putenv(strdup(env_buf));
622 			}
623 #ifdef	DEBUG
624 			dprintf("call_unmount_prog: calling \"%s -D\"\n", rmm);
625 			(void) execl(rmm, rmm, "-D", NULL);
626 #else
627 			(void) execl(rmm, rmm, NULL);
628 #endif
629 		} else {
630 #ifdef	DEBUG
631 			dprintf("call_unmount_prog: calling \"%s %s\"\n",
632 			    etc_umount, mi_gotten ? spcl : bn);
633 #endif
634 			(void) execl(etc_umount, etc_umount,
635 			    mi_gotten ? spcl : bn,
636 			    NULL);
637 		}
638 		exit(-1);
639 		/*NOTREACHED*/
640 	}
641 
642 	/* wait for the umount command to exit */
643 	if (waitpid(pid, &rval, 0) == pid) {
644 		if (WIFEXITED(rval)) {
645 			if (WEXITSTATUS(rval) == 0) {
646 				ret_val = TRUE;	/* success */
647 			}
648 		}
649 	}
650 
651 dun:
652 	return (ret_val);
653 }
654