From 5808636263d72b635a46100a7e7037074dad8f75 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 27 Jun 2020 10:03:35 +0000 Subject: kqnotify|fake_inotify: detect Maildir write ops We need to detect link(2) and rename(2) in other apps writing to the Maildir. We'll be removing the Filesys::Notify::Simple from -watch in favor of using IO::KQueue or Linux::Inotify2 directly. Ensure non-inotify emulations can support everything we expect for Maildir writers. --- lib/PublicInbox/KQNotify.pm | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'lib/PublicInbox/KQNotify.pm') diff --git a/lib/PublicInbox/KQNotify.pm b/lib/PublicInbox/KQNotify.pm index 110594cc..9673b442 100644 --- a/lib/PublicInbox/KQNotify.pm +++ b/lib/PublicInbox/KQNotify.pm @@ -7,6 +7,11 @@ package PublicInbox::KQNotify; use strict; use IO::KQueue; use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY +use PublicInbox::FakeInotify; +use Time::HiRes qw(stat); + +# NOTE_EXTEND detects rename(2), NOTE_WRITE detects link(2) +sub MOVED_TO_OR_CREATE () { NOTE_EXTEND|NOTE_WRITE } sub new { my ($class) = @_; @@ -15,19 +20,28 @@ sub new { sub watch { my ($self, $path, $mask, $cb) = @_; - open(my $fh, '<', $path) or return; + my ($fh, $cls, @extra); + if (-d $path) { + opendir($fh, $path) or return; + my @st = stat($fh); + @extra = ($path, $st[10]); # 10: ctime + $cls = 'PublicInbox::KQNotify::Watchdir'; + } else { + open($fh, '<', $path) or return; + $cls = 'PublicInbox::KQNotify::Watch'; + } my $ident = fileno($fh); $self->{dskq}->{kq}->EV_SET($ident, # ident EVFILT_VNODE, # filter EV_ADD | EV_CLEAR, # flags $mask, # fflags 0, 0); # data, udata - if ($mask == NOTE_WRITE) { - $self->{watch}->{$ident} = [ $fh, $cb ]; + if ($mask == NOTE_WRITE || $mask == MOVED_TO_OR_CREATE) { + $self->{watch}->{$ident} = [ $fh, $cb, @extra ]; } else { die "TODO Not implemented: $mask"; } - bless \$fh, 'PublicInbox::KQNotify::Watch'; + bless \$fh, $cls; } # emulate Linux::Inotify::fileno @@ -48,8 +62,15 @@ sub poll { for my $kev (@kevents) { my $ident = $kev->[KQ_IDENT]; my $mask = $kev->[KQ_FFLAGS]; - if (($mask & NOTE_WRITE) == NOTE_WRITE) { - eval { $self->{watch}->{$ident}->[1]->() }; + my ($dh, $cb, $path, $old_ctime) = @{$self->{watch}->{$ident}}; + if (!defined($path) && ($mask & NOTE_WRITE) == NOTE_WRITE) { + eval { $cb->() }; + } elsif ($mask & MOVED_TO_OR_CREATE) { + my @new_st = stat($path) or next; + $self->{watch}->{$ident}->[3] = $new_st[10]; # ctime + rewinddir($dh); + PublicInbox::FakeInotify::on_new_files($dh, $cb, + $path, $old_ctime); } } } @@ -59,4 +80,9 @@ use strict; sub cancel { close ${$_[0]} or die "close: $!" } +package PublicInbox::KQNotify::Watchdir; +use strict; + +sub cancel { closedir ${$_[0]} or die "closedir: $!" } + 1; -- cgit v1.2.3-24-ge0c7