X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/c3fcee079e395988a7b112b8e5e7e4a654048cf7..d90bd2cf701d08f6acfea11ab39eb0a20f9a6c3e:/doio.c diff --git a/doio.c b/doio.c index 7b6c435..0cc4e55 100644 --- a/doio.c +++ b/doio.c @@ -60,6 +60,291 @@ #include +void +Perl_setfd_cloexec(int fd) +{ + assert(fd >= 0); +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +} + +void +Perl_setfd_inhexec(int fd) +{ + assert(fd >= 0); +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) + (void) fcntl(fd, F_SETFD, 0); +#endif +} + +void +Perl_setfd_cloexec_for_nonsysfd(pTHX_ int fd) +{ + assert(fd >= 0); + if(fd > PL_maxsysfd) + setfd_cloexec(fd); +} + +void +Perl_setfd_inhexec_for_sysfd(pTHX_ int fd) +{ + assert(fd >= 0); + if(fd <= PL_maxsysfd) + setfd_inhexec(fd); +} +void +Perl_setfd_cloexec_or_inhexec_by_sysfdness(pTHX_ int fd) +{ + assert(fd >= 0); + if(fd <= PL_maxsysfd) + setfd_inhexec(fd); + else + setfd_cloexec(fd); +} + + +#define DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + do { \ + int res = (GENOPEN_NORMAL); \ + if(LIKELY(res != -1)) GENSETFD_CLOEXEC; \ + return res; \ + } while(0) +#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) && \ + defined(F_GETFD) +enum { CLOEXEC_EXPERIMENT = 0, CLOEXEC_AT_OPEN, CLOEXEC_AFTER_OPEN }; +# define DO_GENOPEN_EXPERIMENTING_CLOEXEC(strategy, TESTFD, GENOPEN_CLOEXEC, \ + GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + do { \ + switch (strategy) { \ + case CLOEXEC_EXPERIMENT: default: { \ + int res = (GENOPEN_CLOEXEC), eno; \ + if (LIKELY(res != -1)) { \ + int fdflags = fcntl((TESTFD), F_GETFD); \ + if (LIKELY(fdflags != -1) && \ + LIKELY(fdflags & FD_CLOEXEC)) { \ + strategy = CLOEXEC_AT_OPEN; \ + } else { \ + strategy = CLOEXEC_AFTER_OPEN; \ + GENSETFD_CLOEXEC; \ + } \ + } else if (UNLIKELY((eno = errno) == EINVAL || \ + eno == ENOSYS)) { \ + res = (GENOPEN_NORMAL); \ + if (LIKELY(res != -1)) { \ + strategy = CLOEXEC_AFTER_OPEN; \ + GENSETFD_CLOEXEC; \ + } else if (!LIKELY((eno = errno) == EINVAL || \ + eno == ENOSYS)) { \ + strategy = CLOEXEC_AFTER_OPEN; \ + } \ + } \ + return res; \ + } \ + case CLOEXEC_AT_OPEN: \ + return (GENOPEN_CLOEXEC); \ + case CLOEXEC_AFTER_OPEN: \ + DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC); \ + } \ + } while(0) +#else +# define DO_GENOPEN_EXPERIMENTING_CLOEXEC(strategy, TESTFD, GENOPEN_CLOEXEC, \ + GENOPEN_NORMAL, GENSETFD_CLOEXEC) \ + DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC) +#endif + +#define DO_ONEOPEN_THEN_CLOEXEC(ONEOPEN_NORMAL) \ + do { \ + int fd; \ + DO_GENOPEN_THEN_CLOEXEC(fd = (ONEOPEN_NORMAL), \ + setfd_cloexec(fd)); \ + } while(0) +#define DO_ONEOPEN_EXPERIMENTING_CLOEXEC(strategy, \ + ONEOPEN_CLOEXEC, ONEOPEN_NORMAL) \ + do { \ + int fd; \ + DO_GENOPEN_EXPERIMENTING_CLOEXEC(strategy, \ + fd, \ + fd = (ONEOPEN_CLOEXEC), \ + fd = (ONEOPEN_NORMAL), setfd_cloexec(fd)); \ + } while(0) + +#define DO_PIPESETFD_CLOEXEC(PIPEFD) \ + do { \ + setfd_cloexec((PIPEFD)[0]); \ + setfd_cloexec((PIPEFD)[1]); \ + } while(0) +#define DO_PIPEOPEN_THEN_CLOEXEC(PIPEFD, PIPEOPEN_NORMAL) \ + DO_GENOPEN_THEN_CLOEXEC(PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD)) +#define DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(strategy, PIPEFD, PIPEOPEN_CLOEXEC, \ + PIPEOPEN_NORMAL) \ + DO_GENOPEN_EXPERIMENTING_CLOEXEC(strategy, \ + (PIPEFD)[0], PIPEOPEN_CLOEXEC, \ + PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD)) + +int +Perl_PerlLIO_dup_cloexec(pTHX_ int oldfd) +{ +#if !defined(PERL_IMPLICIT_SYS) && defined(F_DUPFD_CLOEXEC) + /* + * struct IPerlLIO doesn't cover fcntl(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ + dVAR; + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_dup, + fcntl(oldfd, F_DUPFD_CLOEXEC, 0), + PerlLIO_dup(oldfd)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup(oldfd)); +#endif +} + +int +Perl_PerlLIO_dup2_cloexec(pTHX_ int oldfd, int newfd) +{ +#if !defined(PERL_IMPLICIT_SYS) && defined(HAS_DUP3) && defined(O_CLOEXEC) + /* + * struct IPerlLIO doesn't cover dup3(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ + dVAR; + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_dup2, + dup3(oldfd, newfd, O_CLOEXEC), + PerlLIO_dup2(oldfd, newfd)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup2(oldfd, newfd)); +#endif +} + +int +Perl_PerlLIO_open_cloexec(pTHX_ const char *file, int flag) +{ + dVAR; + PERL_ARGS_ASSERT_PERLLIO_OPEN_CLOEXEC; +#if defined(O_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_open, + PerlLIO_open(file, flag | O_CLOEXEC), + PerlLIO_open(file, flag)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open(file, flag)); +#endif +} + +int +Perl_PerlLIO_open3_cloexec(pTHX_ const char *file, int flag, int perm) +{ + dVAR; + PERL_ARGS_ASSERT_PERLLIO_OPEN3_CLOEXEC; +#if defined(O_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_open3, + PerlLIO_open3(file, flag | O_CLOEXEC, perm), + PerlLIO_open3(file, flag, perm)); +#else + DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open3(file, flag, perm)); +#endif +} + +int +Perl_my_mkstemp_cloexec(char *templte) +{ + dVAR; + PERL_ARGS_ASSERT_MY_MKSTEMP_CLOEXEC; +#if defined(O_CLOEXEC) + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_mkstemp, + Perl_my_mkostemp(templte, O_CLOEXEC), + Perl_my_mkstemp(templte)); +#else + DO_ONEOPEN_THEN_CLOEXEC(Perl_my_mkstemp(templte)); +#endif +} + +#ifdef HAS_PIPE +int +Perl_PerlProc_pipe_cloexec(pTHX_ int *pipefd) +{ + dVAR; + PERL_ARGS_ASSERT_PERLPROC_PIPE_CLOEXEC; + /* + * struct IPerlProc doesn't cover pipe2(), and there's no clear way + * to extend it, so for the time being this just isn't available on + * PERL_IMPLICIT_SYS builds. + */ +# if !defined(PERL_IMPLICIT_SYS) && defined(HAS_PIPE2) && defined(O_CLOEXEC) + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy_pipe, pipefd, + pipe2(pipefd, O_CLOEXEC), + PerlProc_pipe(pipefd)); +# else + DO_PIPEOPEN_THEN_CLOEXEC(pipefd, PerlProc_pipe(pipefd)); +# endif +} +#endif + +#ifdef HAS_SOCKET + +int +Perl_PerlSock_socket_cloexec(pTHX_ int domain, int type, int protocol) +{ +# if defined(SOCK_CLOEXEC) + dVAR; + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_socket, + PerlSock_socket(domain, type | SOCK_CLOEXEC, protocol), + PerlSock_socket(domain, type, protocol)); +# else + DO_ONEOPEN_THEN_CLOEXEC(PerlSock_socket(domain, type, protocol)); +# endif +} + +int +Perl_PerlSock_accept_cloexec(pTHX_ int listenfd, struct sockaddr *addr, + Sock_size_t *addrlen) +{ +# if !defined(PERL_IMPLICIT_SYS) && \ + defined(HAS_ACCEPT4) && defined(SOCK_CLOEXEC) + /* + * struct IPerlSock doesn't cover accept4(), and there's no clear + * way to extend it, so for the time being this just isn't available + * on PERL_IMPLICIT_SYS builds. + */ + dVAR; + DO_ONEOPEN_EXPERIMENTING_CLOEXEC( + PL_strategy_accept, + accept4(listenfd, addr, addrlen, SOCK_CLOEXEC), + PerlSock_accept(listenfd, addr, addrlen)); +# else + DO_ONEOPEN_THEN_CLOEXEC(PerlSock_accept(listenfd, addr, addrlen)); +# endif +} + +#endif + +#if defined (HAS_SOCKETPAIR) || \ + (defined (HAS_SOCKET) && defined(SOCK_DGRAM) && \ + defined(AF_INET) && defined(PF_INET)) +int +Perl_PerlSock_socketpair_cloexec(pTHX_ int domain, int type, int protocol, + int *pairfd) +{ + dVAR; + PERL_ARGS_ASSERT_PERLSOCK_SOCKETPAIR_CLOEXEC; +# ifdef SOCK_CLOEXEC + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy_socketpair, pairfd, + PerlSock_socketpair(domain, type | SOCK_CLOEXEC, protocol, pairfd), + PerlSock_socketpair(domain, type, protocol, pairfd)); +# else + DO_PIPEOPEN_THEN_CLOEXEC(pairfd, + PerlSock_socketpair(domain, type, protocol, pairfd)); +# endif +} +#endif + static IO * S_openn_setup(pTHX_ GV *gv, char *mode, PerlIO **saveifp, PerlIO **saveofp, int *savefd, char *savetype) @@ -261,7 +546,7 @@ Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len, STRLEN nlen = 0; /* New style explicit name, type is just mode and layer info */ #ifdef USE_STDIO - if (SvROK(*svp) && !strchr(oname,'&')) { + if (SvROK(*svp) && !memchr(oname, '&', len)) { if (ckWARN(WARN_IO)) Perl_warner(aTHX_ packWARN(WARN_IO), "Can't open a reference"); @@ -452,7 +737,7 @@ Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len, } else { if (dodup) - wanted_fd = PerlLIO_dup(wanted_fd); + wanted_fd = PerlLIO_dup_cloexec(wanted_fd); else was_fdopen = TRUE; if (!(fp = PerlIO_openn(aTHX_ type,mode,wanted_fd,0,0,NULL,num_svs,svp))) { @@ -621,15 +906,17 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname, PERL_ARGS_ASSERT_OPENN_CLEANUP; + Zero(&statbuf, 1, Stat_t); + if (!fp) { if (IoTYPE(io) == IoTYPE_RDONLY && ckWARN(WARN_NEWLINE) && should_warn_nl(oname) ) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "open"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } goto say_false; } @@ -741,33 +1028,15 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname, if (was_fdopen) { /* need to close fp without closing underlying fd */ int ofd = PerlIO_fileno(fp); - int dupfd = ofd >= 0 ? PerlLIO_dup(ofd) : -1; -#if defined(HAS_FCNTL) && defined(F_SETFD) - /* Assume if we have F_SETFD we have F_GETFD. */ - /* Get a copy of all the fd flags. */ - int fd_flags = ofd >= 0 ? fcntl(ofd, F_GETFD) : -1; - if (fd_flags < 0) { - if (dupfd >= 0) - PerlLIO_close(dupfd); - goto say_false; - } -#endif + int dupfd = ofd >= 0 ? PerlLIO_dup_cloexec(ofd) : -1; if (ofd < 0 || dupfd < 0) { if (dupfd >= 0) PerlLIO_close(dupfd); goto say_false; } PerlIO_close(fp); - PerlLIO_dup2(dupfd, ofd); -#if defined(HAS_FCNTL) && defined(F_SETFD) - /* The dup trick has lost close-on-exec on ofd, - * and possibly any other flags, so restore them. */ - if (fcntl(ofd,F_SETFD, fd_flags) < 0) { - if (dupfd >= 0) - PerlLIO_close(dupfd); - goto say_false; - } -#endif + PerlLIO_dup2_cloexec(dupfd, ofd); + setfd_inhexec_for_sysfd(ofd); PerlLIO_close(dupfd); } else @@ -777,12 +1046,6 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname, PerlIO_clearerr(fp); fd = PerlIO_fileno(fp); } -#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) - if (fd >= 0 && fd > PL_maxsysfd && fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { - PerlLIO_close(fd); - goto say_false; - } -#endif IoIFP(io) = fp; IoFLAGS(io) &= ~IOf_NOLINE; @@ -850,7 +1113,11 @@ S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) { else sv_setpvs(temp_out_name, "XXXXXXXX"); - fd = Perl_my_mkstemp(SvPVX(temp_out_name)); + { + int old_umask = umask(0177); + fd = Perl_my_mkstemp_cloexec(SvPVX(temp_out_name)); + umask(old_umask); + } if (fd < 0) return FALSE; @@ -871,10 +1138,8 @@ S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) { /* Win32 doesn't necessarily return useful information * in st_dev, st_ino. */ -#ifndef ARGV_USE_ATFUNCTIONS -# ifndef DOSISH -# define ARGV_USE_STAT_INO -# endif +#ifndef DOSISH +# define ARGV_USE_STAT_INO #endif #define ARGVMG_BACKUP_NAME 0 @@ -883,21 +1148,32 @@ S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) { #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. + + We store it even when the *at() functions are available, since + while the C runtime might have definitions for these functions, the + operating system or a specific filesystem might not implement them. + eg. NetBSD 6 implements linkat() but only where the fds are AT_FDCWD. */ -#define ARGVMG_ORIG_CWD_STAT 5 +#ifdef ARGV_USE_STAT_INO +# define ARGVMG_ORIG_CWD_STAT 5 +#endif + +#ifdef ARGV_USE_ATFUNCTIONS +# define ARGVMG_ORIG_DIRP 6 +#endif + +#ifdef ENOTSUP +#define NotSupported(e) ((e) == ENOSYS || (e) == ENOTSUP) +#else +#define NotSupported(e) ((e) == ENOSYS) #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 @@ -906,40 +1182,55 @@ S_argvout_free(pTHX_ SV *io, MAGIC *mg) { /* 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; + if (mg->mg_obj) { #ifdef ARGV_USE_ATFUNCTIONS SV **dir_psv; DIR *dir; + + 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)); #endif - PerlIO *iop = IoIFP(io); + if (IoIFP(io)) { + if (PL_phase == PERL_PHASE_DESTRUCT && PL_statusvalue == 0) { + (void)argvout_final(mg, (IO*)io, FALSE); + } + else { + SV **pid_psv; + PerlIO *iop = IoIFP(io); - assert(SvTYPE(mg->mg_obj) == SVt_PVAV); + assert(SvTYPE(mg->mg_obj) == SVt_PVAV); - pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); + pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE); - assert(pid_psv && *pid_psv); + 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 */ + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + const char *temp_pv = SvPVX(*temp_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)); + assert(temp_psv && *temp_psv && SvPOK(*temp_psv)); + (void)PerlIO_close(iop); + IoIFP(io) = IoOFP(io) = NULL; #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); - } + if (dir) { + if (unlinkat(my_dirfd(dir), temp_pv, 0) < 0 && + NotSupported(errno)) + (void)UNLINK(temp_pv); + } #else - (void)UNLINK(SvPVX(*temp_psv)); + (void)UNLINK(temp_pv); #endif + } + } } +#ifdef ARGV_USE_ATFUNCTIONS + if (dir) + closedir(dir); +#endif } return 0; @@ -964,8 +1255,11 @@ S_argvout_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) { 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 useful inode/device ids in stat_t we also keep: + 5: a stat of the original current working directory + 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 + 6: the DIR * for the current directory when we open the file, stored as an IV */ static const MGVTBL argvout_vtbl = @@ -1150,11 +1444,9 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) /* XXX silently ignore failures */ #ifdef HAS_FCHOWN PERL_UNUSED_RESULT(fchown(PL_lastfd,fileuid,filegid)); -#else -#ifdef HAS_CHOWN +#elif defined(HAS_CHOWN) PERL_UNUSED_RESULT(PerlLIO_chown(PL_oldname,fileuid,filegid)); #endif -#endif } } return IoIFP(GvIOp(gv)); @@ -1193,7 +1485,7 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen) } #ifdef ARGV_USE_ATFUNCTIONS -# if defined(__FreeBSD__) +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) /* FreeBSD 11 renameat() mis-behaves strangely with absolute paths in cases where the * equivalent rename() succeeds @@ -1213,34 +1505,59 @@ S_my_renameat(int olddfd, const char *oldpath, int newdfd, const char *newpath) # else # define S_my_renameat(dh1, pv1, dh2, pv2) renameat((dh1), (pv1), (dh2), (pv2)) -# endif /* if defined(__FreeBSD__) */ +# endif /* if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ #endif -/* explicit renamed to avoid C++ conflict -- kja */ -bool -Perl_do_close(pTHX_ GV *gv, bool not_implicit) -{ - bool retval; - IO *io; - MAGIC *mg; +static bool +S_dir_unchanged(pTHX_ const char *orig_pv, MAGIC *mg) { + Stat_t statbuf; - if (!gv) - gv = PL_argvgv; - if (!gv || !isGV_with_GP(gv)) { - if (not_implicit) - SETERRNO(EBADF,SS_IVCHAN); - return FALSE; +#ifdef 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; + + /* 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(orig_pv) + && 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 %s: %s", + orig_pv, "Current directory has changed"); } - io = GvIO(gv); - if (!io) { /* never opened */ - if (not_implicit) { - report_evil_fh(gv); - SETERRNO(EBADF,SS_IVCHAN); - } - return FALSE; +#else + SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE); + + /* Some platforms don't have useful st_ino etc, so just + check we can see the work file. + */ + if (!PERL_FILE_IS_ABSOLUTE(orig_pv) + && PerlLIO_stat(SvPVX(*temp_psv), &statbuf) < 0) { + Perl_croak(aTHX_ "Cannot complete in-place edit of %s: %s", + orig_pv, + "Work file is missing - did you change directory?"); } - if ((mg = mg_findext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl)) - && mg->mg_obj) { +#endif + + return TRUE; +} + +#define dir_unchanged(orig_psv, mg) \ + S_dir_unchanged(aTHX_ (orig_psv), (mg)) + +STATIC bool +S_argvout_final(pTHX_ MAGIC *mg, IO *io, bool not_implicit) { + bool retval; + + /* ensure args are checked before we start using them */ + PERL_ARGS_ASSERT_ARGVOUT_FINAL; + + { /* 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); @@ -1252,12 +1569,6 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) 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; @@ -1296,34 +1607,6 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } 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)) @@ -1331,11 +1614,18 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) do_close(PL_argvgv, FALSE); } #endif +#ifndef ARGV_USE_ATFUNCTIONS + if (!dir_unchanged(orig_pv, mg)) + goto abort_inplace; +#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 + linkat(dfd, orig_pv, dfd, SvPVX(*back_psv), 0) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + link(orig_pv, SvPVX(*back_psv)) == 0) # else link(orig_pv, SvPVX(*back_psv)) < 0 # endif @@ -1345,14 +1635,20 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) #ifdef HAS_RENAME if ( # ifdef ARGV_USE_ATFUNCTIONS - S_my_renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0 + S_my_renameat(dfd, orig_pv, dfd, SvPVX(*back_psv)) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + PerlLIO_rename(orig_pv, 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); + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) < 0 && + UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg)) + (void)UNLINK(SvPVX_const(*temp_psv)); # else UNLINK(SvPVX(*temp_psv)); # endif @@ -1383,24 +1679,28 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } #endif if ( -#ifdef HAS_RENAME -# ifdef ARGV_USE_ATFUNCTIONS - S_my_renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0 -# else - PerlLIO_rename(SvPVX(*temp_psv), orig_pv) < 0 -# endif -#else +#if !defined(HAS_RENAME) link(SvPVX(*temp_psv), orig_pv) < 0 +#elif defined(ARGV_USE_ATFUNCTIONS) + S_my_renameat(dfd, SvPVX(*temp_psv), dfd, orig_pv) < 0 && + !(UNLIKELY(NotSupported(errno)) && + dir_unchanged(orig_pv, mg) && + PerlLIO_rename(SvPVX(*temp_psv), orig_pv) == 0) +#else + PerlLIO_rename(SvPVX(*temp_psv), orig_pv) < 0 #endif ) { if (!not_implicit) { #ifdef ARGV_USE_ATFUNCTIONS - (void)unlinkat(dfd, SvPVX_const(*temp_psv), 0); + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) < 0 && + NotSupported(errno)) + UNLINK(SvPVX(*temp_psv)); #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)); + /* diag_listed_as: Cannot complete in-place edit of %s: %s */ + Perl_croak(aTHX_ "Cannot complete in-place edit of %s: failed to rename work file '%s' to '%s': %s", + orig_pv, SvPVX(*temp_psv), orig_pv, Strerror(errno)); } abort_inplace: UNLINK(SvPVX_const(*temp_psv)); @@ -1412,7 +1712,10 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) } else { #ifdef ARGV_USE_ATFUNCTIONS - unlinkat(dfd, SvPVX_const(*temp_psv), 0); + if (unlinkat(dfd, SvPVX_const(*temp_psv), 0) && + NotSupported(errno)) + UNLINK(SvPVX_const(*temp_psv)); + #else UNLINK(SvPVX_const(*temp_psv)); #endif @@ -1421,7 +1724,38 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit) SvPVX(*temp_psv), Strerror(errno)); } } - freext: + freext: + ; + } + return retval; +} + +/* explicit renamed to avoid C++ conflict -- kja */ +bool +Perl_do_close(pTHX_ GV *gv, bool not_implicit) +{ + bool retval; + IO *io; + MAGIC *mg; + + if (!gv) + gv = PL_argvgv; + if (!gv || !isGV_with_GP(gv)) { + if (not_implicit) + SETERRNO(EBADF,SS_IVCHAN); + return FALSE; + } + io = GvIO(gv); + if (!io) { /* never opened */ + if (not_implicit) { + report_evil_fh(gv); + SETERRNO(EBADF,SS_IVCHAN); + } + return FALSE; + } + if ((mg = mg_findext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl)) + && mg->mg_obj) { + retval = argvout_final(mg, io, not_implicit); mg_freeext((SV*)io, PERL_MAGIC_uvar, &argvout_vtbl); } else { @@ -1634,7 +1968,7 @@ Perl_mode_from_discipline(pTHX_ const char *s, STRLEN len) else { const char *end; fail_discipline: - end = strchr(s+1, ':'); + end = (char *) memchr(s+1, ':', len); if (!end) end = s+len; #ifndef PERLIO_LAYERS @@ -1783,8 +2117,11 @@ Perl_my_stat_flags(pTHX_ const U32 flags) if (PL_op->op_flags & OPf_REF) { gv = cGVOP_gv; do_fstat: - if (gv == PL_defgv) + if (gv == PL_defgv) { + if (PL_laststatval < 0) + SETERRNO(EBADF,RMS_IFI); return PL_laststatval; + } io = GvIO(gv); do_fstat_have_io: PL_laststype = OP_STAT; @@ -1795,6 +2132,7 @@ Perl_my_stat_flags(pTHX_ const U32 flags) int fd = PerlIO_fileno(IoIFP(io)); if (fd < 0) { /* E.g. PerlIO::scalar has no real fd. */ + SETERRNO(EBADF,RMS_IFI); return (PL_laststatval = -1); } else { return (PL_laststatval = PerlLIO_fstat(fd, &PL_statcache)); @@ -1805,6 +2143,7 @@ Perl_my_stat_flags(pTHX_ const U32 flags) } PL_laststatval = -1; report_evil_fh(gv); + SETERRNO(EBADF,RMS_IFI); return -1; } else if ((PL_op->op_private & (OPpFT_STACKED|OPpFT_AFTER_t)) @@ -1812,7 +2151,7 @@ Perl_my_stat_flags(pTHX_ const U32 flags) return PL_laststatval; else { SV* const sv = TOPs; - const char *s; + const char *s, *d; STRLEN len; if ((gv = MAYBE_DEREF_GV_flags(sv,flags))) { goto do_fstat; @@ -1826,13 +2165,18 @@ Perl_my_stat_flags(pTHX_ const U32 flags) s = SvPV_flags_const(sv, len, flags); PL_statgv = NULL; sv_setpvn(PL_statname, s, len); - s = SvPVX_const(PL_statname); /* s now NUL-terminated */ + d = SvPVX_const(PL_statname); /* s now NUL-terminated */ PL_laststype = OP_STAT; - PL_laststatval = PerlLIO_stat(s, &PL_statcache); + if (!IS_SAFE_PATHNAME(s, len, OP_NAME(PL_op))) { + PL_laststatval = -1; + } + else { + PL_laststatval = PerlLIO_stat(d, &PL_statcache); + } if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(s)) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "stat"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } return PL_laststatval; } @@ -1845,12 +2189,15 @@ Perl_my_lstat_flags(pTHX_ const U32 flags) static const char* const no_prev_lstat = "The stat preceding -l _ wasn't an lstat"; dSP; const char *file; + STRLEN len; SV* const sv = TOPs; bool isio = FALSE; if (PL_op->op_flags & OPf_REF) { if (cGVOP_gv == PL_defgv) { if (PL_laststype != OP_LSTAT) Perl_croak(aTHX_ "%s", no_prev_lstat); + if (PL_laststatval < 0) + SETERRNO(EBADF,RMS_IFI); return PL_laststatval; } PL_laststatval = -1; @@ -1860,6 +2207,7 @@ Perl_my_lstat_flags(pTHX_ const U32 flags) "Use of -l on filehandle %" HEKf, HEKfARG(GvENAME_HEK(cGVOP_gv))); } + SETERRNO(EBADF,RMS_IFI); return -1; } if ((PL_op->op_private & (OPpFT_STACKED|OPpFT_AFTER_t)) @@ -1888,13 +2236,18 @@ Perl_my_lstat_flags(pTHX_ const U32 flags) HEKfARG(GvENAME_HEK((const GV *) (SvROK(sv) ? SvRV(sv) : sv)))); } - file = SvPV_flags_const_nolen(sv, flags); + file = SvPV_flags_const(sv, len, flags); sv_setpv(PL_statname,file); - PL_laststatval = PerlLIO_lstat(file,&PL_statcache); + if (!IS_SAFE_PATHNAME(file, len, OP_NAME(PL_op))) { + PL_laststatval = -1; + } + else { + PL_laststatval = PerlLIO_lstat(file,&PL_statcache); + } if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(file)) { - GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */ + GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */ Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "lstat"); - GCC_DIAG_RESTORE; + GCC_DIAG_RESTORE_STMT; } return PL_laststatval; } @@ -1924,54 +2277,54 @@ Perl_do_aexec5(pTHX_ SV *really, SV **mark, SV **sp, #if defined(__SYMBIAN32__) || defined(__LIBCATAMOUNT__) Perl_croak(aTHX_ "exec? I'm not *that* kind of operating system"); #else - if (sp > mark) { - const char **a; + assert(sp >= mark); + ENTER; + { + const char **argv, **a; const char *tmps = NULL; - Newx(PL_Argv, sp - mark + 1, const char*); - a = PL_Argv; + Newx(argv, sp - mark + 1, const char*); + SAVEFREEPV(argv); + a = argv; while (++mark <= sp) { - if (*mark) - *a++ = SvPV_nolen_const(*mark); - else + if (*mark) { + char *arg = savepv(SvPV_nolen_const(*mark)); + SAVEFREEPV(arg); + *a++ = arg; + } else *a++ = ""; } *a = NULL; - if (really) - tmps = SvPV_nolen_const(really); - if ((!really && *PL_Argv[0] != '/') || + if (really) { + tmps = savepv(SvPV_nolen_const(really)); + SAVEFREEPV(tmps); + } + if ((!really && argv[0] && *argv[0] != '/') || (really && *tmps != '/')) /* will execvp use PATH? */ TAINT_ENV(); /* testing IFS here is overkill, probably */ PERL_FPU_PRE_EXEC if (really && *tmps) { - PerlProc_execvp(tmps,EXEC_ARGV_CAST(PL_Argv)); - } else { - PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv)); - } + PerlProc_execvp(tmps,EXEC_ARGV_CAST(argv)); + } else if (argv[0]) { + PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv)); + } else { + SETERRNO(ENOENT,RMS_FNF); + } PERL_FPU_POST_EXEC - S_exec_failed(aTHX_ (really ? tmps : PL_Argv[0]), fd, do_report); + S_exec_failed(aTHX_ (really ? tmps : argv[0] ? argv[0] : ""), fd, do_report); } - do_execfree(); + LEAVE; #endif return FALSE; } -void -Perl_do_execfree(pTHX) -{ - Safefree(PL_Argv); - PL_Argv = NULL; - Safefree(PL_Cmd); - PL_Cmd = NULL; -} - #ifdef PERL_DEFAULT_DO_EXEC3_IMPLEMENTATION bool Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) { dVAR; - const char **a; + const char **argv, **a; char *s; char *buf; char *cmd; @@ -1980,7 +2333,9 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) PERL_ARGS_ASSERT_DO_EXEC3; + ENTER; Newx(buf, cmdlen, char); + SAVEFREEPV(buf); cmd = buf; memcpy(cmd, incmd, cmdlen); @@ -1993,7 +2348,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) { char flags[PERL_FLAGS_MAX]; if (strnEQ(cmd,PL_cshname,PL_cshlen) && - strEQs(cmd+PL_cshlen," -c")) { + strBEGINs(cmd+PL_cshlen," -c")) { my_strlcpy(flags, "-c", PERL_FLAGS_MAX); s = cmd+PL_cshlen+3; if (*s == 'f') { @@ -2016,8 +2371,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) PERL_FPU_POST_EXEC *s = '\''; S_exec_failed(aTHX_ PL_cshname, fd, do_report); - Safefree(buf); - return FALSE; + goto leave; } } } @@ -2029,7 +2383,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) if (*cmd == '.' && isSPACE(cmd[1])) goto doshell; - if (strEQs(cmd,"exec") && isSPACE(cmd[4])) + if (strBEGINs(cmd,"exec") && isSPACE(cmd[4])) goto doshell; s = cmd; @@ -2064,15 +2418,16 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char *)NULL); PERL_FPU_POST_EXEC S_exec_failed(aTHX_ PL_sh_path, fd, do_report); - Safefree(buf); - return FALSE; + goto leave; } } - Newx(PL_Argv, (s - cmd) / 2 + 2, const char*); - PL_Cmd = savepvn(cmd, s-cmd); - a = PL_Argv; - for (s = PL_Cmd; *s;) { + Newx(argv, (s - cmd) / 2 + 2, const char*); + SAVEFREEPV(argv); + cmd = savepvn(cmd, s-cmd); + SAVEFREEPV(cmd); + a = argv; + for (s = cmd; *s;) { while (isSPACE(*s)) s++; if (*s) @@ -2083,18 +2438,16 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report) *s++ = '\0'; } *a = NULL; - if (PL_Argv[0]) { + if (argv[0]) { PERL_FPU_PRE_EXEC - PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv)); + PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv)); PERL_FPU_POST_EXEC - if (errno == ENOEXEC) { /* for system V NIH syndrome */ - do_execfree(); + if (errno == ENOEXEC) /* for system V NIH syndrome */ goto doshell; - } - S_exec_failed(aTHX_ PL_Argv[0], fd, do_report); + S_exec_failed(aTHX_ argv[0], fd, do_report); } - do_execfree(); - Safefree(buf); +leave: + LEAVE; return FALSE; } @@ -2925,33 +3278,29 @@ Perl_vms_start_glob fp = Perl_vms_start_glob(aTHX_ tmpglob, io); #else /* !VMS */ -#ifdef DOSISH -#ifdef OS2 +# ifdef DOSISH +# if defined(OS2) sv_setpv(tmpcmd, "for a in "); sv_catsv(tmpcmd, tmpglob); - sv_catpv(tmpcmd, "; do echo \"$a\\0\\c\"; done |"); -#else -#ifdef DJGPP + sv_catpvs(tmpcmd, "; do echo \"$a\\0\\c\"; done |"); +# elif defined(DJGPP) sv_setpv(tmpcmd, "/dev/dosglob/"); /* File System Extension */ sv_catsv(tmpcmd, tmpglob); -#else +# else sv_setpv(tmpcmd, "perlglob "); sv_catsv(tmpcmd, tmpglob); - sv_catpv(tmpcmd, " |"); -#endif /* !DJGPP */ -#endif /* !OS2 */ -#else /* !DOSISH */ -#if defined(CSH) + sv_catpvs(tmpcmd, " |"); +# endif +# elif defined(CSH) sv_setpvn(tmpcmd, PL_cshname, PL_cshlen); - sv_catpv(tmpcmd, " -cf 'set nonomatch; glob "); + sv_catpvs(tmpcmd, " -cf 'set nonomatch; glob "); sv_catsv(tmpcmd, tmpglob); - sv_catpv(tmpcmd, "' 2>/dev/null |"); -#else + sv_catpvs(tmpcmd, "' 2>/dev/null |"); +# else sv_setpv(tmpcmd, "echo "); sv_catsv(tmpcmd, tmpglob); - sv_catpv(tmpcmd, "|tr -s ' \t\f\r' '\\n\\n\\n\\n'|"); -#endif /* !CSH */ -#endif /* !DOSISH */ + sv_catpvs(tmpcmd, "|tr -s ' \t\f\r' '\\n\\n\\n\\n'|"); +# endif /* !DOSISH && !CSH */ { SV ** const svp = hv_fetchs(GvHVn(PL_envgv), "LS_COLORS", 0); if (svp && *svp)