]> Sergey Matveev's repositories - public-inbox.git/commitdiff
dir_idle: support IN_DELETE_SELF|IN_MOVE_SELF, too
authorEric Wong <e@80x24.org>
Fri, 14 May 2021 20:14:47 +0000 (20:14 +0000)
committerEric Wong <e@80x24.org>
Sat, 15 May 2021 05:39:17 +0000 (05:39 +0000)
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
lib/PublicInbox/FakeInotify.pm
lib/PublicInbox/KQNotify.pm
t/dir_idle.t

index e53fd9d1e5fe0db5b1a2cac1b053e6d641a276d1..5142d005e4aa326543fe8a99dca0c4e491eb42c5 100644 (file)
@@ -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 {
index 644f5b5b7229316f7ae48745d0e1421b1203668b..641bc5bdc7c62987e390ad0f134dcdee32930774 100644 (file)
@@ -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;
index fc321a1674992558904e85c33251e6c3e15721ae..7efb8b604c99e326d02c38c3876355aa23018a35 100644 (file)
@@ -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;
 }
 
index 969c16e9bb77e328dd987a3d6d61263845c3d21a..0bb3b7585328dc2308c315019b80320c53e4508c 100644 (file)
@@ -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;