/*************************************************************************** * CVSID: $Id$ * * hal-storage-mount.c : Mount wrapper * * Copyright (C) 2006 David Zeuthen, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * **************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #ifdef __FreeBSD__ #include #include #include #include #include #include #elif sun #include #include #include #else #include #endif #include #include #include #include #include #include #ifdef HAVE_POLKIT #include #endif #include "hal-storage-shared.h" #ifdef __FreeBSD__ #define MOUNT "/sbin/mount" #define MOUNT_OPTIONS "noexec,nosuid" #define MOUNT_TYPE_OPT "-t" #elif sun #define MOUNT "/sbin/mount" #define MOUNT_OPTIONS "nosuid" #define MOUNT_TYPE_OPT "-F" #else #define MOUNT "/bin/mount" #define MOUNT_OPTIONS "noexec,nosuid,nodev" #define MOUNT_TYPE_OPT "-t" #endif static void usage (void) { fprintf (stderr, "This program should only be started by hald.\n"); exit (1); } static void permission_denied_volume_ignore (const char *device) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n"); fprintf (stderr, "Device has %s volume.ignore set to TRUE. Refusing to mount.\n", device); exit (1); } static void permission_denied_etc_fstab (const char *device) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n"); fprintf (stderr, "Device %s is listed in /etc/fstab. Refusing to mount.\n", device); exit (1); } static void already_mounted (const char *device) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.AlreadyMounted\n"); fprintf (stderr, "Device %s is already mounted.\n", device); exit (1); } static void invalid_mount_option (const char *option, const char *uid) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountOption\n"); fprintf (stderr, "The option '%s' is not allowed for uid=%s\n", option, uid); exit (1); } static void unknown_filesystem (const char *filesystem) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType\n"); fprintf (stderr, "Unknown file system '%s'\n", filesystem); exit (1); } static void invalid_mount_point (const char *mount_point) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint\n"); fprintf (stderr, "The mount point '%s' is invalid\n", mount_point); exit (1); } static void mount_point_not_available (const char *mount_point) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.MountPointNotAvailable\n"); fprintf (stderr, "The mount point '%s' is already occupied\n", mount_point); exit (1); } static void cannot_remount (const char *device) { fprintf (stderr, "org.freedesktop.Hal.Device.Volume.CannotRemount\n"); fprintf (stderr, "%s not mounted already\n", device); exit (1); } #ifdef HAVE_POLKIT static void permission_denied_privilege (const char *privilege, const char *uid) { fprintf (stderr, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n"); fprintf (stderr, "%s refused uid %s\n", privilege, uid); exit (1); } #endif /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */ static void canonicalize_filename (gchar *filename) { gchar *p, *q; gboolean last_was_slash = FALSE; p = filename; q = filename; while (*p) { if (*p == G_DIR_SEPARATOR) { if (!last_was_slash) *q++ = G_DIR_SEPARATOR; last_was_slash = TRUE; } else { if (last_was_slash && *p == '.') { if (*(p + 1) == G_DIR_SEPARATOR || *(p + 1) == '\0') { if (*(p + 1) == '\0') break; p += 1; } else if (*(p + 1) == '.' && (*(p + 2) == G_DIR_SEPARATOR || *(p + 2) == '\0')) { if (q > filename + 1) { q--; while (q > filename + 1 && *(q - 1) != G_DIR_SEPARATOR) q--; } if (*(p + 2) == '\0') break; p += 2; } else { *q++ = *p; last_was_slash = FALSE; } } else { *q++ = *p; last_was_slash = FALSE; } } p++; } if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR) q--; *q = '\0'; } static char * resolve_symlink (const char *file) { GError *error = NULL; char *dir; char *link; char *f; char *f1; f = g_strdup (file); while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) { link = g_file_read_link (f, &error); if (link == NULL) { g_warning ("Cannot resolve symlink %s: %s", f, error->message); g_error_free (error); g_free (f); f = NULL; goto out; } dir = g_path_get_dirname (f); f1 = g_strdup_printf ("%s/%s", dir, link); g_free (dir); g_free (link); g_free (f); f = f1; } out: if (f != NULL) canonicalize_filename (f); return f; } static LibHalVolume * volume_findby (LibHalContext *hal_ctx, const char *property, const char *value) { int i; char **hal_udis; int num_hal_udis; LibHalVolume *result = NULL; char *found_udi = NULL; DBusError error; dbus_error_init (&error); if ((hal_udis = libhal_manager_find_device_string_match (hal_ctx, property, value, &num_hal_udis, &error)) == NULL) { LIBHAL_FREE_DBUS_ERROR (&error); goto out; } for (i = 0; i < num_hal_udis; i++) { char *udi; udi = hal_udis[i]; if (libhal_device_query_capability (hal_ctx, udi, "volume", &error)) { found_udi = strdup (udi); break; } } libhal_free_string_array (hal_udis); if (found_udi != NULL) result = libhal_volume_from_udi (hal_ctx, found_udi); free (found_udi); out: return result; } static void bailout_if_in_fstab (LibHalContext *hal_ctx, const char *device, const char *label, const char *uuid) { gpointer handle; char *entry; char *_mount_point; printf (" label '%s' uuid '%s'\n", label ? label : "" , uuid ? uuid : ""); /* check if /etc/fstab mentions this device... (with symlinks etc) */ if (! fstab_open (&handle)) { printf ("cannot open /etc/fstab\n"); unknown_error ("Cannot open /etc/fstab"); } while ((entry = fstab_next (handle, &_mount_point)) != NULL) { char *resolved; #ifdef DEBUG printf ("Looking at /etc/fstab entry '%s'\n", entry); #endif if (label != NULL && g_str_has_prefix (entry, "LABEL=")) { if (strcmp (entry + 6, label) == 0) { gboolean skip_fstab_entry; skip_fstab_entry = FALSE; /* (heck, we also do the stuff below in gnome-mount) */ /* OK, so what's if someone attaches an external disk with the label '/' and * /etc/fstab has * * LABEL=/ / ext3 defaults 1 1 * * in /etc/fstab as most Red Hat systems do? Bugger, this is a very common use * case; suppose that you take the disk from your Fedora server and attaches it * to your laptop. Bingo, you now have two disks with the label '/'. One must * seriously wonder if using things like LABEL=/ for / is a good idea; just * what happens if you boot in this configuration? (answer: the initrd gets * it wrong most of the time.. sigh) * * To work around this, check if the listed entry in /etc/fstab is already mounted, * if it is, then check if it's the same device_file as the given one... */ /* see if a volume is mounted at this mount point */ if (_mount_point != NULL) { LibHalVolume *mounted_vol; mounted_vol = volume_findby (hal_ctx, "volume.mount_point", _mount_point); if (mounted_vol != NULL) { const char *mounted_vol_device_file; mounted_vol_device_file = libhal_volume_get_device_file (mounted_vol); /* no need to resolve symlinks, hal uses the canonical device file */ if (mounted_vol_device_file != NULL && strcmp (mounted_vol_device_file, device) !=0) { #ifdef DEBUG 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", device, label, label, _mount_point, mounted_vol_device_file, _mount_point); #endif skip_fstab_entry = TRUE; } libhal_volume_free (mounted_vol); } } if (!skip_fstab_entry) { printf ("%s found in /etc/fstab. Not mounting.\n", entry); permission_denied_etc_fstab (device); } } } else if (uuid != NULL && g_str_has_prefix (entry, "UUID=")) { if (strcmp (entry + 5, uuid) == 0) { printf ("%s found in /etc/fstab. Not mounting.\n", entry); permission_denied_etc_fstab (device); } } else { resolved = resolve_symlink (entry); #ifdef DEBUG printf ("/etc/fstab: device %s -> %s \n", entry, resolved); #endif if (strcmp (device, resolved) == 0) { printf ("%s (-> %s) found in /etc/fstab. Not mounting.\n", entry, resolved); permission_denied_etc_fstab (device); } g_free (resolved); } } fstab_close (handle); } static gboolean device_is_mounted (const char *device, char **mount_point) { gpointer handle; char *entry; gboolean ret; ret = FALSE; /* check if /proc/mounts mentions this device... (with symlinks etc) */ if (! mtab_open (&handle)) { printf ("cannot open mount list\n"); unknown_error ("Cannot open /etc/mtab or equivalent"); } while (((entry = mtab_next (handle, mount_point)) != NULL) && (ret == FALSE)) { char *resolved; resolved = resolve_symlink (entry); #ifdef DEBUG printf ("/proc/mounts: device %s -> %s \n", entry, resolved); #endif if (strcmp (device, resolved) == 0) { printf ("%s (-> %s) found in mount list. Not mounting.\n", entry, resolved); ret = TRUE; } g_free (resolved); } mtab_close (handle); return ret; } /* maps volume_id fs types to the appropriate -t mount option */ static const char * map_fstype (const char *fstype) { #ifdef __FreeBSD__ if (! strcmp (fstype, "iso9660")) return "cd9660"; else if (! strcmp (fstype, "ext2")) return "ext2fs"; else if (! strcmp (fstype, "vfat")) return "msdosfs"; #elif sun if (! strcmp (fstype, "iso9660")) return "hsfs"; else if (! strcmp (fstype, "vfat")) return "pcfs"; #endif return fstype; } static void handle_mount (LibHalContext *hal_ctx, #ifdef HAVE_POLKIT LibPolKitContext *pol_ctx, #endif const char *udi, LibHalVolume *volume, LibHalDrive *drive, const char *device, const char *invoked_by_uid, const char *invoked_by_syscon_name, DBusConnection *system_bus) { int i, j; DBusError error; char mount_point[256]; char mount_fstype[256]; char mount_options[1024]; char **allowed_options; char **given_options; gboolean wants_to_change_uid; char *mount_dir; GError *err = NULL; char *sout = NULL; char *serr = NULL; int exit_status; char *args[10]; int na; GString *mount_option_str; gboolean pol_is_fixed; gboolean pol_change_uid; char *privilege; gboolean is_remount; #ifdef HAVE_POLKIT gboolean allowed_by_privilege; gboolean is_temporary_privilege; #endif gboolean explicit_mount_point_given; const char *end; #ifdef __FreeBSD__ struct passwd *pw; uid_t calling_uid; gid_t calling_gid; #endif const char *label; const char *uuid; const char *model; const char *drive_type; #ifdef sun adt_export_data_t *adt_data; size_t adt_data_size; gboolean append_ro = FALSE; gboolean is_abs_path = FALSE; uid_t calling_uid; calling_uid = atol (invoked_by_uid); #endif #ifdef DEBUG printf ("device = %s\n", device); printf ("invoked by uid = %s\n", invoked_by_uid); printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name); #endif if (volume != NULL) { dbus_error_init (&error); if (libhal_device_get_property_bool (hal_ctx, udi, "volume.ignore", &error) || dbus_error_is_set (&error)) { if (dbus_error_is_set (&error)) { LIBHAL_FREE_DBUS_ERROR (&error); } /* * When device allocation is enabled (bsmconv or TX), we * set volume.ignore on all volumes, but still want * Mount() to succeed when called from the euid=0 * device allocation program. */ if (atol (invoked_by_uid) != 0) { permission_denied_volume_ignore (device); } } label = libhal_volume_get_label (volume); uuid = libhal_volume_get_uuid (volume); } else { label = NULL; uuid = NULL; } bailout_if_in_fstab (hal_ctx, device, label, uuid); /* TODO: sanity check that what hal exports is correct (cf. Martin Pitt's email) */ /* read from stdin */ if (strlen (fgets (mount_point, sizeof (mount_point), stdin)) > 0) mount_point [strlen (mount_point) - 1] = '\0'; if (strlen (fgets (mount_fstype, sizeof (mount_fstype), stdin)) > 0) mount_fstype [strlen (mount_fstype) - 1] = '\0'; if (strlen (fgets (mount_options, sizeof (mount_options), stdin)) > 0) mount_options [strlen (mount_options) - 1] = '\0'; /* validate that input from stdin is UTF-8 */ if (!g_utf8_validate (mount_point, -1, &end)) unknown_error ("Error validating mount_point as UTF-8"); if (!g_utf8_validate (mount_fstype, -1, &end)) unknown_error ("Error validating mount_fstype as UTF-8"); if (!g_utf8_validate (mount_options, -1, &end)) unknown_error ("Error validating mount_options as UTF-8"); #ifdef sun if (calling_uid != 0) { #endif for (i = 0; mount_point[i] != '\0'; i++) { if (mount_point[i] == '\n' || mount_point[i] == G_DIR_SEPARATOR) { unknown_error ("mount_point cannot contain the following characters: newline, G_DIR_SEPARATOR (usually /)"); } } #ifdef sun } is_abs_path = (mount_point[0] == G_DIR_SEPARATOR); #endif #ifdef DEBUG printf ("mount_point = '%s'\n", mount_point); printf ("mount_fstype = '%s'\n", mount_fstype); printf ("mount_options = '%s'\n", mount_options); #endif /* delete any trailing whitespace options from splitting the string */ given_options = g_strsplit (mount_options, "\t", 0); for (i = g_strv_length (given_options) - 1; i >= 0; --i) { if (strlen (given_options[i]) > 0) break; given_options[i] = NULL; } #ifdef sun /* for read-only media append 'ro' option if not already */ append_ro = libhal_device_get_property_bool (hal_ctx, libhal_drive_get_udi(drive), "storage.removable.solaris.read_only", NULL); if (append_ro) { for (i = 0; i < (int) g_strv_length (given_options); i++) { if (strcmp (given_options[i], "ro") == 0) { append_ro = FALSE; } } } #endif /* sun */ /* is option 'remount' included? */ is_remount = FALSE; for (i = 0; i < (int) g_strv_length (given_options); i++) { if (strcmp (given_options[i], "remount") == 0) { is_remount = TRUE; } } mount_dir = NULL; if (is_remount) { if (volume != NULL) { if (!libhal_volume_is_mounted (volume)) { cannot_remount (device); } mount_dir = g_strdup (libhal_volume_get_mount_point (volume)); } else { if (!device_is_mounted (device, &mount_dir)) { cannot_remount (device); } } if (mount_dir == NULL) { unknown_error ("Cannot get mount_dir for remount even though volume is mounted!"); } } else { if (volume != NULL) { if (libhal_volume_is_mounted (volume)) { already_mounted (device); } } else { if (device_is_mounted (device, NULL)) { already_mounted (device); } } } if (!is_remount) { /* figure out mount point if no mount point is given... */ explicit_mount_point_given = FALSE; if (strlen (mount_point) == 0) { char *p; const char *label; if (volume != NULL) label = libhal_volume_get_label (volume); else label = NULL; model = libhal_drive_get_model (drive); drive_type = libhal_drive_get_type_textual (drive); if (label != NULL) { /* best - use label */ g_strlcpy (mount_point, label, sizeof (mount_point)); } else if ((model != NULL) && (strlen (model) > 0)) { g_strlcpy (mount_point, model, sizeof (mount_point)); } else if ((drive_type != NULL) && (strlen (drive_type) > 0)) { g_strlcpy (mount_point, drive_type, sizeof (mount_point)); } else { /* fallback - use "disk" */ g_snprintf (mount_point, sizeof (mount_point), "disk"); } /* sanitize computed mount point name, e.g. replace invalid chars with '-' */ p = mount_point; while (TRUE) { p = g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR); if (p == NULL) break; *p = '-'; }; } else { explicit_mount_point_given = TRUE; } /* check mount point name - only forbid separators */ #ifdef sun if (calling_uid != 0) { #endif if (g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR) != NULL) { printf ("'%s' is an invalid mount point\n", mount_point); invalid_mount_point (mount_point); } #ifdef sun } #endif /* check if mount point is available - append number to mount point */ i = 0; mount_dir = NULL; while (TRUE) { g_free (mount_dir); #ifdef sun if (is_abs_path) mount_dir = g_strdup (mount_point); else #endif if (i == 0) mount_dir = g_strdup_printf ("/media/%s", mount_point); else mount_dir = g_strdup_printf ("/media/%s-%d", mount_point, i); #ifdef DEBUG printf ("trying dir %s\n", mount_dir); #endif /* XXX should test for being a mount point */ if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS)) { break; } if (explicit_mount_point_given) { mount_point_not_available (mount_dir); } i++; } } dbus_error_init (&error); allowed_options = libhal_device_get_property_strlist (hal_ctx, udi, "volume.mount.valid_options", &error); if (dbus_error_is_set (&error)) { unknown_error ("Cannot get volume.mount.valid_options"); dbus_error_free (&error); } #ifdef DEBUG for (i = 0; given_options[i] != NULL; i++) printf ("given_options[%d] = '%s'\n", i, given_options[i]); for (i = 0; allowed_options[i] != NULL; i++) printf ("allowed_options[%d] = '%s'\n", i, allowed_options[i]); #endif wants_to_change_uid = FALSE; /* check mount options */ for (i = 0; given_options[i] != NULL; i++) { char *given = given_options[i]; for (j = 0; allowed_options[j] != NULL; j++) { char *allow = allowed_options[j]; int allow_len = strlen (allow); if (strcmp (given, allow) == 0) { goto option_ok; } if ((allow[allow_len - 1] == '=') && (strncmp (given, allow, allow_len) == 0) && (int) strlen (given) > allow_len) { /* option matched allowed ending in '=', e.g. * given == "umask=foobar" and allowed == "umask=" */ if (strcmp (allow, "uid=") == 0) { uid_t uid; char *endp; /* check for uid=, it requires special handling */ uid = (uid_t) strtol (given + allow_len, &endp, 10); if (*endp != '\0') { printf ("'%s' is not a number?\n", given); unknown_error ("option uid is malformed"); } #ifdef DEBUG printf ("%s with uid %d\n", allow, uid); #endif wants_to_change_uid = TRUE; goto option_ok; } else { goto option_ok; } } } /* apparently option was not ok */ invalid_mount_option (given, invoked_by_uid); option_ok: ; } /* Check privilege */ pol_is_fixed = TRUE; if (libhal_drive_is_hotpluggable (drive) || libhal_drive_uses_removable_media (drive)) pol_is_fixed = FALSE; pol_change_uid = FALSE; /* don't consider uid= on non-pollable drives for the purpose of policy * (since these drives normally use vfat) */ if (volume != NULL) { /* don't consider uid= on vfat, iso9660, udf change-uid for the purpose of policy * (since these doesn't contain uid/gid bits) */ if (strcmp (libhal_volume_get_fstype (volume), "vfat") != 0 && strcmp (libhal_volume_get_fstype (volume), "iso9660") != 0 && strcmp (libhal_volume_get_fstype (volume), "udf") != 0) { pol_change_uid = wants_to_change_uid; } } if (pol_is_fixed) { if (pol_change_uid) { privilege = "hal-storage-fixed-mount-all-options"; } else { privilege = "hal-storage-fixed-mount"; } } else { if (pol_change_uid) { privilege = "hal-storage-removable-mount-all-options"; } else { privilege = "hal-storage-removable-mount"; } } #ifdef DEBUG printf ("using privilege %s for uid %s, system_bus_connection %s\n", privilege, invoked_by_uid, invoked_by_syscon_name); #endif #ifdef HAVE_POLKIT if (libpolkit_is_uid_allowed_for_privilege (pol_ctx, invoked_by_syscon_name, invoked_by_uid, privilege, udi, &allowed_by_privilege, &is_temporary_privilege, NULL) != LIBPOLKIT_RESULT_OK) { printf ("cannot lookup privilege\n"); unknown_error ("Cannot lookup privilege from PolicyKit"); } if (!allowed_by_privilege) { printf ("caller don't possess privilege\n"); permission_denied_privilege (privilege, invoked_by_uid); } #endif #ifdef DEBUG printf ("passed privilege\n"); #endif if (!is_remount) { /* create directory */ #ifdef sun if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS) && (g_mkdir (mount_dir, 0755) != 0)) { #else if (g_mkdir (mount_dir, 0700) != 0) { #endif printf ("Cannot create '%s'\n", mount_dir); unknown_error ("Cannot create mount directory"); } #ifdef __FreeBSD__ calling_uid = (uid_t) strtol (invoked_by_uid, (char **) NULL, 10); pw = getpwuid (calling_uid); if (pw != NULL) { calling_gid = pw->pw_gid; } else { calling_gid = 0; } if (chown (mount_dir, calling_uid, calling_gid) != 0) { printf ("Cannot chown '%s' to uid: %d, gid: %d\n", mount_dir, calling_uid, calling_gid); g_rmdir (mount_dir); unknown_error (); } #endif } char *mount_option_commasep = NULL; char *mount_do_fstype = "auto"; /* construct arguments to mount */ na = 0; args[na++] = MOUNT; if (strlen (mount_fstype) > 0) { mount_do_fstype = (char *) map_fstype (mount_fstype); } else if (volume == NULL) { /* non-pollable drive; force auto */ mount_do_fstype = "auto"; } else if (libhal_volume_get_fstype (volume) != NULL && strlen (libhal_volume_get_fstype (volume)) > 0) { mount_do_fstype = (char *) map_fstype (libhal_volume_get_fstype (volume)); } args[na++] = MOUNT_TYPE_OPT; args[na++] = mount_do_fstype; args[na++] = "-o"; mount_option_str = g_string_new (MOUNT_OPTIONS); for (i = 0; given_options[i] != NULL; i++) { g_string_append (mount_option_str, ","); g_string_append (mount_option_str, given_options[i]); } #ifdef sun if (append_ro) { g_string_append (mount_option_str, ",ro"); } #endif mount_option_commasep = g_string_free (mount_option_str, FALSE); /* leak! */ args[na++] = mount_option_commasep; args[na++] = (char *) device; args[na++] = mount_dir; args[na++] = NULL; /* TODO FIXME XXX HACK: OK, so we should rewrite the options in /media/.hal-mtab .. * but it doesn't really matter much at this point */ if (!is_remount) { FILE *hal_mtab; char *mount_dir_escaped; FILE *hal_mtab_orig; int hal_mtab_orig_len; int num_read; char *hal_mtab_buf; char *hal_mtab_buf_old; /* Maintain a list in /media/.hal-mtab with entries of the following format * * \t\t\t\t\t\n * * where session-id currently is unused and thus set to 0. * * Example: * * /dev/sda2 500 0 hfsplus noexec,nosuid,nodev /media/Macintosh HD * /dev/sda4 500 0 ntfs noexec,nosuid,nodev,umask=222 /media/Windows * /dev/sdb1 500 0 vfat noexec,nosuid,nodev,shortname=winnt,uid=500 /media/davidz */ if (g_file_test ("/media/.hal-mtab", G_FILE_TEST_EXISTS)) { hal_mtab_orig = fopen ("/media/.hal-mtab", "r"); if (hal_mtab_orig == NULL) { unknown_error ("Cannot open /media/.hal-mtab"); } if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) { unknown_error ("Cannot seek to end of /media/.hal-mtab"); } hal_mtab_orig_len = ftell (hal_mtab_orig); if (hal_mtab_orig_len < 0) { unknown_error ("Cannot determine size of /media/.hal-mtab"); } rewind (hal_mtab_orig); hal_mtab_buf = g_new0 (char, hal_mtab_orig_len + 1); num_read = fread (hal_mtab_buf, 1, hal_mtab_orig_len, hal_mtab_orig); if (num_read != hal_mtab_orig_len) { unknown_error ("Cannot read from /media/.hal-mtab"); } fclose (hal_mtab_orig); } else { hal_mtab_buf = g_strdup (""); } mount_dir_escaped = g_strescape (mount_dir, NULL); #ifdef DEBUG printf ("%d: XYA creating /media/.hal-mtab~\n", getpid ()); #endif hal_mtab = fopen ("/media/.hal-mtab~", "w"); if (hal_mtab == NULL) { unknown_error ("Cannot create /media/.hal-mtab~"); } hal_mtab_buf_old = hal_mtab_buf; hal_mtab_buf = g_strdup_printf ("%s%s\t%s\t0\t%s\t%s\t%s\n", hal_mtab_buf_old, device, invoked_by_uid, mount_do_fstype, mount_option_commasep, mount_dir_escaped); g_free (hal_mtab_buf_old); if (hal_mtab_buf_old == NULL) { unknown_error ("Out of memory appending to /media/.hal-mtab~"); } if (fwrite (hal_mtab_buf, 1, strlen (hal_mtab_buf), hal_mtab) != strlen (hal_mtab_buf)) { unknown_error ("Cannot write to /media/.hal-mtab~"); } fclose (hal_mtab); g_free (hal_mtab_buf); g_free (mount_dir_escaped); #ifdef DEBUG printf ("%d: XYA closing /media/.hal-mtab~\n", getpid ()); #endif } /* !is_remount */ /* now try to mount */ if (!g_spawn_sync ("/", args, NULL, 0, NULL, NULL, &sout, &serr, &exit_status, &err)) { printf ("Cannot execute %s\n", MOUNT); g_rmdir (mount_dir); unlink ("/media/.hal-mtab~"); unknown_error ("Cannot spawn " MOUNT); } if (exit_status != 0) { char errstr[] = "mount: unknown filesystem type"; printf ("%s error %d, stdout='%s', stderr='%s'\n", MOUNT, exit_status, sout, serr); if (!is_remount) { g_rmdir (mount_dir); unlink ("/media/.hal-mtab~"); } if (strncmp (errstr, serr, sizeof (errstr) - 1) == 0) { unknown_filesystem (strlen (mount_fstype) > 0 ? mount_fstype : (volume != NULL ? libhal_volume_get_fstype (volume) : "") ); } else { int n; for (n = 0; serr[n] != '\0'; n++) { if (serr[n] == '\n') { serr[n] = ' '; } } unknown_error (serr); } } if (!is_remount) { if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) { printf ("rename(2) failed, errno=%d -> '%s'\n", errno, strerror (errno)); unlink ("/media/.hal-mtab~"); #ifdef DEBUG printf ("%d: XYA failed renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); #endif unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab"); } #ifdef DEBUG printf ("%d: XYA done renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); #endif } openlog ("hald", 0, LOG_DAEMON); if (is_remount) { syslog (LOG_INFO, "remounted %s at '%s' on behalf of uid %s", device, mount_dir, invoked_by_uid); } else { syslog (LOG_INFO, "mounted %s on behalf of uid %s", device, invoked_by_uid); } closelog (); #ifdef sun if ((adt_data = get_audit_export_data (system_bus, invoked_by_syscon_name, &adt_data_size)) != NULL) { audit_volume (adt_data, ADT_attach, WEXITSTATUS(exit_status), auth_from_privilege(privilege), mount_dir, device, mount_option_commasep); free (adt_data); } #endif g_free (sout); g_free (serr); g_free (mount_dir); libhal_free_string_array (allowed_options); g_strfreev (given_options); } int main (int argc, char *argv[]) { char *udi; char *device; LibHalVolume *volume; DBusError error; LibHalContext *hal_ctx = NULL; DBusConnection *system_bus = NULL; #ifdef HAVE_POLKIT LibPolKitContext *pol_ctx = NULL; #endif char *invoked_by_uid; char *invoked_by_syscon_name; if (!lock_hal_mtab ()) { unknown_error ("Cannot obtain lock on /media/.hal-mtab"); } device = getenv ("HAL_PROP_BLOCK_DEVICE"); if (device == NULL) usage (); udi = getenv ("HAL_PROP_INFO_UDI"); if (udi == NULL) usage (); invoked_by_uid = getenv ("HAL_METHOD_INVOKED_BY_UID"); invoked_by_syscon_name = getenv ("HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME"); dbus_error_init (&error); if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) { printf ("Cannot connect to hald\n"); LIBHAL_FREE_DBUS_ERROR (&error); usage (); } dbus_error_init (&error); system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error); if (system_bus == NULL) { printf ("Cannot connect to the system bus\n"); LIBHAL_FREE_DBUS_ERROR (&error); usage (); } #ifdef HAVE_POLKIT pol_ctx = libpolkit_new_context (system_bus); if (pol_ctx == NULL) { printf ("Cannot get libpolkit context\n"); unknown_error ("Cannot get libpolkit context"); } #endif volume = libhal_volume_from_udi (hal_ctx, udi); if (volume == NULL) { LibHalDrive *drive; drive = libhal_drive_from_udi (hal_ctx, udi); if (drive == NULL) { usage (); } else { handle_mount (hal_ctx, #ifdef HAVE_POLKIT pol_ctx, #endif udi, NULL, drive, device, invoked_by_uid, invoked_by_syscon_name, system_bus); } } else { const char *drive_udi; LibHalDrive *drive; drive_udi = libhal_volume_get_storage_device_udi (volume); if (drive_udi == NULL) unknown_error ("Cannot get drive_udi from volume"); drive = libhal_drive_from_udi (hal_ctx, drive_udi); if (drive == NULL) unknown_error ("Cannot get drive from hal"); handle_mount (hal_ctx, #ifdef HAVE_POLKIT pol_ctx, #endif udi, volume, drive, device, invoked_by_uid, invoked_by_syscon_name, system_bus); } unlock_hal_mtab (); return 0; }