From 2c2e41c167bd34d5e2ffbe7bbad7a56a5e47e8ba Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Wed, 26 Sep 2018 13:43:40 +0300 Subject: [PATCH] Handle hard link creation error. Signed-off-by: Andrew Borodin --- src/filemanager/file.c | 138 +++++++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 18 deletions(-) diff --git a/src/filemanager/file.c b/src/filemanager/file.c index 4a212b493..288dab098 100644 --- a/src/filemanager/file.c +++ b/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))