about summary refs log tree commit homepage
path: root/lib/PublicInbox/LeiExportKw.pm
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2021-10-21 21:10:32 +0000
committerEric Wong <e@80x24.org>2021-10-22 00:54:51 +0000
commit4cd7a78f3b8c03670e2d77675229472506eee1eb (patch)
tree2a0e89de3e77b70962e509d41883518fb6574dde /lib/PublicInbox/LeiExportKw.pm
parent2c354e17694da744c6dc1ab19c14af3d456b28bb (diff)
downloadpublic-inbox-4cd7a78f3b8c03670e2d77675229472506eee1eb.tar.gz
One syscall is better than two for atomicity in Maildirs.  This
means there's no window where another process can see both the
old and new file at the same time (link && unlink), nor a window
where we might inadvertantly clobber an existing file if we were
to do `stat && rename'.
Diffstat (limited to 'lib/PublicInbox/LeiExportKw.pm')
-rw-r--r--lib/PublicInbox/LeiExportKw.pm19
1 files changed, 6 insertions, 13 deletions
diff --git a/lib/PublicInbox/LeiExportKw.pm b/lib/PublicInbox/LeiExportKw.pm
index 0b65c276..ceeef7f2 100644
--- a/lib/PublicInbox/LeiExportKw.pm
+++ b/lib/PublicInbox/LeiExportKw.pm
@@ -7,6 +7,7 @@ use strict;
 use v5.10.1;
 use parent qw(PublicInbox::IPC PublicInbox::LeiInput);
 use Errno qw(EEXIST ENOENT);
+use PublicInbox::Syscall qw(rename_noreplace);
 
 sub export_kw_md { # LeiMailSync->each_src callback
         my ($oidbin, $id, $self, $mdir) = @_;
@@ -30,30 +31,22 @@ sub export_kw_md { # LeiMailSync->each_src callback
         my $lei = $self->{lei};
         for my $d (@try) {
                 my $src = "$mdir/$d/$$id";
-
-                # we use link(2) + unlink(2) since rename(2) may
-                # inadvertently clobber if the "uniquefilename" part wasn't
-                # actually unique.
-                if (link($src, $dst)) { # success
-                        # unlink(2) may ENOENT from parallel invocation,
-                        # ignore it, but not other serious errors
-                        if (!unlink($src) and $! != ENOENT) {
-                                $lei->child_error(1, "E: unlink($src): $!");
-                        }
+                if (rename_noreplace($src, $dst)) { # success
                         $self->{lms}->mv_src("maildir:$mdir",
                                                 $oidbin, $id, $bn);
-                        return; # success anyways if link(2) worked
+                        return; # success
                 } elsif ($! == EEXIST) { # lost race with lei/store?
                         return;
                 } elsif ($! != ENOENT) {
-                        $lei->child_error(1, "E: link($src -> $dst): $!");
+                        $lei->child_error(1,
+                                "E: rename_noreplace($src -> $dst): $!");
                 } # else loop @try
         }
         my $e = $!;
         # both tries failed
         my $oidhex = unpack('H*', $oidbin);
         my $src = "$mdir/{".join(',', @try)."}/$$id";
-        $lei->child_error(1, "link($src -> $dst) ($oidhex): $e");
+        $lei->child_error(1, "rename_noreplace($src -> $dst) ($oidhex): $e");
         for (@try) { return if -e "$mdir/$_/$$id" }
         $self->{lms}->clear_src("maildir:$mdir", $id);
 }