From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 101981F5AE for ; Fri, 14 May 2021 20:14:48 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH] dir_idle: support IN_DELETE_SELF|IN_MOVE_SELF, too Date: Fri, 14 May 2021 20:14:47 +0000 Message-Id: <20210514201447.6180-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: We'll treat IN_MOVE_SELF as IN_DELETE_SELF since there doesn't seem to be a reliable way to distinguish them with FakeInotify, nor know the new name with kevent. --- lib/PublicInbox/DirIdle.pm | 12 +++++++++--- lib/PublicInbox/FakeInotify.pm | 23 ++++++++++++++++++++--- lib/PublicInbox/KQNotify.pm | 32 +++++++++++++++++++++++++------- t/dir_idle.t | 27 ++++++++++++++++++++++++--- 4 files changed, 78 insertions(+), 16 deletions(-) diff --git a/lib/PublicInbox/DirIdle.pm b/lib/PublicInbox/DirIdle.pm index e53fd9d1..5142d005 100644 --- a/lib/PublicInbox/DirIdle.pm +++ b/lib/PublicInbox/DirIdle.pm @@ -12,17 +12,23 @@ my ($MAIL_IN, $MAIL_GONE, $ino_cls); if ($^O eq 'linux' && eval { require Linux::Inotify2; 1 }) { $MAIL_IN = Linux::Inotify2::IN_MOVED_TO() | Linux::Inotify2::IN_CREATE(); - $MAIL_GONE = Linux::Inotify2::IN_DELETE(); + $MAIL_GONE = Linux::Inotify2::IN_DELETE() | + Linux::Inotify2::IN_DELETE_SELF() | + Linux::Inotify2::IN_MOVE_SELF(); $ino_cls = 'Linux::Inotify2'; # Perl 5.22+ is needed for fileno(DIRHANDLE) support: } elsif ($^V ge v5.22 && eval { require PublicInbox::KQNotify }) { $MAIL_IN = PublicInbox::KQNotify::MOVED_TO_OR_CREATE(); - $MAIL_GONE = PublicInbox::KQNotify::NOTE_DELETE(); + $MAIL_GONE = PublicInbox::KQNotify::NOTE_DELETE() | + PublicInbox::KQNotify::NOTE_REVOKE() | + PublicInbox::KQNotify::NOTE_RENAME(); $ino_cls = 'PublicInbox::KQNotify'; } else { require PublicInbox::FakeInotify; $MAIL_IN = PublicInbox::FakeInotify::MOVED_TO_OR_CREATE(); - $MAIL_GONE = PublicInbox::FakeInotify::IN_DELETE(); + $MAIL_GONE = PublicInbox::FakeInotify::IN_DELETE() | + PublicInbox::FakeInotify::IN_DELETE_SELF() | + PublicInbox::FakeInotify::IN_MOVE_SELF(); } sub new { diff --git a/lib/PublicInbox/FakeInotify.pm b/lib/PublicInbox/FakeInotify.pm index 644f5b5b..641bc5bd 100644 --- a/lib/PublicInbox/FakeInotify.pm +++ b/lib/PublicInbox/FakeInotify.pm @@ -13,7 +13,9 @@ sub IN_MODIFY () { 0x02 } # match Linux inotify # my $IN_MOVED_TO = 0x80; # my $IN_CREATE = 0x100; sub MOVED_TO_OR_CREATE () { 0x80 | 0x100 } -sub IN_DELETE () { 0x00000200 } +sub IN_DELETE () { 0x200 } +sub IN_DELETE_SELF () { 0x400 } +sub IN_MOVE_SELF () { 0x800 } our @EXPORT_OK = qw(fill_dirlist on_dir_change); @@ -44,7 +46,7 @@ sub watch { # also used by KQNotify since it kevent requires readdir on st_nlink # count changes. -sub on_dir_change ($$$$;$) { +sub on_dir_change ($$$$$) { my ($events, $dh, $path, $old_ctime, $dirlist) = @_; my $oldlist = $dirlist->{$path}; my $newlist = $oldlist ? {} : undef; @@ -79,7 +81,14 @@ sub read { my @watch_gone; for my $x (keys %$watch) { my ($path, $mask) = split(/\0/, $x, 2); - my @now = stat($path) or next; + my @now = stat($path); + if (!@now && $!{ENOENT} && ($mask & IN_DELETE_SELF)) { + push @$events, bless(\$path, + 'PublicInbox::FakeInotify::SelfGoneEvent'); + push @watch_gone, $x; + delete $self->{dirlist}->{$path}; + } + next if !@now; my $old_ctime = $watch->{$x}; $watch->{$x} = $now[10]; next if $old_ctime == $now[10]; @@ -92,6 +101,7 @@ sub read { $self->{dirlist}); } elsif ($!{ENOENT}) { push @watch_gone, $x; + delete $self->{dirlist}->{$path}; } else { warn "W: opendir $path: $!\n"; } @@ -126,6 +136,7 @@ use strict; sub fullname { ${$_[0]} } sub IN_DELETE { 0 } +sub IN_DELETE_SELF { 0 } package PublicInbox::FakeInotify::GoneEvent; use strict; @@ -133,4 +144,10 @@ our @ISA = qw(PublicInbox::FakeInotify::Event); sub IN_DELETE { 1 } +package PublicInbox::FakeInotify::SelfGoneEvent; +use strict; +our @ISA = qw(PublicInbox::FakeInotify::GoneEvent); + +sub IN_DELETE_SELF { 1 } + 1; diff --git a/lib/PublicInbox/KQNotify.pm b/lib/PublicInbox/KQNotify.pm index fc321a16..7efb8b60 100644 --- a/lib/PublicInbox/KQNotify.pm +++ b/lib/PublicInbox/KQNotify.pm @@ -33,14 +33,16 @@ sub watch { 'PublicInbox::KQNotify::Watch'; } my $ident = fileno($fh); - $self->{dskq}->{kq}->EV_SET($ident, # ident + $self->{dskq}->{kq}->EV_SET($ident, # ident (fd) EVFILT_VNODE, # filter EV_ADD | EV_CLEAR, # flags $mask, # fflags 0, 0); # data, udata - if ($mask & (MOVED_TO_OR_CREATE | NOTE_DELETE)) { + if ($mask & (MOVED_TO_OR_CREATE|NOTE_DELETE|NOTE_LINK|NOTE_REVOKE)) { $self->{watch}->{$ident} = $watch; - fill_dirlist($self, $path, $fh) if $mask & NOTE_DELETE; + if ($mask & (NOTE_DELETE|NOTE_LINK|NOTE_REVOKE)) { + fill_dirlist($self, $path, $fh) + } } else { die "TODO Not implemented: $mask"; } @@ -63,21 +65,37 @@ sub read { my ($self) = @_; my @kevents = $self->{dskq}->{kq}->kevent(0); my $events = []; + my @gone; + my $watch = $self->{watch}; for my $kev (@kevents) { my $ident = $kev->[KQ_IDENT]; my $mask = $kev->[KQ_FFLAGS]; - my ($dh, $path, $old_ctime) = @{$self->{watch}->{$ident}}; + my ($dh, $path, $old_ctime) = @{$watch->{$ident}}; if (!defined($old_ctime)) { push @$events, bless(\$path, 'PublicInbox::FakeInotify::Event') - } elsif ($mask & (MOVED_TO_OR_CREATE | NOTE_DELETE)) { - my @new_st = stat($path) or next; - $self->{watch}->{$ident}->[3] = $new_st[10]; # ctime + } elsif ($mask & (MOVED_TO_OR_CREATE|NOTE_DELETE|NOTE_LINK| + NOTE_REVOKE|NOTE_RENAME)) { + my @new_st = stat($path); + if (!@new_st && $!{ENOENT}) { + push @$events, bless(\$path, + 'PublicInbox::FakeInotify::'. + 'SelfGoneEvent'); + push @gone, $ident; + delete $self->{dirlist}->{$path}; + next; + } + if (!@new_st) { + warn "unhandled stat($path) error: $!\n"; + next; + } + $watch->{$ident}->[3] = $new_st[10]; # ctime rewinddir($dh); on_dir_change($events, $dh, $path, $old_ctime, $self->{dirlist}); } } + delete @$watch{@gone}; @$events; } diff --git a/t/dir_idle.t b/t/dir_idle.t index 969c16e9..0bb3b758 100644 --- a/t/dir_idle.t +++ b/t/dir_idle.t @@ -6,10 +6,10 @@ use PublicInbox::DS qw(now); use File::Path qw(make_path); use_ok 'PublicInbox::DirIdle'; my ($tmpdir, $for_destroy) = tmpdir(); -make_path("$tmpdir/a/b"); +make_path("$tmpdir/a/b", "$tmpdir/c"); my @x; my $cb = sub { push @x, \@_ }; -my $di = PublicInbox::DirIdle->new(["$tmpdir/a"], $cb, 1); +my $di = PublicInbox::DirIdle->new(["$tmpdir/a", "$tmpdir/c"], $cb, 1); PublicInbox::DS->SetLoopTimeout(1000); my $end = 3 + now; PublicInbox::DS->SetPostLoopCallback(sub { scalar(@x) == 0 && now < $end }); @@ -17,6 +17,27 @@ tick(0.011); rmdir("$tmpdir/a/b") or xbail "rmdir $!"; PublicInbox::DS->EventLoop; is(scalar(@x), 1, 'got an event') and - is($x[0]->[0]->fullname, "$tmpdir/a/b", 'got expected fullname'); + is($x[0]->[0]->fullname, "$tmpdir/a/b", 'got expected fullname') and + ok($x[0]->[0]->IN_DELETE, 'IN_DELETE set'); + +tick(0.011); +rmdir("$tmpdir/a") or xbail "rmdir $!"; +@x = (); +$end = 3 + now; +PublicInbox::DS->EventLoop; +is(scalar(@x), 1, 'got an event') and + is($x[0]->[0]->fullname, "$tmpdir/a", 'got expected fullname') and + ok($x[0]->[0]->IN_DELETE_SELF, 'IN_DELETE_SELF set'); + +tick(0.011); +rename("$tmpdir/c", "$tmpdir/j") or xbail "rmdir $!"; +@x = (); +$end = 3 + now; +PublicInbox::DS->EventLoop; +is(scalar(@x), 1, 'got an event') and + is($x[0]->[0]->fullname, "$tmpdir/c", 'got expected fullname') and + ok($x[0]->[0]->IN_DELETE_SELF || $x[0]->[0]->IN_MOVE_SELF, + 'IN_DELETE_SELF set on move'); + PublicInbox::DS->Reset; done_testing;