summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2017-09-18 11:18:55 +1000
committerTony Cook <tony@develop-help.com>2017-09-18 11:18:55 +1000
commit269a3b2e76d9e7b398d9faa62b30dc6d93dd9476 (patch)
tree7ae480de94c8794c7a78532c2b3f8e901686379f
parentac1ba09e8362c54848dc27d3484a5882c4b28f43 (diff)
parentb416949bcc78c663292630a0d778398af5dce5f9 (diff)
downloadperl-269a3b2e76d9e7b398d9faa62b30dc6d93dd9476.tar.gz
(127663) fix some portability issues with the new in-place edit
- renameat() is present but broken for absolute paths on FreeBSD 11, add a workaround. https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222258 - the new implementation uses linkat() but the availability checks in doio.c and the tests didn't check for it.
-rw-r--r--doio.c31
-rw-r--r--t/run/switches.t17
2 files changed, 44 insertions, 4 deletions
diff --git a/doio.c b/doio.c
index 8c08455eda..7b6c43531b 100644
--- a/doio.c
+++ b/doio.c
@@ -863,7 +863,8 @@ S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) {
}
#if defined(HAS_UNLINKAT) && defined(HAS_RENAMEAT) && defined(HAS_FCHMODAT) && \
- (defined(HAS_DIRFD) || defined(HAS_DIR_DD_FD)) && !defined(NO_USE_ATFUNCTIONS)
+ (defined(HAS_DIRFD) || defined(HAS_DIR_DD_FD)) && !defined(NO_USE_ATFUNCTIONS) && \
+ defined(HAS_LINKAT)
# define ARGV_USE_ATFUNCTIONS
#endif
@@ -1191,6 +1192,30 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen)
return NULL;
}
+#ifdef ARGV_USE_ATFUNCTIONS
+# if defined(__FreeBSD__)
+
+/* FreeBSD 11 renameat() mis-behaves strangely with absolute paths in cases where the
+ * equivalent rename() succeeds
+ */
+static int
+S_my_renameat(int olddfd, const char *oldpath, int newdfd, const char *newpath) {
+ /* this is intended only for use in Perl_do_close() */
+ assert(olddfd == newdfd);
+ assert(PERL_FILE_IS_ABSOLUTE(oldpath) == PERL_FILE_IS_ABSOLUTE(newpath));
+ if (PERL_FILE_IS_ABSOLUTE(oldpath)) {
+ return PerlLIO_rename(oldpath, newpath);
+ }
+ else {
+ return renameat(olddfd, oldpath, newdfd, newpath);
+ }
+}
+
+# else
+# define S_my_renameat(dh1, pv1, dh2, pv2) renameat((dh1), (pv1), (dh2), (pv2))
+# endif /* if defined(__FreeBSD__) */
+#endif
+
/* explicit renamed to avoid C++ conflict -- kja */
bool
Perl_do_close(pTHX_ GV *gv, bool not_implicit)
@@ -1320,7 +1345,7 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit)
#ifdef HAS_RENAME
if (
# ifdef ARGV_USE_ATFUNCTIONS
- renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0
+ S_my_renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0
# else
PerlLIO_rename(orig_pv, SvPVX(*back_psv)) < 0
# endif
@@ -1360,7 +1385,7 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit)
if (
#ifdef HAS_RENAME
# ifdef ARGV_USE_ATFUNCTIONS
- renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0
+ S_my_renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0
# else
PerlLIO_rename(SvPVX(*temp_psv), orig_pv) < 0
# endif
diff --git a/t/run/switches.t b/t/run/switches.t
index 6725f8fd35..ffc8fabc0d 100644
--- a/t/run/switches.t
+++ b/t/run/switches.t
@@ -12,7 +12,7 @@ BEGIN {
BEGIN { require "./test.pl"; require "./loc_tools.pl"; }
-plan(tests => 136);
+plan(tests => 137);
use Config;
@@ -468,6 +468,7 @@ __EOF__
skip "Not enough *at functions", 3
unless $Config{d_unlinkat} && $Config{d_renameat} && $Config{d_fchmodat}
&& ($Config{d_dirfd} || $Config{d_dir_dd_fd})
+ && $Config{d_linkat}
&& $Config{ccflags} !~ /-DNO_USE_ATFUNCTIONS\b/;
fresh_perl_is(<<'CODE', "ok\n", { },
@ARGV = ("inplacetmp/foo");
@@ -552,6 +553,19 @@ CODE
rmdir "$work.bak" or die "Cannot remove mask backup directory: $!";
}
+ {
+ # test with absolute paths, this was failing on FreeBSD 11ish due
+ # to a bug in renameat()
+ my $abs_work = File::Spec->rel2abs($work);
+ fresh_perl_is(<<'CODE', "",
+while (<>) {
+ print;
+}
+CODE
+ { stderr => 1, args => [ $abs_work ], switches => [ "-i" ] },
+ "abs paths");
+ }
+
# we now use temp files for in-place editing, make sure we didn't leave
# any behind in the above test
opendir my $d, "inplacetmp" or die "Cannot opendir inplacetmp: $!";
@@ -594,6 +608,7 @@ CODE
skip "Testing without *at functions", 1
if $Config{d_unlinkat} && $Config{d_renameat} && $Config{d_fchmodat}
&& ($Config{d_dirfd} || $Config{d_dir_dd_fd})
+ && $Config{d_linkat}
&& $Config{ccflags} !~ /-DNO_USE_ATFUNCTIONS\b/;
fresh_perl_like(<<'CODE', qr/^Cannot complete in-place edit of inplacetmp\/foo: .* - line 5, <> line \d+\./, { },
@ARGV = ("inplacetmp/foo");