/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Vold compatibility for rmvolmgr: emulate old commands as well as * action_filemgr.so to notify legacy apps via /tmp/.removable pipes. * A lot of this code is copied verbatim from vold sources. * * Here's the original description of action_filemgr.so: * * action_filemgr.so - filemgr interface routines for rmmount * * This shared object allows rmmount to communicate with filemgr. * This is done by communicating over a named pipe that filemgr * creates in directory NOTIFY_DIR. The name of the pipe must * begin with NOTIFY_NAME. This source file contains #define * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME. * * After a partition on a medium has been mounted as a result of * either insertion or remounting of the medium, the action() * method creates a file named with the symbolic name of the * device in which the medium is inserted and the partition name * (e.g. "jaz0-s2") in NOTIFY_DIR. The file consists of one text * line containing a string naming the mount point of the partition, * a string giving the raw device path to the partition, and a * string naming the file system type on the partition. The action() * method then sends a single character ('i' for insertion, 'r' for * remounting) through the named pipe NOTIFY_NAME to tell filemgr to * look for new files in NOTIFY_DIR. * * If a medium containing no mountable partitions is inserted * or remounted in a device, the action() method creates a file * named with the symbolic name of the device in NOTIFY_DIR. * The file consists of one text line containing a string * giving the symbolic name of the device and a string naming * the reason that the medium couldn't be mounted. The action * method then sends either an 'i' or an 'r' through the named * pipe to tell filemgr to look for new files in NOTIFY_DIR. * * When a medium is ejected or unmounted, the action() method * removes the files that were created in NOTIFY_DIR when the medium * was inserted or remounted and sends a single character ('e' for * ejection, 'u' for unmounting) through the named pipe. * * The following environment variables must be set before calling action(): * * VOLUME_ACTION action that occurred (e.g. "insert", "eject") * VOLUME_SYMDEV symbolic name (e.g. "cdrom0", "floppy1") * VOLUME_NAME volume name (e.g. "unnamed_cdrom", "s2") */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vold.h" #include "rmm_common.h" int rmm_debug = 0; boolean_t rmm_vold_actions_enabled = B_FALSE; boolean_t rmm_vold_mountpoints_enabled = B_FALSE; static char *prog_name = NULL; static pid_t prog_pid = 0; static int system_labeled = 0; static uid_t mnt_uid = -1; static gid_t mnt_gid = -1; static zoneid_t mnt_zoneid = -1; static char mnt_zoneroot[MAXPATHLEN]; static char mnt_userdir[MAXPATHLEN]; /* * Private attribute types and attributes. */ static const char notify_characters[] = { 'e', 'i', 'r', 'u' }; static const char *result_strings[] = { "FALSE", "TRUE" }; #define NOTIFY_DIR "/tmp/.removable" /* dir where filemgr looks */ #define NOTIFY_NAME "notify" /* named pipe to talk over */ static void volrmmount_usage(); static void volcheck_usage(); static int vold_action(struct action_arg *aap); static void vold_update_mountpoints(struct action_arg *aap); static char *not_mountable(struct action_arg *aa); static int create_one_notify_file(char *fstype, char *mount_point, char *notify_file, char *raw_partitionp, char *reason, char *symdev); static int create_notify_files(struct action_arg **aa); static boolean_t notify_clients(action_t action, int do_notify); static void popdir(int fd); static int pushdir(const char *dir); static boolean_t remove_notify_files(struct action_arg **aa); /* * should be called once from main() */ /* ARGSUSED */ void vold_init(int argc, char **argv) { system_labeled = is_system_labeled(); } /* * Old version of rmmount(1M) */ /* ARGSUSED */ int vold_rmmount(int argc, char **argv) { char *volume_action; char *volume_mediatype; char *volume_mount_mode; char *volume_name; char *volume_path; char *volume_pcfs_id; char *volume_symdev; char *volume_zonename; char *volume_user; action_t action; char mountpoint[MAXPATHLEN]; char *zonemountpoint; char *arg_mountpoint = NULL; LibHalContext *hal_ctx; DBusError error; rmm_error_t rmm_error; int ret; prog_name = argv[0]; prog_pid = getpid(); mnt_zoneroot[0] = '\0'; mnt_userdir[0] = '\0'; volume_action = getenv("VOLUME_ACTION"); volume_mediatype = getenv("VOLUME_MEDIATYPE"); volume_mount_mode = getenv("VOLUME_MOUNT_MODE"); volume_name = getenv("VOLUME_NAME"); volume_path = getenv("VOLUME_PATH"); volume_pcfs_id = getenv("VOLUME_PCFS_ID"); volume_symdev = getenv("VOLUME_SYMDEV"); if (system_labeled) { volume_zonename = getenv("VOLUME_ZONE_NAME"); volume_user = getenv("VOLUME_USER"); } if (volume_action == NULL) { dprintf("%s(%ld): VOLUME_ACTION was null!!\n", prog_name, prog_pid); return (-1); } if (volume_mediatype == NULL) { dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n", prog_name, prog_pid); return (-1); } if (volume_mount_mode == NULL) { volume_mount_mode = "rw"; } if (volume_name == NULL) { dprintf("%s(%ld): VOLUME_NAME was null!!\n", prog_name, prog_pid); return (-1); } if (volume_path == NULL) { dprintf("%s(%ld): VOLUME_PATH was null!!\n", prog_name, prog_pid); return (-1); } if (volume_pcfs_id == NULL) { volume_pcfs_id = ""; } if (volume_symdev == NULL) { dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n", prog_name, prog_pid); return (-1); } if (system_labeled) { if (volume_zonename != NULL && strcmp(volume_zonename, GLOBAL_ZONENAME) != 0) { if ((mnt_zoneid = getzoneidbyname(volume_zonename)) != -1) { if (zone_getattr(mnt_zoneid, ZONE_ATTR_ROOT, mnt_zoneroot, MAXPATHLEN) == -1) { dprintf("%s(%ld): NO ZONEPATH!!\n", prog_name, prog_pid); return (-1); } } } else { mnt_zoneid = GLOBAL_ZONEID; mnt_zoneroot[0] = '\0'; } if (volume_user != NULL) { struct passwd *pw; if ((pw = getpwnam(volume_user)) == NULL) { dprintf("%s(%ld) %s\n", prog_name, prog_pid, ": VOLUME_USER was not a valid user!"); return (-1); } mnt_uid = pw->pw_uid; mnt_gid = pw->pw_gid; if (snprintf(mnt_userdir, sizeof (mnt_userdir), "/%s-%s", volume_user, volume_symdev) >= sizeof (mnt_userdir)) return (-1); } else { mnt_uid = 0; mnt_userdir[0] = '\0'; } rmm_vold_mountpoints_enabled = B_FALSE; rmm_vold_actions_enabled = B_TRUE; } else { rmm_vold_mountpoints_enabled = B_TRUE; rmm_vold_actions_enabled = B_TRUE; } if ((hal_ctx = rmm_hal_init(0, 0, 0, &error, &rmm_error)) == NULL) { rmm_dbus_error_free(&error); /* if HAL's not running, must be root */ if (geteuid() != 0) { (void) fprintf(stderr, gettext("%s(%ld) error: must be root to execute\n"), prog_name, prog_pid); return (-1); } } if (strcmp(volume_action, "eject") == 0) { action = EJECT; } else if (strcmp(volume_action, "insert") == 0) { action = INSERT; if (system_labeled) { /* * create mount point */ if (strlen(mnt_userdir) > 0) { if (snprintf(mountpoint, MAXPATHLEN, "%s/%s%s", mnt_zoneroot, volume_mediatype, mnt_userdir) > MAXPATHLEN) { return (-1); } (void) makepath(mountpoint, 0700); (void) chown(mountpoint, mnt_uid, mnt_gid); /* * set the top level directory bits to 0755 * so user can access it. */ if (snprintf(mountpoint, MAXPATHLEN, "%s/%s", mnt_zoneroot, volume_mediatype) <= MAXPATHLEN) { (void) chmod(mountpoint, 0755); } } if (snprintf(mountpoint, MAXPATHLEN, "%s/%s%s/%s", mnt_zoneroot, volume_mediatype, mnt_userdir, volume_name) > MAXPATHLEN) { (void) fprintf(stderr, gettext("%s(%ld) error: path too long\n"), prog_name, prog_pid); return (-1); } /* make our mountpoint */ (void) makepath(mountpoint, 0755); arg_mountpoint = mountpoint; } } else if (strcmp(volume_action, "remount") == 0) { action = REMOUNT; } else if (strcmp(volume_action, "unmount") == 0) { action = UNMOUNT; } ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0, arg_mountpoint) ? 0 : 1; if (hal_ctx != NULL) { rmm_hal_fini(hal_ctx); } return (ret); } /* * this should be called after rmm_hal_{mount,unmount,eject} */ int vold_postprocess(LibHalContext *hal_ctx, const char *udi, struct action_arg *aap) { int ret = 0; /* valid mountpoint required */ if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap); if ((aap->aa_mountpoint == NULL) || (strlen(aap->aa_mountpoint) == 0)) { return (1); } } if (rmm_vold_mountpoints_enabled) { vold_update_mountpoints(aap); } if (rmm_vold_actions_enabled) { ret = vold_action(aap); } return (ret); } /* * update legacy symlinks * * For cdrom: * * /cdrom/ -> original mountpoint * /cdrom/cdrom0 -> ./ * /cdrom/cdrom -> cdrom0 (only for cdrom0) * * If it's a slice or partition, /cdrom/ becomes a directory: * * /cdrom//s0 * * Same for rmdisk and floppy. * * On labeled system (Trusted Solaris), links are in a user directory. */ static void vold_update_mountpoints(struct action_arg *aap) { boolean_t is_partition; char part_dir[2 * MAXNAMELEN]; char symname_mp[2 * MAXNAMELEN]; char symcontents_mp[MAXNAMELEN]; char symname[2 * MAXNAMELEN]; char symcontents[MAXNAMELEN]; is_partition = (aap->aa_partname != NULL); if (!system_labeled) { if (!is_partition) { /* /cdrom/ -> original mountpoint */ (void) snprintf(symcontents_mp, sizeof (symcontents_mp), "%s", aap->aa_mountpoint); (void) snprintf(symname_mp, sizeof (symname_mp), "/%s/%s", aap->aa_media, aap->aa_name); } else { /* /cdrom//slice -> original mountpoint */ (void) snprintf(part_dir, sizeof (part_dir), "/%s/%s", aap->aa_media, aap->aa_name); (void) snprintf(symcontents_mp, sizeof (symcontents_mp), "%s", aap->aa_mountpoint); (void) snprintf(symname_mp, sizeof (symname_mp), "/%s/%s/%s", aap->aa_media, aap->aa_name, aap->aa_partname); } /* /cdrom/cdrom0 -> ./ */ (void) snprintf(symcontents, sizeof (symcontents), "./%s", aap->aa_name); (void) snprintf(symname, sizeof (symname), "/%s/%s", aap->aa_media, aap->aa_symdev); } else { if (!is_partition) { /* /cdrom// -> original mountpoint */ (void) snprintf(symcontents_mp, sizeof (symcontents_mp), "%s", aap->aa_mountpoint); (void) snprintf(symname_mp, sizeof (symname_mp), "%s/%s/%s", mnt_zoneroot, aap->aa_media, aap->aa_symdev); } else { /* /cdrom///slice -> original mountpoint */ (void) snprintf(symcontents_mp, sizeof (symcontents_mp), "%s", aap->aa_mountpoint); (void) snprintf(symname_mp, sizeof (symname_mp), "%s/%s/%s", mnt_zoneroot, aap->aa_media, aap->aa_symdev, aap->aa_partname); } /* /cdrom//cdrom0 -> .// */ (void) snprintf(symcontents, sizeof (symcontents), ".%s/%s", mnt_userdir, aap->aa_name); (void) snprintf(symname, sizeof (symname), "%s/%s/%s", mnt_zoneroot, aap->aa_media, aap->aa_symdev); } (void) unlink(symname); (void) unlink(symname_mp); if (is_partition) { (void) rmdir(part_dir); } if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { (void) mkdir(aap->aa_media, 0755); if (is_partition) { (void) mkdir(part_dir, 0755); } (void) symlink(symcontents_mp, symname_mp); (void) symlink(symcontents, symname); } } static int vold_action(struct action_arg *aap) { action_t action; int result; int do_notify = FALSE; action_t notify_act = EJECT; struct action_arg *aa[2]; struct action_arg a1; dprintf("%s[%d]: entering action()\n", __FILE__, __LINE__); /* * on Trusted Extensions, actions are executed in the user's zone */ if (mnt_zoneid > GLOBAL_ZONEID) { pid_t pid; int status; int ifx; int tmpl_fd; int err = 0; tmpl_fd = open64(CTFS_ROOT "/process/template", O_RDWR); if (tmpl_fd == -1) return (1); /* * Deliver no events, don't inherit, * and allow it to be orphaned. */ err |= ct_tmpl_set_critical(tmpl_fd, 0); err |= ct_tmpl_set_informative(tmpl_fd, 0); err |= ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR); err |= ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY | CT_PR_REGENT); if (err || ct_tmpl_activate(tmpl_fd)) { (void) close(tmpl_fd); return (1); } switch (pid = fork1()) { case 0: (void) ct_tmpl_clear(tmpl_fd); for (ifx = 0; ifx < _NFILE; ifx++) (void) close(ifx); if (zone_enter(mnt_zoneid) == -1) _exit(0); /* entered zone, proceed to action */ break; case -1: dprintf("fork1 failed \n "); return (1); default : (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); if (waitpid(pid, &status, 0) < 0) { dprintf("%s(%ld): waitpid() " "failed (errno %d) \n", prog_name, prog_pid, errno); return (1); } } } /* only support one action at a time XXX */ a1.aa_path = NULL; aa[0] = aap; aa[1] = &a1; action = aa[0]->aa_action; if (action == CLEAR_MOUNTS) { /* * Remove the notifications files, but don't * notify the client. The "clear_mounts" action * simply clears all existing mounts of a medium's * partitions after a medium has been repartitioned. * Then vold builds a new file system that reflects * the medium's new partition structure and mounts * the new partitions by calling rmmount, and therefore * action(), with the VOLUME_ACTION environment variable * set to "remount". */ result = remove_notify_files(aa); result = TRUE; } else if (action == EJECT) { result = remove_notify_files(aa); if (result == TRUE) { do_notify = TRUE; notify_act = EJECT; } } else if (action = INSERT) { result = create_notify_files(aa); if (result == TRUE) { do_notify = TRUE; notify_act = INSERT; } } else if (action == REMOUNT) { result = create_notify_files(aa); if (result == TRUE) { do_notify = TRUE; notify_act = REMOUNT; } } else if (action == UNMOUNT) { result = remove_notify_files(aa); if (result == TRUE) { do_notify = TRUE; notify_act = UNMOUNT; } } else { dprintf("%s[%d]: action(): invalid action: %s\n", __FILE__, __LINE__, action); result = FALSE; } if (result == TRUE) { result = notify_clients(notify_act, do_notify); } dprintf("%s[%d]: leaving action(), result = %s\n", __FILE__, __LINE__, result_strings[result]); if (mnt_zoneid > GLOBAL_ZONEID) { /* exit forked local zone process */ _exit(0); } if (result == TRUE) { /* * File Manager is running. return 0. * see man page rmmount.conf(4). */ return (0); } else { return (1); } } /* * Returns NULL if a medium or partition is mountable * and a string stating the reason the medium or partition * can't be mounted if the medium or partition isn't mountable. * * If the volume_name of the medium or partition is one of the * following, the medium or partition isn't mountable. * * unlabeled_ * unknown_format * password_protected */ /* ARGSUSED */ static char * not_mountable(struct action_arg *aa) { return (NULL); } static int create_notify_files(struct action_arg **aa) { int ai; char *fstype; char *mount_point; char notify_file[64]; char *raw_partitionp; char *reason; /* Why the medium wasn't mounted */ int result; char *symdev; dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__); ai = 0; result = FALSE; symdev = aa[ai]->aa_symdev; while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) { if (aa[ai]->aa_mountpoint != NULL) { if (aa[ai]->aa_type) { fstype = aa[ai]->aa_type; } else { fstype = "unknown"; } mount_point = aa[ai]->aa_mountpoint; if (aa[ai]->aa_partname != NULL) { /* * Is aa_partname ever NULL? * When time permits, check. * If it is, the action taken * in the else clause could produce * file name conflicts. */ sprintf(notify_file, "%s-%s", symdev, aa[ai]->aa_partname); } else { sprintf(notify_file, "%s-0", symdev); } reason = NULL; } else { /* * The partition isn't mounted. */ fstype = "none"; mount_point = "none"; reason = not_mountable(aa[ai]); if (reason != NULL) { sprintf(notify_file, "%s-0", symdev); } else { /* * Either the partition is a backup slice, or * rmmount tried to mount the partition, but * idenf_fs couldn't identify the file system * type; that can occur when rmmount is * trying to mount all the slices in a Solaris * VTOC, and one or more partitions don't have * file systems in them. */ if (aa[0]->aa_partname != NULL) { /* * Is aa_partname ever NULL? * When time permits, check. * If it is, the action taken * in the else clause could produce * file name conflicts. */ sprintf(notify_file, "%s-%s", symdev, aa[0]->aa_partname); } else { sprintf(notify_file, "%s-0", symdev); } if ((aa[0]->aa_type != NULL) && (strcmp(aa[0]->aa_type, "backup_slice") == 0)) { reason = "backup_slice"; } else { reason = "unformatted_media"; } /* * "unformatted_media" should be * changed to "unformmated_medium" for * grammatical correctness, but * "unformatted_media" is now specified * in the interface to filemgr, so the * change can't be made without the * approval of the CDE group. */ } } raw_partitionp = aa[0]->aa_rawpath; result = create_one_notify_file(fstype, mount_point, notify_file, raw_partitionp, reason, symdev); ai++; } dprintf("%s[%d]: leaving create_notify_files(), result = %s\n", __FILE__, __LINE__, result_strings[result]); return (result); } static int create_one_notify_file(char *fstype, char *mount_point, char *notify_file, char *raw_partitionp, char *reason, char *symdev) { /* * For a mounted partition, create a notification file * indicating the mount point, the raw device pathname * of the partition, and the partition's file system * type. For an unmounted partition, create a * notification file containing the reason that the * partition wasn't mounted and the raw device pathname * of the partition. * * Create the file as root in a world-writable * directory that resides in a world-writable directory. * * Handle two possible race conditions that could * allow security breaches. */ int current_working_dir_fd; int file_descriptor; FILE *filep; int result; dprintf("%s[%d]:Entering create_one_notify_file()\n", __FILE__, __LINE__); dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype); dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point); dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file); dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n", raw_partitionp); if (reason != NULL) { dprintf("\tcreate_one_notify_file(): reason = %s\n", reason); } else { dprintf("\tcreate_one_notify_file(): reason = NULL\n"); } dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev); result = TRUE; /* * Handle Race Condition One: * * If NOTIFY_DIR exists, make sure it is not a symlink. * if it is, remove it and try to create it. Check * again to make sure NOTIFY_DIR isn't a symlink. * If it is, remove it and return without creating * a notification file. The condition can only occur if * someone is trying to break into the system by running * a program that repeatedly creates NOTIFY_DIR as a * symlink. If NOTIFY_DIR exists and isn't a symlink, * change the working directory to NOTIFY_DIR. */ current_working_dir_fd = pushdir(NOTIFY_DIR); if (current_working_dir_fd < 0) { (void) makepath(NOTIFY_DIR, 0777); current_working_dir_fd = pushdir(NOTIFY_DIR); if (current_working_dir_fd < 0) { result = FALSE; } } /* * Handle Race Condition Two: * * Create the notification file in NOTIFY_DIR. * Remove any files with the same name that may already be * there, using remove(), as it safely removes directories. * Then open the file O_CREAT|O_EXCL, which doesn't follow * symlinks and requires that the file not exist already, * so the new file actually resides in the current working * directory. Create the file with access mode 644, which * renders it unusable by anyone trying to break into the * system. */ if (result == TRUE) { /* * The current working directory is now NOTIFY_DIR. */ (void) remove(notify_file); file_descriptor = open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644); if (file_descriptor < 0) { dprintf("%s[%d]: can't create %s/%s; %m\n", __FILE__, __LINE__, NOTIFY_DIR, notify_file); result = FALSE; } else { filep = fdopen(file_descriptor, "w"); if (filep != NULL) { if (reason == NULL) { (void) fprintf(filep, "%s %s %s", mount_point, raw_partitionp, fstype); (void) fclose(filep); dprintf("%s[%d]: Just wrote %s %s %s to %s\n", __FILE__, __LINE__, mount_point, raw_partitionp, fstype, notify_file); } else { (void) fprintf(filep, "%s %s", reason, raw_partitionp); (void) fclose(filep); dprintf("%s[%d]: Just wrote %s %s to %s\n", __FILE__, __LINE__, reason, raw_partitionp, notify_file); } } else { dprintf("%s[%d]: can't write %s/%s; %m\n", __FILE__, __LINE__, NOTIFY_DIR, notify_file); (void) close(file_descriptor); result = FALSE; } } popdir(current_working_dir_fd); } dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n", __FILE__, __LINE__, result_strings[result]); return (result); } static boolean_t notify_clients(action_t action, int do_notify) { /* * Notify interested applications of changes in the state * of removable media. Interested applications are those * that create a named pipe in NOTIFY_DIR with a name that * begins with "notify". Open the pipe and write a * character through it that indicates the type of state * change = 'e' for ejections, 'i' for insertions, 'r' * for remounts of the file systems on repartitioned media, * and 'u' for unmounts of file systems. */ int current_working_dir_fd; DIR *dirp; struct dirent *dir_entryp; size_t len; int fd; char namebuf[MAXPATHLEN]; char notify_character; void (*old_signal_handler)(); int result; struct stat sb; dprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__); result = TRUE; /* * Use relative pathnames after changing the * working directory to the notification directory. * Check to make sure that each "notify" file is a * named pipe to make sure that it hasn't changed * its file type, which could mean that someone is * trying to use "notify" files to break into the * system. */ if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) { result = FALSE; } if (result == TRUE) { dirp = opendir("."); if (dirp == NULL) { dprintf("%s[%d]:opendir failed on '.'; %m\n", __FILE__, __LINE__); popdir(current_working_dir_fd); result = FALSE; } } if (result == TRUE) { /* * Read through the directory and write a notify * character to all files whose names start with "notify". */ result = FALSE; old_signal_handler = signal(SIGPIPE, SIG_IGN); len = strlen(NOTIFY_NAME); while (dir_entryp = readdir(dirp)) { if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len) != 0) { continue; } result = TRUE; if (do_notify != TRUE) { continue; } (void) sprintf(namebuf, "%s/%s", NOTIFY_DIR, dir_entryp->d_name); if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) { dprintf("%s[%d]: open failed for %s; %m\n", __FILE__, __LINE__, namebuf); continue; } /* * Check to be sure that the entry is a named pipe. * That closes a small security hole that could * enable unauthorized access to the system root. */ if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) { dprintf("%s[%d]: %s isn't a named pipe\n", __FILE__, __LINE__, namebuf); (void) close(fd); continue; } notify_character = notify_characters[action]; if (write(fd, ¬ify_character, 1) < 0) { dprintf("%s[%d]: write failed for %s; %m\n", __FILE__, __LINE__, namebuf); (void) close(fd); continue; } (void) close(fd); } (void) closedir(dirp); (void) signal(SIGPIPE, old_signal_handler); popdir(current_working_dir_fd); } dprintf("%s[%d]: leaving notify_clients(), result = %s\n", __FILE__, __LINE__, result_strings[result]); return (result); } static void popdir(int fd) { /* * Change the current working directory to the directory * specified by fd and close the fd. Exit the program * on failure. */ if (fchdir(fd) < 0) { dprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__); exit(1); } (void) close(fd); } static int pushdir(const char *dir) { /* * Change the current working directory to dir and * return a file descriptor for the old working * directory. * * Exception handling: * * If dir doesn't exist, leave the current working * directory the same and return -1. * * If dir isn't a directory, remove it, leave the * current working directory the same, and return -1. * * If open() fails on the current working directory * or the chdir operation fails on dir, leave the * current working directory the same and return -1. */ int current_working_dir_fd; struct stat stat_buf; if (lstat(dir, &stat_buf) < 0) { dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n", __FILE__, __LINE__, dir); return (-1); } if (!(S_ISDIR(stat_buf.st_mode))) { dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n", __FILE__, __LINE__, dir); (void) remove(dir); return (-1); } if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) { dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n", __FILE__, __LINE__, dir); return (-1); } if (chdir(dir) < 0) { (void) close(current_working_dir_fd); dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n", __FILE__, __LINE__, dir); return (-1); } return (current_working_dir_fd); } static boolean_t remove_notify_files(struct action_arg **aa) { int ai; int current_working_dir_fd; char notify_file[64]; int result; char *symdev; dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__); ai = 0; result = TRUE; symdev = aa[ai]->aa_symdev; while ((result == TRUE) && (aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) { if (not_mountable(aa[ai])) { sprintf(notify_file, "%s-0", symdev); } else if (aa[ai]->aa_partname != NULL) { /* * Is aa_partname ever NULL? * When time permits, check. * If it is, the action taken * in the else clause could produce * file name conflicts. */ sprintf(notify_file, "%s-%s", symdev, aa[0]->aa_partname); } else { sprintf(notify_file, "%s-0", symdev); } current_working_dir_fd = pushdir(NOTIFY_DIR); if (current_working_dir_fd < 0) { result = FALSE; } if ((result == TRUE) && (remove(notify_file) < 0)) { dprintf("%s[%d]: remove %s/%s; %m\n", __FILE__, __LINE__, NOTIFY_DIR, notify_file); result = FALSE; } if (current_working_dir_fd != -1) { popdir(current_working_dir_fd); } ai++; } dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n", __FILE__, __LINE__, result_strings[result]); return (result); }