xref: /illumos-gate/usr/src/cmd/hal/tools/hal-storage-mount.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1 /***************************************************************************
2  * CVSID: $Id$
3  *
4  * hal-storage-mount.c : Mount wrapper
5  *
6  * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  **************************************************************************/
23 
24 
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #ifdef __FreeBSD__
35 #include <fstab.h>
36 #include <sys/param.h>
37 #include <sys/ucred.h>
38 #include <sys/mount.h>
39 #include <limits.h>
40 #include <pwd.h>
41 #elif sun
42 #include <sys/mnttab.h>
43 #include <sys/vfstab.h>
44 #include <sys/wait.h>
45 #else
46 #include <mntent.h>
47 #endif
48 #include <sys/types.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <syslog.h>
52 
53 #include <libhal.h>
54 #include <libhal-storage.h>
55 #ifdef HAVE_POLKIT
56 #include <libpolkit.h>
57 #endif
58 
59 #include "hal-storage-shared.h"
60 
61 #ifdef __FreeBSD__
62 #define MOUNT		"/sbin/mount"
63 #define MOUNT_OPTIONS	"noexec,nosuid"
64 #define MOUNT_TYPE_OPT	"-t"
65 #elif sun
66 #define MOUNT		"/sbin/mount"
67 #define MOUNT_OPTIONS	"nosuid"
68 #define MOUNT_TYPE_OPT	"-F"
69 #else
70 #define MOUNT		"/bin/mount"
71 #define MOUNT_OPTIONS	"noexec,nosuid,nodev"
72 #define MOUNT_TYPE_OPT	"-t"
73 #endif
74 
75 static void
76 usage (void)
77 {
78 	fprintf (stderr, "This program should only be started by hald.\n");
79 	exit (1);
80 }
81 
82 static void
83 permission_denied_volume_ignore (const char *device)
84 {
85 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n");
86 	fprintf (stderr, "Device has %s volume.ignore set to TRUE. Refusing to mount.\n", device);
87 	exit (1);
88 }
89 
90 static void
91 permission_denied_etc_fstab (const char *device)
92 {
93 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n");
94 	fprintf (stderr, "Device %s is listed in /etc/fstab. Refusing to mount.\n", device);
95 	exit (1);
96 }
97 
98 static void
99 already_mounted (const char *device)
100 {
101 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.AlreadyMounted\n");
102 	fprintf (stderr, "Device %s is already mounted.\n", device);
103 	exit (1);
104 }
105 
106 static void
107 invalid_mount_option (const char *option, const char *uid)
108 {
109 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountOption\n");
110 	fprintf (stderr, "The option '%s' is not allowed for uid=%s\n", option, uid);
111 	exit (1);
112 }
113 
114 static void
115 unknown_filesystem (const char *filesystem)
116 {
117 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType\n");
118 	fprintf (stderr, "Unknown file system '%s'\n", filesystem);
119 	exit (1);
120 }
121 
122 static void
123 invalid_mount_point (const char *mount_point)
124 {
125 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint\n");
126 	fprintf (stderr, "The mount point '%s' is invalid\n", mount_point);
127 	exit (1);
128 }
129 
130 static void
131 mount_point_not_available (const char *mount_point)
132 {
133 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.MountPointNotAvailable\n");
134 	fprintf (stderr, "The mount point '%s' is already occupied\n", mount_point);
135 	exit (1);
136 }
137 
138 
139 static void
140 cannot_remount (const char *device)
141 {
142 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.CannotRemount\n");
143 	fprintf (stderr, "%s not mounted already\n", device);
144 	exit (1);
145 }
146 
147 #ifdef HAVE_POLKIT
148 static void
149 permission_denied_privilege (const char *privilege, const char *uid)
150 {
151 	fprintf (stderr, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n");
152 	fprintf (stderr, "%s refused uid %s\n", privilege, uid);
153 	exit (1);
154 }
155 #endif
156 
157 
158 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
159 static void
160 canonicalize_filename (gchar *filename)
161 {
162 	gchar *p, *q;
163 	gboolean last_was_slash = FALSE;
164 
165 	p = filename;
166 	q = filename;
167 
168 	while (*p)
169 	{
170 		if (*p == G_DIR_SEPARATOR)
171 		{
172 			if (!last_was_slash)
173 				*q++ = G_DIR_SEPARATOR;
174 
175 			last_was_slash = TRUE;
176 		}
177 		else
178 		{
179 			if (last_was_slash && *p == '.')
180 			{
181 				if (*(p + 1) == G_DIR_SEPARATOR ||
182 				    *(p + 1) == '\0')
183 				{
184 					if (*(p + 1) == '\0')
185 						break;
186 
187 					p += 1;
188 				}
189 				else if (*(p + 1) == '.' &&
190 					 (*(p + 2) == G_DIR_SEPARATOR ||
191 					  *(p + 2) == '\0'))
192 				{
193 					if (q > filename + 1)
194 					{
195 						q--;
196 						while (q > filename + 1 &&
197 						       *(q - 1) != G_DIR_SEPARATOR)
198 							q--;
199 					}
200 
201 					if (*(p + 2) == '\0')
202 						break;
203 
204 					p += 2;
205 				}
206 				else
207 				{
208 					*q++ = *p;
209 					last_was_slash = FALSE;
210 				}
211 			}
212 			else
213 			{
214 				*q++ = *p;
215 				last_was_slash = FALSE;
216 			}
217 		}
218 
219 		p++;
220 	}
221 
222 	if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
223 		q--;
224 
225 	*q = '\0';
226 }
227 
228 static char *
229 resolve_symlink (const char *file)
230 {
231 	GError *error;
232 	char *dir;
233 	char *link;
234 	char *f;
235 	char *f1;
236 
237 	f = g_strdup (file);
238 
239 	while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) {
240 		link = g_file_read_link (f, &error);
241 		if (link == NULL) {
242 			g_warning ("Cannot resolve symlink %s: %s", f, error->message);
243 			g_error_free (error);
244 			g_free (f);
245 			f = NULL;
246 			goto out;
247 		}
248 
249 		dir = g_path_get_dirname (f);
250 		f1 = g_strdup_printf ("%s/%s", dir, link);
251 		g_free (dir);
252 		g_free (link);
253 		g_free (f);
254 		f = f1;
255 	}
256 
257 out:
258 	if (f != NULL)
259 		canonicalize_filename (f);
260 	return f;
261 }
262 
263 static LibHalVolume *
264 volume_findby (LibHalContext *hal_ctx, const char *property, const char *value)
265 {
266 	int i;
267 	char **hal_udis;
268 	int num_hal_udis;
269 	LibHalVolume *result = NULL;
270 	char *found_udi = NULL;
271 	DBusError error;
272 
273 	dbus_error_init (&error);
274 	if ((hal_udis = libhal_manager_find_device_string_match (hal_ctx, property,
275 								 value, &num_hal_udis, &error)) == NULL) {
276 		LIBHAL_FREE_DBUS_ERROR (&error);
277 		goto out;
278 	}
279 	for (i = 0; i < num_hal_udis; i++) {
280 		char *udi;
281 		udi = hal_udis[i];
282 		if (libhal_device_query_capability (hal_ctx, udi, "volume", &error)) {
283 			found_udi = strdup (udi);
284 			break;
285 		}
286 	}
287 
288 	libhal_free_string_array (hal_udis);
289 
290 	if (found_udi != NULL)
291 		result = libhal_volume_from_udi (hal_ctx, found_udi);
292 
293 	free (found_udi);
294 out:
295 	return result;
296 }
297 
298 static void
299 bailout_if_in_fstab (LibHalContext *hal_ctx, const char *device, const char *label, const char *uuid)
300 {
301 	gpointer handle;
302 	char *entry;
303 	char *_mount_point;
304 
305 	printf (" label '%s'  uuid '%s'\n", label ? label : "" , uuid ? uuid : "");
306 
307 	/* check if /etc/fstab mentions this device... (with symlinks etc) */
308 	if (! fstab_open (&handle)) {
309 		printf ("cannot open /etc/fstab\n");
310 		unknown_error ("Cannot open /etc/fstab");
311 	}
312 	while ((entry = fstab_next (handle, &_mount_point)) != NULL) {
313 		char *resolved;
314 
315 #ifdef DEBUG
316 		printf ("Looking at /etc/fstab entry '%s'\n", entry);
317 #endif
318 		if (label != NULL && g_str_has_prefix (entry, "LABEL=")) {
319 			if (strcmp (entry + 6, label) == 0) {
320 				gboolean skip_fstab_entry;
321 
322 				skip_fstab_entry = FALSE;
323 
324 				/* (heck, we also do the stuff below in gnome-mount) */
325 
326 				/* OK, so what's if someone attaches an external disk with the label '/' and
327 				 * /etc/fstab has
328 				 *
329 				 *    LABEL=/    /    ext3    defaults    1 1
330 				 *
331 				 * in /etc/fstab as most Red Hat systems do? Bugger, this is a very common use
332 				 * case; suppose that you take the disk from your Fedora server and attaches it
333 				 * to your laptop. Bingo, you now have two disks with the label '/'. One must
334 				 * seriously wonder if using things like LABEL=/ for / is a good idea; just
335 				 * what happens if you boot in this configuration? (answer: the initrd gets
336 				 * it wrong most of the time.. sigh)
337 				 *
338 				 * To work around this, check if the listed entry in /etc/fstab is already mounted,
339 				 * if it is, then check if it's the same device_file as the given one...
340 				 */
341 
342 				/* see if a volume is mounted at this mount point  */
343 				if (_mount_point != NULL) {
344 					LibHalVolume *mounted_vol;
345 
346 					mounted_vol = volume_findby (hal_ctx, "volume.mount_point", _mount_point);
347 					if (mounted_vol != NULL) {
348 						const char *mounted_vol_device_file;
349 
350 						mounted_vol_device_file = libhal_volume_get_device_file (mounted_vol);
351 						/* no need to resolve symlinks, hal uses the canonical device file */
352 						if (mounted_vol_device_file != NULL &&
353 						    strcmp (mounted_vol_device_file, device) !=0) {
354 #ifdef DEBUG
355 							printf ("Wanting to mount %s that has label %s, but /etc/fstab says LABEL=%s is to be mounted at mount point '%s'. However %s (that also has label %s), is already mounted at said mount point. So, skipping said /etc/fstab entry.\n",
356 								   device, label, label, _mount_point, mounted_vol_device_file, _mount_point);
357 #endif
358 							skip_fstab_entry = TRUE;
359 						}
360 						libhal_volume_free (mounted_vol);
361 					}
362 				}
363 
364 				if (!skip_fstab_entry) {
365 					printf ("%s found in /etc/fstab. Not mounting.\n", entry);
366 					permission_denied_etc_fstab (device);
367 				}
368 			}
369 		} else if (uuid != NULL && g_str_has_prefix (entry, "UUID=")) {
370 			if (strcmp (entry + 5, uuid) == 0) {
371 				printf ("%s found in /etc/fstab. Not mounting.\n", entry);
372 				permission_denied_etc_fstab (device);
373 			}
374 		} else {
375 
376 			resolved = resolve_symlink (entry);
377 #ifdef DEBUG
378 			printf ("/etc/fstab: device %s -> %s \n", entry, resolved);
379 #endif
380 			if (strcmp (device, resolved) == 0) {
381 				printf ("%s (-> %s) found in /etc/fstab. Not mounting.\n", entry, resolved);
382 				permission_denied_etc_fstab (device);
383 			}
384 
385 			g_free (resolved);
386 		}
387 	}
388 	fstab_close (handle);
389 }
390 
391 static gboolean
392 device_is_mounted (const char *device, char **mount_point)
393 {
394 	gpointer handle;
395 	char *entry;
396 	gboolean ret;
397 
398 	ret = FALSE;
399 
400 	/* check if /proc/mounts mentions this device... (with symlinks etc) */
401 	if (! mtab_open (&handle)) {
402 		printf ("cannot open mount list\n");
403 		unknown_error ("Cannot open /etc/mtab or equivalent");
404 	}
405 	while (((entry = mtab_next (handle, mount_point)) != NULL) && (ret == FALSE)) {
406 		char *resolved;
407 
408 		resolved = resolve_symlink (entry);
409 #ifdef DEBUG
410 		printf ("/proc/mounts: device %s -> %s \n", entry, resolved);
411 #endif
412 		if (strcmp (device, resolved) == 0) {
413 			printf ("%s (-> %s) found in mount list. Not mounting.\n", entry, resolved);
414 			ret = TRUE;
415 		}
416 
417 		g_free (resolved);
418 	}
419 	mtab_close (handle);
420 	return ret;
421 }
422 
423 /* maps volume_id fs types to the appropriate -t mount option */
424 static const char *
425 map_fstype (const char *fstype)
426 {
427 #ifdef __FreeBSD__
428 	if (! strcmp (fstype, "iso9660"))
429 		return "cd9660";
430 	else if (! strcmp (fstype, "ext2"))
431 		return "ext2fs";
432 	else if (! strcmp (fstype, "vfat"))
433 		return "msdosfs";
434 #elif sun
435 	if (! strcmp (fstype, "iso9660"))
436 		return "hsfs";
437 	else if (! strcmp (fstype, "vfat"))
438 		return "pcfs";
439 #endif
440 
441 	return fstype;
442 }
443 
444 static void
445 handle_mount (LibHalContext *hal_ctx,
446 #ifdef HAVE_POLKIT
447 	      LibPolKitContext *pol_ctx,
448 #endif
449 	      const char *udi,
450 	      LibHalVolume *volume, LibHalDrive *drive, const char *device,
451 	      const char *invoked_by_uid, const char *invoked_by_syscon_name,
452 	      DBusConnection *system_bus)
453 {
454 	int i, j;
455 	DBusError error;
456 	char mount_point[256];
457 	char mount_fstype[256];
458 	char mount_options[1024];
459 	char **allowed_options;
460 	char **given_options;
461 	gboolean wants_to_change_uid;
462 	char *mount_dir;
463 	GError *err = NULL;
464 	char *sout = NULL;
465 	char *serr = NULL;
466 	int exit_status;
467 	char *args[10];
468 	int na;
469 	GString *mount_option_str;
470 	gboolean pol_is_fixed;
471 	gboolean pol_change_uid;
472 	char *privilege;
473 	gboolean is_remount;
474 #ifdef HAVE_POLKIT
475 	gboolean allowed_by_privilege;
476 	gboolean is_temporary_privilege;
477 #endif
478 	gboolean explicit_mount_point_given;
479 	const char *end;
480 #ifdef __FreeBSD__
481 	struct passwd *pw;
482 	uid_t calling_uid;
483 	gid_t calling_gid;
484 #endif
485 	const char *label;
486 	const char *uuid;
487 	const char *model;
488 	const char *drive_type;
489 #ifdef sun
490 	adt_export_data_t *adt_data;
491 	size_t adt_data_size;
492 	gboolean append_ro = FALSE;
493 	gboolean is_abs_path = FALSE;
494 	uid_t calling_uid;
495 
496 	calling_uid = atol (invoked_by_uid);
497 #endif
498 
499 #ifdef DEBUG
500 	printf ("device                           = %s\n", device);
501 	printf ("invoked by uid                   = %s\n", invoked_by_uid);
502 	printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name);
503 #endif
504 
505 	if (volume != NULL) {
506 		dbus_error_init (&error);
507 		if (libhal_device_get_property_bool (hal_ctx, udi, "volume.ignore", &error) ||
508 		    dbus_error_is_set (&error)) {
509 			if (dbus_error_is_set (&error)) {
510 				LIBHAL_FREE_DBUS_ERROR (&error);
511 			}
512 			/*
513 			 * When device allocation is enabled (bsmconv or TX), we
514 			 * set volume.ignore on all volumes, but still want
515 			 * Mount() to succeed when called from the euid=0
516 			 * device allocation program.
517 			 */
518 			if (atol (invoked_by_uid) != 0) {
519 				permission_denied_volume_ignore (device);
520 			}
521 		}
522 
523 		label = libhal_volume_get_label (volume);
524 		uuid = libhal_volume_get_uuid (volume);
525 	} else {
526 		label = NULL;
527 		uuid = NULL;
528 	}
529 
530 	bailout_if_in_fstab (hal_ctx, device, label, uuid);
531 
532 	/* TODO: sanity check that what hal exports is correct (cf. Martin Pitt's email) */
533 
534 	/* read from stdin */
535 	if (strlen (fgets (mount_point, sizeof (mount_point), stdin)) > 0)
536 		mount_point   [strlen (mount_point)   - 1] = '\0';
537 	if (strlen (fgets (mount_fstype, sizeof (mount_fstype), stdin)) > 0)
538 		mount_fstype  [strlen (mount_fstype)  - 1] = '\0';
539 	if (strlen (fgets (mount_options, sizeof (mount_options), stdin)) > 0)
540 		mount_options [strlen (mount_options) - 1] = '\0';
541 	/* validate that input from stdin is UTF-8 */
542 	if (!g_utf8_validate (mount_point, -1, &end))
543 		unknown_error ("Error validating mount_point as UTF-8");
544 	if (!g_utf8_validate (mount_fstype, -1, &end))
545 		unknown_error ("Error validating mount_fstype as UTF-8");
546 	if (!g_utf8_validate (mount_options, -1, &end))
547 		unknown_error ("Error validating mount_options as UTF-8");
548 
549 #ifdef sun
550 	if (calling_uid != 0) {
551 #endif
552 	for (i = 0; mount_point[i] != '\0'; i++) {
553 		if (mount_point[i] == '\n' ||
554 		    mount_point[i] == G_DIR_SEPARATOR) {
555 			unknown_error ("mount_point cannot contain the following characters: newline, G_DIR_SEPARATOR (usually /)");
556 		}
557 	}
558 #ifdef sun
559 	}
560 	is_abs_path = (mount_point[0] == G_DIR_SEPARATOR);
561 #endif
562 
563 #ifdef DEBUG
564 	printf ("mount_point    = '%s'\n", mount_point);
565 	printf ("mount_fstype   = '%s'\n", mount_fstype);
566 	printf ("mount_options  = '%s'\n", mount_options);
567 #endif
568 
569 	/* delete any trailing whitespace options from splitting the string */
570 	given_options = g_strsplit (mount_options, "\t", 0);
571 	for (i = g_strv_length (given_options) - 1; i >= 0; --i) {
572 		if (strlen (given_options[i]) > 0)
573 			break;
574 		given_options[i] = NULL;
575 	}
576 
577 #ifdef sun
578 	/* for read-only media append 'ro' option if not already */
579 	append_ro = libhal_device_get_property_bool (hal_ctx, libhal_drive_get_udi(drive),
580 	    "storage.removable.solaris.read_only", NULL);
581 
582 	if (append_ro) {
583 		for (i = 0; i < (int) g_strv_length (given_options); i++) {
584 			if (strcmp (given_options[i], "ro") == 0) {
585 				append_ro = FALSE;
586 			}
587 		}
588 	}
589 #endif /* sun */
590 
591 	/* is option 'remount' included? */
592 	is_remount = FALSE;
593 	for (i = 0; i < (int) g_strv_length (given_options); i++) {
594 		if (strcmp (given_options[i], "remount") == 0) {
595 			is_remount = TRUE;
596 		}
597 	}
598 
599 	mount_dir = NULL;
600 	if (is_remount) {
601 		if (volume != NULL) {
602 			if (!libhal_volume_is_mounted (volume)) {
603 				cannot_remount (device);
604 			}
605 			mount_dir = g_strdup (libhal_volume_get_mount_point (volume));
606 		} else {
607 			if (!device_is_mounted (device, &mount_dir)) {
608 				cannot_remount (device);
609 			}
610 		}
611 
612 		if (mount_dir == NULL) {
613 			unknown_error ("Cannot get mount_dir for remount even though volume is mounted!");
614 		}
615 
616 	} else {
617 		if (volume != NULL) {
618 			if (libhal_volume_is_mounted (volume)) {
619 				already_mounted (device);
620 			}
621 		} else {
622 			if (device_is_mounted (device, NULL)) {
623 				already_mounted (device);
624 			}
625 		}
626 	}
627 
628 	if (!is_remount) {
629 		/* figure out mount point if no mount point is given... */
630 		explicit_mount_point_given = FALSE;
631 		if (strlen (mount_point) == 0) {
632 			char *p;
633 			const char *label;
634 
635 			if (volume != NULL)
636 				label = libhal_volume_get_label (volume);
637 			else
638 				label = NULL;
639 
640 			model = libhal_drive_get_model (drive);
641 			drive_type = libhal_drive_get_type_textual (drive);
642 
643 			if (label != NULL) {
644 				/* best - use label */
645 				g_strlcpy (mount_point, label, sizeof (mount_point));
646 
647 			} else if ((model != NULL) && (strlen (model) > 0)) {
648 				g_strlcpy (mount_point, model, sizeof (mount_point));
649 			} else if ((drive_type != NULL) && (strlen (drive_type) > 0)) {
650 				g_strlcpy (mount_point, drive_type, sizeof (mount_point));
651 			} else {
652 				/* fallback - use "disk" */
653 				g_snprintf (mount_point, sizeof (mount_point), "disk");
654 			}
655 
656 			/* sanitize computed mount point name, e.g. replace invalid chars with '-' */
657 			p = mount_point;
658 			while (TRUE) {
659 				p = g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR);
660 				if (p == NULL)
661 					break;
662 				*p = '-';
663 			};
664 
665 		} else {
666 			explicit_mount_point_given = TRUE;
667 		}
668 
669 		/* check mount point name - only forbid separators */
670 #ifdef sun
671 		if (calling_uid != 0) {
672 #endif
673 		if (g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR) != NULL) {
674 			printf ("'%s' is an invalid mount point\n", mount_point);
675 			invalid_mount_point (mount_point);
676 		}
677 #ifdef sun
678 		}
679 #endif
680 
681 		/* check if mount point is available - append number to mount point */
682 		i = 0;
683 		mount_dir = NULL;
684 		while (TRUE) {
685 			g_free (mount_dir);
686 #ifdef sun
687 			if (is_abs_path)
688 				mount_dir = g_strdup (mount_point);
689 			else
690 #endif
691 			if (i == 0)
692 				mount_dir = g_strdup_printf ("/media/%s", mount_point);
693 			else
694 				mount_dir = g_strdup_printf ("/media/%s-%d", mount_point, i);
695 
696 #ifdef DEBUG
697 			printf ("trying dir %s\n", mount_dir);
698 #endif
699 
700 			/* XXX should test for being a mount point */
701 			if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS)) {
702 				break;
703 			}
704 
705 			if (explicit_mount_point_given) {
706 				mount_point_not_available (mount_dir);
707 			}
708 
709 			i++;
710 		}
711 	}
712 
713 	dbus_error_init (&error);
714 	allowed_options = libhal_device_get_property_strlist (hal_ctx, udi, "volume.mount.valid_options", &error);
715 	if (dbus_error_is_set (&error)) {
716 		unknown_error ("Cannot get volume.mount.valid_options");
717 		dbus_error_free (&error);
718 	}
719 
720 #ifdef DEBUG
721 	for (i = 0; given_options[i] != NULL; i++)
722 		printf ("given_options[%d] = '%s'\n", i, given_options[i]);
723 	for (i = 0; allowed_options[i] != NULL; i++)
724 		printf ("allowed_options[%d] = '%s'\n", i, allowed_options[i]);
725 #endif
726 
727 	wants_to_change_uid = FALSE;
728 
729 	/* check mount options */
730 	for (i = 0; given_options[i] != NULL; i++) {
731 		char *given = given_options[i];
732 
733 		for (j = 0; allowed_options[j] != NULL; j++) {
734 			char *allow = allowed_options[j];
735 			int allow_len = strlen (allow);
736 
737 			if (strcmp (given, allow) == 0) {
738 				goto option_ok;
739 			}
740 
741 			if ((allow[allow_len - 1] == '=') &&
742 			    (strncmp (given, allow, allow_len) == 0) &&
743 			    (int) strlen (given) > allow_len) {
744 
745 				/* option matched allowed ending in '=', e.g.
746 				 * given == "umask=foobar" and allowed == "umask="
747 				 */
748 				if (strcmp (allow, "uid=") == 0) {
749 					uid_t uid;
750 					char *endp;
751 					/* check for uid=, it requires special handling */
752 					uid = (uid_t) strtol (given + allow_len, &endp, 10);
753 					if (*endp != '\0') {
754 						printf ("'%s' is not a number?\n", given);
755 						unknown_error ("option uid is malformed");
756 					}
757 #ifdef DEBUG
758 					printf ("%s with uid %d\n", allow, uid);
759 #endif
760 					wants_to_change_uid = TRUE;
761 
762 					goto option_ok;
763 				} else {
764 
765 					goto option_ok;
766 				}
767 			}
768 		}
769 
770 		/* apparently option was not ok */
771 		invalid_mount_option (given, invoked_by_uid);
772 
773 	option_ok:
774 		;
775 	}
776 
777 	/* Check privilege */
778 	pol_is_fixed = TRUE;
779 	if (libhal_drive_is_hotpluggable (drive) || libhal_drive_uses_removable_media (drive))
780 		pol_is_fixed = FALSE;
781 
782 	pol_change_uid = FALSE;
783 	/* don't consider uid= on non-pollable drives for the purpose of policy
784 	 * (since these drives normally use vfat)
785 	 */
786 	if (volume != NULL) {
787 		/* don't consider uid= on vfat, iso9660, udf change-uid for the purpose of policy
788 		 * (since these doesn't contain uid/gid bits)
789 		 */
790 		if (strcmp (libhal_volume_get_fstype (volume), "vfat") != 0 &&
791 		    strcmp (libhal_volume_get_fstype (volume), "iso9660") != 0 &&
792 		    strcmp (libhal_volume_get_fstype (volume), "udf") != 0) {
793 			pol_change_uid = wants_to_change_uid;
794 		}
795 	}
796 
797 	if (pol_is_fixed) {
798 		if (pol_change_uid) {
799 			privilege = "hal-storage-fixed-mount-all-options";
800 		} else {
801 			privilege = "hal-storage-fixed-mount";
802 		}
803 	} else {
804 		if (pol_change_uid) {
805 			privilege = "hal-storage-removable-mount-all-options";
806 		} else {
807 			privilege = "hal-storage-removable-mount";
808 		}
809 	}
810 
811 #ifdef DEBUG
812 	printf ("using privilege %s for uid %s, system_bus_connection %s\n", privilege, invoked_by_uid,
813 		invoked_by_syscon_name);
814 #endif
815 
816 #ifdef HAVE_POLKIT
817 	if (libpolkit_is_uid_allowed_for_privilege (pol_ctx,
818 						    invoked_by_syscon_name,
819 						    invoked_by_uid,
820 						    privilege,
821 						    udi,
822 						    &allowed_by_privilege,
823 						    &is_temporary_privilege,
824 						    NULL) != LIBPOLKIT_RESULT_OK) {
825 		printf ("cannot lookup privilege\n");
826 		unknown_error ("Cannot lookup privilege from PolicyKit");
827 	}
828 
829 	if (!allowed_by_privilege) {
830 		printf ("caller don't possess privilege\n");
831 		permission_denied_privilege (privilege, invoked_by_uid);
832 	}
833 #endif
834 
835 #ifdef DEBUG
836 	printf ("passed privilege\n");
837 #endif
838 
839 	if (!is_remount) {
840 		/* create directory */
841 #ifdef sun
842 		if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS) &&
843 		    (g_mkdir (mount_dir, 0755) != 0)) {
844 #else
845 		if (g_mkdir (mount_dir, 0700) != 0) {
846 #endif
847 			printf ("Cannot create '%s'\n", mount_dir);
848 			unknown_error ("Cannot create mount directory");
849 		}
850 
851 #ifdef __FreeBSD__
852 		calling_uid = (uid_t) strtol (invoked_by_uid, (char **) NULL, 10);
853 		pw = getpwuid (calling_uid);
854 		if (pw != NULL) {
855 			calling_gid = pw->pw_gid;
856 		} else {
857 			calling_gid = 0;
858 		}
859 		if (chown (mount_dir, calling_uid, calling_gid) != 0) {
860 			printf ("Cannot chown '%s' to uid: %d, gid: %d\n", mount_dir,
861 				calling_uid, calling_gid);
862 			g_rmdir (mount_dir);
863 			unknown_error ();
864 		}
865 #endif
866 	}
867 
868 	char *mount_option_commasep = NULL;
869 	char *mount_do_fstype = "auto";
870 
871 	/* construct arguments to mount */
872 	na = 0;
873 	args[na++] = MOUNT;
874 	if (strlen (mount_fstype) > 0) {
875 		mount_do_fstype = (char *) map_fstype (mount_fstype);
876 	} else if (volume == NULL) {
877 		/* non-pollable drive; force auto */
878 		mount_do_fstype = "auto";
879 	} else if (libhal_volume_get_fstype (volume) != NULL && strlen (libhal_volume_get_fstype (volume)) > 0) {
880 		mount_do_fstype = (char *) map_fstype (libhal_volume_get_fstype (volume));
881 	}
882 	args[na++] = MOUNT_TYPE_OPT;
883 	args[na++] = mount_do_fstype;
884 
885 	args[na++] = "-o";
886 	mount_option_str = g_string_new (MOUNT_OPTIONS);
887 	for (i = 0; given_options[i] != NULL; i++) {
888 		g_string_append (mount_option_str, ",");
889 		g_string_append (mount_option_str, given_options[i]);
890 	}
891 #ifdef sun
892 	if (append_ro) {
893 		g_string_append (mount_option_str, ",ro");
894 	}
895 #endif
896 	mount_option_commasep = g_string_free (mount_option_str, FALSE); /* leak! */
897 	args[na++] = mount_option_commasep;
898 	args[na++] = (char *) device;
899 	args[na++] = mount_dir;
900 	args[na++] = NULL;
901 
902 	/* TODO FIXME XXX HACK: OK, so we should rewrite the options in /media/.hal-mtab ..
903 	 *                      but it doesn't really matter much at this point */
904 	if (!is_remount) {
905 		FILE *hal_mtab;
906 		char *mount_dir_escaped;
907 		FILE *hal_mtab_orig;
908 		int hal_mtab_orig_len;
909 		int num_read;
910 		char *hal_mtab_buf;
911 		char *hal_mtab_buf_old;
912 
913 		/* Maintain a list in /media/.hal-mtab with entries of the following format
914 		 *
915 		 *  <device_file>\t<uid>\t<session-id>\t<fstype>\t<options_sep_by_comma>\t<mount point>\n
916 		 *
917 		 * where session-id currently is unused and thus set to 0.
918 		 *
919 		 * Example:
920 		 *
921 		 *  /dev/sda2	500	0	hfsplus	noexec,nosuid,nodev	/media/Macintosh HD
922 		 *  /dev/sda4	500	0	ntfs	noexec,nosuid,nodev,umask=222	/media/Windows
923 		 *  /dev/sdb1	500	0	vfat	noexec,nosuid,nodev,shortname=winnt,uid=500	/media/davidz
924 		 */
925 
926 
927 		if (g_file_test ("/media/.hal-mtab", G_FILE_TEST_EXISTS)) {
928 			hal_mtab_orig = fopen ("/media/.hal-mtab", "r");
929 			if (hal_mtab_orig == NULL) {
930 				unknown_error ("Cannot open /media/.hal-mtab");
931 			}
932 			if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) {
933 				unknown_error ("Cannot seek to end of /media/.hal-mtab");
934 			}
935 			hal_mtab_orig_len = ftell (hal_mtab_orig);
936 			if (hal_mtab_orig_len < 0) {
937 				unknown_error ("Cannot determine size of /media/.hal-mtab");
938 			}
939 			rewind (hal_mtab_orig);
940 			hal_mtab_buf = g_new0 (char, hal_mtab_orig_len + 1);
941 			num_read = fread (hal_mtab_buf, 1, hal_mtab_orig_len, hal_mtab_orig);
942 			if (num_read != hal_mtab_orig_len) {
943 				unknown_error ("Cannot read from /media/.hal-mtab");
944 			}
945 			fclose (hal_mtab_orig);
946 		} else {
947 			hal_mtab_buf = g_strdup ("");
948 		}
949 
950 		mount_dir_escaped = g_strescape (mount_dir, NULL);
951 #ifdef DEBUG
952 		printf ("%d: XYA creating /media/.hal-mtab~\n", getpid ());
953 #endif
954 		hal_mtab = fopen ("/media/.hal-mtab~", "w");
955 		if (hal_mtab == NULL) {
956 			unknown_error ("Cannot create /media/.hal-mtab~");
957 		}
958 		hal_mtab_buf_old = hal_mtab_buf;
959 		hal_mtab_buf = g_strdup_printf ("%s%s\t%s\t0\t%s\t%s\t%s\n",
960 						hal_mtab_buf_old,
961 						device, invoked_by_uid, mount_do_fstype,
962 						mount_option_commasep, mount_dir_escaped);
963 		g_free (hal_mtab_buf_old);
964 		if (hal_mtab_buf_old == NULL) {
965 			unknown_error ("Out of memory appending to /media/.hal-mtab~");
966 		}
967 		if (fwrite (hal_mtab_buf, 1, strlen (hal_mtab_buf), hal_mtab) != strlen (hal_mtab_buf)) {
968 			unknown_error ("Cannot write to /media/.hal-mtab~");
969 		}
970 		fclose (hal_mtab);
971 		g_free (hal_mtab_buf);
972 		g_free (mount_dir_escaped);
973 #ifdef DEBUG
974 		printf ("%d: XYA closing /media/.hal-mtab~\n", getpid ());
975 #endif
976 	} /* !is_remount */
977 
978 	/* now try to mount */
979 	if (!g_spawn_sync ("/",
980 			   args,
981 			   NULL,
982 			   0,
983 			   NULL,
984 			   NULL,
985 			   &sout,
986 			   &serr,
987 			   &exit_status,
988 			   &err)) {
989 		printf ("Cannot execute %s\n", MOUNT);
990 		g_rmdir (mount_dir);
991 		unlink ("/media/.hal-mtab~");
992 		unknown_error ("Cannot spawn " MOUNT);
993 	}
994 
995 
996 	if (exit_status != 0) {
997 		char errstr[]  = "mount: unknown filesystem type";
998 
999 		printf ("%s error %d, stdout='%s', stderr='%s'\n", MOUNT, exit_status, sout, serr);
1000 
1001 		if (!is_remount) {
1002 			g_rmdir (mount_dir);
1003 			unlink ("/media/.hal-mtab~");
1004 		}
1005 
1006 		if (strncmp (errstr, serr, sizeof (errstr) - 1) == 0) {
1007 			unknown_filesystem (strlen (mount_fstype) > 0 ?
1008 					    mount_fstype :
1009 					    (volume != NULL ? libhal_volume_get_fstype (volume) : "") );
1010 		} else {
1011 			int n;
1012 			for (n = 0; serr[n] != '\0'; n++) {
1013 				if (serr[n] == '\n') {
1014 					serr[n] = ' ';
1015 				}
1016 			}
1017 			unknown_error (serr);
1018 		}
1019 	}
1020 
1021 	if (!is_remount) {
1022 		if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) {
1023 			printf ("rename(2) failed, errno=%d -> '%s'\n", errno, strerror (errno));
1024 			unlink ("/media/.hal-mtab~");
1025 #ifdef DEBUG
1026 			printf ("%d: XYA failed renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ());
1027 #endif
1028 			unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab");
1029 		}
1030 #ifdef DEBUG
1031 		printf ("%d: XYA done renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ());
1032 #endif
1033 	}
1034 
1035 	openlog ("hald", 0, LOG_DAEMON);
1036 	if (is_remount) {
1037 		syslog (LOG_INFO, "remounted %s at '%s' on behalf of uid %s", device, mount_dir, invoked_by_uid);
1038 	} else {
1039 		syslog (LOG_INFO, "mounted %s on behalf of uid %s", device, invoked_by_uid);
1040 	}
1041 	closelog ();
1042 
1043 #ifdef sun
1044 	if ((adt_data = get_audit_export_data (system_bus,
1045 	    invoked_by_syscon_name, &adt_data_size)) != NULL) {
1046 		audit_volume (adt_data, ADT_attach,
1047 		    WEXITSTATUS(exit_status), auth_from_privilege(privilege),
1048 		    mount_dir, device, mount_option_commasep);
1049 		free (adt_data);
1050 	}
1051 #endif
1052 
1053 	g_free (sout);
1054 	g_free (serr);
1055 	g_free (mount_dir);
1056 	libhal_free_string_array (allowed_options);
1057 	g_strfreev (given_options);
1058 }
1059 
1060 
1061 int
1062 main (int argc, char *argv[])
1063 {
1064 	char *udi;
1065 	char *device;
1066 	LibHalVolume *volume;
1067 	DBusError error;
1068 	LibHalContext *hal_ctx = NULL;
1069 	DBusConnection *system_bus = NULL;
1070 #ifdef HAVE_POLKIT
1071 	LibPolKitContext *pol_ctx = NULL;
1072 #endif
1073 	char *invoked_by_uid;
1074 	char *invoked_by_syscon_name;
1075 
1076 	if (!lock_hal_mtab ()) {
1077 		unknown_error ("Cannot obtain lock on /media/.hal-mtab");
1078 	}
1079 
1080 	device = getenv ("HAL_PROP_BLOCK_DEVICE");
1081 	if (device == NULL)
1082 		usage ();
1083 
1084 	udi = getenv ("HAL_PROP_INFO_UDI");
1085 	if (udi == NULL)
1086 		usage ();
1087 
1088 	invoked_by_uid = getenv ("HAL_METHOD_INVOKED_BY_UID");
1089 
1090 	invoked_by_syscon_name = getenv ("HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME");
1091 
1092 	dbus_error_init (&error);
1093 	if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) {
1094 		printf ("Cannot connect to hald\n");
1095 		LIBHAL_FREE_DBUS_ERROR (&error);
1096 		usage ();
1097 	}
1098 
1099 	dbus_error_init (&error);
1100 	system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
1101 	if (system_bus == NULL) {
1102 		printf ("Cannot connect to the system bus\n");
1103 		LIBHAL_FREE_DBUS_ERROR (&error);
1104 		usage ();
1105 	}
1106 #ifdef HAVE_POLKIT
1107 	pol_ctx = libpolkit_new_context (system_bus);
1108 	if (pol_ctx == NULL) {
1109 		printf ("Cannot get libpolkit context\n");
1110 		unknown_error ("Cannot get libpolkit context");
1111 	}
1112 #endif
1113 
1114 	volume = libhal_volume_from_udi (hal_ctx, udi);
1115 	if (volume == NULL) {
1116 		LibHalDrive *drive;
1117 
1118 		drive = libhal_drive_from_udi (hal_ctx, udi);
1119 		if (drive == NULL) {
1120 			usage ();
1121 		} else {
1122 			handle_mount (hal_ctx,
1123 #ifdef HAVE_POLKIT
1124 				      pol_ctx,
1125 #endif
1126 				      udi, NULL, drive, device, invoked_by_uid,
1127 				      invoked_by_syscon_name, system_bus);
1128 		}
1129 
1130 	} else {
1131 		const char *drive_udi;
1132 		LibHalDrive *drive;
1133 
1134 		drive_udi = libhal_volume_get_storage_device_udi (volume);
1135 
1136 		if (drive_udi == NULL)
1137 			unknown_error ("Cannot get drive_udi from volume");
1138 		drive = libhal_drive_from_udi (hal_ctx, drive_udi);
1139 		if (drive == NULL)
1140 			unknown_error ("Cannot get drive from hal");
1141 
1142 		handle_mount (hal_ctx,
1143 #ifdef HAVE_POLKIT
1144 			      pol_ctx,
1145 #endif
1146 			      udi, volume, drive, device, invoked_by_uid,
1147 			      invoked_by_syscon_name, system_bus);
1148 
1149 	}
1150 
1151 	unlock_hal_mtab ();
1152 
1153 	return 0;
1154 }
1155