From 45e558b58a1175ac7372fc8676e814ea88c68c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Palancher?= Date: Thu, 19 Dec 2024 10:14:01 +0100 Subject: [PATCH] dsync/dcmp: support symlinks targets changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dsync and dcmp to detect a symlinks targets changes. In this case, the symlinks are reported to be different by dcmp and are updated by dsync. New function mfu_compare_symlinks() is introduced in library API to factorize the logic for all commands. Signed-off-by: RĂ©mi Palancher --- src/common/mfu_util.c | 28 ++++++++++++++++++++++++++++ src/common/mfu_util.h | 9 +++++++++ src/dcmp/dcmp.c | 35 +++++++++++++++++++++++++++++++---- src/dsync/dsync.c | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/common/mfu_util.c b/src/common/mfu_util.c index 9105e932..5b374cfc 100644 --- a/src/common/mfu_util.c +++ b/src/common/mfu_util.c @@ -996,6 +996,34 @@ int mfu_compare_contents( return rc; } +/* compares targets of two symlinks, returns 0 if equal, positive value if + * different, -1 on error when reading symlink. */ +int mfu_compare_symlinks( + const char* src_name, /* IN - path name to source file */ + const char* dst_name, /* IN - path name to destination file */ + mfu_file_t* mfu_src_file, /* IN - I/O filesystem functions to use for source */ + mfu_file_t* mfu_dst_file) /* IN - I/O filesystem functions to use for destination */ +{ + char src_target[PATH_MAX + 1], dst_target[PATH_MAX + 1]; + ssize_t readlink_rc = mfu_file_readlink(src_name, src_target, sizeof(src_target) - 1, mfu_src_file); + if(readlink_rc < 0) { + MFU_LOG(MFU_LOG_ERR, "Failed to read source link `%s' readlink() (errno=%d %s)", + src_name, errno, strerror(errno) + ); + return -1; + } + readlink_rc = mfu_file_readlink(dst_name, dst_target, sizeof(dst_target) - 1, mfu_dst_file); + if(readlink_rc < 0) { + MFU_LOG(MFU_LOG_ERR, "Failed to read destination link `%s' readlink() (errno=%d %s)", + dst_name, errno, strerror(errno) + ); + return -1; + } + /* ensure that strings end with NUL */ + src_target[readlink_rc] = dst_target[readlink_rc] = '\0'; + return abs(strcmp(src_target, dst_target)); +} + /* uses the lustre api to obtain stripe count and stripe size of a file */ int mfu_stripe_get(const char *path, uint64_t *stripe_size, uint64_t *stripe_count) { diff --git a/src/common/mfu_util.h b/src/common/mfu_util.h index 73b1c27e..f8e25720 100644 --- a/src/common/mfu_util.h +++ b/src/common/mfu_util.h @@ -266,6 +266,15 @@ int mfu_compare_contents( mfu_file_t* mfu_dst_file /* IN - I/O filesystem functions to use for destination */ ); +/* compares targets of two symlinks, returns 0 if equal, positive value if + * different, -1 on error when reading symlink. */ +int mfu_compare_symlinks( + const char* src_name, /* IN - path name to souce file */ + const char* dst_name, /* IN - path name to destination file */ + mfu_file_t* mfu_src_file, /* IN - I/O filesystem functions to use for source */ + mfu_file_t* mfu_dst_file /* IN - I/O filesystem functions to use for destination */ +); + /* uses the lustre api to obtain stripe count and stripe size of a file */ int mfu_stripe_get(const char *path, uint64_t *stripe_size, uint64_t *stripe_count); diff --git a/src/dcmp/dcmp.c b/src/dcmp/dcmp.c index aedb9b43..654b598c 100644 --- a/src/dcmp/dcmp.c +++ b/src/dcmp/dcmp.c @@ -1084,15 +1084,42 @@ static int dcmp_strmap_compare( continue; } - /* for now, we can only compare content of regular files */ - /* TODO: add support for symlinks */ - if (! S_ISREG(dst_mode)) { - /* not regular file, take them as common content */ + /* for now, we can only compare content of regular files and symlinks */ + if (! S_ISREG(dst_mode) && ! S_ISLNK(dst_mode)) { + /* not regular file or symlink, take them as common content */ dcmp_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_COMMON); dcmp_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_COMMON); continue; } + /* For symlinks, compare targets */ + if (S_ISLNK(dst_mode)) { + const char* src_name = mfu_flist_file_get_name(src_list, src_index); + const char* dst_name = mfu_flist_file_get_name(dst_list, dst_index); + int compare_rc = mfu_compare_symlinks(src_name, dst_name, mfu_src_file, mfu_dst_file); + if (compare_rc == -1) { + /* we hit an error while reading the symlink */ + rc = -1; + MFU_LOG(MFU_LOG_ERR, + "Failed to readlink on %s and/or %s. Assuming contents are different.", + src_name, dst_name); + + /* consider files to be different, + * they could be the same, but we'll draw attention to them this way */ + compare_rc = 1; + } + if (!compare_rc) { + /* update to say contents of the symlinks were found to be the same */ + dcmp_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_COMMON); + dcmp_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_COMMON); + } else { + /* update to say contents of the symlinks were found to be different */ + dcmp_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_DIFFER); + dcmp_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_DIFFER); + } + continue; + } + dcmp_state state; tmp_rc = dcmp_strmap_item_state(src_map, key, DCMPF_SIZE, &state); assert(tmp_rc == 0); diff --git a/src/dsync/dsync.c b/src/dsync/dsync.c index 35d93318..48f018cb 100644 --- a/src/dsync/dsync.c +++ b/src/dsync/dsync.c @@ -1748,15 +1748,46 @@ static int dsync_strmap_compare( continue; } - /* for now, we can only compare content of regular files */ - /* TODO: add support for symlinks */ - if (! S_ISREG(dst_mode)) { - /* not regular file, take them as common content */ + /* for now, we can only compare content of regular files and symlinks */ + if (! S_ISREG(dst_mode) && ! S_ISLNK(dst_mode)) { + /* not regular file or symlink, take them as common content */ dsync_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_COMMON); dsync_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_COMMON); continue; } + /* if symlink, check if targets of source and destination files match. If not, + * mark the files as being different. */ + if (S_ISLNK(dst_mode)) { + const char* src_name = mfu_flist_file_get_name(src_list, src_index); + const char* dst_name = mfu_flist_file_get_name(dst_list, dst_index); + int compare_rc = mfu_compare_symlinks(src_name, dst_name, mfu_src_file, mfu_dst_file); + if (compare_rc == -1) { + /* we hit an error while reading the symlink */ + rc = -1; + MFU_LOG(MFU_LOG_ERR, + "Failed to readlink on %s and/or %s. Assuming contents are different.", + src_name, dst_name); + } + if (!compare_rc) { + /* update to say contents of the files were found to be the same */ + dsync_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_COMMON); + dsync_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_COMMON); + } else { + /* update to say contents of the symlinks were found to be different */ + dsync_strmap_item_update(src_map, key, DCMPF_CONTENT, DCMPS_DIFFER); + dsync_strmap_item_update(dst_map, key, DCMPF_CONTENT, DCMPS_DIFFER); + + /* if the destinations are different then we need to remove the file in + * the dst directory, and replace it with the one in the src directory */ + if (compare_rc > 0 !options.dry_run) { + mfu_flist_file_copy(src_list, src_index, src_cp_list); + mfu_flist_file_copy(dst_list, dst_index, dst_remove_list); + } + } + continue; + } + /* first check whether file sizes match */ dsync_state state; tmp_rc = dsync_strmap_item_state(src_map, key, DCMPF_SIZE, &state);