Skip to content

Commit 6ede57f

Browse files
author
Will
committed
adapter/inotify: support partial rename events
1 parent f4296a7 commit 6ede57f

5 files changed

Lines changed: 88 additions & 2 deletions

File tree

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 0.14.4
44

5+
- Fixed rename event reporting in the inotify adapter when there is no "moved-from" half of the event provided (#105, thanks to @scoueille and @Gautrot for reporting; introduced with fixes for #89).
6+
- Addsd a regression test for issue #105.
57
- Fixed a rare misreporting error in the darwin/fsevents adapter where a rename event very quickly after a creation event on the same path could cause the creation event to be duplicated alongside the rename event.
68
- Only add CMake install rules if we are the main project, to be good neighbors when added as a submodule (#111, thanks to @unravel-dev)
79
* Fix Windows distribution archive structure to place headers in wtr/ dir (#108, thanks to @dunglas)

devel/include/detail/wtr/watcher/adapter/linux/inotify/watch.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ inline auto peek = [](
194194
struct parsed {
195195
static constexpr uint16_t err_pending = 1 << 0;
196196
static constexpr uint16_t err_overflow = 1 << 1;
197+
static constexpr uint16_t err_partial = 1 << 2;
197198
::wtr::watcher::event ev{};
198199
inotify_event* next = nullptr;
199200
uint16_t err = 0;
@@ -241,7 +242,12 @@ inline auto parse_ev = [](
241242
return {{ke.fae.evs[i].ev, {path, et, pt}}, next};
242243
}
243244
}
244-
/* Otherwise, save the current event for later */
245+
/* Partial rename events are inferred if the MOVED_FROM
246+
half of the event is not adjacent nor previously seen
247+
in the associated event buffer. */
248+
if (in->mask & IN_MOVED_TO)
249+
return {{{"", et, pt}, {path, et, pt}}, next, parsed::err_partial};
250+
/* Otherwise, save the current event (MOVED_FROM) for later */
245251
auto err = ke.fae.evs[ke.fae.idx_rm].cookie != 0
246252
? parsed::err_overflow
247253
: parsed::err_pending;
@@ -385,6 +391,8 @@ inline auto do_ev_recv = [](auto const& cb, sysres& sr) -> result
385391
auto parsed = parse_ev(sr.ke, in_ev, in_ev_tail);
386392
if (parsed.err & parsed::err_overflow)
387393
send_msg(result::w_self_q_overflow, parsed.ev.path_name.c_str(), cb);
394+
if (parsed.err & parsed::err_partial)
395+
send_msg(result::w_sys_partial, parsed.ev.associated->path_name.c_str(), cb);
388396
if (msk & IN_ISDIR && msk & IN_CREATE)
389397
walkdir_do(parsed.ev.path_name.c_str(), [&](auto dir) {
390398
do_mark(dir, sr.ke.fd, sr.ke.dm, cb);

devel/include/detail/wtr/watcher/adapter/linux/sysres.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum class result : unsigned short {
3535
w_sys_bad_meta,
3636
w_sys_q_overflow,
3737
w_self_q_overflow,
38+
w_sys_partial,
3839
complete,
3940
e,
4041
e_sys_api_inotify,
@@ -84,6 +85,7 @@ inline constexpr auto to_str(result r)
8485
case result::w_sys_bad_meta: return "w/sys/bad_meta@";
8586
case result::w_sys_q_overflow: return "w/sys/q_overflow@";
8687
case result::w_self_q_overflow: return "w/self/q_overflow@";
88+
case result::w_sys_partial: return "w/sys/partial@";
8789
case result::complete: return "complete@";
8890
case result::e: return "e@";
8991
case result::e_sys_api_inotify: return "e/sys/api/inotify@";

include/wtr/watcher.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@ enum class result : unsigned short {
861861
w_sys_bad_meta,
862862
w_sys_q_overflow,
863863
w_self_q_overflow,
864+
w_sys_partial,
864865
complete,
865866
e,
866867
e_sys_api_inotify,
@@ -910,6 +911,7 @@ inline constexpr auto to_str(result r)
910911
case result::w_sys_bad_meta: return "w/sys/bad_meta@";
911912
case result::w_sys_q_overflow: return "w/sys/q_overflow@";
912913
case result::w_self_q_overflow: return "w/self/q_overflow@";
914+
case result::w_sys_partial: return "w/sys/partial@";
913915
case result::complete: return "complete@";
914916
case result::e: return "e@";
915917
case result::e_sys_api_inotify: return "e/sys/api/inotify@";
@@ -1489,6 +1491,7 @@ inline auto peek = [](
14891491
struct parsed {
14901492
static constexpr uint16_t err_pending = 1 << 0;
14911493
static constexpr uint16_t err_overflow = 1 << 1;
1494+
static constexpr uint16_t err_partial = 1 << 2;
14921495
::wtr::watcher::event ev{};
14931496
inotify_event* next = nullptr;
14941497
uint16_t err = 0;
@@ -1536,7 +1539,12 @@ inline auto parse_ev = [](
15361539
return {{ke.fae.evs[i].ev, {path, et, pt}}, next};
15371540
}
15381541
}
1539-
/* Otherwise, save the current event for later */
1542+
/* Partial rename events are inferred if the MOVED_FROM
1543+
half of the event is not adjacent nor previously seen
1544+
in the associated event buffer. */
1545+
if (in->mask & IN_MOVED_TO)
1546+
return {{{"", et, pt}, {path, et, pt}}, next, parsed::err_partial};
1547+
/* Otherwise, save the current event (MOVED_FROM) for later */
15401548
auto err = ke.fae.evs[ke.fae.idx_rm].cookie != 0
15411549
? parsed::err_overflow
15421550
: parsed::err_pending;
@@ -1680,6 +1688,8 @@ inline auto do_ev_recv = [](auto const& cb, sysres& sr) -> result
16801688
auto parsed = parse_ev(sr.ke, in_ev, in_ev_tail);
16811689
if (parsed.err & parsed::err_overflow)
16821690
send_msg(result::w_self_q_overflow, parsed.ev.path_name.c_str(), cb);
1691+
if (parsed.err & parsed::err_partial)
1692+
send_msg(result::w_sys_partial, parsed.ev.associated->path_name.c_str(), cb);
16831693
if (msk & IN_ISDIR && msk & IN_CREATE)
16841694
walkdir_do(parsed.ev.path_name.c_str(), [&](auto dir) {
16851695
do_mark(dir, sr.ke.fd, sr.ke.dm, cb);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#! /usr/bin/env bash
2+
# shellcheck source=tool/test/.ctx
3+
# Regression test for issue #105
4+
5+
ec=0
6+
7+
(
8+
desc='Rename from outside watched tree produces partial rename event'
9+
10+
if [ "$(uname -s)" != Linux ]; then
11+
echo "$desc ... [Skipped on non-Linux system]"
12+
exit 0
13+
fi
14+
15+
read -r -d '' expect << .
16+
[
17+
{
18+
"effect_type": "create",
19+
"path_name": "s/self/live@d",
20+
"path_type": "watcher"
21+
},
22+
{
23+
"effect_type": "other",
24+
"path_name": "w/sys/partial@d/target",
25+
"path_type": "watcher"
26+
},
27+
{
28+
"effect_type": "rename",
29+
"path_name": "",
30+
"path_type": "file",
31+
"associated": {
32+
"effect_type": "rename",
33+
"path_name": "d/target",
34+
"path_type": "file"
35+
}
36+
},
37+
{
38+
"effect_type": "destroy",
39+
"path_name": "s/self/die@d",
40+
"path_type": "watcher"
41+
}
42+
]
43+
.
44+
45+
echo -n "$desc ... "
46+
47+
. "$(dirname "$0")/.ctx"
48+
49+
unwatched=$(mktemp -d -t unwatched-XXXXXX)
50+
trap "rm -rf '$unwatched'" EXIT
51+
52+
actual=$(
53+
watch-async "$testdir" -ms 800 > "$testdir.json"
54+
echo "test content" > "$unwatched/source"
55+
mv "$unwatched/source" "$testdir/target"
56+
wait # for the watcher
57+
show-events "$testdir" | without-effect-time
58+
)
59+
60+
check-result "$expect" "$actual"
61+
)
62+
ec=$((ec + $?))
63+
64+
exit "$ec"

0 commit comments

Comments
 (0)