Browse Source

Handle hard link creation error.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
Andrew Borodin 6 years ago
parent
commit
2c2e41c167
1 changed files with 120 additions and 18 deletions
  1. 120 18
      src/filemanager/file.c

+ 120 - 18
src/filemanager/file.c

@@ -124,6 +124,17 @@ typedef enum
     DEST_FULL = 2               /* Created, fully copied */
 } dest_status_t;
 
+/* Status of hard link creation */
+typedef enum
+{
+    HARDLINK_OK = 0,            /**< Hardlink was created successfully */
+    HARDLINK_CACHED,            /**< Hardlink was added to the cache */
+    HARDLINK_NOTLINK,           /**< This is not a hard link */
+    HARDLINK_UNSUPPORTED,       /**< VFS doesn't support hard links */
+    HARDLINK_ERROR,             /**< Hard link creation error */
+    HARDLINK_ABORT              /**< Stop file operation after hardlink creation error */
+} hardlink_status_t;
+
 /*
  * This array introduced to avoid translation problems. The former (op_names)
  * is assumed to be nouns, suitable in dialog box titles; this one should
@@ -332,16 +343,18 @@ is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *
  * and a hardlink was successfully made
  */
 
-static gboolean
+static hardlink_status_t
 check_hardlinks (const vfs_path_t * src_vpath, const struct stat *src_stat,
-                 const vfs_path_t * dst_vpath)
+                 const vfs_path_t * dst_vpath, gboolean * skip_all)
 {
     struct link *lnk;
     ino_t ino = src_stat->st_ino;
     dev_t dev = src_stat->st_dev;
 
-    if (src_stat->st_nlink < 2 || (vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
-        return FALSE;
+    if (src_stat->st_nlink < 2)
+        return HARDLINK_NOTLINK;
+    if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
+        return HARDLINK_UNSUPPORTED;
 
     lnk = (struct link *) is_in_linklist (linklist, src_vpath, src_stat);
     if (lnk != NULL)
@@ -366,15 +379,82 @@ check_hardlinks (const vfs_path_t * src_vpath, const struct stat *src_stat,
                 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
                 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
 
-                if (dst_name_class == p_class && mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
-                    mc_link (lnk->dst_vpath, dst_vpath) == 0)
-                    return TRUE;
+                if (dst_name_class == p_class)
+                {
+                    gboolean ok;
+
+                    while (!(ok = mc_stat (lnk->dst_vpath, &link_stat) == 0) && !*skip_all)
+                    {
+                        FileProgressStatus status;
+
+                        status =
+                            file_error (TRUE, _("Cannot stat hardlink source file \"%s\"\n%s"),
+                                        vfs_path_as_str (lnk->dst_vpath));
+                        if (status == FILE_ABORT)
+                            return HARDLINK_ABORT;
+                        if (status == FILE_RETRY)
+                            continue;
+                        if (status == FILE_SKIPALL)
+                            *skip_all = TRUE;
+                        break;
+                    }
+
+                    /* if stat() finished unsuccessfully, don't try to create link */
+                    if (!ok)
+                        return HARDLINK_ERROR;
+
+                    while (!(ok = mc_link (lnk->dst_vpath, dst_vpath) == 0) && !*skip_all)
+                    {
+                        FileProgressStatus status;
+
+                        status =
+                            file_error (TRUE, _("Cannot create target hardlink \"%s\"\n%s"),
+                                        vfs_path_as_str (dst_vpath));
+                        if (status == FILE_ABORT)
+                            return HARDLINK_ABORT;
+                        if (status == FILE_RETRY)
+                            continue;
+                        if (status == FILE_SKIPALL)
+                            *skip_all = TRUE;
+                        break;
+                    }
+
+                    /* Success? */
+                    return (ok ? HARDLINK_OK : HARDLINK_ERROR);
+                }
             }
         }
 
-        message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink\n%s\nto\n%s"),
-                 vfs_path_as_str (dst_vpath), vfs_path_as_str (lnk->dst_vpath));
-        return FALSE;
+        if (!*skip_all)
+        {
+            FileProgressStatus status;
+
+            /* Message w/o "Retry" action.
+             *
+             * FIXME: Can't say what errno is here. Define it and don't display.
+             *
+             * file_error() displays a message with text representation of errno
+             * and the string passed to file_error() should provide the format "%s"
+             * for that at end (see previous file_error() call for the reference).
+             * But if format for errno isn't provided, it is safe, because C standard says:
+             * "If the format is exhausted while arguments remain, the excess arguments
+             * are evaluated (as always) but are otherwise ignored" (ISO/IEC 9899:1999,
+             * section 7.19.6.1, paragraph 2).
+             *
+             */
+            errno = 0;
+            status =
+                file_error (FALSE, _("Cannot create target hardlink \"%s\""),
+                            vfs_path_as_str (dst_vpath));
+
+            if (status == FILE_ABORT)
+                return HARDLINK_ABORT;
+
+            if (status == FILE_SKIPALL)
+                *skip_all = TRUE;
+        }
+
+        return HARDLINK_ERROR;
     }
 
     lnk = g_try_new (struct link, 1);
@@ -391,7 +471,7 @@ check_hardlinks (const vfs_path_t * src_vpath, const struct stat *src_stat,
         linklist = g_slist_prepend (linklist, lnk);
     }
 
-    return FALSE;
+    return HARDLINK_CACHED;
 }
 
 /* --------------------------------------------------------------------------------------------- */
@@ -2201,11 +2281,22 @@ copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
     if (!ctx->do_append)
     {
         /* Check the hardlinks */
-        if (!ctx->follow_links && check_hardlinks (src_vpath, &src_stat, dst_vpath))
+        if (!ctx->follow_links)
         {
-            /* We have made a hardlink - no more processing is necessary */
-            return_status = FILE_CONT;
-            goto ret_fast;
+            switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
+            {
+            case HARDLINK_OK:
+                /* We have made a hardlink - no more processing is necessary */
+                return_status = FILE_CONT;
+                goto ret_fast;
+
+            case HARDLINK_ABORT:
+                return_status = FILE_ABORT;
+                goto ret_fast;
+
+            default:
+                break;
+            }
         }
 
         if (S_ISLNK (src_stat.st_mode))
@@ -2708,10 +2799,21 @@ copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const cha
     /* Hmm, hardlink to directory??? - Norbert */
     /* FIXME: In this step we should do something in case the destination already exist */
     /* Check the hardlinks */
-    if (ctx->preserve && check_hardlinks (src_vpath, &cbuf, dst_vpath))
+    if (ctx->preserve)
     {
-        /* We have made a hardlink - no more processing is necessary */
-        goto ret_fast;
+        switch (check_hardlinks (src_vpath, &cbuf, dst_vpath, &ctx->skip_all))
+        {
+        case HARDLINK_OK:
+            /* We have made a hardlink - no more processing is necessary */
+            goto ret_fast;
+
+        case HARDLINK_ABORT:
+            return_status = FILE_ABORT;
+            goto ret_fast;
+
+        default:
+            break;
+        }
     }
 
     if (!S_ISDIR (cbuf.st_mode))