]> Sergey Matveev's repositories - public-inbox.git/commitdiff
lei: attempt to canonicalize away "/../" pathnames
authorEric Wong <e@80x24.org>
Wed, 11 Aug 2021 11:26:18 +0000 (11:26 +0000)
committerEric Wong <e@80x24.org>
Wed, 11 Aug 2021 21:50:09 +0000 (21:50 +0000)
As documented, File::Spec->canonpath does not canonicalize
"/../".  While we want to do our best to preserve symlinks in
pathnames, leaving "/../" can mislead our inotify|kqueue usage.

lib/PublicInbox/LEI.pm
lib/PublicInbox/LeiALE.pm
lib/PublicInbox/LeiBlob.pm
lib/PublicInbox/LeiInit.pm
lib/PublicInbox/LeiLcat.pm
lib/PublicInbox/LeiOverview.pm
lib/PublicInbox/LeiQuery.pm
lib/PublicInbox/LeiRediff.pm
lib/PublicInbox/LeiUp.pm
t/lei_xsearch.t

index be4754df8d162448fe9a81b69093d3242084f6d2..54fac7b4f14fcf3a26f67d3ec8b3614d39d0b49a 100644 (file)
@@ -94,6 +94,12 @@ sub rel2abs {
 # abs_path resolves symlinks in parent iff all parents exist
 sub abs_path { Cwd::abs_path($_[1]) // rel2abs(@_) }
 
+sub canonpath_harder {
+       my $p = $_[-1]; # $_[0] may be self
+       $p = File::Spec->canonpath($p);
+       $p =~ m!(?:/*|\A)\.\.(?:/*|\z)! && -e $p ? Cwd::abs_path($p) : $p;
+}
+
 sub share_path ($) { # $HOME/.local/share/lei/$FOO
        my ($self) = @_;
        rel2abs($self, ($self->{env}->{XDG_DATA_HOME} //
@@ -808,8 +814,8 @@ sub _lei_cfg ($;$) {
        my $cfg = PublicInbox::Config->git_config_dump($f);
        $cfg->{-st} = $cur_st;
        $cfg->{'-f'} = $f;
-       if ($sto && File::Spec->canonpath($sto_dir // store_path($self))
-                       eq File::Spec->canonpath($cfg->{'leistore.dir'} //
+       if ($sto && canonpath_harder($sto_dir // store_path($self))
+                       eq canonpath_harder($cfg->{'leistore.dir'} //
                                                store_path($self))) {
                $cfg->{-lei_store} = $sto;
                $cfg->{-lei_note_event} = $lne;
@@ -1382,7 +1388,7 @@ sub refresh_watches {
                        next;
                }
                if ($url =~ /\Amaildir:(.+)/i) {
-                       my $d = File::Spec->canonpath($1);
+                       my $d = canonpath_harder($1);
                        if ($state eq 'pause') {
                                cancel_maildir_watch($d, $cfg_f);
                        } elsif (!exists($MDIR2CFGPATH->{$d}->{$cfg_f})) {
@@ -1400,7 +1406,7 @@ sub refresh_watches {
                        next if exists $seen{$url};
                        delete $old->{$url};
                        if ($url =~ /\Amaildir:(.+)/i) {
-                               my $d = File::Spec->canonpath($1);
+                               my $d = canonpath_harder($1);
                                cancel_maildir_watch($d, $cfg_f);
                        } else { # TODO: imap/nntp/jmap
                                $lei->child_error(1, "E: watch $url TODO");
index cb570ab4bf8edfd050e424482574372d2fbe0dd2..cc9a2095af08e6b8f55145735ceb1e65c00cc44d 100644 (file)
@@ -33,7 +33,7 @@ sub new {
        for my $loc ($lei->externals_each) { # locals only
                $lxs->prepare_external($loc) if -d $loc;
        }
-       $self->refresh_externals($lxs);
+       $self->refresh_externals($lxs, $lei);
        $self;
 }
 
@@ -50,7 +50,7 @@ sub overs_all { # for xoids_for (called only in lei workers?)
 }
 
 sub refresh_externals {
-       my ($self, $lxs) = @_;
+       my ($self, $lxs, $lei) = @_;
        $self->git->cleanup;
        my $lk = $self->lock_for_scope;
        my $cur_lxs = ref($lxs)->new;
@@ -72,7 +72,7 @@ sub refresh_externals {
        }
        my @ibxish = $cur_lxs->locals;
        for my $x ($lxs->locals) {
-               my $d = File::Spec->canonpath($x->{inboxdir} // $x->{topdir});
+               my $d = $lei->canonpath_harder($x->{inboxdir} // $x->{topdir});
                $seen_ibxish{$d} //= do {
                        $new .= "$d\n";
                        push @ibxish, $x;
@@ -95,7 +95,7 @@ sub refresh_externals {
                $old = <$fh> // die "readline($f): $!";
        }
        for my $x (@ibxish) {
-               $new .= File::Spec->canonpath($x->git->{git_dir})."/objects\n";
+               $new .= $lei->canonpath_harder($x->git->{git_dir})."/objects\n";
        }
        $self->{ibxish} = \@ibxish;
        return if $old eq $new;
index 0921796428b4254af4f07ed00a85ecd179a28652..3158ca3beffda3ce8d4a2c0d473324f26e750f5d 100644 (file)
@@ -112,7 +112,7 @@ sub lei_blob {
        if ($opt->{mail} // ($has_hints ? 0 : 1)) {
                if (grep(defined, @$opt{qw(include only)})) {
                        $lxs = $lei->lxs_prepare;
-                       $lei->ale->refresh_externals($lxs);
+                       $lei->ale->refresh_externals($lxs, $lei);
                }
                my $rdr = {};
                if ($opt->{mail}) {
@@ -155,7 +155,7 @@ sub lei_blob {
        return $lei->fail('no --git-dir to try') unless @$git_dirs;
        unless ($lxs) {
                $lxs = $lei->lxs_prepare or return;
-               $lei->ale->refresh_externals($lxs);
+               $lei->ale->refresh_externals($lxs, $lei);
        }
        if ($lxs->remotes) {
                require PublicInbox::LeiRemote;
index c6c0c01b1332e5694279b828d9c5e6f0e1852b44..6558ac0a350672ec0f928dbec7c4896d546ea861 100644 (file)
@@ -4,7 +4,6 @@
 # for the "lei init" command, not sure if it's even needed...
 package PublicInbox::LeiInit;
 use v5.10.1;
-use File::Spec;
 
 sub lei_init {
        my ($self, $dir) = @_;
@@ -13,7 +12,7 @@ sub lei_init {
        $dir //= $self->store_path;
        $dir = $self->rel2abs($dir);
        my @cur = stat($cur) if defined($cur);
-       $cur = File::Spec->canonpath($cur // $dir);
+       $cur = $self->canonpath_harder($cur // $dir);
        my @dir = stat($dir);
        my $exists = "# leistore.dir=$cur already initialized" if @dir;
        if (@cur) {
index cb5eb5b4fd7fedbddacc1f003e49b0c875cbd19d..4a0c24a96bdb9f480163222ab28b4ef08e11f27c 100644 (file)
@@ -128,7 +128,7 @@ sub _stdin { # PublicInbox::InputPipe::consume callback for --stdin
 sub lei_lcat {
        my ($lei, @argv) = @_;
        my $lxs = $lei->lxs_prepare or return;
-       $lei->ale->refresh_externals($lxs);
+       $lei->ale->refresh_externals($lxs, $lei);
        my $sto = $lei->_lei_store(1);
        $lei->{lse} = $sto->search;
        my $opt = $lei->{opt};
index e4242d9be5ef05ec53029eb2bf66d2de32511f1d..223db22244eca51cf9554c76d685f4625d02e88a 100644 (file)
@@ -76,7 +76,7 @@ sub new {
        $fmt //= $devfd >= 0 ? 'json' : (detect_fmt($lei, $dst) or return);
 
        if (index($dst, '://') < 0) { # not a URL, so assume path
-                $dst = File::Spec->canonpath($dst);
+                $dst = $lei->canonpath_harder($dst);
        } # else URL
 
        my $self = bless { fmt => $fmt, dst => $dst }, $class;
index eb7b98d416fa20e2c323cad6d7d7eda2453c8a89..37b660f9ce1ca46a31e5c9ed4a7230be16398f35 100644 (file)
@@ -116,7 +116,7 @@ sub lei_q {
        my ($self, @argv) = @_;
        PublicInbox::Config->json; # preload before forking
        my $lxs = lxs_prepare($self) or return;
-       $self->ale->refresh_externals($lxs);
+       $self->ale->refresh_externals($lxs, $self);
        my $opt = $self->{opt};
        my %mset_opt = map { $_ => $opt->{$_} } qw(threads limit offset);
        $mset_opt{asc} = $opt->{'reverse'} ? 1 : 0;
index 7607b44f5aece34d5db15989f78904935096b210..0ba5897c7ba8748ec1a1a358fd44516975c265b3 100644 (file)
@@ -215,7 +215,7 @@ sub lei_rediff {
                $lei->{curl} //= which('curl') or return
                        $lei->fail('curl needed for', $lxs->remotes);
        }
-       $lei->ale->refresh_externals($lxs);
+       $lei->ale->refresh_externals($lxs, $lei);
        my $self = bless {
                -force_eml => 1, # for LeiInput->input_fh
                lxs => $lxs,
index 9069232b4b616e99a50027b40d56bf4db51fe4f6..3356d11e1fe6b7e4f399d9c8ef54816d47aae7ac 100644 (file)
@@ -42,7 +42,7 @@ sub up1 ($$) {
        }
        $lei->{lss} = $lss; # for LeiOverview->new
        my $lxs = $lei->lxs_prepare or return;
-       $lei->ale->refresh_externals($lxs);
+       $lei->ale->refresh_externals($lxs, $lei);
        $lei->_start_query;
 }
 
index 3eb442338ddc4dd8b919b5e60a56aab126284a91..d9ddb2977c17114ae8f7ce78fabb2e5fd38375e9 100644 (file)
@@ -11,6 +11,7 @@ require PublicInbox::ExtSearchIdx;
 require_git 2.6;
 require_ok 'PublicInbox::LeiXSearch';
 require_ok 'PublicInbox::LeiALE';
+require_ok 'PublicInbox::LEI';
 my ($home, $for_destroy) = tmpdir();
 my @ibx;
 for my $V (1..2) {
@@ -88,18 +89,19 @@ is($lxs->over, undef, '->over fails');
        my $smsg = $lxs->smsg_for($mitem) or BAIL_OUT 'smsg_for broken';
 
        my $ale = PublicInbox::LeiALE::_new("$home/ale");
-       $ale->refresh_externals($lxs);
+       my $lei = bless {}, 'PublicInbox::LEI';
+       $ale->refresh_externals($lxs, $lei);
        my $exp = [ $smsg->{blob}, 'blob', -s 't/utf8.eml' ];
        is_deeply([ $ale->git->check($smsg->{blob}) ], $exp, 'ale->git->check');
 
        $lxs = PublicInbox::LeiXSearch->new;
        $lxs->prepare_external($v2ibx);
-       $ale->refresh_externals($lxs);
+       $ale->refresh_externals($lxs, $lei);
        is_deeply([ $ale->git->check($smsg->{blob}) ], $exp,
                        'ale->git->check remembered inactive external');
 
        rename("$home/v1tmp", "$home/v1moved") or BAIL_OUT "rename: $!";
-       $ale->refresh_externals($lxs);
+       $ale->refresh_externals($lxs, $lei);
        is($ale->git->check($smsg->{blob}), undef,
                        'missing after directory gone');
 }