Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sh: support reflinking directories #619

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/formats/sh.c.in
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static bool rm_sh_emit_handler_clone(RmFmtHandlerShScript *self, char **out, RmF
case RM_LINK_ERROR:
case RM_LINK_XDEV:
case RM_LINK_SYMLINK:
case RM_LINK_DIR:
rm_log_warning_line("Unexpected return code %d from rm_util_link_type()", link_type);
return FALSE;
case RM_LINK_HARDLINK:
Expand Down Expand Up @@ -137,6 +138,7 @@ static bool rm_sh_emit_handler_reflink(RmFmtHandlerShScript *self, char **out, R
case RM_LINK_ERROR:
rm_log_warning_line("Unexpected return code %d from rm_util_link_type()", link_type);
return FALSE;
case RM_LINK_DIR:
case RM_LINK_HARDLINK:
case RM_LINK_SYMLINK:
case RM_LINK_INLINE_EXTENTS:
Expand Down
50 changes: 39 additions & 11 deletions lib/formats/sh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ DO_KEEP_DIR_TIMESTAMPS=
# Set to true on -i
DO_ASK_BEFORE_DELETE=

# Tempfiles for saving timestamps
STAMPFILE=
STAMPFILE2=

##################################
# GENERAL LINT HANDLER FUNCTIONS #
##################################
Expand All @@ -51,6 +55,19 @@ COL_GREEN='\e[0;32m'
COL_YELLOW='\e[0;33m'
COL_RESET='\e[0m'

exit_cleanup() {
trap - INT TERM EXIT
if [ -n "$STAMPFILE" ]; then
rm -f -- "$STAMPFILE"
fi
if [ -n "$STAMPFILE2" ]; then
rm -f -- "$STAMPFILE2"
fi
}

trap exit_cleanup EXIT
trap exit INT TERM

print_progress_prefix() {
if [ -n "$DO_SHOW_PROGRESS" ]; then
PROGRESS_PERC=0
Expand Down Expand Up @@ -208,22 +225,34 @@ cp_hardlink() {
}

cp_reflink() {
if [ -d "$1" ]; then
# for duplicate dir's, can't clone so use symlink
cp_symlink "$@"
return $?
fi
print_progress_prefix
# reflink $1 to $2's data, preserving $1's mtime
printf "${COL_YELLOW}Reflinking to original: ${COL_RESET}%%s\n" "$1"
if original_check "$1" "$2"; then
if [ -z "$DO_DRY_RUN" ]; then
touch -mr "$1" -- "$0"
if [ -d "$1" ]; then
local STAMPFILE2="$(mktemp -d "${TMPDIR:-/tmp}/rmlint.XXXXXXXX.stamp.d")"
elif [ -z "$STAMPFILE2" ]; then
STAMPFILE2=$(mktemp "${TMPDIR:-/tmp}/rmlint.XXXXXXXX.stamp")
fi
cp --archive --attributes-only --no-target-directory -- "$1" "$STAMPFILE2"
if [ -d "$1" ]; then
# to reflink a directory, we will have to delete it, thus changing parent mtime
# take care of preserving parent mtime if requested
if [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
touch -r "$(dirname "$1")" -- "$STAMPFILE"
fi
rm -rf -- "$1"
fi
cp --archive --reflink=always -- "$2" "$1"
touch -mr "$0" -- "$1"
cp --archive --attributes-only --no-target-directory -- "$STAMPFILE2" "$1"
if [ -d "$1" ]; then
rm -rf -- "$STAMPFILE2"
if [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
# restore parent mtime if we saved it
touch -r "$STAMPFILE" -- "$(dirname "$1")"
fi
fi
fi
fi
}
Expand Down Expand Up @@ -278,7 +307,6 @@ remove_cmd() {
if [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
# Swap back old directory timestamp:
touch -r "$STAMPFILE" -- "$(dirname "$1")"
rm -- "$STAMPFILE"
fi

if [ -n "$DO_DELETE_EMPTY_DIRS" ]; then
Expand Down Expand Up @@ -380,7 +408,6 @@ do
;;
k)
DO_KEEP_DIR_TIMESTAMPS=true
STAMPFILE=$(mktemp 'rmlint.XXXXXXXX.stamp')
;;
i)
DO_ASK_BEFORE_DELETE=true
Expand All @@ -402,11 +429,12 @@ then
ask
fi

if [ -n "$DO_DRY_RUN" ]
then
if [ -n "$DO_DRY_RUN" ]; then
printf "#${COL_YELLOW} ////////////////////////////////////////////////////////////${COL_RESET}\n"
printf "#${COL_YELLOW} /// ${COL_RESET} This is only a dry run; nothing will be modified! ${COL_YELLOW}///${COL_RESET}\n"
printf "#${COL_YELLOW} ////////////////////////////////////////////////////////////${COL_RESET}\n"
elif [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
STAMPFILE=$(mktemp "${TMPDIR:-/tmp}/rmlint.XXXXXXXX.stamp")
fi

######### START OF AUTOGENERATED OUTPUT #########
Expand Down
4 changes: 3 additions & 1 deletion lib/reflink.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ int rm_is_reflink_main(int argc, const char **argv) {
" %i: %s\n"
" %i: %s\n"
" %i: %s\n"
" %i: %s\n"
" %i: %s\n",
_("Test if two files are reflinks (share same data extents)"),
_("Returns 0 if the files are reflinks."),
Expand All @@ -486,7 +487,8 @@ int rm_is_reflink_main(int argc, const char **argv) {
RM_LINK_HARDLINK, desc[RM_LINK_HARDLINK],
RM_LINK_SYMLINK, desc[RM_LINK_SYMLINK],
RM_LINK_XDEV, desc[RM_LINK_XDEV],
RM_LINK_NONE, desc[RM_LINK_NONE]);
RM_LINK_NONE, desc[RM_LINK_NONE],
RM_LINK_DIR, desc[RM_LINK_DIR]);


g_option_context_set_summary(context, summary);
Expand Down
26 changes: 22 additions & 4 deletions lib/utilities.c
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
RM_RETURN(RM_LINK_ERROR);
}

if(!S_ISREG(stat1.st_mode)) {
if(!S_ISREG(stat1.st_mode) && !S_ISDIR(stat1.st_mode)) {
RM_RETURN(S_ISLNK(stat1.st_mode) ? RM_LINK_SYMLINK : RM_LINK_NOT_FILE);
}

Expand All @@ -1386,11 +1386,23 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
RM_RETURN(RM_LINK_ERROR);
}

if(!S_ISREG(stat2.st_mode)) {
if(!S_ISREG(stat2.st_mode) && !S_ISDIR(stat2.st_mode)) {
RM_RETURN(S_ISLNK(stat2.st_mode) ? RM_LINK_SYMLINK : RM_LINK_NOT_FILE);
}

if(stat1.st_size != stat2.st_size) {
/* At this point, path1 or path2 may be a regular file or a directory.
* Ensure they both have the same type, otherwise fail. */
bool is_dir;
if(S_ISDIR(stat1.st_mode) && S_ISDIR(stat2.st_mode)) {
is_dir = true;
} else if (S_ISREG(stat1.st_mode) && S_ISREG(stat2.st_mode)) {
is_dir = false;
} else {
RM_RETURN(RM_LINK_NOT_FILE);
}

if(!is_dir && stat1.st_size != stat2.st_size) {
/* st_size is not defined for directories */
#if _RM_OFFSET_DEBUG
rm_log_debug_line(
"rm_util_link_type: Files have different sizes: %" G_GUINT64_FORMAT
Expand Down Expand Up @@ -1419,6 +1431,11 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
}
}

if (is_dir) {
/* further tests do not make sense for directories */
RM_RETURN(RM_LINK_DIR);
}

if(use_fiemap) {
RmLinkType reflink_type = rm_reflink_type_from_fd(fd1, fd2, stat1.st_size);
RM_RETURN(reflink_type);
Expand All @@ -1440,7 +1457,8 @@ const char **rm_link_type_to_desc() {
N_("Hardlink"),
N_("Encountered a symlink"),
N_("Files are on different devices"),
N_("Not linked")};
N_("Not linked"),
N_("Both files are directories")};
return RM_LINK_TYPE_TO_DESC;
}

Expand Down
1 change: 1 addition & 0 deletions lib/utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ typedef enum RmLinkType {
RM_LINK_SYMLINK = 9,
RM_LINK_XDEV = 10,
RM_LINK_NONE = 11,
RM_LINK_DIR = 12,
} RmLinkType;

#if HAVE_STAT64 && !RM_IS_APPLE
Expand Down