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
usage(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
permission_denied_volume_ignore(const char * device)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
permission_denied_etc_fstab(const char * device)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
already_mounted(const char * device)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
invalid_mount_option(const char * option,const char * uid)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
unknown_filesystem(const char * filesystem)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
invalid_mount_point(const char * mount_point)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
mount_point_not_available(const char * mount_point)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
cannot_remount(const char * device)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
permission_denied_privilege(const char * privilege,const char * uid)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
canonicalize_filename(gchar * filename)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 *
resolve_symlink(const char * file)229 resolve_symlink (const char *file)
230 {
231 GError *error = NULL;
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 *
volume_findby(LibHalContext * hal_ctx,const char * property,const char * value)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
bailout_if_in_fstab(LibHalContext * hal_ctx,const char * device,const char * label,const char * uuid)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
device_is_mounted(const char * device,char ** mount_point)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 *
map_fstype(const char * fstype)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
handle_mount(LibHalContext * hal_ctx,LibPolKitContext * pol_ctx,const char * udi,LibHalVolume * volume,LibHalDrive * drive,const char * device,const char * invoked_by_uid,const char * invoked_by_syscon_name,DBusConnection * system_bus)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