Bug fix: Collections and unmounted drives
authorColin Clark <colin.clark@cclark.uk>
Tue, 9 Apr 2024 09:48:01 +0000 (10:48 +0100)
committerColin Clark <colin.clark@cclark.uk>
Tue, 9 Apr 2024 09:48:01 +0000 (10:48 +0100)
The bug:
If a collection file is on the local filesystem
The collection file contains references to files on a mounted drive
The drive is unmounted
The collection file is opened

Geeqie sees that the files do not exist and deletes them from the
collection file.

The fix:
If Geeqie does not find the file-

If the file path prefix contains /home, /tmp or /usr it is assumed that
the file was on the local drive and has been deleted by the user. It is
ignored.

If the file path prefix contains a mount path, it is assumed that the
file was on a currently mounted drive and has been deleted.

Otherwise it is assumed that the file is on a removable drive that is
not currently mounted. The collection will not be opened.

If this is not the case the user may need to use a text editor to remove
the offending line from the collection file.

Created problem:
When a collection window contents have not been changed, but its
geometry has, the geometry changes will not be saved. The user must make
a minor positional change to any thumbnail to trigger a save.

src/collect-io.cc
src/collect-table.cc

index 31d4496..5b9c5ba 100644 (file)
@@ -27,6 +27,7 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gdk/gdk.h>
 #include <glib-object.h>
+#include <mntent.h>
 
 #include <config.h>
 
@@ -40,6 +41,7 @@
 #include "secure-save.h"
 #include "thumb.h"
 #include "ui-fileops.h"
+#include "ui-utildlg.h"
 
 #define GQ_COLLECTION_MARKER "#" GQ_APPNAME
 
@@ -123,8 +125,7 @@ static gboolean collection_load_private(CollectionData *cd, const gchar *path, C
 
        pathl = path_from_utf8(path);
 
-       DEBUG_1("collection load: append=%d flush=%d only_geometry=%d path=%s",
-                         append, flush, only_geometry, pathl);
+       DEBUG_1("collection load: append=%d flush=%d only_geometry=%d path=%s", append, flush, only_geometry, pathl);
 
        /* load it */
        f = fopen(pathl, "r");
@@ -161,8 +162,7 @@ static gboolean collection_load_private(CollectionData *cd, const gchar *path, C
                                        has_official_header = TRUE;
                                        limit_failures = FALSE;
                                        }
-                               else if (strncmp(p, "#geometry:", 10 ) == 0 &&
-                                        scan_geometry(p + 10, cd->window))
+                               else if (strncmp(p, "#geometry:", 10 ) == 0 && scan_geometry(p + 10, cd->window))
                                        {
                                        has_geometry_header = TRUE;
                                        cd->window_read = TRUE;
@@ -236,15 +236,64 @@ static gboolean collection_load_private(CollectionData *cd, const gchar *path, C
                                changed |= collect_manager_process_action(entry, &buffer2);
 
                        valid = (buffer2[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_simple(buffer2), FALSE, TRUE));
-                       if (!valid) DEBUG_1("collection invalid file: %s", buffer2);
+                       if (!valid)
+                               {
+                               log_printf("Warning: Collection: %s Invalid file: %s", cd->name, buffer2);
+                               DEBUG_1("collection invalid file: %s", buffer2);
+                               }
 
                        total++;
                        if (!valid)
                                {
+                               /* If the file path has the prefix home, tmp or usr it was on the local file system and has been deleted. Ignore it. */
+                               if (!g_str_has_prefix(buffer2, "/home") && !g_str_has_prefix(buffer2, "/tmp") && !g_str_has_prefix(buffer2, "/usr"))
+                                       {
+                                       /* The file was on a mounted drive and either has been deleted or the drive is not mounted */
+                                       struct mntent *mount_entry;
+                                       FILE *mount_entries;
+                                       gboolean found = FALSE;
+
+                                       mount_entries = setmntent("/proc/mounts", "r");
+                                       if (mount_entries == nullptr)
+                                               {
+                                               /* It is assumed this will never fail */
+                                               perror("setmntent");
+                                               exit(1);
+                                               }
+
+                                       while (nullptr != (mount_entry = getmntent(mount_entries)))
+                                               {
+                                               if (g_strcmp0(mount_entry->mnt_dir, G_DIR_SEPARATOR_S) != 0)
+                                                       {
+                                                       if (g_str_has_prefix(buffer2, mount_entry->mnt_dir))
+                                                               {
+                                                               log_printf("%s was a file on a mounted filesystem but has been deleted: %s", buffer2, cd->name);
+                                                               found = TRUE;
+                                                               break;
+                                                               }
+                                                       }
+                                               }
+                                       endmntent(mount_entries);
+
+                                       if (!found)
+                                               {
+                                               log_printf("%s is a file on an unmounted filesystem: %s", buffer2, cd->path);
+                                               gchar *text = g_strdup_printf(_("This Collection cannot be opened because it contains a link to a file on a drive which is not yet mounted.\n\nCollection: %s\nFile: %s\n"), cd->path, buffer2);
+                                               warning_dialog(_("Cannot open Collection"), text, GQ_ICON_DIALOG_WARNING, nullptr);
+                                               g_free(text);
+
+                                               collection_window_close_by_collection(cd);
+                                               success = FALSE;
+                                               break;
+                                               }
+                                       }
+                               else
+                                       {
+                                       log_printf("%s was a file on local filesystem but has been deleted: %s", buffer2, cd->name);
+                                       }
+
                                fail++;
-                               if (limit_failures &&
-                                   fail > GQ_COLLECTION_FAIL_MIN &&
-                                   fail * 100 / total > GQ_COLLECTION_FAIL_PERCENT)
+                               if (limit_failures && fail > GQ_COLLECTION_FAIL_MIN && fail * 100 / total > GQ_COLLECTION_FAIL_PERCENT)
                                        {
                                        log_printf("%d invalid filenames in unofficial collection file, closing: %s\n", fail, path);
                                        success = FALSE;
@@ -257,8 +306,7 @@ static gboolean collection_load_private(CollectionData *cd, const gchar *path, C
 
        g_string_free(extended_filename_buffer, TRUE);
 
-       DEBUG_1("collection files: total = %d fail = %d official=%d gqview=%d geometry=%d",
-                         total, fail, has_official_header, has_gqview_header, has_geometry_header);
+       DEBUG_1("collection files: total = %d fail = %d official=%d gqview=%d geometry=%d", total, fail, has_official_header, has_gqview_header, has_geometry_header);
 
        fclose(f);
        if (only_geometry) return has_geometry_header;
index 7f47ff7..ee39fb9 100644 (file)
@@ -2644,6 +2644,8 @@ static void collection_table_destroy(GtkWidget *, gpointer data)
 
        /* If there is no unsaved data, save the window geometry
         */
+       /** @FIXME  This code interferes with the code detecting files on unmounted drives. See collection_load_private() in collect-io,cc. If the user wants to save the geometry of an unchanged Collection, just slightly move one of the thumbnails. */
+/*
        if (!ct->cd->changed)
                {
                if (!collection_save(ct->cd, ct->cd->path))
@@ -2651,6 +2653,7 @@ static void collection_table_destroy(GtkWidget *, gpointer data)
                        log_printf("failed saving to collection path: %s\n", ct->cd->path);
                        }
                }
+*/
 
        if (ct->popup)
                {