/** * udisks_linux_block_object_trigger_uevent: * @object: A #UDisksLinuxBlockObject. * * Triggers a 'change' uevent in the kernel. * * The triggered event will bubble up from the kernel through the udev * stack and will eventually be received by the udisks daemon process * itself. This method does not wait for the event to be received. */ void udisks_linux_block_object_trigger_uevent (UDisksLinuxBlockObject *object) { gchar* path = NULL; gint fd = -1; g_return_if_fail (UDISKS_IS_LINUX_BLOCK_OBJECT (object)); /* TODO: would be nice with a variant to wait until the request uevent has been received by ourselves */ path = g_strconcat (g_udev_device_get_sysfs_path (object->device->udev_device), "/uevent", NULL); fd = open (path, O_WRONLY); if (fd < 0) { udisks_warning ("Error opening %s: %m", path); goto out; } if (write (fd, "change", sizeof "change" - 1) != sizeof "change" - 1) { udisks_warning ("Error writing 'change' to file %s: %m", path); goto out; } out: if (fd >= 0) close (fd); g_free (path); }
/** * udisks_decode_udev_string: * @str: An udev-encoded string or %NULL. * * Unescapes sequences like \x20 to " " and ensures the returned string is valid UTF-8. * * If the string is not valid UTF-8, try as hard as possible to convert to UTF-8. * * If %NULL is passed, then %NULL is returned. * * See udev_util_encode_string() in libudev/libudev-util.c in the udev * tree for what kinds of strings can be used. * * Returns: A valid UTF-8 string that must be freed with g_free(). */ gchar * udisks_decode_udev_string (const gchar *str) { GString *s; gchar *ret; const gchar *end_valid; guint n; if (str == NULL) { ret = NULL; goto out; } s = g_string_new (NULL); for (n = 0; str[n] != '\0'; n++) { if (str[n] == '\\') { gint val; if (str[n + 1] != 'x' || str[n + 2] == '\0' || str[n + 3] == '\0') { udisks_warning ("**** NOTE: malformed encoded string `%s'", str); break; } val = (g_ascii_xdigit_value (str[n + 2]) << 4) | g_ascii_xdigit_value (str[n + 3]); g_string_append_c (s, val); n += 3; } else { g_string_append_c (s, str[n]); } } if (!g_utf8_validate (s->str, -1, &end_valid)) { udisks_warning ("The string `%s' is not valid UTF-8. Invalid characters begins at `%s'", s->str, end_valid); ret = g_strndup (s->str, end_valid - s->str); g_string_free (s, TRUE); } else { ret = g_string_free (s, FALSE); } out: return ret; }
/* Runs in housekeeping thread - called without lock held */ static void housekeeping_all_drives (UDisksLinuxProvider *provider, guint secs_since_last) { GList *objects; GList *l; G_LOCK (provider_lock); objects = g_hash_table_get_values (provider->vpd_to_drive); g_list_foreach (objects, (GFunc) g_object_ref, NULL); G_UNLOCK (provider_lock); for (l = objects; l != NULL; l = l->next) { UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (l->data); GError *error; error = NULL; if (!udisks_linux_drive_object_housekeeping (object, secs_since_last, NULL, /* TODO: cancellable */ &error)) { udisks_warning ("Error performing housekeeping for drive %s: %s (%s, %d)", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } } g_list_foreach (objects, (GFunc) g_object_unref, NULL); g_list_free (objects); }
static GSource * watch_attr (UDisksLinuxDevice *device, const gchar *attr, GSourceFunc callback, gpointer user_data) { GError *error = NULL; gchar *path = NULL; GIOChannel *channel = NULL; GSource *ret = NULL;; g_return_val_if_fail (UDISKS_IS_LINUX_DEVICE (device), NULL); path = g_strdup_printf ("%s/%s", g_udev_device_get_sysfs_path (device->udev_device), attr); channel = g_io_channel_new_file (path, "r", &error); if (channel != NULL) { ret = g_io_create_watch (channel, G_IO_ERR); g_source_set_callback (ret, callback, user_data, NULL); g_source_attach (ret, g_main_context_get_thread_default ()); g_source_unref (ret); g_io_channel_unref (channel); /* the keeps a reference to this object */ } else { udisks_warning ("Error creating watch for file %s: %s (%s, %d)", path, error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); } g_free (path); return ret; }
/** * udisks_linux_drive_object_uevent: * @object: A #UDisksLinuxDriveObject. * @action: Uevent action or %NULL * @device: A #UDisksLinuxDevice device object or %NULL if the device hasn't changed. * * Updates all information on interfaces on @drive. */ void udisks_linux_drive_object_uevent (UDisksLinuxDriveObject *object, const gchar *action, UDisksLinuxDevice *device) { GList *link; gboolean conf_changed; g_return_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object)); g_return_if_fail (device == NULL || UDISKS_IS_LINUX_DEVICE (device)); link = NULL; if (device != NULL) link = find_link_for_sysfs_path (object, g_udev_device_get_sysfs_path (device->udev_device)); if (g_strcmp0 (action, "remove") == 0) { if (link != NULL) { g_object_unref (UDISKS_LINUX_DEVICE (link->data)); object->devices = g_list_delete_link (object->devices, link); } else { udisks_warning ("Drive doesn't have device with sysfs path %s on remove event", g_udev_device_get_sysfs_path (device->udev_device)); } } else { if (link != NULL) { g_object_unref (UDISKS_LINUX_DEVICE (link->data)); link->data = g_object_ref (device); } else { if (device != NULL) object->devices = g_list_append (object->devices, g_object_ref (device)); } } conf_changed = FALSE; conf_changed |= update_iface (object, action, drive_check, drive_connect, drive_update, UDISKS_TYPE_LINUX_DRIVE, &object->iface_drive); conf_changed |= update_iface (object, action, drive_ata_check, drive_ata_connect, drive_ata_update, UDISKS_TYPE_LINUX_DRIVE_ATA, &object->iface_drive_ata); if (conf_changed) apply_configuration (object); }
/** * udisks_linux_block_object_reread_partition_table: * @object: A #UDisksLinuxBlockObject. * * Requests the kernel to re-read the partition table for @object. * * The events from any change this may cause will bubble up from the * kernel through the udev stack and will eventually be received by * the udisks daemon process itself. This method does not wait for the * event to be received. */ void udisks_linux_block_object_reread_partition_table (UDisksLinuxBlockObject *object) { const gchar *device_file; gint fd; g_return_if_fail (UDISKS_IS_LINUX_BLOCK_OBJECT (object)); device_file = g_udev_device_get_device_file (object->device->udev_device); fd = open (device_file, O_RDONLY); if (fd == -1) { udisks_warning ("Error opening %s: %m", device_file); } else { if (ioctl (fd, BLKRRPART) != 0) { udisks_warning ("Error issuing BLKRRPART to %s: %m", device_file); } close (fd); } }
static void udisks_linux_provider_init (UDisksLinuxProvider *provider) { const gchar *subsystems[] = {"block", "iscsi_connection", "scsi", NULL}; GFile *file; GError *error = NULL; /* get ourselves an udev client */ provider->gudev_client = g_udev_client_new (subsystems); g_signal_connect (provider->gudev_client, "uevent", G_CALLBACK (on_uevent), provider); provider->probe_request_queue = g_async_queue_new (); provider->probe_request_thread = g_thread_new ("probing-thread", probe_request_thread_func, provider); file = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/udisks2"); provider->etc_udisks2_dir_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error); if (provider->etc_udisks2_dir_monitor != NULL) { g_signal_connect (provider->etc_udisks2_dir_monitor, "changed", G_CALLBACK (on_etc_udisks2_dir_monitor_changed), provider); } else { udisks_warning ("Error monitoring directory %s: %s (%s, %d)", PACKAGE_SYSCONF_DIR "/udisks2", error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); } g_object_unref (file); }
static gboolean perform_initial_housekeeping_for_drive (GIOSchedulerJob *job, GCancellable *cancellable, gpointer user_data) { UDisksLinuxDriveObject *object = UDISKS_LINUX_DRIVE_OBJECT (user_data); GError *error; error = NULL; if (!udisks_linux_drive_object_housekeeping (object, 0, NULL, /* TODO: cancellable */ &error)) { udisks_warning ("Error performing initial housekeeping for drive %s: %s (%s, %d)", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } return FALSE; /* job is complete */ }
static void lvm_update_vgs (GObject *source_obj, GAsyncResult *result, gpointer user_data) { UDisksLVM2State *state; UDisksDaemon *daemon = UDISKS_DAEMON (source_obj); GDBusObjectManagerServer *manager; GTask *task = G_TASK (result); GError *error = NULL; VGsPVsData *data = g_task_propagate_pointer (task, &error); BDLVMVGdata **vgs = NULL; BDLVMPVdata **pvs = NULL; GHashTableIter vg_name_iter; gpointer key, value; const gchar *vg_name; if (!data) { if (error) udisks_warning ("LVM2 plugin: %s", error->message); else /* this should never happen */ udisks_warning ("LVM2 plugin: failure but no error when getting VGs!"); return; } vgs = data->vgs; pvs = data->pvs; /* free the data container (but not 'vgs' and 'pvs') */ g_free (data); manager = udisks_daemon_get_object_manager (daemon); state = get_module_state (daemon); /* Remove obsolete groups */ g_hash_table_iter_init (&vg_name_iter, udisks_lvm2_state_get_name_to_volume_group (state)); while (g_hash_table_iter_next (&vg_name_iter, &key, &value)) { UDisksLinuxVolumeGroupObject *group; gboolean found = FALSE; vg_name = key; group = value; for (BDLVMVGdata **vgs_p=vgs; !found && (*vgs_p); vgs_p++) found = g_strcmp0 ((*vgs_p)->name, vg_name) == 0; if (!found) { udisks_linux_volume_group_object_destroy (group); g_dbus_object_manager_server_unexport (manager, g_dbus_object_get_object_path (G_DBUS_OBJECT (group))); g_hash_table_iter_remove (&vg_name_iter); } } /* Add new groups and update existing groups */ for (BDLVMVGdata **vgs_p=vgs; *vgs_p; vgs_p++) { UDisksLinuxVolumeGroupObject *group; GSList *vg_pvs = NULL; vg_name = (*vgs_p)->name; group = g_hash_table_lookup (udisks_lvm2_state_get_name_to_volume_group (state), vg_name); if (group == NULL) { group = udisks_linux_volume_group_object_new (daemon, vg_name); g_hash_table_insert (udisks_lvm2_state_get_name_to_volume_group (state), g_strdup (vg_name), group); } for (BDLVMPVdata **pvs_p=pvs; *pvs_p; pvs_p++) if (g_strcmp0 ((*pvs_p)->vg_name, vg_name) == 0) vg_pvs = g_slist_prepend (vg_pvs, *pvs_p); udisks_linux_volume_group_object_update (group, *vgs_p, vg_pvs); } /* this is safe to do -- all BDLVMPVdata objects are still existing because the function that frees them is scheduled in main loop by the udisks_linux_volume_group_object_update() call above */ for (BDLVMPVdata **pvs_p=pvs; *pvs_p; pvs_p++) if ((*pvs_p)->vg_name == NULL) bd_lvm_pvdata_free (*pvs_p); /* only free the containers, the contents were passed further */ g_free (vgs); g_free (pvs); }
/** * udisks_linux_mdraid_object_uevent: * @object: A #UDisksLinuxMDRaidObject. * @action: Uevent action or %NULL * @device: A #UDisksLinuxDevice device object or %NULL if the device hasn't changed. * @is_member: %TRUE if @device is a member, %FALSE if it's the raid device. * * Updates all information on interfaces on @mdraid. */ void udisks_linux_mdraid_object_uevent (UDisksLinuxMDRaidObject *object, const gchar *action, UDisksLinuxDevice *device, gboolean is_member) { gboolean conf_changed = FALSE; g_return_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object)); g_return_if_fail (UDISKS_IS_LINUX_DEVICE (device)); /* udisks_debug ("is_member=%d for uuid %s and device %s", is_member, object->uuid, g_udev_device_get_device_file (device->udev_device)); */ if (is_member) { GList *link = NULL; link = NULL; if (device != NULL) link = find_link_for_sysfs_path_for_member (object, g_udev_device_get_sysfs_path (device->udev_device)); if (g_strcmp0 (action, "remove") == 0) { if (link != NULL) { g_object_unref (UDISKS_LINUX_DEVICE (link->data)); object->member_devices = g_list_delete_link (object->member_devices, link); } else { udisks_warning ("MDRaid with UUID %s doesn't have member device with sysfs path %s on remove event", object->uuid, g_udev_device_get_sysfs_path (device->udev_device)); } } else { if (link != NULL) { if (device != link->data) { g_object_unref (UDISKS_LINUX_DEVICE (link->data)); link->data = g_object_ref (device); } } else { if (device != NULL) { object->member_devices = g_list_append (object->member_devices, g_object_ref (device)); } } } } else { /* Skip partitions of raid devices */ if (g_strcmp0 (g_udev_device_get_devtype (device->udev_device), "disk") != 0) goto out; if (g_strcmp0 (action, "remove") == 0) { if (object->raid_device != NULL) if (g_strcmp0 (g_udev_device_get_sysfs_path (object->raid_device->udev_device), g_udev_device_get_sysfs_path (device->udev_device)) == 0) { g_clear_object (&object->raid_device); raid_device_removed (object, object->raid_device); } else { udisks_warning ("MDRaid with UUID %s doesn't have raid device with sysfs path %s on remove event (it has %s)", object->uuid, g_udev_device_get_sysfs_path (device->udev_device), g_udev_device_get_sysfs_path (object->raid_device->udev_device)); } else { udisks_warning ("MDRaid with UUID %s doesn't have raid device with sysfs path %s on remove event", object->uuid, g_udev_device_get_sysfs_path (device->udev_device)); } } else { if (object->raid_device == NULL) { object->raid_device = g_object_ref (device); raid_device_added (object, object->raid_device); } else { if (device != object->raid_device) { /* device changed -- remove and re-add the file watchers */ raid_device_removed (object, object->raid_device); g_clear_object (&object->raid_device); object->raid_device = g_object_ref (device); raid_device_added (object, object->raid_device); } } } } /* if we don't have any devices, no point in updating (we should get nuked soon anyway) */ if (udisks_linux_mdraid_object_have_devices (object)) { conf_changed = FALSE; conf_changed |= update_iface (object, action, mdraid_check, mdraid_connect, mdraid_update, UDISKS_TYPE_LINUX_MDRAID, &object->iface_mdraid); } out: ; }
gboolean take_filesystem_ownership (const gchar *device, const gchar *fstype, uid_t caller_uid, gid_t caller_gid, gboolean recursive, GError **error) { gchar *mountpoint = NULL; GError *local_error = NULL; gboolean unmount = FALSE; gboolean success = TRUE; mountpoint = bd_fs_get_mountpoint (device, &local_error); if (mountpoint == NULL) { if (local_error != NULL) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Error when getting mountpoint for %s: %s.", device, local_error->message); g_clear_error (&local_error); success = FALSE; goto out; } else { /* device is not mounted, we need to mount it */ mountpoint = g_mkdtemp (g_strdup (PACKAGE_LOCALSTATE_DIR "/run/udisks2/temp-mount-XXXXXX")); if (mountpoint == NULL) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Cannot create temporary mountpoint."); success = FALSE; goto out; } if (!bd_fs_mount (device, mountpoint, fstype, NULL, NULL, &local_error)) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Cannot mount %s at %s: %s", device, mountpoint, local_error->message); g_clear_error (&local_error); if (g_rmdir (mountpoint) != 0) udisks_warning ("Error removing temporary mountpoint directory %s.", mountpoint); success = FALSE; goto out; } else unmount = TRUE; // unmount during cleanup } } if (recursive) { if (!recursive_chown (mountpoint, caller_uid, caller_gid)) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Cannot recursively chown %s to uid=%u and gid=%u: %m", mountpoint, caller_uid, caller_gid); success = FALSE; goto out; } } else { if (chown (mountpoint, caller_uid, caller_gid) != 0) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Cannot chown %s to uid=%u and gid=%u: %m", mountpoint, caller_uid, caller_gid); success = FALSE; goto out; } } if (chmod (mountpoint, 0700) != 0) { g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Cannot chmod %s to mode 0700: %m", mountpoint); success = FALSE; goto out; } out: if (unmount) { if (! bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error)) { udisks_warning ("Error unmounting temporary mountpoint %s: %s", mountpoint, local_error->message); g_clear_error (&local_error); } if (g_rmdir (mountpoint) != 0) udisks_warning ("Error removing temporary mountpoint directory %s.", mountpoint); } g_free (mountpoint); return success; }