Skip to content

Commit 247cb92

Browse files
andy5995claude
andcommitted
ficlone: improve error reporting in do_ficlone_dir; factor out shared safeguard tests
- Add stderr messages for previously silent failure paths in do_ficlone_dir: lstat, readlink, symlink, and unlink of src_child all now print the filename and error string. - Add files_moved counter; emit a partial-move warning (naming both src and dst) only when at least one entry was already transferred before the failure, so a clean early failure stays silent. - Factor the two read-only-dir safeguard test cases out of test_btrfs_clone.sh and test_bcachefs.sh into test/test_ficlone_safeguards.sh; each script now sets FS_MOUNTPOINT/FS_RMW_CMD/FS_WASTE_DIR and sources the shared file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 05f3a8e commit 247cb92

4 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/ficlone.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ do_ficlone(const char *source, const char *dest, int *save_errno)
127127
{
128128
*save_errno = errno;
129129
perror("unlink source");
130+
/* dest is a valid clone but source couldn't be removed; clean up dest
131+
so the caller can retry rather than leaving an orphan in the waste folder */
132+
if (unlink(dest) != 0)
133+
fprintf(stderr, "unlink: %s in %s\n", strerror(errno), __func__);
130134
return -1;
131135
}
132136

@@ -162,6 +166,7 @@ do_ficlone_dir(const char *src, const char *dst, int *save_errno)
162166
}
163167

164168
int result = 0;
169+
int files_moved = 0;
165170
struct dirent *entry;
166171
while ((entry = readdir(dir)) != NULL)
167172
{
@@ -175,6 +180,7 @@ do_ficlone_dir(const char *src, const char *dst, int *save_errno)
175180
if (lstat(src_child, &st) != 0)
176181
{
177182
*save_errno = errno;
183+
fprintf(stderr, "lstat '%s': %s\n", src_child, strerror(*save_errno));
178184
result = -1;
179185
}
180186
else
@@ -193,6 +199,7 @@ do_ficlone_dir(const char *src, const char *dst, int *save_errno)
193199
if (len == -1)
194200
{
195201
*save_errno = errno;
202+
fprintf(stderr, "readlink '%s': %s\n", src_child, strerror(*save_errno));
196203
result = -1;
197204
}
198205
else
@@ -201,11 +208,15 @@ do_ficlone_dir(const char *src, const char *dst, int *save_errno)
201208
if (symlink(link_target, dst_child) != 0)
202209
{
203210
*save_errno = errno;
211+
fprintf(stderr, "symlink '%s': %s\n", dst_child, strerror(*save_errno));
204212
result = -1;
205213
}
206214
else if (unlink(src_child) != 0)
207215
{
208216
*save_errno = errno;
217+
fprintf(stderr, "unlink '%s': %s\n", src_child, strerror(*save_errno));
218+
/* src_child is still intact; remove dst_child to avoid duplicate */
219+
unlink(dst_child);
209220
result = -1;
210221
}
211222
}
@@ -224,12 +235,19 @@ do_ficlone_dir(const char *src, const char *dst, int *save_errno)
224235

225236
if (result != 0)
226237
break;
238+
files_moved++;
227239
}
228240

229241
closedir(dir);
230242

231243
if (result != 0)
244+
{
245+
if (files_moved > 0)
246+
fprintf(stderr,
247+
"partial move: check both '%s' and '%s' -- some files may have already been moved\n",
248+
src, dst);
232249
return -1;
250+
}
233251

234252
if (rmdir(src) != 0)
235253
{

test/test_bcachefs.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ test -f "$BCACHEFS_SYM_DIR/real_file"
159159
test -L "$BCACHEFS_SYM_DIR/link_file"
160160
test ! -f "$BCACHEFS_WASTE_DIR/info/sym_dir.trashinfo"
161161

162+
FS_MOUNTPOINT="$BCACHEFS_MOUNTPOINT"
163+
FS_RMW_CMD="$BCACHEFS_RMW_CMD"
164+
FS_WASTE_DIR="$BCACHEFS_WASTE_DIR"
165+
. "${MESON_SOURCE_ROOT}/test/test_ficlone_safeguards.sh"
166+
162167
# --- Test: purge an expired file from bcachefs waste ---
163168
echo "== Test: purge an expired file from bcachefs waste"
164169
RMW_FAKE_YEAR=true $BCACHEFS_RMW_CMD foo

test/test_btrfs_clone.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ test -f "$BTRFS_SYM_DIR/real_file"
122122
test -L "$BTRFS_SYM_DIR/link_file"
123123
test ! -f "$BTRFS_WASTE_DIR/info/sym_dir.trashinfo"
124124

125+
FS_MOUNTPOINT="$BTRFS_MOUNTPOINT"
126+
FS_RMW_CMD="$BTRFS_RMW_CMD"
127+
FS_WASTE_DIR="$BTRFS_WASTE_DIR"
128+
. "${MESON_SOURCE_ROOT}/test/test_ficlone_safeguards.sh"
129+
125130
# --- Test: move a file across btrfs subvolumes ---
126131
echo "== Test: move a file across btrfs subvolumes"
127132
touch foo

test/test_ficlone_safeguards.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Sourced by test_btrfs_clone.sh and test_bcachefs.sh.
2+
# Requires: FS_MOUNTPOINT, FS_RMW_CMD, FS_WASTE_DIR
3+
4+
# --- Test: no orphan file in waste when source unlink fails ---
5+
echo "== Test: no orphan in waste when source file unlink fails"
6+
FS_RO_DIR="$FS_MOUNTPOINT/ro_dir"
7+
chmod 0755 "$FS_RO_DIR" 2>/dev/null || true
8+
rm -rf "$FS_RO_DIR"
9+
mkdir "$FS_RO_DIR"
10+
touch "$FS_RO_DIR/file"
11+
chmod 0555 "$FS_RO_DIR"
12+
$FS_RMW_CMD "$FS_RO_DIR" || true
13+
chmod 0755 "$FS_RO_DIR"
14+
test ! -f "$FS_WASTE_DIR/files/ro_dir/file"
15+
test -f "$FS_RO_DIR/file"
16+
rm -rf "$FS_RO_DIR"
17+
18+
# --- Test: no orphan symlink in waste when source symlink unlink fails ---
19+
echo "== Test: no orphan symlink in waste when source symlink unlink fails"
20+
chmod 0755 "$FS_RO_DIR" 2>/dev/null || true
21+
rm -rf "$FS_RO_DIR"
22+
mkdir "$FS_RO_DIR"
23+
ln -s nonexistent "$FS_RO_DIR/link"
24+
chmod 0555 "$FS_RO_DIR"
25+
$FS_RMW_CMD "$FS_RO_DIR" || true
26+
chmod 0755 "$FS_RO_DIR"
27+
test ! -L "$FS_WASTE_DIR/files/ro_dir/link"
28+
test -L "$FS_RO_DIR/link"
29+
rm -rf "$FS_RO_DIR"

0 commit comments

Comments
 (0)