In perl.git, the branch blead has been updated <http://perl5.git.perl.org/perl.git/commitdiff/9c6681cc159f89641fc077464b7f7b3fcf64e6f1?hp=3ff4feb5bd226622062600b4127bb8c276c9d5ec>
- Log ----------------------------------------------------------------- commit 9c6681cc159f89641fc077464b7f7b3fcf64e6f1 Merge: 3ff4feb5bd 1b4d0d79ac Author: Tony Cook <t...@develop-help.com> Date: Mon Sep 11 14:48:21 2017 +1000 [perl #127663] safer in-place editing ----------------------------------------------------------------------- Summary of changes: Configure | 26 +++ Cross/config.sh-arm-linux | 5 + INSTALL | 6 + NetWare/config.wc | 5 + Porting/Glossary | 20 ++ Porting/config.sh | 5 + config_h.SH | 25 +++ configure.com | 5 + doio.c | 482 ++++++++++++++++++++++++++++++++++++++-------- embed.fnc | 5 + embed.h | 1 + embedvar.h | 1 + intrpvar.h | 8 + mg.c | 36 ++++ perl.c | 37 +++- plan9/config_sh.sample | 5 + pod/perldiag.pod | 16 +- pod/perlrun.pod | 12 ++ pp_sort.c | 2 +- proto.h | 8 + symbian/config.sh | 5 + t/io/fs.t | 27 +-- t/io/nargv.t | 24 ++- t/run/switches.t | 221 ++++++++++++++++++++- uconfig.h | 29 ++- uconfig.sh | 5 + uconfig64.sh | 5 + util.c | 38 +++- util.h | 10 + win32/config.ce | 5 + win32/config.gc | 5 + win32/config.vc | 5 + 32 files changed, 978 insertions(+), 111 deletions(-) diff --git a/Configure b/Configure index bdcfaf17ef..3736fae165 100755 --- a/Configure +++ b/Configure @@ -383,6 +383,11 @@ d_alarm='' asctime_r_proto='' d_asctime_r='' d_asinh='' +d_openat='' +d_unlinkat='' +d_renameat='' +d_linkat='' +d_fchmodat='' d_atanh='' d_attribute_deprecated='' d_attribute_format='' @@ -19769,6 +19774,22 @@ esac set strxfrm d_strxfrm eval $inlibc +: check for openat, unlinkat, renameat, linkat, fchmodat +set openat d_openat +eval $inlibc + +set unlinkat d_unlinkat +eval $inlibc + +set renameat d_renameat +eval $inlibc + +set linkat d_linkat +eval $inlibc + +set fchmodat d_fchmodat +eval $inlibc + : see if symlink exists set symlink d_symlink eval $inlibc @@ -24445,6 +24466,11 @@ d_asctime64='$d_asctime64' d_asctime_r='$d_asctime_r' d_asinh='$d_asinh' d_atanh='$d_atanh' +d_openat='$d_openat' +d_unlinkat='$d_unlinkat' +d_renameat='$d_renameat' +d_linkat='$d_linkat' +d_fchmodat='$d_fchmodat' d_atolf='$d_atolf' d_atoll='$d_atoll' d_attribute_deprecated='$d_attribute_deprecated' diff --git a/Cross/config.sh-arm-linux b/Cross/config.sh-arm-linux index 1477fb7405..96a408de31 100644 --- a/Cross/config.sh-arm-linux +++ b/Cross/config.sh-arm-linux @@ -202,6 +202,7 @@ d_expm1='undef' d_faststdio='define' d_fchdir='define' d_fchmod='define' +d_fchmodat='undef' d_fchown='define' d_fcntl='define' d_fcntl_can_lock='define' @@ -342,6 +343,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='define' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -420,6 +422,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='define' +d_openat='undef' d_pathconf='define' d_pause='define' d_perl_otherlibdirs='undef' @@ -458,6 +461,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -606,6 +610,7 @@ d_ualarm='define' d_umask='define' d_uname='define' d_union_semun='undef' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='define' d_uselocale='undef' diff --git a/INSTALL b/INSTALL index 5a88ded723..fbce4f5469 100644 --- a/INSTALL +++ b/INSTALL @@ -2721,6 +2721,12 @@ F<mathoms.c> will not be compiled in. Those functions are no longer used by perl itself; for source compatibility reasons, though, they weren't completely removed. +=head2 C<-DNO_PERL_INTERNAL_RAND_SEED> +X<PERL_INTERNAL_RAND_SEED> + +If you configure perl with C<-Accflags=-DNO_PERL_INTERNAL_RAND_SEED>, +perl will ignore the C<PERL_INTERNAL_RAND_SEED> enviroment variable. + =head1 DOCUMENTATION Read the manual entries before running perl. The main documentation diff --git a/NetWare/config.wc b/NetWare/config.wc index 3150bcbf17..b4e501b9bd 100644 --- a/NetWare/config.wc +++ b/NetWare/config.wc @@ -190,6 +190,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -331,6 +332,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -409,6 +411,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='undef' d_perl_otherlibdirs='undef' @@ -447,6 +450,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -596,6 +600,7 @@ d_ualarm='undef' d_umask='define' d_uname='define' d_union_semun='define' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/Porting/Glossary b/Porting/Glossary index a94eaabe8a..cabf016f8e 100644 --- a/Porting/Glossary +++ b/Porting/Glossary @@ -849,6 +849,10 @@ d_fchmod (d_fchmod.U): indicates to the C program that the fchmod() routine is available to change mode of opened files. +d_fchmodat (d_fchmodat.U): + This variable conditionally defines the HAS_FCHMODAT symbol, which + indicates the POSIX fchmodat() function is available. + d_fchown (d_fchown.U): This variable conditionally defines the HAS_FCHOWN symbol, which indicates to the C program that the fchown() routine is available @@ -1525,6 +1529,10 @@ d_link (d_link.U): This variable conditionally defines HAS_LINK if link() is available to create hard links. +d_linkat (d_linkat.U): + This variable conditionally defines the HAS_LINKAT symbol, which + indicates the POSIX linkat() function is available. + d_llrint (d_llrint.U): This variable conditionally defines the HAS_LLRINT symbol, which indicates to the C program that the llrint() routine is available @@ -1897,6 +1905,10 @@ d_open3 (d_open3.U): which indicates to the C program that the 3 argument version of the open(2) function is available. +d_openat (d_openat.U): + This variable conditionally defines the HAS_OPENAT symbol, which + indicates the POSIX openat() function is available. + d_pathconf (d_pathconf.U): This variable conditionally defines the HAS_PATHCONF symbol, which indicates to the C program that the pathconf() routine is available @@ -2137,6 +2149,10 @@ d_rename (d_rename.U): indicates to the C program that the rename() routine is available to rename files. +d_renameat (d_renameat.U): + This variable conditionally defines the HAS_RENAMEAT symbol, which + indicates the POSIX renameat() function is available. + d_rewinddir (d_readdir.U): This variable conditionally defines HAS_REWINDDIR if rewinddir() is available. @@ -2821,6 +2837,10 @@ d_union_semun (d_union_semun.U): This variable conditionally defines HAS_UNION_SEMUN if the union semun is defined by including <sys/sem.h>. +d_unlinkat (d_unlinkat.U): + This variable conditionally defines the HAS_UNLINKAT symbol, which + indicates the POSIX unlinkat() function isavailable. + d_unordered (d_unordered.U): This variable conditionally defines the HAS_UNORDERED symbol, which indicates to the C program that the unordered() routine is available. diff --git a/Porting/config.sh b/Porting/config.sh index 52a3b5a8d2..178db06c21 100644 --- a/Porting/config.sh +++ b/Porting/config.sh @@ -211,6 +211,7 @@ d_expm1='define' d_faststdio='define' d_fchdir='define' d_fchmod='define' +d_fchmodat='undef' d_fchown='define' d_fcntl='define' d_fcntl_can_lock='define' @@ -352,6 +353,7 @@ d_lgamma_r='define' d_libm_lib_version='undef' d_libname_unique='undef' d_link='define' +d_linkat='undef' d_llrint='define' d_llrintl='define' d_llround='define' @@ -430,6 +432,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='define' +d_openat='undef' d_pathconf='define' d_pause='define' d_perl_otherlibdirs='undef' @@ -470,6 +473,7 @@ d_regcomp='define' d_remainder='define' d_remquo='define' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='define' d_rmdir='define' @@ -619,6 +623,7 @@ d_ualarm='define' d_umask='define' d_uname='define' d_union_semun='define' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='define' d_uselocale='define' diff --git a/config_h.SH b/config_h.SH index e9b144f02b..da1a1f3627 100755 --- a/config_h.SH +++ b/config_h.SH @@ -63,6 +63,31 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un */ #$d_alarm HAS_ALARM /**/ +/* HAS_OPENAT: + * This symbol is defined if the openat() routine is available. + */ +#$d_openat HAS_OPENAT /**/ + +/* HAS_UNLINKAT: + * This symbol is defined if the unlinkat() routine is available. + */ +#$d_unlinkat HAS_UNLINKAT /**/ + +/* HAS_RENAMEAT: + * This symbol is defined if the renameat() routine is available. + */ +#$d_renameat HAS_RENAMEAT /**/ + +/* HAS_LINKAT: + * This symbol is defined if the linkat() routine is available. + */ +#$d_linkat HAS_LINKAT /**/ + +/* HAS_FCHMODAT: + * This symbol is defined if the fchmodat() routine is available. + */ +#$d_fchmodat HAS_FCHMODAT /**/ + /* HAS_BCMP: * This symbol is defined if the bcmp() routine is available to * compare blocks of memory. diff --git a/configure.com b/configure.com index f54722eab8..f76d8f752c 100644 --- a/configure.com +++ b/configure.com @@ -6246,6 +6246,11 @@ $ WC "d_oldarchlib='define'" $ WC "d_oldpthreads='" + d_oldpthreads + "'" $ WC "d_oldsock='undef'" $ WC "d_open3='define'" +$ WC "d_openat='undef'" +$ WC "d_unlinkat='undef'" +$ WC "d_renameat='undef'" +$ WC "d_linkat='undef'" +$ WC "d_fchmodat='undef'" $ WC "d_pathconf='" + d_pathconf + "'" $ WC "d_pause='define'" $ WC "d_perl_otherlibdirs='undef'" diff --git a/doio.c b/doio.c index 6f4cd84f8c..8c08455eda 100644 --- a/doio.c +++ b/doio.c @@ -813,6 +813,172 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname, return FALSE; } +/* Open a temp file in the same directory as an original name. +*/ + +static bool +S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) { + int fd; + PerlIO *fp; + const char *p = SvPV_nolen(orig_name); + const char *sep; + + /* look for the last directory separator */ + sep = strrchr(p, '/'); + +#ifdef DOSISH + { + const char *sep2; + if ((sep2 = strrchr(sep ? sep : p, '\\'))) + sep = sep2; + } +#endif +#ifdef VMS + if (!sep) { + const char *openp = strchr(p, '['); + if (openp) + sep = strchr(openp, ']'); + else { + sep = strchr(p, ':'); + } + } +#endif + if (sep) { + sv_setpvn(temp_out_name, p, sep - p + 1); + sv_catpvs(temp_out_name, "XXXXXXXX"); + } + else + sv_setpvs(temp_out_name, "XXXXXXXX"); + + fd = Perl_my_mkstemp(SvPVX(temp_out_name)); + + if (fd < 0) + return FALSE; + + fp = PerlIO_fdopen(fd, "w+"); + if (!fp) + return FALSE; + + return do_openn(gv, "+>&", 3, 0, 0, 0, fp, NULL, 0); +} + +#if defined(HAS_UNLINKAT) && defined(HAS_RENAMEAT) && defined(HAS_FCHMODAT) && \ + (defined(HAS_DIRFD) || defined(HAS_DIR_DD_FD)) && !defined(NO_USE_ATFUNCTIONS) +# define ARGV_USE_ATFUNCTIONS +#endif + +/* Win32 doesn't necessarily return useful information + * in st_dev, st_ino. + */ +#ifndef ARGV_USE_ATFUNCTIONS +# ifndef DOSISH +# define ARGV_USE_STAT_INO +# endif +#endif + +#define ARGVMG_BACKUP_NAME 0 +#define ARGVMG_TEMP_NAME 1 +#define ARGVMG_ORIG_NAME 2 +#define ARGVMG_ORIG_MODE 3 +#define ARGVMG_ORIG_PID 4 + +#if defined(ARGV_USE_ATFUNCTIONS) +#define ARGVMG_ORIG_DIRP 5 +#elif defined(ARGV_USE_STAT_INO) +/* we store the entire stat_t since the ino_t and dev_t values might + not fit in an IV. I could have created a new structure and + transferred them across, but this seemed too much effort for very + little win. + */ +#define ARGVMG_ORIG_CWD_STAT 5 +#endif + +static int +S_argvout_free(pTHX_ SV *io, MAGIC *mg) { + SV **temp_psv; + + PERL_UNUSED_ARG(io); + + /* note this can be entered once the file has been + successfully deleted too */ + assert(IoTYPE(io) != IoTYPE_PIPE); + + /* mg_obj can be NULL if a thread is created with the handle open, in which + case we leave any clean up to the parent thread */ + if (mg->mg_obj && IoIFP(io)) { + SV **pid_psv; +#ifdef ARGV_USE_ATFUNCTIONS + SV **dir_psv; + DIR *dir; +#endif + PerlIO *iop = IoIFP(io); + + assert(SvTYPE(mg->mg_obj) == SVt_PVAV); + + pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); + + assert(pid_psv && *pid_psv); + + if (SvIV(*pid_psv) == (IV)PerlProc_getpid()) { + /* if we get here the file hasn't been closed explicitly by the + user and hadn't been closed implicitly by nextargv(), so + abandon the edit */ + (void)PerlIO_close(iop); + IoIFP(io) = IoOFP(io) = NULL; + temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + assert(temp_psv && *temp_psv && SvPOK(*temp_psv)); +#ifdef ARGV_USE_ATFUNCTIONS + dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE); + assert(dir_psv && *dir_psv && SvIOK(*dir_psv)); + dir = INT2PTR(DIR *, SvIV(*dir_psv)); + if (dir) { + (void)unlinkat(my_dirfd(dir), SvPVX(*temp_psv), 0); + closedir(dir); + } +#else + (void)UNLINK(SvPVX(*temp_psv)); +#endif + } + } + + return 0; +} + +static int +S_argvout_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) { + PERL_UNUSED_ARG(param); + + /* ideally we could just remove the magic from the SV but we don't get the SV here */ + SvREFCNT_dec(mg->mg_obj); + mg->mg_obj = NULL; + + return 0; +} + +/* Magic of this type has an AV containing the following: + 0: name of the backup file (if any) + 1: name of the temp output file + 2: name of the original file + 3: file mode of the original file + 4: pid of the process we opened at, to prevent doing the renaming + etc in both the child and the parent after a fork + +If we have unlinkat(), renameat(), fchmodat(), dirfd() we also keep: + 5: the DIR * for the current directory when we open the file, stored as an IV + */ + +static const MGVTBL argvout_vtbl = + { + NULL, /* svt_get */ + NULL, /* svt_set */ + NULL, /* svt_len */ + NULL, /* svt_clear */ + S_argvout_free, /* svt_free */ + NULL, /* svt_copy */ + S_argvout_dup, /* svt_dup */ + NULL /* svt_local */ + }; + PerlIO * Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) { @@ -834,15 +1000,14 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) SvREFCNT_inc_simple_NN(PL_defoutgv)); } } - if (PL_filemode & (S_ISUID|S_ISGID)) { - PerlIO_flush(IoIFP(GvIOn(PL_argvoutgv))); /* chmod must follow last write */ -#ifdef HAS_FCHMOD - if (PL_lastfd != -1) - (void)fchmod(PL_lastfd,PL_filemode); -#else - (void)PerlLIO_chmod(PL_oldname,PL_filemode); -#endif + + { + IO * const io = GvIOp(PL_argvoutgv); + if (io && IoIFP(io) && old_out_name) { + do_close(PL_argvoutgv, FALSE); + } } + PL_lastfd = -1; PL_filemode = 0; if (!GvAV(gv)) @@ -865,13 +1030,6 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) } else { Stat_t statbuf; - { - IO * const io = GvIOp(PL_argvoutgv); - if (io && IoIFP(io) && old_out_name && !io_close(io, PL_argvoutgv, FALSE, FALSE)) { - Perl_croak(aTHX_ "Failed to close in-place edit file %" - SVf ": %s\n", old_out_name, Strerror(errno)); - } - } /* This very long block ends with return IoIFP(GvIOp(gv)); Both this block and the block above fall through on open failure to the warning code, and then the while loop above tries @@ -880,9 +1038,15 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) #ifndef FLEXFILENAMES int filedev; int fileino; +#endif +#ifdef ARGV_USE_ATFUNCTIONS + DIR *curdir; #endif Uid_t fileuid; Gid_t filegid; + AV *magic_av = NULL; + SV *temp_name_sv = NULL; + MAGIC *mg; TAINT_PROPER("inplace open"); if (oldlen == 1 && *PL_oldname == '-') { @@ -904,6 +1068,7 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) do_close(gv,FALSE); continue; } + magic_av = newAV(); if (*PL_inplace && strNE(PL_inplace, "*")) { const char *star = strchr(PL_inplace, '*'); if (star) { @@ -933,71 +1098,45 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) "Can't do inplace edit: %" SVf " would not be unique", SVfARG(sv)); - do_close(gv,FALSE); - continue; - } -#endif -#ifdef HAS_RENAME -#if !defined(DOSISH) && !defined(__CYGWIN__) - if (PerlLIO_rename(PL_oldname,SvPVX_const(sv)) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't rename %s to %" SVf - ": %s, skipping file", - PL_oldname, SVfARG(sv), - Strerror(errno)); - do_close(gv,FALSE); - continue; - } -#else - do_close(gv,FALSE); - (void)PerlLIO_unlink(SvPVX_const(sv)); - (void)PerlLIO_rename(PL_oldname,SvPVX_const(sv)); - do_open_raw(gv, SvPVX_const(sv), SvCUR(sv), O_RDONLY, 0, NULL); -#endif /* DOSISH */ -#else - (void)UNLINK(SvPVX_const(sv)); - if (link(PL_oldname,SvPVX_const(sv)) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't rename %s to %" SVf ": %s, skipping file", - PL_oldname, SVfARG(sv), Strerror(errno) ); - do_close(gv,FALSE); - continue; - } - (void)UNLINK(PL_oldname); -#endif - } - else { -#if !defined(DOSISH) && !defined(__amigaos4__) -# ifndef VMS /* Don't delete; use automatic file versioning */ - if (UNLINK(PL_oldname) < 0) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), - "Can't remove %s: %s, skipping file", - PL_oldname, Strerror(errno) ); - do_close(gv,FALSE); - continue; + goto cleanup_argv; } -# endif -#else - Perl_croak(aTHX_ "Can't do inplace edit without backup"); #endif + av_store(magic_av, ARGVMG_BACKUP_NAME, newSVsv(sv)); } sv_setpvn(sv,PL_oldname,oldlen); SETERRNO(0,0); /* in case sprintf set errno */ - if (!Perl_do_open_raw(aTHX_ PL_argvoutgv, SvPVX_const(sv), - SvCUR(sv), -#ifdef VMS - O_WRONLY|O_CREAT|O_TRUNC, 0, -#else - O_WRONLY|O_CREAT|OPEN_EXCL, 0600, -#endif - NULL)) { - Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), "Can't do inplace edit on %s: %s", + temp_name_sv = newSV(0); + if (!S_openindirtemp(aTHX_ PL_argvoutgv, GvSV(gv), temp_name_sv)) { + SvREFCNT_dec(temp_name_sv); + /* diag_listed_as: Can't do inplace edit on %s: %s */ + Perl_ck_warner_d(aTHX_ packWARN(WARN_INPLACE), "Can't do inplace edit on %s: Cannot make temp name: %s", PL_oldname, Strerror(errno) ); - do_close(gv,FALSE); - continue; +#ifndef FLEXFILENAMES + cleanup_argv: +#endif + do_close(gv,FALSE); + SvREFCNT_dec(magic_av); + continue; } + av_store(magic_av, ARGVMG_TEMP_NAME, temp_name_sv); + av_store(magic_av, ARGVMG_ORIG_NAME, newSVsv(sv)); + av_store(magic_av, ARGVMG_ORIG_MODE, newSVuv(PL_filemode)); + av_store(magic_av, ARGVMG_ORIG_PID, newSViv((IV)PerlProc_getpid())); +#if defined(ARGV_USE_ATFUNCTIONS) + curdir = opendir("."); + av_store(magic_av, ARGVMG_ORIG_DIRP, newSViv(PTR2IV(curdir))); +#elif defined(ARGV_USE_STAT_INO) + if (PerlLIO_stat(".", &statbuf) >= 0) { + av_store(magic_av, ARGVMG_ORIG_CWD_STAT, + newSVpvn((char *)&statbuf, sizeof(statbuf))); + } +#endif setdefout(PL_argvoutgv); + sv_setsv(GvSVn(PL_argvoutgv), temp_name_sv); + mg = sv_magicext((SV*)GvIOp(PL_argvoutgv), (SV*)magic_av, PERL_MAGIC_uvar, &argvout_vtbl, NULL, 0); + mg->mg_flags |= MGf_DUP; + SvREFCNT_dec(magic_av); PL_lastfd = PerlIO_fileno(IoIFP(GvIOp(PL_argvoutgv))); if (PL_lastfd >= 0) { (void)PerlLIO_fstat(PL_lastfd,&statbuf); @@ -1039,17 +1178,6 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) if (io && (IoFLAGS(io) & IOf_ARGV)) IoFLAGS(io) |= IOf_START; if (PL_inplace) { - if (old_out_name) { - IO * const io = GvIOp(PL_argvoutgv); - if (io && IoIFP(io) && !io_close(io, PL_argvoutgv, FALSE, FALSE)) { - Perl_croak(aTHX_ "Failed to close in-place edit file %" SVf ": %s\n", - old_out_name, Strerror(errno)); - } - } - else { - /* maybe this is no longer wanted */ - (void)do_close(PL_argvoutgv,FALSE); - } if (io && (IoFLAGS(io) & IOf_ARGV) && PL_argvout_stack && AvFILLp(PL_argvout_stack) >= 0) { @@ -1069,6 +1197,7 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) { bool retval; IO *io; + MAGIC *mg; if (!gv) gv = PL_argvgv; @@ -1085,7 +1214,194 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } return FALSE; } - retval = io_close(io, NULL, not_implicit, FALSE); + if ((mg = mg_findext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl)) + && mg->mg_obj) { + /* handle to an in-place edit work file */ + SV **back_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_BACKUP_NAME, FALSE); + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + /* PL_oldname may have been modified by a nested ARGV use at this point */ + SV **orig_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_NAME, FALSE); + SV **mode_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_MODE, FALSE); + SV **pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); +#if defined(ARGV_USE_ATFUNCTIONS) + SV **dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE); + DIR *dir; + int dfd; +#elif defined(ARGV_USE_STAT_INO) + SV **stat_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_CWD_STAT, FALSE); + Stat_t *orig_cwd_stat = stat_psv && *stat_psv ? (Stat_t *)SvPVX(*stat_psv) : NULL; +#endif +#ifndef ARGV_USE_ATFUNCTIONS + Stat_t statbuf; +#endif + UV mode; + int fd; + + const char *orig_pv; + + assert(temp_psv && *temp_psv); + assert(orig_psv && *orig_psv); + assert(mode_psv && *mode_psv); + assert(pid_psv && *pid_psv); +#ifdef ARGV_USE_ATFUNCTIONS + assert(dir_psv && *dir_psv); + dir = INT2PTR(DIR *, SvIVX(*dir_psv)); + dfd = my_dirfd(dir); +#endif + + orig_pv = SvPVX(*orig_psv); + mode = SvUV(*mode_psv); + + if ((mode & (S_ISUID|S_ISGID)) != 0 + && (fd = PerlIO_fileno(IoIFP(io))) >= 0) { + (void)PerlIO_flush(IoIFP(io)); +#ifdef HAS_FCHMOD + (void)fchmod(fd, mode); +#else + (void)PerlLIO_chmod(orig_pv, mode); +#endif + } + + retval = io_close(io, NULL, not_implicit, FALSE); + + if (SvIV(*pid_psv) != (IV)PerlProc_getpid()) { + /* this is a child process, don't duplicate our rename() etc + processing below */ + goto freext; + } + + if (retval) { +#ifdef ARGV_USE_STAT_INO + /* if the path is absolute the possible moving of cwd (which the file + might be in) isn't our problem. + This code tries to be reasonably balanced about detecting a changed + CWD, if we have the information needed to check that curdir has changed, we + check it + */ + if (!PERL_FILE_IS_ABSOLUTE(SvPVX(*orig_psv)) + && orig_cwd_stat + && PerlLIO_stat(".", &statbuf) >= 0 + && ( statbuf.st_dev != orig_cwd_stat->st_dev + || statbuf.st_ino != orig_cwd_stat->st_ino)) { + Perl_croak(aTHX_ "Cannot complete in-place edit of %" SVf ": %s", + *orig_psv, "Current directory has changed"); + } +#endif +#if !defined(ARGV_USE_ATFUNCTIONS) && !defined(ARGV_USE_STAT_INO) + /* Some platforms don't have useful st_ino etc, so just + check we can see the work file. + */ + if (!PERL_FILE_IS_ABSOLUTE(SvPVX(*orig_psv)) + && PerlLIO_stat(SvPVX(*temp_psv), &statbuf) < 0) { + Perl_croak(aTHX_ "Cannot complete in-place edit of %" SVf ": %s", + *orig_psv, + "Work file is missing - did you change directory?"); + } +#endif + +#if defined(DOSISH) || defined(__CYGWIN__) + if (PL_argvgv && GvIOp(PL_argvgv) + && IoIFP(GvIOp(PL_argvgv)) + && (IoFLAGS(GvIOp(PL_argvgv)) & (IOf_ARGV|IOf_START)) == IOf_ARGV) { + do_close(PL_argvgv, FALSE); + } +#endif + if (back_psv && *back_psv) { +#if defined(HAS_LINK) && !defined(DOSISH) && !defined(__CYGWIN__) && defined(HAS_RENAME) + if ( +# ifdef ARGV_USE_ATFUNCTIONS + linkat(dfd, orig_pv, dfd, SvPVX(*back_psv), 0) < 0 +# else + link(orig_pv, SvPVX(*back_psv)) < 0 +# endif + ) +#endif + { +#ifdef HAS_RENAME + if ( +# ifdef ARGV_USE_ATFUNCTIONS + renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0 +# else + PerlLIO_rename(orig_pv, SvPVX(*back_psv)) < 0 +# endif + ) { + if (!not_implicit) { +# ifdef ARGV_USE_ATFUNCTIONS + (void)unlinkat(dfd, SvPVX_const(*temp_psv), 0); +# else + UNLINK(SvPVX(*temp_psv)); +# endif + Perl_croak(aTHX_ "Can't rename %s to %s: %s, skipping file", + SvPVX(*orig_psv), SvPVX(*back_psv), Strerror(errno)); + } + /* should we warn here? */ + goto abort_inplace; + } +#else + (void)UNLINK(SvPVX(*back_psv)); + if (link(orig_pv, SvPVX(*back_psv))) { + if (!not_implicit) { + Perl_croak(aTHX_ "Can't rename %s to %s: %s, skipping file", + SvPVX(*orig_psv), SvPVX(*back_psv), Strerror(errno)); + } + goto abort_inplace; + } + /* we need to use link() to get the temp into place too, and linK() + fails if the new link name exists */ + (void)UNLINK(orig_pv); +#endif + } + } +#if defined(DOSISH) || defined(__CYGWIN__) || !defined(HAS_RENAME) + else { + UNLINK(orig_pv); + } +#endif + if ( +#ifdef HAS_RENAME +# ifdef ARGV_USE_ATFUNCTIONS + renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0 +# else + PerlLIO_rename(SvPVX(*temp_psv), orig_pv) < 0 +# endif +#else + link(SvPVX(*temp_psv), orig_pv) < 0 +#endif + ) { + if (!not_implicit) { +#ifdef ARGV_USE_ATFUNCTIONS + (void)unlinkat(dfd, SvPVX_const(*temp_psv), 0); +#else + UNLINK(SvPVX(*temp_psv)); +#endif + Perl_croak(aTHX_ "Can't rename in-place work file '%s' to '%s': %s\n", + SvPVX(*temp_psv), SvPVX(*orig_psv), Strerror(errno)); + } + abort_inplace: + UNLINK(SvPVX_const(*temp_psv)); + retval = FALSE; + } +#ifndef HAS_RENAME + UNLINK(SvPVX(*temp_psv)); +#endif + } + else { +#ifdef ARGV_USE_ATFUNCTIONS + unlinkat(dfd, SvPVX_const(*temp_psv), 0); +#else + UNLINK(SvPVX_const(*temp_psv)); +#endif + if (!not_implicit) { + Perl_croak(aTHX_ "Failed to close in-place work file %s: %s", + SvPVX(*temp_psv), Strerror(errno)); + } + } + freext: + mg_freeext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl); + } + else { + retval = io_close(io, NULL, not_implicit, FALSE); + } if (not_implicit) { IoLINES(io) = 0; IoPAGE(io) = 0; diff --git a/embed.fnc b/embed.fnc index 44d8d40adf..bcef22ad2e 100644 --- a/embed.fnc +++ b/embed.fnc @@ -1036,6 +1036,7 @@ ApdRn |MAGIC* |mg_findext |NULLOK const SV* sv|int type|NULLOK const MGVTBL *vtb EXpR |MAGIC* |mg_find_mglob |NN SV* sv Apd |int |mg_free |NN SV* sv Apd |void |mg_free_type |NN SV* sv|int how +Apd |void |mg_freeext |NN SV* sv|int how|NULLOK const MGVTBL *vtbl Apd |int |mg_get |NN SV* sv ApdD |U32 |mg_length |NN SV* sv Apdn |void |mg_magical |NN SV* sv @@ -3053,6 +3054,10 @@ Apnod |Size_t |my_strlcat |NULLOK char *dst|NULLOK const char *src|Size_t size Apnod |Size_t |my_strlcpy |NULLOK char *dst|NULLOK const char *src|Size_t size #endif +#ifndef HAS_MKSTEMP +pno |int |my_mkstemp |NN char *templte +#endif + APpdn |bool |isinfnan |NV nv p |bool |isinfnansv |NN SV *sv diff --git a/embed.h b/embed.h index 6d2fa1ccb7..0f491afd77 100644 --- a/embed.h +++ b/embed.h @@ -345,6 +345,7 @@ #define mg_findext Perl_mg_findext #define mg_free(a) Perl_mg_free(aTHX_ a) #define mg_free_type(a,b) Perl_mg_free_type(aTHX_ a,b) +#define mg_freeext(a,b,c) Perl_mg_freeext(aTHX_ a,b,c) #define mg_get(a) Perl_mg_get(aTHX_ a) #define mg_length(a) Perl_mg_length(aTHX_ a) #define mg_magical Perl_mg_magical diff --git a/embedvar.h b/embedvar.h index 8b9842f9cc..76efb9104b 100644 --- a/embedvar.h +++ b/embedvar.h @@ -174,6 +174,7 @@ #define PL_incgv (vTHX->Iincgv) #define PL_initav (vTHX->Iinitav) #define PL_inplace (vTHX->Iinplace) +#define PL_internal_random_state (vTHX->Iinternal_random_state) #define PL_isarev (vTHX->Iisarev) #define PL_known_layers (vTHX->Iknown_layers) #define PL_langinfo_buf (vTHX->Ilanginfo_buf) diff --git a/intrpvar.h b/intrpvar.h index b6b20bcad9..766e552aed 100644 --- a/intrpvar.h +++ b/intrpvar.h @@ -830,6 +830,14 @@ PERLVAR(I, random_state, PL_RANDOM_STATE_TYPE) PERLVARI(I, dump_re_max_len, STRLEN, 0) +/* For internal uses of randomness, this ensures the sequence of + * random numbers returned by rand() isn't modified by perl's internal + * use of randomness. + * This is important if the user has called srand() with a seed. + */ + +PERLVAR(I, internal_random_state, PL_RANDOM_STATE_TYPE) + /* If you are adding a U8 or U16, check to see if there are 'Space' comments * above on where there are gaps which currently will be structure padding. */ diff --git a/mg.c b/mg.c index 971fceed2b..fe4f8a5935 100644 --- a/mg.c +++ b/mg.c @@ -626,6 +626,42 @@ Perl_mg_free_type(pTHX_ SV *sv, int how) mg_magical(sv); } +/* +=for apidoc mg_freeext + +Remove any magic of type C<how> using virtual table C<vtbl> from the +SV C<sv>. See L</sv_magic>. + +C<mg_freeext(sv, how, NULL)> is equivalent to C<mg_free_type(sv, how)>. + +=cut +*/ + +void +Perl_mg_freeext(pTHX_ SV *sv, int how, const MGVTBL *vtbl) +{ + MAGIC *mg, *prevmg, *moremg; + PERL_ARGS_ASSERT_MG_FREEEXT; + for (prevmg = NULL, mg = SvMAGIC(sv); mg; prevmg = mg, mg = moremg) { + MAGIC *newhead; + moremg = mg->mg_moremagic; + if (mg->mg_type == how && (vtbl == NULL || mg->mg_virtual == vtbl)) { + /* temporarily move to the head of the magic chain, in case + custom free code relies on this historical aspect of mg_free */ + if (prevmg) { + prevmg->mg_moremagic = moremg; + mg->mg_moremagic = SvMAGIC(sv); + SvMAGIC_set(sv, mg); + } + newhead = mg->mg_moremagic; + mg_free_struct(sv, mg); + SvMAGIC_set(sv, newhead); + mg = prevmg; + } + } + mg_magical(sv); +} + #include <signal.h> U32 diff --git a/perl.c b/perl.c index a3f8ac367d..fd100cc64a 100644 --- a/perl.c +++ b/perl.c @@ -261,6 +261,22 @@ perl_construct(pTHXx) init_constants(); +#ifdef NO_PERL_INTERNAL_RAND_SEED + Perl_drand48_init_r(&PL_internal_random_state, seed()); +#else + { + UV seed; + const char *env_pv; + if (PerlProc_getuid() != PerlProc_geteuid() || + PerlProc_getgid() != PerlProc_getegid() || + !(env_pv = PerlEnv_getenv("PERL_INTERNAL_RAND_SEED")) || + grok_number(env_pv, strlen(env_pv), &seed) != IS_NUMBER_IN_UV) { + seed = seed(); + } + Perl_drand48_init_r(&PL_internal_random_state, (U32)seed); + } +#endif + SvREADONLY_on(&PL_sv_placeholder); SvREFCNT(&PL_sv_placeholder) = SvREFCNT_IMMORTAL; @@ -2202,6 +2218,21 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) } } +#ifndef NO_PERL_INTERNAL_RAND_SEED + /* If we're not set[ug]id, we might have honored + PERL_INTERNAL_RAND_SEED in perl_construct(). + At this point command-line options have been parsed, so if + we're now tainting and not set[ug]id re-seed. + This could possibly be wasteful if PERL_INTERNAL_RAND_SEED is invalid, + but avoids duplicating the logic from perl_construct(). + */ + if (PL_tainting && + PerlProc_getuid() == PerlProc_geteuid() && + PerlProc_getgid() == PerlProc_getegid()) { + Perl_drand48_init_r(&PL_internal_random_state, seed()); + } +#endif + /* Set $^X early so that it can be used for relocatable paths in @INC */ /* and for SITELIB_EXP in USE_SITECUSTOMIZE */ assert (!TAINT_get); @@ -3376,12 +3407,6 @@ Perl_moreswitches(pTHX_ const char *s) case 'i': Safefree(PL_inplace); -#if defined(__CYGWIN__) /* do backup extension automagically */ - if (*(s+1) == '\0') { - PL_inplace = savepvs(".bak"); - return s+1; - } -#endif /* __CYGWIN__ */ { const char * const start = ++s; while (*s && !isSPACE(*s)) diff --git a/plan9/config_sh.sample b/plan9/config_sh.sample index 49a42ff13d..9ca482d5b2 100644 --- a/plan9/config_sh.sample +++ b/plan9/config_sh.sample @@ -202,6 +202,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='define' +d_fchmodat='undef' d_fchown='undef' d_fcntl='define' d_fcntl_can_lock='undef' @@ -342,6 +343,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='0' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -420,6 +422,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='define' +d_openat='undef' d_pathconf='define' d_pause='define' d_perl_otherlibdirs='undef' @@ -458,6 +461,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -606,6 +610,7 @@ d_ualarm='undef' d_umask='define' d_uname='define' d_union_semun='undef' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/pod/perldiag.pod b/pod/perldiag.pod index 7a7b220747..689e9609bc 100644 --- a/pod/perldiag.pod +++ b/pod/perldiag.pod @@ -656,6 +656,13 @@ the warning. See L<perlsub>. (F) You passed an invalid number (like an infinity or not-a-number) to C<chr>. +=item Cannot complete in-place edit of %s: %s + +(F) Your perl script appears to have changed directory while +performing an in-place edit of a file specified by a relative path, +and your system doesn't include the directory relative POSIX functions +needed to handle that. + =item Cannot compress %f in pack (F) You tried compressing an infinity or not-a-number as an unsigned @@ -1311,9 +1318,14 @@ the modified file. The file was left unmodified. =item Can't rename %s to %s: %s, skipping file -(S inplace) The rename done by the B<-i> switch failed for some reason, +(F) The rename done by the B<-i> switch failed for some reason, probably because you don't have write permission to the directory. +=item Can't rename in-place work file '%s' to '%s': %s + +(F) When closed implicitly, the temporary file for in-place editing +couldn't be renamed to the original filename. + =item Can't reopen input pipe (name: %s) in binary mode (P) An error peculiar to VMS. Perl thought stdin was a pipe, and tried @@ -2311,7 +2323,7 @@ Check the #! line, or manually feed your script into Perl yourself. CHECK, INIT, or END subroutine. Processing of the remainder of the queue of such routines has been prematurely ended. -=item Failed to close in-place edit file %s: %s +=item Failed to close in-place work file %s: %s (F) Closing an output file from in-place editing, as with the C<-i> command-line switch, failed. diff --git a/pod/perlrun.pod b/pod/perlrun.pod index f32c8edc98..b5e4c06517 100644 --- a/pod/perlrun.pod +++ b/pod/perlrun.pod @@ -1392,6 +1392,18 @@ X<SYS$LOGIN> Used if chdir has no argument and HOME and LOGDIR are not set. +=item PERL_INTERNAL_RAND_SEED +X<PERL_INTERNAL_RAND_SEED> + +Set to a non-negative integer to seed the random number generator used +internally by perl for a variety of purposes. + +Ignored if perl is run setuid or setgid. Used only for some limited +startup randomization (hash keys) if C<-T> or C<-t> perl is started +with tainting enabled. + +Perl may be built to ignore this variable. + =back Perl also has environment variables that control how Perl handles data diff --git a/pp_sort.c b/pp_sort.c index 5f39dba40e..604950910a 100644 --- a/pp_sort.c +++ b/pp_sort.c @@ -788,7 +788,7 @@ S_qsortsvu(pTHX_ SV ** array, size_t num_elts, SVCOMPARE_t compare) size_t n; SV ** const q = array; for (n = num_elts; n > 1; ) { - const size_t j = (size_t)(n-- * Drand01()); + const size_t j = (size_t)(n-- * Perl_internal_drand48()); temp = q[j]; q[j] = q[n]; q[n] = temp; diff --git a/proto.h b/proto.h index 637b3c913b..b02c677f32 100644 --- a/proto.h +++ b/proto.h @@ -2006,6 +2006,9 @@ PERL_CALLCONV int Perl_mg_free(pTHX_ SV* sv); PERL_CALLCONV void Perl_mg_free_type(pTHX_ SV* sv, int how); #define PERL_ARGS_ASSERT_MG_FREE_TYPE \ assert(sv) +PERL_CALLCONV void Perl_mg_freeext(pTHX_ SV* sv, int how, const MGVTBL *vtbl); +#define PERL_ARGS_ASSERT_MG_FREEEXT \ + assert(sv) PERL_CALLCONV int Perl_mg_get(pTHX_ SV* sv); #define PERL_ARGS_ASSERT_MG_GET \ assert(sv) @@ -3856,6 +3859,11 @@ STATIC int S_dooneliner(pTHX_ const char *cmd, const char *filename) # endif #endif +#if !defined(HAS_MKSTEMP) +PERL_CALLCONV int Perl_my_mkstemp(char *templte); +#define PERL_ARGS_ASSERT_MY_MKSTEMP \ + assert(templte) +#endif #if !defined(HAS_RENAME) PERL_CALLCONV I32 Perl_same_dirent(pTHX_ const char* a, const char* b); #define PERL_ARGS_ASSERT_SAME_DIRENT \ diff --git a/symbian/config.sh b/symbian/config.sh index dc4ad64b7e..7ed6bb3de3 100644 --- a/symbian/config.sh +++ b/symbian/config.sh @@ -146,6 +146,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -288,6 +289,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='undef' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -366,6 +368,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='undef' d_perl_otherlibdirs='undef' @@ -404,6 +407,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -554,6 +558,7 @@ d_ualarm='undef' d_umask='undef' d_uname='undef' d_union_semun='undef' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/t/io/fs.t b/t/io/fs.t index b6754d6568..09eede1e44 100644 --- a/t/io/fs.t +++ b/t/io/fs.t @@ -468,18 +468,23 @@ SKIP: { chdir $wd || die "Can't cd back to $wd"; } -# check if rename() works on directories -if ($^O eq 'VMS') { - # must have delete access to rename a directory - `set file $tmpdir.dir/protection=o:d`; - ok(rename("$tmpdir.dir", "$tmpdir1.dir"), "rename on directories") || - print "# errno: $!\n"; -} -else { - ok(rename($tmpdir, $tmpdir1), "rename on directories"); -} +SKIP: +{ + $Config{d_rename} + or skip "Cannot rename directories with link()", 2; + # check if rename() works on directories + if ($^O eq 'VMS') { + # must have delete access to rename a directory + `set file $tmpdir.dir/protection=o:d`; + ok(rename("$tmpdir.dir", "$tmpdir1.dir"), "rename on directories") || + print "# errno: $!\n"; + } + else { + ok(rename($tmpdir, $tmpdir1), "rename on directories"); + } -ok(-d $tmpdir1, "rename on directories working"); + ok(-d $tmpdir1, "rename on directories working"); +} { # Change 26011: Re: A surprising segfault diff --git a/t/io/nargv.t b/t/io/nargv.t index f0eee30c59..598ceed617 100644 --- a/t/io/nargv.t +++ b/t/io/nargv.t @@ -6,7 +6,7 @@ BEGIN { set_up_inc('../lib'); } -print "1..5\n"; +print "1..6\n"; my $j = 1; for $i ( 1,2,5,4,3 ) { @@ -43,6 +43,28 @@ while (<>) { show(); } +# test setuid is preserved (and hopefully setgid) +# +# With nested in-place editing PL_oldname and PL_filemode would +# be overwritten by the values for the last file in the nested +# loop. This is now all stored as magic in *ARGVOUT{IO} +$^I = ""; +@ARGV = mkfiles(1..3); +my $sidfile = $ARGV[1]; +chmod(04600, $sidfile); +my $mode = (stat $ARGV[1])[2]; +$n = 0; +while (<>) { + print STDOUT "#final \@ARGV: [@ARGV]\n"; + if ($n++ == 1) { + other(); + } + print; +} +my $newmode = (stat $sidfile)[2]; +printf "# before %#o after %#o\n", $mode, $newmode; +print +($mode == $newmode ? "" : "not "). "ok 6 # check setuid mode preserved\n"; + sub show { #warn "$ARGV: $_"; s/^not //; diff --git a/t/run/switches.t b/t/run/switches.t index b61be56834..6725f8fd35 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 => 115); +plan(tests => 136); use Config; @@ -355,11 +355,12 @@ for (qw( e f x E S V )) { sub do_i_unlink { unlink_all("file", "file.bak") } open(FILE, ">file") or die "$0: Failed to create 'file': $!"; - print FILE <<__EOF__; + my $yada = <<__EOF__; foo yada dada bada foo bing king kong foo __EOF__ + print FILE $yada; close FILE; END { do_i_unlink() } @@ -400,6 +401,222 @@ __EOF__ args => ['file'], ); is($out2, "", "no warning when files given"); + + open my $f, ">", "file" or die "$0: failed to create 'file': $!"; + print $f "foo\nbar\n"; + close $f; + + # a backup extension is no longer required on any platform + my $out3 = runperl( + switches => [ '-i', '-p' ], + prog => 's/foo/quux/', + stderr => 1, + args => [ 'file' ], + ); + is($out3, "", "no warnings/errors without backup extension"); + open $f, "<", "file" or die "$0: cannot open 'file': $!"; + chomp(my @out4 = <$f>); + close $f; + is(join(":", @out4), "quux:bar", "correct output without backup extension"); + + -d "inplacetmp" or mkdir("inplacetmp") + or die "Cannot mkdir 'inplacetmp': $!"; + require File::Spec; + my $work = File::Spec->catfile("inplacetmp", "foo"); + + # exit or die should leave original content in file + for my $inplace (qw/-i -i.bak/) { + for my $prog (qw/die exit/) { + open my $fh, ">", $work or die "$0: failed to open '$work': $!"; + print $fh $yada; + close $fh or die "Failed to close: $!"; + my $out = runperl ( + switches => [ $inplace, '-n' ], + prog => "print q(foo\n); $prog", + stderr => 1, + args => [ $work ], + ); + open my $in, "<", $work or die "$0: failed to open '$work': $!"; + my $data = do { local $/; <$in> }; + close $in; + is ($data, $yada, "check original content still in file"); + unlink $work; + } + } + + # test that path parsing is correct + open $f, ">", $work or die "Cannot create $work: $!"; + print $f "foo\nbar\n"; + close $f; + + my $out4 = runperl + ( + switches => [ "-i", "-p" ], + prog => 's/foo/bar/', + stderr => 1, + args => [ $work ], + ); + is ($out4, "", "no errors or warnings"); + open $f, "<", $work or die "Cannot open $work: $!"; + chomp(my @file4 = <$f>); + close $f; + is(join(":", @file4), "bar:bar", "check output"); + + SKIP: + { + # this needs to match how ARGV_USE_ATFUNCTIONS is defined in doio.c + 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{ccflags} !~ /-DNO_USE_ATFUNCTIONS\b/; + fresh_perl_is(<<'CODE', "ok\n", { }, +@ARGV = ("inplacetmp/foo"); +$^I = ""; +while (<>) { + chdir ".."; + print "xx\n"; +} +print "ok\n"; +CODE + "chdir while in-place editing"); + ok(open(my $fh, "<", $work), "open out file"); + is(scalar <$fh>, "xx\n", "file successfully saved after chdir"); + close $fh; + } + + SKIP: + { + skip "Need threads and full perl", 3 + if !$Config{useithreads} || is_miniperl(); + fresh_perl_is(<<'CODE', "ok\n", { stderr => 1 }, +use threads; +use strict; +@ARGV = ("inplacetmp/foo"); +$^I = ""; +while (<>) { + threads->create(sub { })->join; + print "yy\n"; +} +print "ok\n"; +CODE + "threads while in-place editing"); + ok(open(my $fh, "<", $work), "open out file"); + is(scalar <$fh>, "yy\n", "file successfully saved after chdir"); + close $fh; + } + + SKIP: + { + skip "Need fork", 3 if !$Config{d_fork}; + open my $fh, ">", $work + or die "Cannot open $work: $!"; + # we want only a single line for this test, otherwise + # it attempts to close the file twice + print $fh "foo\n"; + close $fh or die "Cannot close $work: $!"; + fresh_perl_is(<<'CODE', "ok\n", { stderr => 1 }, +use strict; +@ARGV = ("inplacetmp/foo"); +$^I = ""; +while (<>) { + my $pid = fork; + if (defined $pid && !$pid) { + # child + close ARGVOUT or die "Cannot close in child\n"; # this shouldn't do ARGVOUT magic + exit 0; + } + wait; + print "yy\n"; + close ARGVOUT or die "Cannot close in parent\n"; # this should +} +print "ok\n"; +CODE + "fork while in-place editing"); + ok(open($fh, "<", $work), "open out file"); + is(scalar <$fh>, "yy\n", "file successfully saved after chdir"); + close $fh; + } + + { + # test we handle the rename to the backup failing + # make it fail by creating a directory of the backup name + mkdir "$work.bak" or die "Cannot make mask backup directory: $!"; + fresh_perl_like(<<'CODE', qr/Can't rename/, { stderr => 1 }, "fail backup rename"); +@ARGV = ("inplacetmp/foo"); +$^I = ".bak"; +while (<>) { + print; +} +print "ok\n"; +CODE + rmdir "$work.bak" or die "Cannot remove mask backup directory: $!"; + } + + # 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: $!"; + my @names = grep !/^\.\.?$/ && $_ ne 'foo', readdir $d; + closedir $d; + is(scalar(@names), 0, "no extra files") + or diag "Found @names, expected none"; + + # the following tests might leave work files behind + + # this test can leave the work file in the directory, since making + # the directory non-writable also prevents removing the work file + SKIP: + { + # test we handle the rename of the work to the original failing + # make it fail by removing write perms from the directory + # but first check that doesn't prevent writing + chmod 0500, "inplacetmp"; + my $check = File::Spec->catfile("inplacetmp", "check"); + my $canwrite = open my $fh, ">", $check; + unlink $check; + chmod 0700, "inplacetmp" or die "Cannot make inplacetmp writable again: $!"; + skip "Cannot make inplacetmp read only", 1 + if $canwrite; + fresh_perl_like(<<'CODE', qr/Can't rename/, { stderr => 1 }, "fail final rename"); +@ARGV = ("inplacetmp/foo"); +$^I = ""; +while (<>) { + chmod 0500, "inplacetmp"; + print; +} +print "ok\n"; +CODE + chmod 0700, "inplacetmp" or die "Cannot make inplacetmp writable again: $!"; + } + + SKIP: + { + # this needs to reverse match how ARGV_USE_ATFUNCTIONS is defined in doio.c + 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{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"); +$^I = ""; +while (<>) { + chdir ".."; + print "xx\n"; +} +print "ok\n"; +CODE + "chdir while in-place editing (no at-functions)"); + } + + unlink $work; + + opendir $d, "inplacetmp" or die "Cannot opendir inplacetmp: $!"; + @names = grep !/^\.\.?$/ && !/foo$/aai, readdir $d; + closedir $d; + + # clean up in case the above failed + unlink map File::Spec->catfile("inplacetmp", $_), @names; + + rmdir "inplacetmp"; } # Tests for -E diff --git a/uconfig.h b/uconfig.h index b663e7edc7..e3646d27b2 100644 --- a/uconfig.h +++ b/uconfig.h @@ -28,6 +28,31 @@ */ /*#define HAS_ALARM / **/ +/* HAS_OPENAT: + * This symbol is defined if the openat() routine is available. + */ +/*#define HAS_OPENAT / **/ + +/* HAS_UNLINKAT: + * This symbol is defined if the unlinkat() routine is available. + */ +/*#define HAS_UNLINKAT / **/ + +/* HAS_RENAMEAT: + * This symbol is defined if the renameat() routine is available. + */ +/*#define HAS_RENAMEAT / **/ + +/* HAS_LINKAT: + * This symbol is defined if the linkat() routine is available. + */ +/*#define HAS_LINKAT / **/ + +/* HAS_FCHMODAT: + * This symbol is defined if the fchmodat() routine is available. + */ +/*#define HAS_FCHMODAT / **/ + /* HAS_BCMP: * This symbol is defined if the bcmp() routine is available to * compare blocks of memory. @@ -5354,6 +5379,6 @@ #endif /* Generated from: - * a0cea6273c16c1c5d8625665c84bda01926ce960c26873d4e5596b5b02a53e92 config_h.SH - * 58f9d541683fff4dcacfd3cb2d9e1f444fd3c8e3f120ef92c21eb47e880ba924 uconfig.sh + * e192beb070dfc2b1167cdec538893718edff9a3659073ef7ad4a47a7fed9f082 config_h.SH + * a088a21b0ddb63b48c794da959469ddb98da178e5b2d4ec568331a988d59e146 uconfig.sh * ex: set ro: */ diff --git a/uconfig.sh b/uconfig.sh index 0da4e46f5e..9ebda12bdc 100644 --- a/uconfig.sh +++ b/uconfig.sh @@ -140,6 +140,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -281,6 +282,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='undef' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -359,6 +361,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='undef' d_perl_otherlibdirs='undef' @@ -397,6 +400,7 @@ d_regcomp='define' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='undef' d_rint='undef' d_rmdir='undef' @@ -545,6 +549,7 @@ d_ualarm='undef' d_umask='undef' d_uname='undef' d_union_semun='undef' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/uconfig64.sh b/uconfig64.sh index 7ec0017b20..40caa1907a 100644 --- a/uconfig64.sh +++ b/uconfig64.sh @@ -141,6 +141,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -282,6 +283,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='undef' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -360,6 +362,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='undef' d_perl_otherlibdirs='undef' @@ -398,6 +401,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='undef' d_rint='undef' d_rmdir='undef' @@ -546,6 +550,7 @@ d_ualarm='undef' d_umask='undef' d_uname='undef' d_union_semun='undef' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/util.c b/util.c index b470681bdd..e2feb7f473 100644 --- a/util.c +++ b/util.c @@ -4677,10 +4677,8 @@ Perl_get_hash_seed(pTHX_ unsigned char * const seed_buffer) else #endif /* NO_PERL_HASH_ENV */ { - (void)seedDrand01((Rand_seed_t)seed()); - for( i = 0; i < PERL_HASH_SEED_BYTES; i++ ) { - seed_buffer[i] = (unsigned char)(Drand01() * (U8_MAX+1)); + seed_buffer[i] = (unsigned char)(Perl_internal_drand48() * (U8_MAX+1)); } } #ifdef USE_PERL_PERTURB_KEYS @@ -5791,6 +5789,40 @@ Perl_my_dirfd(DIR * dir) { #endif } +#ifndef HAS_MKSTEMP + +#define TEMP_FILE_CH "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789" +#define TEMP_FILE_CH_COUNT (sizeof(TEMP_FILE_CH)-1) + +int +Perl_my_mkstemp(char *templte) { + dTHX; + STRLEN len = strlen(templte); + int fd; + int attempts = 0; + + PERL_ARGS_ASSERT_MY_MKSTEMP; + + if (len < 6 || + templte[len-1] != 'X' || templte[len-2] != 'X' || templte[len-3] != 'X' || + templte[len-4] != 'X' || templte[len-5] != 'X' || templte[len-6] != 'X') { + errno = EINVAL; + return -1; + } + + do { + int i; + for (i = 1; i <= 6; ++i) { + templte[len-i] = TEMP_FILE_CH[(int)(Perl_internal_drand48() * TEMP_FILE_CH_COUNT)]; + } + fd = PerlLIO_open3(templte, O_RDWR | O_CREAT | O_EXCL, 0600); + } while (fd == -1 && errno == EEXIST && ++attempts <= 100); + + return fd; +} + +#endif + REGEXP * Perl_get_re_arg(pTHX_ SV *sv) { diff --git a/util.h b/util.h index 12a1c470ee..6b63d90e4f 100644 --- a/util.h +++ b/util.h @@ -89,6 +89,12 @@ typedef struct PERL_DRAND48_T perl_drand48_t; #define Perl_drand48_init(seed) (Perl_drand48_init_r(&PL_random_state, (seed))) #define Perl_drand48() (Perl_drand48_r(&PL_random_state)) +#ifdef PERL_CORE +/* uses a different source of randomness to avoid interfering with the results + * of rand() */ +#define Perl_internal_drand48() (Perl_drand48_r(&PL_internal_random_state)) +#endif + #ifdef USE_C_BACKTRACE typedef struct { @@ -240,6 +246,10 @@ means arg not present, 1 is empty string/null byte */ ((char *) memmem(big, bigend - big, little, lend - little)) #endif +#if defined(HAS_MKSTEMP) && defined(PERL_CORE) +# define Perl_my_mkstemp(templte) mkstemp(templte) +#endif + #endif /* PERL_UTIL_H_ */ /* diff --git a/win32/config.ce b/win32/config.ce index d73b16672c..9325e57cd6 100644 --- a/win32/config.ce +++ b/win32/config.ce @@ -188,6 +188,7 @@ d_expm1='undef' d_faststdio='undef' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -329,6 +330,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -407,6 +409,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='define' d_perl_otherlibdirs='undef' @@ -445,6 +448,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -594,6 +598,7 @@ d_ualarm='undef' d_umask='define' d_uname='define' d_union_semun='define' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/win32/config.gc b/win32/config.gc index 8a50293705..3ad6aa3ecf 100644 --- a/win32/config.gc +++ b/win32/config.gc @@ -189,6 +189,7 @@ d_expm1='undef' d_faststdio='define' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -329,6 +330,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -408,6 +410,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='define' d_perl_otherlibdirs='undef' @@ -446,6 +449,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -594,6 +598,7 @@ d_ualarm='undef' d_umask='define' d_uname='define' d_union_semun='define' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' diff --git a/win32/config.vc b/win32/config.vc index ff6ea8780b..baa210d140 100644 --- a/win32/config.vc +++ b/win32/config.vc @@ -189,6 +189,7 @@ d_expm1='undef' d_faststdio='define' d_fchdir='undef' d_fchmod='undef' +d_fchmodat='undef' d_fchown='undef' d_fcntl='undef' d_fcntl_can_lock='undef' @@ -329,6 +330,7 @@ d_lgamma='undef' d_lgamma_r='undef' d_libm_lib_version='undef' d_link='define' +d_linkat='undef' d_llrint='undef' d_llrintl='undef' d_llround='undef' @@ -408,6 +410,7 @@ d_old_pthread_create_joinable='undef' d_oldpthreads='undef' d_oldsock='undef' d_open3='undef' +d_openat='undef' d_pathconf='undef' d_pause='define' d_perl_otherlibdirs='undef' @@ -446,6 +449,7 @@ d_regcomp='undef' d_remainder='undef' d_remquo='undef' d_rename='define' +d_renameat='undef' d_rewinddir='define' d_rint='undef' d_rmdir='define' @@ -594,6 +598,7 @@ d_ualarm='undef' d_umask='define' d_uname='define' d_union_semun='define' +d_unlinkat='undef' d_unordered='undef' d_unsetenv='undef' d_uselocale='undef' -- Perl5 Master Repository