[easytag] Handle renaming on case-insensitive filesystems



commit 656946c80805acc39c3102c1dff480658064bb8c
Author: David King <amigadave amigadave com>
Date:   Thu Sep 25 17:57:39 2014 +0100

    Handle renaming on case-insensitive filesystems
    
    A rename will fail on a filesystem that is case-insensitive, if the
    filenames only differ by case. Work around this by moving to a temporary
    file first, in the case that a rename fails because the target exists.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=737302

 src/easytag.c |   77 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 72 insertions(+), 5 deletions(-)
---
diff --git a/src/easytag.c b/src/easytag.c
index f026e13..f4fd4be 100644
--- a/src/easytag.c
+++ b/src/easytag.c
@@ -790,17 +790,84 @@ et_rename_file (const char *old_filepath, const char *new_filepath,
     if (!g_file_move (file_old, file_new, G_FILE_COPY_NONE, NULL, NULL, NULL,
                       error))
     {
-        /* Error moving file. */
-        g_object_unref (file_old);
-        g_object_unref (file_new);
-        g_assert (error == NULL || *error != NULL);
-        return FALSE;
+        if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+        {
+            /* Possibly a case change on a case-insensitive filesystem. */
+            /* TODO: casefold the paths of both files, and check to see whether
+             * they only differ by case? */
+            gchar *tmp_filename;
+            mode_t old_mode;
+            gint fd;
+            GFile *tmp_file;
+            GError *tmp_error = NULL;
+
+            tmp_filename = g_strconcat (old_filepath, ".XXXXXX", NULL);
+
+            old_mode = umask (077);
+            fd = mkstemp (tmp_filename);
+            umask (old_mode);
+
+            if (fd >= 0)
+            {
+                close (fd);
+            }
+
+            tmp_file = g_file_new_for_path (tmp_filename);
+            g_free (tmp_filename);
+
+            if (!g_file_move (file_old, tmp_file, G_FILE_COPY_OVERWRITE, NULL,
+                              NULL, NULL, &tmp_error))
+            {
+                g_file_delete (tmp_file, NULL, NULL);
+
+                g_object_unref (tmp_file);
+                g_clear_error (error);
+                g_propagate_error (error, tmp_error);
+                goto err;
+            }
+            else
+            {
+                /* Move to temporary file succeeded, now move to the real new
+                 * location. */
+                if (!g_file_move (tmp_file, file_new, G_FILE_COPY_NONE, NULL,
+                                  NULL, NULL, &tmp_error))
+                {
+                    g_file_move (tmp_file, file_old, G_FILE_COPY_NONE, NULL,
+                                 NULL, NULL, NULL);
+                    g_object_unref (tmp_file);
+                    g_clear_error (error);
+                    g_propagate_error (error, tmp_error);
+                    goto err;
+                }
+                else
+                {
+                    /* Move succeeded, so clear the original error about the
+                     * new file already existing. */
+                    g_object_unref (tmp_file);
+                    g_clear_error (error);
+                    goto out;
+                }
+            }
+        }
+        else
+        {
+            /* Error moving file. */
+            goto err;
+        }
     }
 
+out:
     g_object_unref (file_old);
     g_object_unref (file_new);
     g_assert (error == NULL || *error == NULL);
     return TRUE;
+
+err:
+    g_object_unref (file_new_parent);
+    g_object_unref (file_old);
+    g_object_unref (file_new);
+    g_assert (error == NULL || *error != NULL);
+    return FALSE;
 }
 
 /*


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]