Merge illumos-gate
[unleashed.git] / usr / src / cmd / rmvolmgr / vold.c
blob1723527b4796b89ccd8e341af266652fe2114632
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Vold compatibility for rmvolmgr: emulate old commands as well as
28 * action_filemgr.so to notify legacy apps via /tmp/.removable pipes.
29 * A lot of this code is copied verbatim from vold sources.
31 * Here's the original description of action_filemgr.so:
33 * action_filemgr.so - filemgr interface routines for rmmount
35 * This shared object allows rmmount to communicate with filemgr.
36 * This is done by communicating over a named pipe that filemgr
37 * creates in directory NOTIFY_DIR. The name of the pipe must
38 * begin with NOTIFY_NAME. This source file contains #define
39 * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME.
41 * After a partition on a medium has been mounted as a result of
42 * either insertion or remounting of the medium, the action()
43 * method creates a file named with the symbolic name of the
44 * device in which the medium is inserted and the partition name
45 * (e.g. "jaz0-s2") in NOTIFY_DIR. The file consists of one text
46 * line containing a string naming the mount point of the partition,
47 * a string giving the raw device path to the partition, and a
48 * string naming the file system type on the partition. The action()
49 * method then sends a single character ('i' for insertion, 'r' for
50 * remounting) through the named pipe NOTIFY_NAME to tell filemgr to
51 * look for new files in NOTIFY_DIR.
53 * If a medium containing no mountable partitions is inserted
54 * or remounted in a device, the action() method creates a file
55 * named with the symbolic name of the device in NOTIFY_DIR.
56 * The file consists of one text line containing a string
57 * giving the symbolic name of the device and a string naming
58 * the reason that the medium couldn't be mounted. The action
59 * method then sends either an 'i' or an 'r' through the named
60 * pipe to tell filemgr to look for new files in NOTIFY_DIR.
62 * When a medium is ejected or unmounted, the action() method
63 * removes the files that were created in NOTIFY_DIR when the medium
64 * was inserted or remounted and sends a single character ('e' for
65 * ejection, 'u' for unmounting) through the named pipe.
67 * The following environment variables must be set before calling action():
69 * VOLUME_ACTION action that occurred (e.g. "insert", "eject")
70 * VOLUME_SYMDEV symbolic name (e.g. "cdrom0", "floppy1")
71 * VOLUME_NAME volume name (e.g. "unnamed_cdrom", "s2")
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <unistd.h>
78 #include <fcntl.h>
79 #include <string.h>
80 #include <strings.h>
81 #include <dirent.h>
82 #include <signal.h>
83 #include <errno.h>
84 #include <libintl.h>
85 #include <zone.h>
86 #include <pwd.h>
87 #include <sys/types.h>
88 #include <sys/stat.h>
89 #include <sys/dkio.h>
90 #include <sys/cdio.h>
91 #include <sys/vtoc.h>
92 #include <sys/param.h>
93 #include <sys/wait.h>
94 #include <libcontract.h>
95 #include <sys/contract/process.h>
96 #include <sys/ctfs.h>
98 #include "vold.h"
99 #include "rmm_common.h"
101 int rmm_debug = 0;
102 boolean_t rmm_vold_actions_enabled = B_FALSE;
103 boolean_t rmm_vold_mountpoints_enabled = B_FALSE;
105 static char *prog_name = NULL;
106 static pid_t prog_pid = 0;
107 static uid_t mnt_uid = (uid_t)-1;
108 static gid_t mnt_gid = (gid_t)-1;
109 static zoneid_t mnt_zoneid = -1;
110 static char mnt_zoneroot[MAXPATHLEN];
111 static char mnt_userdir[MAXPATHLEN];
114 * Private attribute types and attributes.
116 static const char notify_characters[] = {
117 'e',
118 'i',
119 'r',
123 static const char *result_strings[] = {
124 "FALSE",
125 "TRUE"
128 #define NOTIFY_DIR "/tmp/.removable" /* dir where filemgr looks */
129 #define NOTIFY_NAME "notify" /* named pipe to talk over */
131 static void volrmmount_usage();
132 static void volcheck_usage();
133 static int vold_action(struct action_arg *aap);
134 static void vold_update_mountpoints(struct action_arg *aap);
135 static char *not_mountable(struct action_arg *aa);
136 static int create_one_notify_file(char *fstype,
137 char *mount_point,
138 char *notify_file,
139 char *raw_partitionp,
140 char *reason,
141 char *symdev);
142 static int create_notify_files(struct action_arg **aa);
143 static boolean_t notify_clients(action_t action, int do_notify);
144 static void popdir(int fd);
145 static int pushdir(const char *dir);
146 static boolean_t remove_notify_files(struct action_arg **aa);
149 * Old version of rmmount(8)
151 /* ARGSUSED */
153 vold_rmmount(int argc, char **argv)
155 char *volume_action;
156 char *volume_mediatype;
157 char *volume_mount_mode;
158 char *volume_name;
159 char *volume_path;
160 char *volume_pcfs_id;
161 char *volume_symdev;
162 char *volume_zonename;
163 char *volume_user;
164 action_t action;
165 char mountpoint[MAXPATHLEN];
166 char *zonemountpoint;
167 char *arg_mountpoint = NULL;
168 LibHalContext *hal_ctx;
169 DBusError error;
170 rmm_error_t rmm_error;
171 int ret;
173 prog_name = argv[0];
174 prog_pid = getpid();
176 mnt_zoneroot[0] = '\0';
177 mnt_userdir[0] = '\0';
179 volume_action = getenv("VOLUME_ACTION");
180 volume_mediatype = getenv("VOLUME_MEDIATYPE");
181 volume_mount_mode = getenv("VOLUME_MOUNT_MODE");
182 volume_name = getenv("VOLUME_NAME");
183 volume_path = getenv("VOLUME_PATH");
184 volume_pcfs_id = getenv("VOLUME_PCFS_ID");
185 volume_symdev = getenv("VOLUME_SYMDEV");
187 if (volume_action == NULL) {
188 dprintf("%s(%ld): VOLUME_ACTION was null!!\n",
189 prog_name, prog_pid);
190 return (-1);
192 if (volume_mediatype == NULL) {
193 dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n",
194 prog_name, prog_pid);
195 return (-1);
197 if (volume_mount_mode == NULL) {
198 volume_mount_mode = "rw";
200 if (volume_name == NULL) {
201 dprintf("%s(%ld): VOLUME_NAME was null!!\n",
202 prog_name, prog_pid);
203 return (-1);
205 if (volume_path == NULL) {
206 dprintf("%s(%ld): VOLUME_PATH was null!!\n",
207 prog_name, prog_pid);
208 return (-1);
210 if (volume_pcfs_id == NULL) {
211 volume_pcfs_id = "";
213 if (volume_symdev == NULL) {
214 dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n",
215 prog_name, prog_pid);
216 return (-1);
219 rmm_vold_mountpoints_enabled = B_TRUE;
220 rmm_vold_actions_enabled = B_TRUE;
222 if ((hal_ctx = rmm_hal_init(0, 0, 0, 0, &error, &rmm_error)) == NULL) {
223 rmm_dbus_error_free(&error);
225 /* if HAL's not running, must be root */
226 if (geteuid() != 0) {
227 (void) fprintf(stderr,
228 gettext("%s(%ld) error: must be root to execute\n"),
229 prog_name, prog_pid);
230 return (-1);
234 if (strcmp(volume_action, "eject") == 0) {
235 action = EJECT;
236 } else if (strcmp(volume_action, "insert") == 0) {
237 action = INSERT;
238 } else if (strcmp(volume_action, "remount") == 0) {
239 action = REMOUNT;
240 } else if (strcmp(volume_action, "unmount") == 0) {
241 action = UNMOUNT;
244 ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0,
245 arg_mountpoint) ? 0 : 1;
247 if (hal_ctx != NULL) {
248 rmm_hal_fini(hal_ctx);
251 return (ret);
256 * this should be called after rmm_hal_{mount,unmount,eject}
259 vold_postprocess(LibHalContext *hal_ctx, const char *udi,
260 struct action_arg *aap)
262 int ret = 0;
264 /* valid mountpoint required */
265 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
266 rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap);
267 if ((aap->aa_mountpoint == NULL) ||
268 (strlen(aap->aa_mountpoint) == 0)) {
269 return (1);
273 if (rmm_vold_mountpoints_enabled) {
274 vold_update_mountpoints(aap);
276 if (rmm_vold_actions_enabled) {
277 ret = vold_action(aap);
280 return (ret);
284 * update legacy symlinks
286 * For cdrom:
288 * /cdrom/<name> -> original mountpoint
289 * /cdrom/cdrom0 -> ./<name>
290 * /cdrom/cdrom -> cdrom0 (only for cdrom0)
292 * If it's a slice or partition, /cdrom/<name> becomes a directory:
294 * /cdrom/<name>/s0
296 * Same for rmdisk and floppy.
298 static void
299 vold_update_mountpoints(struct action_arg *aap)
301 boolean_t is_partition;
302 char part_dir[2 * MAXNAMELEN];
303 char symname_mp[2 * MAXNAMELEN];
304 char symcontents_mp[MAXNAMELEN];
305 char symname[2 * MAXNAMELEN];
306 char symcontents[MAXNAMELEN];
308 is_partition = (aap->aa_partname != NULL);
310 if (!is_partition) {
311 /* /cdrom/<name> -> original mountpoint */
312 (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
313 "%s", aap->aa_mountpoint);
314 (void) snprintf(symname_mp, sizeof (symname_mp),
315 "/%s/%s", aap->aa_media, aap->aa_name);
316 } else {
317 /* /cdrom/<name>/slice -> original mountpoint */
318 (void) snprintf(part_dir, sizeof (part_dir),
319 "/%s/%s", aap->aa_media, aap->aa_name);
320 (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
321 "%s", aap->aa_mountpoint);
322 (void) snprintf(symname_mp, sizeof (symname_mp),
323 "/%s/%s/%s", aap->aa_media, aap->aa_name,
324 aap->aa_partname);
327 /* /cdrom/cdrom0 -> ./<name> */
328 (void) snprintf(symcontents, sizeof (symcontents),
329 "./%s", aap->aa_name);
330 (void) snprintf(symname, sizeof (symname),
331 "/%s/%s", aap->aa_media, aap->aa_symdev);
333 (void) unlink(symname);
334 (void) unlink(symname_mp);
335 if (is_partition) {
336 (void) rmdir(part_dir);
339 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
340 (void) mkdir(aap->aa_media, 0755);
341 if (is_partition) {
342 (void) mkdir(part_dir, 0755);
344 (void) symlink(symcontents_mp, symname_mp);
345 (void) symlink(symcontents, symname);
350 static int
351 vold_action(struct action_arg *aap)
353 action_t action;
354 int result;
355 int do_notify = FALSE;
356 action_t notify_act = EJECT;
357 struct action_arg *aa[2];
358 struct action_arg a1;
360 dprintf("%s[%d]: entering action()\n", __FILE__, __LINE__);
362 if (mnt_zoneid > GLOBAL_ZONEID) {
363 pid_t pid;
364 int status;
365 int ifx;
366 int tmpl_fd;
367 int err = 0;
369 tmpl_fd = open(CTFS_ROOT "/process/template",
370 O_RDWR);
371 if (tmpl_fd == -1)
372 return (1);
375 * Deliver no events, don't inherit,
376 * and allow it to be orphaned.
378 err |= ct_tmpl_set_critical(tmpl_fd, 0);
379 err |= ct_tmpl_set_informative(tmpl_fd, 0);
380 err |= ct_pr_tmpl_set_fatal(tmpl_fd,
381 CT_PR_EV_HWERR);
382 err |= ct_pr_tmpl_set_param(tmpl_fd,
383 CT_PR_PGRPONLY |
384 CT_PR_REGENT);
385 if (err || ct_tmpl_activate(tmpl_fd)) {
386 (void) close(tmpl_fd);
387 return (1);
389 switch (pid = fork1()) {
390 case 0:
391 (void) ct_tmpl_clear(tmpl_fd);
392 for (ifx = 0; ifx < _NFILE; ifx++)
393 (void) close(ifx);
395 if (zone_enter(mnt_zoneid) == -1)
396 _exit(0);
398 /* entered zone, proceed to action */
399 break;
400 case -1:
401 dprintf("fork1 failed \n ");
402 return (1);
403 default :
404 (void) ct_tmpl_clear(tmpl_fd);
405 (void) close(tmpl_fd);
406 if (waitpid(pid, &status, 0) < 0) {
407 dprintf("%s(%ld): waitpid() "
408 "failed (errno %d) \n",
409 prog_name, prog_pid, errno);
410 return (1);
415 /* only support one action at a time XXX */
416 a1.aa_path = NULL;
417 aa[0] = aap;
418 aa[1] = &a1;
420 action = aa[0]->aa_action;
422 if (action == CLEAR_MOUNTS) {
424 * Remove the notifications files, but don't
425 * notify the client. The "clear_mounts" action
426 * simply clears all existing mounts of a medium's
427 * partitions after a medium has been repartitioned.
428 * Then vold builds a new file system that reflects
429 * the medium's new partition structure and mounts
430 * the new partitions by calling rmmount, and therefore
431 * action(), with the VOLUME_ACTION environment variable
432 * set to "remount".
434 result = remove_notify_files(aa);
435 result = TRUE;
436 } else if (action == EJECT) {
437 result = remove_notify_files(aa);
438 if (result == TRUE) {
439 do_notify = TRUE;
440 notify_act = EJECT;
442 } else if (action = INSERT) {
443 result = create_notify_files(aa);
444 if (result == TRUE) {
445 do_notify = TRUE;
446 notify_act = INSERT;
448 } else if (action == REMOUNT) {
449 result = create_notify_files(aa);
450 if (result == TRUE) {
451 do_notify = TRUE;
452 notify_act = REMOUNT;
454 } else if (action == UNMOUNT) {
455 result = remove_notify_files(aa);
456 if (result == TRUE) {
457 do_notify = TRUE;
458 notify_act = UNMOUNT;
460 } else {
461 dprintf("%s[%d]: action(): invalid action: %s\n",
462 __FILE__, __LINE__, action);
463 result = FALSE;
466 if (result == TRUE) {
467 result = notify_clients(notify_act, do_notify);
470 dprintf("%s[%d]: leaving action(), result = %s\n",
471 __FILE__, __LINE__, result_strings[result]);
473 if (mnt_zoneid > GLOBAL_ZONEID) {
474 /* exit forked local zone process */
475 _exit(0);
478 if (result == TRUE) {
480 * File Manager is running. return 0.
481 * see man page rmmount.conf(4).
483 return (0);
484 } else {
485 return (1);
491 * Returns NULL if a medium or partition is mountable
492 * and a string stating the reason the medium or partition
493 * can't be mounted if the medium or partition isn't mountable.
495 * If the volume_name of the medium or partition is one of the
496 * following, the medium or partition isn't mountable.
498 * unlabeled_<media_type>
499 * unknown_format
500 * password_protected
502 /* ARGSUSED */
503 static char *
504 not_mountable(struct action_arg *aa)
506 return (NULL);
509 static int
510 create_notify_files(struct action_arg **aa)
512 int ai;
513 char *fstype;
514 char *mount_point;
515 char notify_file[64];
516 char *raw_partitionp;
517 char *reason; /* Why the medium wasn't mounted */
518 int result;
519 char *symdev;
521 dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__);
523 ai = 0;
524 result = FALSE;
525 symdev = aa[ai]->aa_symdev;
526 while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) {
527 if (aa[ai]->aa_mountpoint != NULL) {
528 if (aa[ai]->aa_type) {
529 fstype = aa[ai]->aa_type;
530 } else {
531 fstype = "unknown";
533 mount_point = aa[ai]->aa_mountpoint;
534 if (aa[ai]->aa_partname != NULL) {
536 * Is aa_partname ever NULL?
537 * When time permits, check.
538 * If it is, the action taken
539 * in the else clause could produce
540 * file name conflicts.
542 sprintf(notify_file, "%s-%s", symdev,
543 aa[ai]->aa_partname);
544 } else {
545 sprintf(notify_file, "%s-0", symdev);
547 reason = NULL;
548 } else {
550 * The partition isn't mounted.
552 fstype = "none";
553 mount_point = "none";
554 reason = not_mountable(aa[ai]);
555 if (reason != NULL) {
556 sprintf(notify_file, "%s-0", symdev);
557 } else {
559 * Either the partition is a backup slice, or
560 * rmmount tried to mount the partition, but
561 * idenf_fs couldn't identify the file system
562 * type; that can occur when rmmount is
563 * trying to mount all the slices in a Solaris
564 * VTOC, and one or more partitions don't have
565 * file systems in them.
567 if (aa[0]->aa_partname != NULL) {
569 * Is aa_partname ever NULL?
570 * When time permits, check.
571 * If it is, the action taken
572 * in the else clause could produce
573 * file name conflicts.
575 sprintf(notify_file, "%s-%s", symdev,
576 aa[0]->aa_partname);
577 } else {
578 sprintf(notify_file, "%s-0", symdev);
580 if ((aa[0]->aa_type != NULL) &&
581 (strcmp(aa[0]->aa_type, "backup_slice")
582 == 0)) {
583 reason = "backup_slice";
584 } else {
585 reason = "unformatted_media";
588 * "unformatted_media" should be
589 * changed to "unformmated_medium" for
590 * grammatical correctness, but
591 * "unformatted_media" is now specified
592 * in the interface to filemgr, so the
593 * change can't be made without the
594 * approval of the CDE group.
598 raw_partitionp = aa[0]->aa_rawpath;
599 result = create_one_notify_file(fstype,
600 mount_point,
601 notify_file,
602 raw_partitionp,
603 reason,
604 symdev);
605 ai++;
607 dprintf("%s[%d]: leaving create_notify_files(), result = %s\n",
608 __FILE__, __LINE__, result_strings[result]);
609 return (result);
612 static int
613 create_one_notify_file(char *fstype,
614 char *mount_point,
615 char *notify_file,
616 char *raw_partitionp,
617 char *reason,
618 char *symdev)
621 * For a mounted partition, create a notification file
622 * indicating the mount point, the raw device pathname
623 * of the partition, and the partition's file system
624 * type. For an unmounted partition, create a
625 * notification file containing the reason that the
626 * partition wasn't mounted and the raw device pathname
627 * of the partition.
629 * Create the file as root in a world-writable
630 * directory that resides in a world-writable directory.
632 * Handle two possible race conditions that could
633 * allow security breaches.
636 int current_working_dir_fd;
637 int file_descriptor;
638 FILE *filep;
639 int result;
641 dprintf("%s[%d]:Entering create_one_notify_file()\n",
642 __FILE__, __LINE__);
643 dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype);
644 dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point);
645 dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file);
646 dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n",
647 raw_partitionp);
648 if (reason != NULL) {
649 dprintf("\tcreate_one_notify_file(): reason = %s\n", reason);
650 } else {
651 dprintf("\tcreate_one_notify_file(): reason = NULL\n");
653 dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev);
655 result = TRUE;
657 * Handle Race Condition One:
659 * If NOTIFY_DIR exists, make sure it is not a symlink.
660 * if it is, remove it and try to create it. Check
661 * again to make sure NOTIFY_DIR isn't a symlink.
662 * If it is, remove it and return without creating
663 * a notification file. The condition can only occur if
664 * someone is trying to break into the system by running
665 * a program that repeatedly creates NOTIFY_DIR as a
666 * symlink. If NOTIFY_DIR exists and isn't a symlink,
667 * change the working directory to NOTIFY_DIR.
669 current_working_dir_fd = pushdir(NOTIFY_DIR);
670 if (current_working_dir_fd < 0) {
671 (void) makepath(NOTIFY_DIR, 0777);
672 current_working_dir_fd = pushdir(NOTIFY_DIR);
673 if (current_working_dir_fd < 0) {
674 result = FALSE;
678 * Handle Race Condition Two:
680 * Create the notification file in NOTIFY_DIR.
681 * Remove any files with the same name that may already be
682 * there, using remove(), as it safely removes directories.
683 * Then open the file O_CREAT|O_EXCL, which doesn't follow
684 * symlinks and requires that the file not exist already,
685 * so the new file actually resides in the current working
686 * directory. Create the file with access mode 644, which
687 * renders it unusable by anyone trying to break into the
688 * system.
690 if (result == TRUE) {
692 * The current working directory is now NOTIFY_DIR.
694 (void) remove(notify_file);
695 file_descriptor =
696 open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644);
697 if (file_descriptor < 0) {
698 dprintf("%s[%d]: can't create %s/%s; %m\n",
699 __FILE__, __LINE__, NOTIFY_DIR, notify_file);
700 result = FALSE;
701 } else {
702 filep = fdopen(file_descriptor, "w");
703 if (filep != NULL) {
704 if (reason == NULL) {
705 (void) fprintf(filep, "%s %s %s",
706 mount_point,
707 raw_partitionp,
708 fstype);
709 (void) fclose(filep);
710 dprintf("%s[%d]: Just wrote %s %s %s to %s\n",
711 __FILE__,
712 __LINE__,
713 mount_point,
714 raw_partitionp,
715 fstype,
716 notify_file);
717 } else {
718 (void) fprintf(filep, "%s %s",
719 reason, raw_partitionp);
720 (void) fclose(filep);
721 dprintf("%s[%d]: Just wrote %s %s to %s\n",
722 __FILE__,
723 __LINE__,
724 reason,
725 raw_partitionp,
726 notify_file);
728 } else {
729 dprintf("%s[%d]: can't write %s/%s; %m\n",
730 __FILE__, __LINE__,
731 NOTIFY_DIR, notify_file);
732 (void) close(file_descriptor);
733 result = FALSE;
736 popdir(current_working_dir_fd);
738 dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n",
739 __FILE__, __LINE__, result_strings[result]);
740 return (result);
743 static boolean_t
744 notify_clients(action_t action, int do_notify)
747 * Notify interested applications of changes in the state
748 * of removable media. Interested applications are those
749 * that create a named pipe in NOTIFY_DIR with a name that
750 * begins with "notify". Open the pipe and write a
751 * character through it that indicates the type of state
752 * change = 'e' for ejections, 'i' for insertions, 'r'
753 * for remounts of the file systems on repartitioned media,
754 * and 'u' for unmounts of file systems.
757 int current_working_dir_fd;
758 DIR *dirp;
759 struct dirent *dir_entryp;
760 size_t len;
761 int fd;
762 char namebuf[MAXPATHLEN];
763 char notify_character;
764 void (*old_signal_handler)();
765 int result;
766 struct stat sb;
768 dprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__);
770 result = TRUE;
772 * Use relative pathnames after changing the
773 * working directory to the notification directory.
774 * Check to make sure that each "notify" file is a
775 * named pipe to make sure that it hasn't changed
776 * its file type, which could mean that someone is
777 * trying to use "notify" files to break into the
778 * system.
780 if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) {
781 result = FALSE;
783 if (result == TRUE) {
784 dirp = opendir(".");
785 if (dirp == NULL) {
786 dprintf("%s[%d]:opendir failed on '.'; %m\n",
787 __FILE__, __LINE__);
788 popdir(current_working_dir_fd);
789 result = FALSE;
792 if (result == TRUE) {
794 * Read through the directory and write a notify
795 * character to all files whose names start with "notify".
797 result = FALSE;
798 old_signal_handler = signal(SIGPIPE, SIG_IGN);
799 len = strlen(NOTIFY_NAME);
800 while (dir_entryp = readdir(dirp)) {
801 if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len)
802 != 0) {
803 continue;
805 result = TRUE;
806 if (do_notify != TRUE) {
807 continue;
809 (void) sprintf(namebuf, "%s/%s",
810 NOTIFY_DIR, dir_entryp->d_name);
811 if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) {
812 dprintf("%s[%d]: open failed for %s; %m\n",
813 __FILE__, __LINE__, namebuf);
814 continue;
817 * Check to be sure that the entry is a named pipe.
818 * That closes a small security hole that could
819 * enable unauthorized access to the system root.
821 if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) {
822 dprintf("%s[%d]: %s isn't a named pipe\n",
823 __FILE__, __LINE__, namebuf);
825 (void) close(fd);
826 continue;
828 notify_character = notify_characters[action];
829 if (write(fd, &notify_character, 1) < 0) {
830 dprintf("%s[%d]: write failed for %s; %m\n",
831 __FILE__, __LINE__, namebuf);
832 (void) close(fd);
833 continue;
835 (void) close(fd);
837 (void) closedir(dirp);
838 (void) signal(SIGPIPE, old_signal_handler);
839 popdir(current_working_dir_fd);
841 dprintf("%s[%d]: leaving notify_clients(), result = %s\n",
842 __FILE__, __LINE__, result_strings[result]);
843 return (result);
846 static void
847 popdir(int fd)
850 * Change the current working directory to the directory
851 * specified by fd and close the fd. Exit the program
852 * on failure.
854 if (fchdir(fd) < 0) {
855 dprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__);
856 exit(1);
858 (void) close(fd);
861 static int
862 pushdir(const char *dir)
865 * Change the current working directory to dir and
866 * return a file descriptor for the old working
867 * directory.
869 * Exception handling:
871 * If dir doesn't exist, leave the current working
872 * directory the same and return -1.
874 * If dir isn't a directory, remove it, leave the
875 * current working directory the same, and return -1.
877 * If open() fails on the current working directory
878 * or the chdir operation fails on dir, leave the
879 * current working directory the same and return -1.
882 int current_working_dir_fd;
883 struct stat stat_buf;
885 if (lstat(dir, &stat_buf) < 0) {
886 dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n",
887 __FILE__, __LINE__, dir);
888 return (-1);
891 if (!(S_ISDIR(stat_buf.st_mode))) {
892 dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n",
893 __FILE__, __LINE__, dir);
894 (void) remove(dir);
895 return (-1);
897 if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) {
898 dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n",
899 __FILE__, __LINE__, dir);
900 return (-1);
902 if (chdir(dir) < 0) {
903 (void) close(current_working_dir_fd);
904 dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n",
905 __FILE__, __LINE__, dir);
906 return (-1);
908 return (current_working_dir_fd);
911 static boolean_t
912 remove_notify_files(struct action_arg **aa)
914 int ai;
915 int current_working_dir_fd;
916 char notify_file[64];
917 int result;
918 char *symdev;
920 dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__);
922 ai = 0;
923 result = TRUE;
924 symdev = aa[ai]->aa_symdev;
925 while ((result == TRUE) &&
926 (aa[ai] != NULL) &&
927 (aa[ai]->aa_path != NULL)) {
929 if (not_mountable(aa[ai])) {
930 sprintf(notify_file, "%s-0", symdev);
931 } else if (aa[ai]->aa_partname != NULL) {
933 * Is aa_partname ever NULL?
934 * When time permits, check.
935 * If it is, the action taken
936 * in the else clause could produce
937 * file name conflicts.
939 sprintf(notify_file, "%s-%s",
940 symdev, aa[0]->aa_partname);
941 } else {
942 sprintf(notify_file, "%s-0", symdev);
945 current_working_dir_fd = pushdir(NOTIFY_DIR);
946 if (current_working_dir_fd < 0) {
947 result = FALSE;
949 if ((result == TRUE) && (remove(notify_file) < 0)) {
950 dprintf("%s[%d]: remove %s/%s; %m\n",
951 __FILE__, __LINE__, NOTIFY_DIR, notify_file);
952 result = FALSE;
954 if (current_working_dir_fd != -1) {
955 popdir(current_working_dir_fd);
957 ai++;
959 dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n",
960 __FILE__, __LINE__, result_strings[result]);
962 return (result);