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
_dev_mounted(char * path)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
_dev_unmount(char * path)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
vol_getmntdev(FILE * fp,struct mnttab * mp,dev_t dev,struct dk_cinfo * ip)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 *
vol_basename(char * path)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
get_media_info(char * path,char ** mtypep,int * mnump,char ** spclp)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
call_unmount_prog(int mi_gotten,int use_rmm,char * mtype,int mnum,char * spcl,char * bn)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