vold.c revision 4321:a8930ec16e52
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 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Vold compatibility for rmvolmgr: emulate old commands as well as
30 * action_filemgr.so to notify legacy apps via /tmp/.removable pipes.
31 * A lot of this code is copied verbatim from vold sources.
32 *
33 * Here's the original description of action_filemgr.so:
34 *
35 * action_filemgr.so - filemgr interface routines for rmmount
36 *
37 * This shared object allows rmmount to communicate with filemgr.
38 * This is done by communicating over a named pipe that filemgr
39 * creates in directory NOTIFY_DIR.  The name of the pipe must
40 * begin with NOTIFY_NAME.  This source file contains #define
41 * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME.
42 *
43 * After a partition on a medium has been mounted as a result of
44 * either insertion or remounting of the medium, the action()
45 * method creates a file named with the symbolic name of the
46 * device in which the medium is inserted and the partition name
47 * (e.g. "jaz0-s2") in NOTIFY_DIR.  The file consists of one text
48 * line containing a string naming the mount point of the partition,
49 * a string giving the raw device path to the partition, and a
50 * string naming the file system type on the partition.  The action()
51 * method then sends a single character ('i' for insertion, 'r' for
52 * remounting) through the named pipe NOTIFY_NAME to tell filemgr to
53 * look for new files in NOTIFY_DIR.
54 *
55 * If a medium containing no mountable partitions is inserted
56 * or remounted in a device, the action() method creates a file
57 * named with the symbolic name of the device in NOTIFY_DIR.
58 * The file consists of one text line containing a string
59 * giving the symbolic name of the device and a string naming
60 * the reason that the medium couldn't be mounted.  The action
61 * method then sends either an 'i' or an 'r' through the named
62 * pipe to tell filemgr to look for new files in NOTIFY_DIR.
63 *
64 * When a medium is ejected or unmounted, the action() method
65 * removes the files that were created in NOTIFY_DIR when the medium
66 * was inserted or remounted and sends a single character ('e' for
67 * ejection, 'u' for unmounting) through the named pipe.
68 *
69 * The following environment variables must be set before calling action():
70 *
71 *	VOLUME_ACTION		action that occurred (e.g. "insert", "eject")
72 *	VOLUME_SYMDEV		symbolic name (e.g. "cdrom0", "floppy1")
73 *	VOLUME_NAME		volume name (e.g. "unnamed_cdrom", "s2")
74 */
75
76
77#include <stdio.h>
78#include <stdlib.h>
79#include <unistd.h>
80#include <fcntl.h>
81#include <string.h>
82#include <strings.h>
83#include <dirent.h>
84#include <signal.h>
85#include <errno.h>
86#include <libintl.h>
87#include <zone.h>
88#include <pwd.h>
89#include <sys/types.h>
90#include <sys/stat.h>
91#include <sys/dkio.h>
92#include <sys/cdio.h>
93#include <sys/vtoc.h>
94#include <sys/param.h>
95#include <sys/wait.h>
96#include <libcontract.h>
97#include <sys/contract/process.h>
98#include <sys/ctfs.h>
99#include <tsol/label.h>
100
101#include "vold.h"
102#include "rmm_common.h"
103
104int		rmm_debug = 0;
105boolean_t	rmm_vold_actions_enabled = B_FALSE;
106boolean_t	rmm_vold_mountpoints_enabled = B_FALSE;
107
108static char	*prog_name = NULL;
109static pid_t	prog_pid = 0;
110static int	system_labeled = 0;
111static uid_t	mnt_uid = (uid_t)-1;
112static gid_t	mnt_gid = (gid_t)-1;
113static zoneid_t	mnt_zoneid = -1;
114static char	mnt_zoneroot[MAXPATHLEN];
115static char	mnt_userdir[MAXPATHLEN];
116
117/*
118 * Private attribute types and attributes.
119 */
120static const char notify_characters[] = {
121	'e',
122	'i',
123	'r',
124	'u'
125};
126
127static const char *result_strings[] = {
128	"FALSE",
129	"TRUE"
130};
131
132#define	NOTIFY_DIR	"/tmp/.removable"	/* dir where filemgr looks */
133#define	NOTIFY_NAME	"notify"		/* named pipe to talk over */
134
135static void	volrmmount_usage();
136static void	volcheck_usage();
137static int	vold_action(struct action_arg *aap);
138static void	vold_update_mountpoints(struct action_arg *aap);
139static char	*not_mountable(struct action_arg *aa);
140static int	create_one_notify_file(char *fstype,
141				char *mount_point,
142				char *notify_file,
143				char *raw_partitionp,
144				char *reason,
145				char *symdev);
146static int	create_notify_files(struct action_arg **aa);
147static boolean_t notify_clients(action_t action, int do_notify);
148static void	popdir(int fd);
149static int	pushdir(const char *dir);
150static boolean_t remove_notify_files(struct action_arg **aa);
151
152/*
153 * should be called once from main()
154 */
155/* ARGSUSED */
156void
157vold_init(int argc, char **argv)
158{
159	system_labeled = is_system_labeled();
160}
161
162/*
163 * Old version of rmmount(1M)
164 */
165/* ARGSUSED */
166int
167vold_rmmount(int argc, char **argv)
168{
169	char		*volume_action;
170	char		*volume_mediatype;
171	char		*volume_mount_mode;
172	char		*volume_name;
173	char		*volume_path;
174	char		*volume_pcfs_id;
175	char		*volume_symdev;
176	char		*volume_zonename;
177	char		*volume_user;
178	action_t	action;
179	char		mountpoint[MAXPATHLEN];
180	char		*zonemountpoint;
181	char		*arg_mountpoint = NULL;
182	LibHalContext	*hal_ctx;
183	DBusError	error;
184	rmm_error_t	rmm_error;
185	int		ret;
186
187	prog_name = argv[0];
188	prog_pid = getpid();
189
190	mnt_zoneroot[0] = '\0';
191	mnt_userdir[0] = '\0';
192
193	volume_action = getenv("VOLUME_ACTION");
194	volume_mediatype = getenv("VOLUME_MEDIATYPE");
195	volume_mount_mode = getenv("VOLUME_MOUNT_MODE");
196	volume_name = getenv("VOLUME_NAME");
197	volume_path = getenv("VOLUME_PATH");
198	volume_pcfs_id = getenv("VOLUME_PCFS_ID");
199	volume_symdev = getenv("VOLUME_SYMDEV");
200
201	if (system_labeled) {
202		volume_zonename = getenv("VOLUME_ZONE_NAME");
203		volume_user = getenv("VOLUME_USER");
204	}
205	if (volume_action == NULL) {
206		dprintf("%s(%ld): VOLUME_ACTION was null!!\n",
207		    prog_name, prog_pid);
208		return (-1);
209	}
210	if (volume_mediatype == NULL) {
211		dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n",
212		    prog_name, prog_pid);
213		return (-1);
214	}
215	if (volume_mount_mode == NULL) {
216		volume_mount_mode = "rw";
217	}
218	if (volume_name == NULL) {
219		dprintf("%s(%ld): VOLUME_NAME was null!!\n",
220		    prog_name, prog_pid);
221		return (-1);
222	}
223	if (volume_path == NULL) {
224		dprintf("%s(%ld): VOLUME_PATH was null!!\n",
225		    prog_name, prog_pid);
226		return (-1);
227	}
228	if (volume_pcfs_id == NULL) {
229		volume_pcfs_id = "";
230	}
231	if (volume_symdev == NULL) {
232		dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n",
233		    prog_name, prog_pid);
234		return (-1);
235	}
236
237	if (system_labeled) {
238		if (volume_zonename != NULL &&
239		    strcmp(volume_zonename, GLOBAL_ZONENAME) != 0) {
240			if ((mnt_zoneid =
241			    getzoneidbyname(volume_zonename)) != -1) {
242				if (zone_getattr(mnt_zoneid, ZONE_ATTR_ROOT,
243				    mnt_zoneroot, MAXPATHLEN) == -1) {
244					dprintf("%s(%ld): NO ZONEPATH!!\n",
245					    prog_name, prog_pid);
246					return (-1);
247				}
248			}
249		} else {
250			mnt_zoneid = GLOBAL_ZONEID;
251			mnt_zoneroot[0] = '\0';
252		}
253		if (volume_user != NULL) {
254			struct passwd	 *pw;
255
256			if ((pw = getpwnam(volume_user)) == NULL) {
257				dprintf("%s(%ld) %s\n", prog_name, prog_pid,
258				    ": VOLUME_USER was not a valid user!");
259				return (-1);
260			}
261			mnt_uid = pw->pw_uid;
262			mnt_gid = pw->pw_gid;
263
264			if (snprintf(mnt_userdir, sizeof (mnt_userdir),
265			    "/%s-%s", volume_user, volume_symdev) >=
266			    sizeof (mnt_userdir))
267				return (-1);
268		} else {
269			mnt_uid = 0;
270			mnt_userdir[0] = '\0';
271		}
272
273		rmm_vold_mountpoints_enabled = B_FALSE;
274		rmm_vold_actions_enabled = B_TRUE;
275	} else {
276		rmm_vold_mountpoints_enabled = B_TRUE;
277		rmm_vold_actions_enabled = B_TRUE;
278	}
279
280	if ((hal_ctx = rmm_hal_init(0, 0, 0, &error, &rmm_error)) == NULL) {
281		rmm_dbus_error_free(&error);
282
283		/* if HAL's not running, must be root */
284		if (geteuid() != 0) {
285			(void) fprintf(stderr,
286			    gettext("%s(%ld) error: must be root to execute\n"),
287			    prog_name, prog_pid);
288			return (-1);
289		}
290	}
291
292	if (strcmp(volume_action, "eject") == 0) {
293		action = EJECT;
294	} else if (strcmp(volume_action, "insert") == 0) {
295		action = INSERT;
296
297		if (system_labeled) {
298			/*
299			 * create mount point
300			 */
301			if (strlen(mnt_userdir) > 0) {
302				if (snprintf(mountpoint, MAXPATHLEN,
303				    "%s/%s%s", mnt_zoneroot, volume_mediatype,
304				    mnt_userdir) > MAXPATHLEN) {
305					return (-1);
306
307				}
308				(void) makepath(mountpoint, 0700);
309				(void) chown(mountpoint, mnt_uid, mnt_gid);
310				/*
311				 * set the top level directory bits to 0755
312				 * so user can access it.
313				 */
314				if (snprintf(mountpoint, MAXPATHLEN,
315				    "%s/%s", mnt_zoneroot,
316				    volume_mediatype) <= MAXPATHLEN) {
317					(void) chmod(mountpoint, 0755);
318				}
319			}
320			if (snprintf(mountpoint, MAXPATHLEN,
321			    "%s/%s%s/%s", mnt_zoneroot, volume_mediatype,
322			    mnt_userdir, volume_name) > MAXPATHLEN) {
323				(void) fprintf(stderr,
324				    gettext("%s(%ld) error: path too long\n"),
325				    prog_name, prog_pid);
326				return (-1);
327			}
328
329			/* make our mountpoint */
330			(void) makepath(mountpoint, 0755);
331
332			arg_mountpoint = mountpoint;
333		}
334	} else if (strcmp(volume_action, "remount") == 0) {
335		action = REMOUNT;
336	} else if (strcmp(volume_action, "unmount") == 0) {
337		action = UNMOUNT;
338	}
339
340	ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0,
341	    arg_mountpoint) ? 0 : 1;
342
343	if (hal_ctx != NULL) {
344		rmm_hal_fini(hal_ctx);
345	}
346
347	return (ret);
348}
349
350
351/*
352 * this should be called after rmm_hal_{mount,unmount,eject}
353 */
354int
355vold_postprocess(LibHalContext *hal_ctx, const char *udi,
356    struct action_arg *aap)
357{
358	int	ret = 0;
359
360	/* valid mountpoint required */
361	if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
362		rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap);
363		if ((aap->aa_mountpoint == NULL) ||
364		    (strlen(aap->aa_mountpoint) == 0)) {
365			return (1);
366		}
367	}
368
369	if (rmm_vold_mountpoints_enabled) {
370		vold_update_mountpoints(aap);
371	}
372	if (rmm_vold_actions_enabled) {
373		ret = vold_action(aap);
374	}
375
376	return (ret);
377}
378
379/*
380 * update legacy symlinks
381 *
382 * For cdrom:
383 *
384 *	/cdrom/<name> -> original mountpoint
385 *	/cdrom/cdrom0 -> ./<name>
386 *	/cdrom/cdrom -> cdrom0  (only for cdrom0)
387 *
388 * If it's a slice or partition, /cdrom/<name> becomes a directory:
389 *
390 *	/cdrom/<name>/s0
391 *
392 * Same for rmdisk and floppy.
393 *
394 * On labeled system (Trusted Solaris), links are in a user directory.
395 */
396static void
397vold_update_mountpoints(struct action_arg *aap)
398{
399	boolean_t	is_partition;
400	char		part_dir[2 * MAXNAMELEN];
401	char		symname_mp[2 * MAXNAMELEN];
402	char		symcontents_mp[MAXNAMELEN];
403	char		symname[2 * MAXNAMELEN];
404	char		symcontents[MAXNAMELEN];
405
406	is_partition = (aap->aa_partname != NULL);
407
408	if (!system_labeled) {
409		if (!is_partition) {
410			/* /cdrom/<name> -> original mountpoint */
411			(void) snprintf(symcontents_mp, sizeof (symcontents_mp),
412			    "%s", aap->aa_mountpoint);
413			(void) snprintf(symname_mp, sizeof (symname_mp),
414			    "/%s/%s", aap->aa_media, aap->aa_name);
415		} else {
416			/* /cdrom/<name>/slice -> original mountpoint */
417			(void) snprintf(part_dir, sizeof (part_dir),
418			    "/%s/%s", aap->aa_media, aap->aa_name);
419			(void) snprintf(symcontents_mp, sizeof (symcontents_mp),
420			    "%s", aap->aa_mountpoint);
421			(void) snprintf(symname_mp, sizeof (symname_mp),
422			    "/%s/%s/%s", aap->aa_media, aap->aa_name,
423			    aap->aa_partname);
424
425		}
426		/* /cdrom/cdrom0 -> ./<name> */
427		(void) snprintf(symcontents, sizeof (symcontents),
428		    "./%s", aap->aa_name);
429		(void) snprintf(symname, sizeof (symname),
430		    "/%s/%s", aap->aa_media, aap->aa_symdev);
431	} else {
432		if (!is_partition) {
433			/* /cdrom/<user>/<name> -> original mountpoint */
434			(void) snprintf(symcontents_mp, sizeof (symcontents_mp),
435			    "%s", aap->aa_mountpoint);
436			(void) snprintf(symname_mp, sizeof (symname_mp),
437			    "%s/%s/%s", mnt_zoneroot, aap->aa_media,
438			    aap->aa_symdev);
439		} else {
440			/* /cdrom/<user>/<name>/slice -> original mountpoint */
441			(void) snprintf(symcontents_mp, sizeof (symcontents_mp),
442			    "%s", aap->aa_mountpoint);
443			(void) snprintf(symname_mp, sizeof (symname_mp),
444			    "%s/%s/%s", mnt_zoneroot, aap->aa_media,
445			    aap->aa_symdev, aap->aa_partname);
446		}
447
448		/* /cdrom/<user>/cdrom0 -> ./<user>/<name> */
449		(void) snprintf(symcontents, sizeof (symcontents),
450		    ".%s/%s", mnt_userdir, aap->aa_name);
451		(void) snprintf(symname, sizeof (symname), "%s/%s/%s",
452		    mnt_zoneroot, aap->aa_media, aap->aa_symdev);
453	}
454
455	(void) unlink(symname);
456	(void) unlink(symname_mp);
457	if (is_partition) {
458		(void) rmdir(part_dir);
459	}
460
461	if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
462		(void) mkdir(aap->aa_media, 0755);
463		if (is_partition) {
464			(void) mkdir(part_dir, 0755);
465		}
466		(void) symlink(symcontents_mp, symname_mp);
467		(void) symlink(symcontents, symname);
468	}
469}
470
471
472static int
473vold_action(struct action_arg *aap)
474{
475	action_t	action;
476	int		result;
477	int		do_notify = FALSE;
478	action_t	notify_act = EJECT;
479	struct action_arg *aa[2];
480	struct action_arg a1;
481
482	dprintf("%s[%d]: entering action()\n", __FILE__, __LINE__);
483
484	/*
485	 * on Trusted Extensions, actions are executed in the user's zone
486	 */
487	if (mnt_zoneid > GLOBAL_ZONEID) {
488		pid_t	pid;
489		int	status;
490		int	ifx;
491		int	tmpl_fd;
492		int	err = 0;
493
494		tmpl_fd = open64(CTFS_ROOT "/process/template",
495		    O_RDWR);
496		if (tmpl_fd == -1)
497			return (1);
498
499		/*
500		 * Deliver no events, don't inherit,
501		 * and allow it to be orphaned.
502		 */
503		err |= ct_tmpl_set_critical(tmpl_fd, 0);
504		err |= ct_tmpl_set_informative(tmpl_fd, 0);
505		err |= ct_pr_tmpl_set_fatal(tmpl_fd,
506		    CT_PR_EV_HWERR);
507		err |= ct_pr_tmpl_set_param(tmpl_fd,
508		    CT_PR_PGRPONLY |
509		    CT_PR_REGENT);
510		if (err || ct_tmpl_activate(tmpl_fd)) {
511			(void) close(tmpl_fd);
512			return (1);
513		}
514		switch (pid = fork1()) {
515		case 0:
516			(void) ct_tmpl_clear(tmpl_fd);
517			for (ifx = 0; ifx < _NFILE; ifx++)
518				(void) close(ifx);
519
520			if (zone_enter(mnt_zoneid) == -1)
521				_exit(0);
522
523			/* entered zone, proceed to action */
524			break;
525		case -1:
526			dprintf("fork1 failed \n ");
527			return (1);
528		default :
529			(void) ct_tmpl_clear(tmpl_fd);
530			(void) close(tmpl_fd);
531			if (waitpid(pid, &status, 0) < 0) {
532				dprintf("%s(%ld): waitpid() "
533				    "failed (errno %d) \n",
534				    prog_name, prog_pid, errno);
535				return (1);
536			}
537		    }
538
539	}
540
541	/* only support one action at a time XXX */
542	a1.aa_path = NULL;
543	aa[0] = aap;
544	aa[1] = &a1;
545
546	action = aa[0]->aa_action;
547
548	if (action == CLEAR_MOUNTS) {
549		/*
550		 * Remove the notifications files, but don't
551		 * notify the client.  The "clear_mounts" action
552		 * simply clears all existing mounts of a medium's
553		 * partitions after a medium has been repartitioned.
554		 * Then vold builds a new file system that reflects
555		 * the medium's new partition structure and mounts
556		 * the new partitions by calling rmmount, and therefore
557		 * action(), with the VOLUME_ACTION environment variable
558		 * set to "remount".
559		 */
560		result = remove_notify_files(aa);
561		result = TRUE;
562	} else if (action == EJECT) {
563		result = remove_notify_files(aa);
564		if (result == TRUE) {
565			do_notify = TRUE;
566			notify_act = EJECT;
567		}
568	} else if (action = INSERT) {
569		result = create_notify_files(aa);
570		if (result == TRUE) {
571			do_notify = TRUE;
572			notify_act = INSERT;
573		}
574	} else if (action == REMOUNT) {
575		result = create_notify_files(aa);
576		if (result == TRUE) {
577			do_notify = TRUE;
578			notify_act = REMOUNT;
579		}
580	} else if (action == UNMOUNT) {
581		result = remove_notify_files(aa);
582		if (result == TRUE) {
583			do_notify = TRUE;
584			notify_act = UNMOUNT;
585		}
586	} else {
587		dprintf("%s[%d]: action(): invalid action: %s\n",
588			__FILE__, __LINE__, action);
589		result = FALSE;
590	}
591
592	if (result == TRUE) {
593		result = notify_clients(notify_act, do_notify);
594	}
595
596	dprintf("%s[%d]: leaving action(), result = %s\n",
597		__FILE__, __LINE__, result_strings[result]);
598
599	if (mnt_zoneid > GLOBAL_ZONEID) {
600		/* exit forked local zone process */
601		_exit(0);
602	}
603
604	if (result == TRUE) {
605		/*
606		 * File Manager is running. return 0.
607		 * see man page rmmount.conf(4).
608		 */
609		return (0);
610	} else {
611		return (1);
612	}
613}
614
615
616/*
617 * Returns NULL if a medium or partition is mountable
618 * and a string stating the reason the medium or partition
619 * can't be mounted if the medium or partition isn't mountable.
620 *
621 * If the volume_name of the medium or partition is one of the
622 * following, the medium or partition isn't mountable.
623 *
624 * unlabeled_<media_type>
625 * unknown_format
626 * password_protected
627 */
628/* ARGSUSED */
629static char *
630not_mountable(struct action_arg *aa)
631{
632	return (NULL);
633}
634
635static int
636create_notify_files(struct action_arg **aa)
637{
638	int	ai;
639	char	*fstype;
640	char	*mount_point;
641	char	notify_file[64];
642	char	*raw_partitionp;
643	char	*reason; /* Why the medium wasn't mounted */
644	int	result;
645	char	*symdev;
646
647	dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__);
648
649	ai = 0;
650	result = FALSE;
651	symdev = aa[ai]->aa_symdev;
652	while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) {
653		if (aa[ai]->aa_mountpoint != NULL) {
654			if (aa[ai]->aa_type) {
655				fstype = aa[ai]->aa_type;
656			} else {
657				fstype = "unknown";
658			}
659			mount_point = aa[ai]->aa_mountpoint;
660			if (aa[ai]->aa_partname != NULL) {
661				/*
662				 * Is aa_partname ever NULL?
663				 * When time permits, check.
664				 * If it is, the action taken
665				 * in the else clause could produce
666				 * file name conflicts.
667				 */
668				sprintf(notify_file, "%s-%s", symdev,
669						aa[ai]->aa_partname);
670			} else {
671				sprintf(notify_file, "%s-0", symdev);
672			}
673			reason = NULL;
674		} else {
675			/*
676			 * The partition isn't mounted.
677			 */
678			fstype = "none";
679			mount_point = "none";
680			reason = not_mountable(aa[ai]);
681			if (reason != NULL) {
682				sprintf(notify_file, "%s-0", symdev);
683			} else {
684				/*
685				 * Either the partition is a backup slice, or
686				 * rmmount tried to mount the partition, but
687				 * idenf_fs couldn't identify the file system
688				 * type; that can occur when rmmount is
689				 * trying to mount all the slices in a Solaris
690				 * VTOC, and one or more partitions don't have
691				 * file systems in them.
692				 */
693				if (aa[0]->aa_partname != NULL) {
694					/*
695					 * Is aa_partname ever NULL?
696					 * When time permits, check.
697					 * If it is, the action taken
698					 * in the else clause could produce
699					 * file name conflicts.
700					 */
701					sprintf(notify_file, "%s-%s", symdev,
702							aa[0]->aa_partname);
703				} else {
704					sprintf(notify_file, "%s-0", symdev);
705				}
706				if ((aa[0]->aa_type != NULL) &&
707					(strcmp(aa[0]->aa_type, "backup_slice")
708						== 0)) {
709					reason = "backup_slice";
710				} else {
711					reason = "unformatted_media";
712				}
713				/*
714				 * "unformatted_media" should be
715				 * changed to "unformmated_medium" for
716				 * grammatical correctness, but
717				 * "unformatted_media" is now specified
718				 * in the interface to filemgr, so the
719				 * change can't be made without the
720				 * approval of the CDE group.
721				 */
722			}
723		}
724		raw_partitionp = aa[0]->aa_rawpath;
725		result = create_one_notify_file(fstype,
726				mount_point,
727				notify_file,
728				raw_partitionp,
729				reason,
730				symdev);
731		ai++;
732	}
733	dprintf("%s[%d]: leaving create_notify_files(), result = %s\n",
734		__FILE__, __LINE__, result_strings[result]);
735	return (result);
736}
737
738static int
739create_one_notify_file(char *fstype,
740	char *mount_point,
741	char *notify_file,
742	char *raw_partitionp,
743	char *reason,
744	char *symdev)
745{
746	/*
747	 * For a mounted partition, create a notification file
748	 * indicating the mount point,  the raw device pathname
749	 * of the partition, and the partition's file system
750	 * type.  For an unmounted partition, create a
751	 * notification file containing the reason that the
752	 * partition wasn't mounted and the raw device pathname
753	 * of the partition.
754	 *
755	 * Create the file as root in a world-writable
756	 * directory that resides in a world-writable directory.
757	 *
758	 * Handle two possible race conditions that could
759	 * allow security breaches.
760	 */
761
762	int	current_working_dir_fd;
763	int	file_descriptor;
764	FILE	*filep;
765	int	result;
766
767	dprintf("%s[%d]:Entering create_one_notify_file()\n",
768		__FILE__, __LINE__);
769	dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype);
770	dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point);
771	dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file);
772	dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n",
773		raw_partitionp);
774	if (reason != NULL) {
775		dprintf("\tcreate_one_notify_file(): reason = %s\n", reason);
776	} else {
777		dprintf("\tcreate_one_notify_file(): reason = NULL\n");
778	}
779	dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev);
780
781	result = TRUE;
782	/*
783	 * Handle Race Condition One:
784	 *
785	 *   If NOTIFY_DIR exists, make sure it is not a symlink.
786	 *   if it is, remove it and try to create it.  Check
787	 *   again to make sure NOTIFY_DIR isn't a symlink.
788	 *   If it is, remove it and return without creating
789	 *   a notification file.  The condition can only occur if
790	 *   someone is trying to break into the system by running
791	 *   a program that repeatedly creates NOTIFY_DIR as a
792	 *   symlink.  If NOTIFY_DIR exists and isn't a symlink,
793	 *   change the working directory to NOTIFY_DIR.
794	 */
795	current_working_dir_fd = pushdir(NOTIFY_DIR);
796	if (current_working_dir_fd < 0) {
797		(void) makepath(NOTIFY_DIR, 0777);
798		current_working_dir_fd = pushdir(NOTIFY_DIR);
799		if (current_working_dir_fd < 0) {
800			result = FALSE;
801		}
802	}
803	/*
804	 * Handle Race Condition Two:
805	 *
806	 * Create the notification file in NOTIFY_DIR.
807	 * Remove any files with the same name that may already be
808	 * there, using remove(), as it safely removes directories.
809	 * Then open the file O_CREAT|O_EXCL, which doesn't follow
810	 * symlinks and requires that the file not exist already,
811	 * so the new file actually resides in the current working
812	 * directory.  Create the file with access mode 644, which
813	 * renders it unusable by anyone trying to break into the
814	 * system.
815	 */
816	if (result == TRUE) {
817		/*
818		 * The current working directory is now NOTIFY_DIR.
819		 */
820		(void) remove(notify_file);
821		file_descriptor =
822			open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644);
823		if (file_descriptor < 0) {
824			dprintf("%s[%d]: can't create %s/%s; %m\n",
825				__FILE__, __LINE__, NOTIFY_DIR, notify_file);
826			result = FALSE;
827		} else {
828			filep = fdopen(file_descriptor, "w");
829			if (filep != NULL) {
830				if (reason == NULL) {
831					(void) fprintf(filep, "%s %s %s",
832						mount_point,
833						raw_partitionp,
834						fstype);
835					(void) fclose(filep);
836				dprintf("%s[%d]: Just wrote %s %s %s to %s\n",
837						__FILE__,
838						__LINE__,
839						mount_point,
840						raw_partitionp,
841						fstype,
842						notify_file);
843				} else {
844					(void) fprintf(filep, "%s %s",
845						reason, raw_partitionp);
846					(void) fclose(filep);
847				dprintf("%s[%d]: Just wrote %s %s to %s\n",
848						__FILE__,
849						__LINE__,
850						reason,
851						raw_partitionp,
852						notify_file);
853				}
854			} else {
855				dprintf("%s[%d]: can't write %s/%s; %m\n",
856					__FILE__, __LINE__,
857					NOTIFY_DIR, notify_file);
858				(void) close(file_descriptor);
859				result = FALSE;
860			}
861		}
862		popdir(current_working_dir_fd);
863	}
864	dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n",
865		__FILE__, __LINE__, result_strings[result]);
866	return (result);
867}
868
869static boolean_t
870notify_clients(action_t action, int do_notify)
871{
872	/*
873	 * Notify interested applications of changes in the state
874	 * of removable media.  Interested applications are those
875	 * that create a named pipe in NOTIFY_DIR with a name that
876	 * begins with "notify".  Open the pipe and write a
877	 * character through it that indicates the type of state
878	 * change = 'e' for ejections, 'i' for insertions, 'r'
879	 * for remounts of the file systems on repartitioned media,
880	 * and 'u' for unmounts of file systems.
881	 */
882
883	int		current_working_dir_fd;
884	DIR		*dirp;
885	struct dirent	*dir_entryp;
886	size_t		len;
887	int		fd;
888	char		namebuf[MAXPATHLEN];
889	char		notify_character;
890	void		(*old_signal_handler)();
891	int		result;
892	struct stat	sb;
893
894	dprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__);
895
896	result = TRUE;
897	/*
898	 * Use relative pathnames after changing the
899	 * working directory to the notification directory.
900	 * Check to make sure that each "notify" file is a
901	 * named pipe to make sure that it hasn't changed
902	 * its file type, which could mean that someone is
903	 * trying to use "notify" files to break into the
904	 * system.
905	 */
906	if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) {
907		result = FALSE;
908	}
909	if (result == TRUE) {
910		dirp = opendir(".");
911		if (dirp == NULL) {
912			dprintf("%s[%d]:opendir failed on '.'; %m\n",
913				__FILE__, __LINE__);
914			popdir(current_working_dir_fd);
915			result = FALSE;
916		}
917	}
918	if (result == TRUE) {
919		/*
920		 * Read through the directory and write a notify
921		 * character to all files whose names start with "notify".
922		 */
923		result = FALSE;
924		old_signal_handler = signal(SIGPIPE, SIG_IGN);
925		len = strlen(NOTIFY_NAME);
926		while (dir_entryp = readdir(dirp)) {
927			if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len)
928			    != 0) {
929				continue;
930			}
931			result = TRUE;
932			if (do_notify != TRUE) {
933				continue;
934			}
935			(void) sprintf(namebuf, "%s/%s",
936				NOTIFY_DIR, dir_entryp->d_name);
937			if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) {
938				dprintf("%s[%d]: open failed for %s; %m\n",
939				    __FILE__, __LINE__, namebuf);
940				continue;
941			}
942			/*
943			 * Check to be sure that the entry is a named pipe.
944			 * That closes a small security hole that could
945			 * enable unauthorized access to the system root.
946			 */
947			if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) {
948				dprintf("%s[%d]: %s isn't a named pipe\n",
949					__FILE__, __LINE__, namebuf);
950
951				(void) close(fd);
952				continue;
953			}
954			notify_character = notify_characters[action];
955			if (write(fd, &notify_character, 1) < 0) {
956				dprintf("%s[%d]: write failed for %s; %m\n",
957				    __FILE__, __LINE__, namebuf);
958				(void) close(fd);
959				continue;
960			}
961			(void) close(fd);
962		}
963		(void) closedir(dirp);
964		(void) signal(SIGPIPE, old_signal_handler);
965		popdir(current_working_dir_fd);
966	}
967	dprintf("%s[%d]: leaving notify_clients(), result = %s\n",
968		__FILE__, __LINE__, result_strings[result]);
969	return (result);
970}
971
972static void
973popdir(int fd)
974{
975	/*
976	 * Change the current working directory to the directory
977	 * specified by fd and close the fd.  Exit the program
978	 * on failure.
979	 */
980	if (fchdir(fd) < 0) {
981		dprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__);
982		exit(1);
983	}
984	(void) close(fd);
985}
986
987static int
988pushdir(const char *dir)
989{
990	/*
991	 * Change the current working directory to dir and
992	 * return a file descriptor for the old working
993	 * directory.
994	 *
995	 * Exception handling:
996	 *
997	 * If dir doesn't exist, leave the current working
998	 * directory the same and return -1.
999	 *
1000	 * If dir isn't a directory, remove it, leave the
1001	 * current working directory the same, and return -1.
1002	 *
1003	 * If open() fails on the current working directory
1004	 * or the chdir operation fails on dir, leave the
1005	 * current working directory the same and return -1.
1006	 */
1007
1008	int		current_working_dir_fd;
1009	struct stat	stat_buf;
1010
1011	if (lstat(dir, &stat_buf) < 0) {
1012		dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n",
1013			__FILE__, __LINE__, dir);
1014		return (-1);
1015	}
1016
1017	if (!(S_ISDIR(stat_buf.st_mode))) {
1018		dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n",
1019			__FILE__, __LINE__, dir);
1020		(void) remove(dir);
1021		return (-1);
1022	}
1023	if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) {
1024		dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n",
1025			__FILE__, __LINE__, dir);
1026		return (-1);
1027	}
1028	if (chdir(dir) < 0) {
1029		(void) close(current_working_dir_fd);
1030		dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n",
1031			__FILE__, __LINE__, dir);
1032		return (-1);
1033	}
1034	return (current_working_dir_fd);
1035}
1036
1037static boolean_t
1038remove_notify_files(struct action_arg **aa)
1039{
1040	int	ai;
1041	int	current_working_dir_fd;
1042	char	notify_file[64];
1043	int	result;
1044	char	*symdev;
1045
1046	dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__);
1047
1048	ai = 0;
1049	result = TRUE;
1050	symdev = aa[ai]->aa_symdev;
1051	while ((result == TRUE) &&
1052		(aa[ai] != NULL) &&
1053		(aa[ai]->aa_path != NULL)) {
1054
1055		if (not_mountable(aa[ai])) {
1056			sprintf(notify_file, "%s-0", symdev);
1057		} else if (aa[ai]->aa_partname != NULL) {
1058			/*
1059			 * Is aa_partname ever NULL?
1060			 * When time permits, check.
1061			 * If it is, the action taken
1062			 * in the else clause could produce
1063			 * file name conflicts.
1064			 */
1065			sprintf(notify_file, "%s-%s",
1066				symdev, aa[0]->aa_partname);
1067		} else {
1068			sprintf(notify_file, "%s-0", symdev);
1069		}
1070
1071		current_working_dir_fd = pushdir(NOTIFY_DIR);
1072		if (current_working_dir_fd < 0) {
1073			result = FALSE;
1074		}
1075		if ((result == TRUE) && (remove(notify_file) < 0)) {
1076			dprintf("%s[%d]: remove %s/%s; %m\n",
1077				__FILE__, __LINE__, NOTIFY_DIR, notify_file);
1078			result = FALSE;
1079		}
1080		if (current_working_dir_fd != -1) {
1081			popdir(current_working_dir_fd);
1082		}
1083		ai++;
1084	}
1085	dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n",
1086		__FILE__, __LINE__, result_strings[result]);
1087
1088	return (result);
1089}
1090