Handle hard link creation error.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2018-09-26 13:43:40 +03:00
parent 0f0dbc2b2e
commit 2c2e41c167

View File

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