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.
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.
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]
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.
28 * routines in this module are meant to be called by other libvolmgt
45 #include <sys/types.h>
46 #include <sys/mkdev.h>
49 #include <sys/param.h>
51 #include <sys/mnttab.h>
52 #include "volmgt_private.h"
55 #define NULL_PATH "/dev/null"
58 static int vol_getmntdev(FILE *, struct mnttab
*, dev_t
,
62 * This is an ON Consolidation Private interface.
64 * Is the specified path mounted?
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
74 _dev_mounted(char *path
)
78 static FILE *fp
= NULL
; /* mnttab file pointer */
79 struct mnttab mnt
; /* set bug not used */
80 char *cn
= NULL
; /* char spcl pathname */
85 /* ensure we have the block spcl pathname */
86 if ((cn
= (char *)volmgt_getfullrawname(path
)) == NULL
) {
90 if ((fp
= fopen(MNTTAB
, "rF")) == NULL
) {
91 /* mtab is gone... let it go */
95 if ((fd
= open(cn
, O_RDONLY
|O_NDELAY
)) < 0) {
99 if (fstat64(fd
, &sb
) < 0) {
103 if (ioctl(fd
, DKIOCINFO
, &info
) != 0) {
107 if (vol_getmntdev(fp
, &mnt
, sb
.st_rdev
, &info
) != 0) {
108 ret_val
= 1; /* match found! */
125 static int call_unmount_prog(int, int, char *, int, char *,
127 static int get_media_info(char *, char **, int *, char **);
129 * This is an ON Consolidation Private interface.
131 * Forks off rmmount and (in essence) returns the result
133 * a return value of 0 (FALSE) means failure, non-zero (TRUE) means success
136 _dev_unmount(char *path
)
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;
146 int volume_is_not_managed
;
147 char *pathbuf
, *absname
;
150 if ((bn
= (char *)volmgt_getfullblkname(path
)) == NULL
) {
154 if ((pathbuf
= malloc(PATH_MAX
+1)) == NULL
)
158 if (realpath(bn
, pathbuf
) != NULL
)
161 volume_is_not_managed
= !volmgt_running() ||
162 (!volmgt_ownspath(absname
) && volmgt_symname(bn
) == NULL
);
166 /* decide of we should use rmmount to unmount the media */
167 if (!volume_is_not_managed
) {
168 int use_rmm
= FALSE
; /* use rmmount?? */
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 */
177 /* try to get info about media */
178 media_info_gotten
= get_media_info(bn
, &mtype
, &mnum
, &spcl
);
180 ret_val
= call_unmount_prog(media_info_gotten
, use_rmm
, mtype
,
185 /* volmgt is *not* running */
187 if (get_media_info(bn
, &mtype
, &mnum
, &spcl
)) {
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
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
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,
207 if (spcl_failed
!= NULL
) {
208 if (strcmp(spcl
, spcl_failed
) == 0) {
212 ret_val
= call_unmount_prog(TRUE
, FALSE
,
213 mtype
, mnum
, spcl
, bn
);
216 /* save spcl device name that failed */
217 spcl_failed
= strdup(spcl
);
220 * unmount succeeded, so clean up
222 if (spcl_failed
!= NULL
) {
228 } while (get_media_info(bn
, &mtype
, &mnum
, &spcl
));
232 /* just do the unmmount cycle once */
233 ret_val
= call_unmount_prog(FALSE
, FALSE
, NULL
, 0,
245 if (spcl_failed
!= NULL
) {
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
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)
267 vol_getmntdev(FILE *fp
, struct mnttab
*mp
, dev_t dev
, struct dk_cinfo
*ip
)
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 */
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
,
283 /* reset the mnttab -- just in case */
286 /* scan each entry in mnttab */
287 while (getmntent(fp
, mp
) == 0) {
289 /* don't even try unless it's a local pathname */
290 if (mp
->mnt_special
[0] != '/') {
294 /* get char pathname */
295 if ((cn
= volmgt_getfullrawname(mp
->mnt_special
)) == NULL
) {
298 if (cn
[0] == NULLC
) {
300 continue; /* couldn't get raw name */
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 */
310 /* stat the device */
311 if (fstat64(fd
, &sb
) < 0) {
314 continue; /* ain't there: can't be a match */
317 /* ensure we have a spcl device (a double check) */
318 if (!S_ISBLK(sb
.st_mode
) && !S_ISCHR(sb
.st_mode
)) {
324 /* (almost) finally -- check the dev_t for equality */
325 if (sb
.st_rdev
== dev
) {
326 ret_val
= 1; /* match found! */
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
338 if (major(sb
.st_rdev
) != major(dev
)) {
339 /* no use continuing, since major devs are different */
345 /* one last check -- for diff. slices of the same dev/unit */
346 if (ioctl(fd
, DKIOCINFO
, &dkinfo
) < 0) {
352 free(cn
); /* all done with raw pathname */
353 (void) close(fd
); /* all done with file descriptor */
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
)) {
360 * even though minor numbers differ we have a
367 /* go around again */
375 vol_basename(char *path
)
380 /* check for the degenerate case */
381 if (strcmp(path
, "/") == 0) {
385 /* look for the last slash in the name */
386 if ((cp
= strrchr(path
, '/')) == NULL
) {
391 /* ensure something is after the slash */
392 if (*++cp
!= NULLC
) {
396 /* a name that ends in slash -- back up until previous slash */
403 /* the only slash is the end of the name */
407 static int vol_getmntdev(FILE *, struct mnttab
*, dev_t
,
411 get_media_info(char *path
, char **mtypep
, int *mnump
, char **spclp
)
415 char *cn
= NULL
; /* char spcl pathname */
417 struct dk_cinfo info
;
421 if ((fp
= fopen(MNTTAB
, "rF")) == NULL
) {
422 /* mtab is gone... let it go */
426 /* get char spcl pathname */
427 if ((cn
= volmgt_getfullrawname(path
)) == NULL
) {
430 if (cn
[0] == NULLC
) {
434 if ((fd
= open(cn
, O_RDONLY
|O_NDELAY
)) < 0) {
438 if (fstat64(fd
, &sb
) < 0) {
442 if (ioctl(fd
, DKIOCINFO
, &info
) != 0) {
446 /* if we found the entry then disect it */
447 if (vol_getmntdev(fp
, &mnt
, sb
.st_rdev
, &info
) != 0) {
457 /* return the spcl device name found */
458 *spclp
= strdup(mnt
.mnt_special
);
461 * try to get the media type (e.g. "floppy") from the mount
462 * point (e.g. "/floppy/NAME") if vold is running
465 if (!volmgt_running() ||
466 (!volmgt_ownspath(*spclp
) &&
467 volmgt_symname(*spclp
) == NULL
)) {
468 ret_val
= TRUE
; /* success (if limited) */
472 /* get the first part of the mount point (e.g. "floppy") */
478 if ((cp
= strchr(mtype
, '/')) == NULL
) {
482 mnt_dir
= mnt
.mnt_mountp
; /* save dir path */
484 /* get the volume name (e.g. "unnamed_floppy") */
487 /* scan for the symlink that points to our volname */
488 if ((dirp
= opendir(mnt_dir
)) == NULL
) {
491 mtype_len
= strlen(mtype
);
492 while ((dp
= readdir64(dirp
)) != NULL
) {
493 char lpath
[2 * (MAXNAMELEN
+1)];
494 char linkbuf
[MAXPATHLEN
+4];
499 if (strncmp(dp
->d_name
, mtype
, mtype_len
) != 0) {
500 continue; /* not even close */
503 (void) sprintf(lpath
, "%s/%s", mnt_dir
,
505 if (lstat64(lpath
, &sb
) < 0) {
506 continue; /* what? */
508 if (!S_ISLNK(sb
.st_mode
)) {
509 continue; /* not our baby */
511 if ((lb_len
= readlink(lpath
, linkbuf
,
512 sizeof (linkbuf
))) < 0) {
515 linkbuf
[lb_len
] = NULLC
; /* null terminate */
516 if ((cp
= vol_basename(linkbuf
)) == NULL
) {
519 /* now we have the name! */
520 if (strcmp(cp
, volname
) == 0) {
522 if (sscanf(dp
->d_name
+ mtype_len
, "%d",
524 *mtypep
= strdup(mtype
);
530 (void) closedir(dirp
);
545 dexit("get_media_info: returning mtype=%s, mnum=%d, spcl=%s\n",
546 *mtypep
== NULL
? "<null ptr>" : *mtypep
,
548 *spclp
== NULL
? "<null ptr>" : *spclp
);
550 dexit("get_media_info: FAILED\n");
558 * call the appropriate unmount program, returning its success (TRUE)
562 call_unmount_prog(int mi_gotten
, int use_rmm
, char *mtype
, int mnum
,
563 char *spcl
, char *bn
)
565 pid_t pid
; /* forked proc's pid */
567 const char *etc_umount
= "/etc/umount";
568 const char *rmm
= "/usr/sbin/rmmount";
569 int rval
; /* proc's return value */
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>",
579 /* create a child to unmount the path */
580 if ((pid
= fork()) < 0) {
589 char env_buf
[MAXPATHLEN
];
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
));
601 /* set up environment vars */
602 (void) putenv("VOLUME_ACTION=eject");
603 (void) putenv(strdup(env_buf
));
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",
610 (void) putenv(strdup(env_buf
));
611 (void) sprintf(env_buf
, "VOLUME_PATH=%s",
613 (void) putenv(strdup(env_buf
));
614 (void) sprintf(env_buf
, "VOLUME_NAME=%s",
616 (void) putenv(strdup(env_buf
));
618 (void) sprintf(env_buf
, "VOLUME_PATH=%s", bn
);
619 (void) putenv(strdup(env_buf
));
620 (void) sprintf(env_buf
, "VOLUME_NAME=%s",
622 (void) putenv(strdup(env_buf
));
625 dprintf("call_unmount_prog: calling \"%s -D\"\n", rmm
);
626 (void) execl(rmm
, rmm
, "-D", NULL
);
628 (void) execl(rmm
, rmm
, NULL
);
632 dprintf("call_unmount_prog: calling \"%s %s\"\n",
633 etc_umount
, mi_gotten
? spcl
: bn
);
635 (void) execl(etc_umount
, etc_umount
,
636 mi_gotten
? spcl
: bn
,
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 */