|
@@ -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))
|