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