summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/udiskscleanup.c262
-rw-r--r--src/udiskslinuxprovider.c9
2 files changed, 266 insertions, 5 deletions
diff --git a/src/udiskscleanup.c b/src/udiskscleanup.c
index 7fa5801..204baa1 100644
--- a/src/udiskscleanup.c
+++ b/src/udiskscleanup.c
@@ -24,6 +24,11 @@
#include <glib/gstdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/loop.h>
+
#include "udisksdaemon.h"
#include "udiskscleanup.h"
#include "udiskspersistentstore.h"
@@ -1241,7 +1246,6 @@ udisks_cleanup_check_unlocked_luks_entry (UDisksCleanup *cleanup,
error_message = NULL;
escaped_device_file = g_strescape (device_file_cleartext, NULL);
- /* right now -l is the only way to "force unmount" file systems... */
if (!udisks_daemon_launch_spawned_job_sync (cleanup->daemon,
NULL, /* GCancellable */
&error_message,
@@ -1758,12 +1762,266 @@ udisks_cleanup_unignore_unlocked_luks (UDisksCleanup *cleanup,
/* ---------------------------------------------------------------------------------------------------- */
+/* returns TRUE if the entry should be kept */
+static gboolean
+udisks_cleanup_check_loop_entry (UDisksCleanup *cleanup,
+ GVariant *value,
+ gboolean check_only,
+ GArray *devs_to_clean)
+{
+ const gchar *loop_device;
+ GVariant *details;
+ gchar *s;
+ gboolean keep;
+ gboolean is_setup;
+ gboolean has_backing_device;
+ gboolean backing_device_mounted;
+ gboolean attempt_no_cleanup;
+ GVariant *backing_file_value;
+ GVariant *backing_file_device_value;
+ const gchar *backing_file;
+ dev_t backing_file_device;
+ GUdevClient *udev_client;
+ GUdevDevice *udev_backing_file_device;
+ struct stat loop_device_statbuf;
+ gint loop_device_fd;
+ struct loop_info64 li64;
+ UDisksMountMonitor *monitor;
+
+ keep = FALSE;
+ attempt_no_cleanup = FALSE;
+ is_setup = FALSE;
+ has_backing_device = FALSE;
+ backing_device_mounted = FALSE;
+ backing_file_value = NULL;
+ backing_file_device_value = NULL;
+ details = NULL;
+
+ monitor = udisks_daemon_get_mount_monitor (cleanup->daemon);
+ udev_client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (cleanup->daemon));
+
+ g_variant_get (value,
+ "{&s@a{sv}}",
+ &loop_device,
+ &details);
+
+ /* Don't consider entries being ignored (e.g. in the process of being locked) */
+ if (g_hash_table_lookup (cleanup->currently_deleting, &loop_device) != NULL)
+ {
+ keep = TRUE;
+ goto out;
+ }
+
+ backing_file_value = lookup_asv (details, "backing-file");
+ if (backing_file_value == NULL)
+ {
+ s = g_variant_print (value, TRUE);
+ udisks_error ("loop entry %s is invalid: no backing-file key/value pair", s);
+ g_free (s);
+ attempt_no_cleanup = TRUE;
+ goto out;
+ }
+ backing_file = g_variant_get_bytestring (backing_file_value);
+
+ backing_file_device_value = lookup_asv (details, "backing-file-device");
+ if (backing_file_device_value == NULL)
+ {
+ s = g_variant_print (value, TRUE);
+ udisks_error ("loop entry %s is invalid: no backing-file-device key/value pair", s);
+ g_free (s);
+ attempt_no_cleanup = TRUE;
+ goto out;
+ }
+ backing_file_device = g_variant_get_uint64 (backing_file_device_value);
+
+ if (stat (loop_device, &loop_device_statbuf) != 0)
+ {
+ udisks_error ("error statting %s: %m", loop_device);
+ attempt_no_cleanup = TRUE;
+ goto out;
+ }
+
+ loop_device_fd = open (loop_device, O_RDONLY);
+ if (loop_device_fd == -1 )
+ {
+ udisks_error ("error opening %s: %m", loop_device);
+ attempt_no_cleanup = TRUE;
+ goto out;
+ }
+ if (ioctl (loop_device_fd, LOOP_GET_STATUS64, &li64) == -1)
+ {
+ udisks_error ("error issuing LOOP_GET_STATUS64 ioctl on %s: %m", loop_device);
+ attempt_no_cleanup = TRUE;
+ close (loop_device_fd);
+ goto out;
+ }
+ close (loop_device_fd);
+ if (strncmp ((const char *) li64.lo_file_name, backing_file, LO_NAME_SIZE - 1) != 0)
+ {
+ udisks_error ("unexpected name for device %s - expected `%s' but got `%s'",
+ loop_device, backing_file, li64.lo_file_name);
+ attempt_no_cleanup = TRUE;
+ goto out;
+ }
+ is_setup = TRUE;
+
+ udev_backing_file_device = g_udev_client_query_by_device_number (udev_client,
+ G_UDEV_DEVICE_TYPE_BLOCK,
+ backing_file_device);
+ if (udev_backing_file_device != NULL)
+ {
+ GList *mounts;
+ /* Figure out if still mounted */
+ mounts = udisks_mount_monitor_get_mounts_for_dev (monitor, backing_file_device);
+ if (mounts != NULL)
+ {
+ backing_device_mounted = TRUE;
+ }
+ g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
+ g_list_free (mounts);
+ has_backing_device = TRUE;
+ g_object_unref (udev_backing_file_device);
+ }
+
+ /* OK, entry is valid - keep it around */
+ if (is_setup && has_backing_device && backing_device_mounted)
+ keep = TRUE;
+
+ out:
+
+ if (check_only && !keep)
+ {
+ g_array_append_val (devs_to_clean, loop_device_statbuf.st_rdev);
+ keep = TRUE;
+ goto out2;
+ }
+
+ if (!keep && !attempt_no_cleanup)
+ {
+ if (is_setup)
+ {
+ gchar *escaped_loop_device_file;
+ gchar *error_message;
+
+ if (!has_backing_device)
+ udisks_notice ("Cleaning up loop device %s (backing device %d:%d no longer exist)",
+ loop_device,
+ major (backing_file_device), minor (backing_file_device));
+ else
+ udisks_notice ("Cleaning up loop device %s (backing device %d:%d no longer mounted)",
+ loop_device,
+ major (backing_file_device), minor (backing_file_device));
+
+ error_message = NULL;
+ escaped_loop_device_file = g_strescape (loop_device, NULL);
+ if (!udisks_daemon_launch_spawned_job_sync (cleanup->daemon,
+ NULL, /* GCancellable */
+ &error_message,
+ NULL, /* input_string */
+ "losetup -d \"%s\"",
+ escaped_loop_device_file))
+ {
+ udisks_error ("Error cleaning up loop device %s: %s",
+ loop_device, error_message);
+ g_free (escaped_loop_device_file);
+ g_free (error_message);
+ /* keep the entry so we can clean it up later */
+ keep = TRUE;
+ goto out2;
+ }
+ g_free (escaped_loop_device_file);
+ g_free (error_message);
+ }
+ else
+ {
+ udisks_notice ("loop device %s was manually deleted", loop_device);
+ }
+ }
+
+ out2:
+ if (backing_file_value != NULL)
+ g_variant_unref (backing_file_value);
+ if (backing_file_device_value != NULL)
+ g_variant_unref (backing_file_device_value);
+ if (details != NULL)
+ g_variant_unref (details);
+ return keep;
+}
+
static void
udisks_cleanup_check_loop (UDisksCleanup *cleanup,
gboolean check_only,
GArray *devs_to_clean)
{
- /* TODO */
+ gboolean changed;
+ GVariant *value;
+ GVariant *new_value;
+ GVariantBuilder builder;
+ GError *error;
+
+ changed = FALSE;
+
+ /* load existing entries */
+ error = NULL;
+ value = udisks_persistent_store_get (cleanup->persistent_store,
+ UDISKS_PERSISTENT_FLAGS_TEMPORARY_STORE,
+ "loop",
+ G_VARIANT_TYPE ("a{sa{sv}}"),
+ &error);
+ if (error != NULL)
+ {
+ udisks_warning ("Error getting loop: %s (%s, %d)",
+ error->message, g_quark_to_string (error->domain), error->code);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* check valid entries */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
+ if (value != NULL)
+ {
+ GVariantIter iter;
+ GVariant *child;
+ g_variant_iter_init (&iter, value);
+ while ((child = g_variant_iter_next_value (&iter)) != NULL)
+ {
+ if (udisks_cleanup_check_loop_entry (cleanup, child, check_only, devs_to_clean))
+ g_variant_builder_add_value (&builder, child);
+ else
+ changed = TRUE;
+ g_variant_unref (child);
+ }
+ g_variant_unref (value);
+ }
+
+ new_value = g_variant_builder_end (&builder);
+
+ /* save new entries */
+ if (changed)
+ {
+ error = NULL;
+ if (!udisks_persistent_store_set (cleanup->persistent_store,
+ UDISKS_PERSISTENT_FLAGS_TEMPORARY_STORE,
+ "loop",
+ G_VARIANT_TYPE ("a{sa{sv}}"),
+ new_value, /* consumes new_value */
+ &error))
+ {
+ udisks_warning ("Error setting loop: %s (%s, %d)",
+ error->message,
+ g_quark_to_string (error->domain),
+ error->code);
+ g_error_free (error);
+ goto out;
+ }
+ }
+ else
+ {
+ g_variant_unref (new_value);
+ }
+
+ out:
+ ;
}
/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c
index 3923dec..329bd54 100644
--- a/src/udiskslinuxprovider.c
+++ b/src/udiskslinuxprovider.c
@@ -343,15 +343,18 @@ handle_block_uevent (UDisksLinuxProvider *provider,
{
handle_block_uevent_for_block (provider, action, device);
handle_block_uevent_for_drive (provider, action, device);
-
- /* Possibly need to clean up */
- udisks_cleanup_check (udisks_daemon_get_cleanup (udisks_provider_get_daemon (UDISKS_PROVIDER (provider))));
}
else
{
handle_block_uevent_for_drive (provider, action, device);
handle_block_uevent_for_block (provider, action, device);
}
+
+ if (g_strcmp0 (action, "add") != 0)
+ {
+ /* Possibly need to clean up */
+ udisks_cleanup_check (udisks_daemon_get_cleanup (udisks_provider_get_daemon (UDISKS_PROVIDER (provider))));
+ }
}
static void