about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2021-05-14 20:14:47 +0000
committerEric Wong <e@80x24.org>2021-05-15 05:39:17 +0000
commit79e274b293a71fb41dd8bf6e9598a8e8a24fed4f (patch)
tree2847419f054afcd738b12c1a6fd3d130b9a4a3a1 /lib
parent671e7f4c9d82b053fba475aaeaa16a94dc3adad2 (diff)
downloadpublic-inbox-79e274b293a71fb41dd8bf6e9598a8e8a24fed4f.tar.gz
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.
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/DirIdle.pm12
-rw-r--r--lib/PublicInbox/FakeInotify.pm23
-rw-r--r--lib/PublicInbox/KQNotify.pm32
3 files changed, 54 insertions, 13 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;
 }