Skip to content

Commit 28f54dd

Browse files
committed
sh: support reflinking directories
- modify rm_util_link_type() to specifically detect directory pairs - modify sh output generator to emit cp_reflink for directory pairs - modify sh template to support reflinking directories and extend stampfile code to preserve attributes recursively Fixes sahib#618.
1 parent 4d9f215 commit 28f54dd

File tree

5 files changed

+43
-11
lines changed

5 files changed

+43
-11
lines changed

lib/formats/sh.c.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ static bool rm_sh_emit_handler_clone(RmFmtHandlerShScript *self, char **out, RmF
106106
case RM_LINK_XDEV:
107107
case RM_LINK_SYMLINK:
108108
case RM_LINK_BOTH_EMPTY:
109+
case RM_LINK_DIR:
109110
rm_log_warning_line("Unexpected return code %d from rm_util_link_type()", link_type);
110111
return FALSE;
111112
case RM_LINK_HARDLINK:
@@ -144,6 +145,7 @@ static bool rm_sh_emit_handler_reflink(RmFmtHandlerShScript *self, char **out, R
144145
case RM_LINK_BOTH_EMPTY:
145146
rm_log_warning_line("Unexpected return code %d from rm_util_link_type()", link_type);
146147
return FALSE;
148+
case RM_LINK_DIR:
147149
case RM_LINK_HARDLINK:
148150
case RM_LINK_SYMLINK:
149151
case RM_LINK_NONE:

lib/formats/sh.sh

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,25 +225,34 @@ cp_hardlink() {
225225
}
226226

227227
cp_reflink() {
228-
if [ -d "$1" ]; then
229-
# for duplicate dir's, can't clone so use symlink
230-
cp_symlink "$@"
231-
return $?
232-
fi
233228
print_progress_prefix
234229
# reflink $1 to $2's data, preserving $1's mtime
235230
printf "${COL_YELLOW}Reflinking to original: ${COL_RESET}%%s\n" "$1"
236231
if original_check "$1" "$2"; then
237232
if [ -z "$DO_DRY_RUN" ]; then
238-
if [ -z "$STAMPFILE2" ]; then
233+
if [ -d "$1" ]; then
234+
local STAMPFILE2="$(mktemp -d "${TMPDIR:-/tmp}/rmlint.XXXXXXXX.stamp.d")"
235+
elif [ -z "$STAMPFILE2" ]; then
239236
STAMPFILE2=$(mktemp "${TMPDIR:-/tmp}/rmlint.XXXXXXXX.stamp")
240237
fi
241238
cp --archive --attributes-only --no-target-directory -- "$1" "$STAMPFILE2"
242239
if [ -d "$1" ]; then
240+
# to reflink a directory, we will have to delete it, thus changing parent mtime
241+
# take care of preserving parent mtime if requested
242+
if [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
243+
touch -r "$(dirname "$1")" -- "$STAMPFILE"
244+
fi
243245
rm -rf -- "$1"
244246
fi
245247
cp --archive --reflink=always -- "$2" "$1"
246248
cp --archive --attributes-only --no-target-directory -- "$STAMPFILE2" "$1"
249+
if [ -d "$1" ]; then
250+
rm -rf -- "$STAMPFILE2"
251+
if [ -n "$DO_KEEP_DIR_TIMESTAMPS" ]; then
252+
# restore parent mtime if we saved it
253+
touch -r "$STAMPFILE" -- "$(dirname "$1")"
254+
fi
255+
fi
247256
fi
248257
fi
249258
}

lib/reflink.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ int rm_is_reflink_main(int argc, const char **argv) {
468468
" %i: %s\n"
469469
" %i: %s\n"
470470
" %i: %s\n"
471+
" %i: %s\n"
471472
" %i: %s\n",
472473
_("Test if two files are reflinks (share same data extents)"),
473474
_("Returns 0 if the files are reflinks."),
@@ -481,7 +482,8 @@ int rm_is_reflink_main(int argc, const char **argv) {
481482
RM_LINK_HARDLINK, desc[RM_LINK_HARDLINK],
482483
RM_LINK_SYMLINK, desc[RM_LINK_SYMLINK],
483484
RM_LINK_XDEV, desc[RM_LINK_XDEV],
484-
RM_LINK_NONE, desc[RM_LINK_NONE]);
485+
RM_LINK_NONE, desc[RM_LINK_NONE],
486+
RM_LINK_DIR, desc[RM_LINK_DIR]);
485487

486488

487489
g_option_context_set_summary(context, summary);

lib/utilities.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,7 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
13841384
RM_RETURN(RM_LINK_ERROR);
13851385
}
13861386

1387-
if(!S_ISREG(stat1.st_mode)) {
1387+
if(!S_ISREG(stat1.st_mode) && !S_ISDIR(stat1.st_mode)) {
13881388
RM_RETURN(S_ISLNK(stat1.st_mode) ? RM_LINK_SYMLINK : RM_LINK_NOT_FILE);
13891389
}
13901390

@@ -1409,11 +1409,23 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
14091409
RM_RETURN(RM_LINK_ERROR);
14101410
}
14111411

1412-
if(!S_ISREG(stat2.st_mode)) {
1412+
if(!S_ISREG(stat2.st_mode) && !S_ISDIR(stat2.st_mode)) {
14131413
RM_RETURN(S_ISLNK(stat2.st_mode) ? RM_LINK_SYMLINK : RM_LINK_NOT_FILE);
14141414
}
14151415

1416-
if(stat1.st_size != stat2.st_size) {
1416+
/* At this point, path1 or path2 may be a regular file or a directory.
1417+
* Ensure they both have the same type, otherwise fail. */
1418+
bool is_dir;
1419+
if(S_ISDIR(stat1.st_mode) && S_ISDIR(stat2.st_mode)) {
1420+
is_dir = true;
1421+
} else if (S_ISREG(stat1.st_mode) && S_ISREG(stat2.st_mode)) {
1422+
is_dir = false;
1423+
} else {
1424+
RM_RETURN(RM_LINK_NOT_FILE);
1425+
}
1426+
1427+
if(!is_dir && stat1.st_size != stat2.st_size) {
1428+
/* st_size is not defined for directories */
14171429
#if _RM_OFFSET_DEBUG
14181430
rm_log_debug_line(
14191431
"rm_util_link_type: Files have different sizes: %" G_GUINT64_FORMAT
@@ -1446,6 +1458,11 @@ RmLinkType rm_util_link_type(const char *path1, const char *path2, bool use_fiem
14461458
}
14471459
}
14481460

1461+
if (is_dir) {
1462+
/* further tests do not make sense for directories */
1463+
RM_RETURN(RM_LINK_DIR);
1464+
}
1465+
14491466
if(use_fiemap) {
14501467
RmLinkType reflink_type = rm_reflink_type_from_fd(fd1, fd2);
14511468
RM_RETURN(reflink_type);
@@ -1468,7 +1485,8 @@ const char **rm_link_type_to_desc() {
14681485
N_("Encountered a symlink"),
14691486
N_("Files are on different devices"),
14701487
N_("Not linked"),
1471-
N_("Both files are empty")};
1488+
N_("Both files are empty"),
1489+
N_("Both files are directories")};
14721490
return RM_LINK_TYPE_TO_DESC;
14731491
}
14741492

lib/utilities.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ typedef enum RmLinkType {
5252
RM_LINK_XDEV = 10,
5353
RM_LINK_NONE = 11,
5454
RM_LINK_BOTH_EMPTY = 12,
55+
RM_LINK_DIR = 13,
5556
} RmLinkType;
5657

5758
#if HAVE_STAT64 && !RM_IS_APPLE

0 commit comments

Comments
 (0)