From 3da9cd2a0deb1f5b0412ecf4ea2899183024c56b Mon Sep 17 00:00:00 2001 From: Joshua Root Date: Wed, 28 Aug 2024 00:24:25 +1000 Subject: [PATCH] Use clonefile(2) in more places A lot of file copies that we create are not subsequently modified, especially in the destroot phase. This makes cloning a more efficient alternative. * Modified xinstall and 'file copy' Tcl commands to try cloning. * Created a vendored copy of install(1) that tries cloning. * Created a thin wrapper around cp(1) that adds the -c flag. * If the build directory is on a filesystem that supports cloning, the configure.install option defaults to our install(1), and the directory containing our install and cp commands is added to the front of PATH, currently only during the destroot phase. The code for all of the above is not built on systems that lack clonefile(2). If cloning fails, everything will fall back to creating a copy as previously done. --- .gitignore | 3 +- Mk/macports.autoconf.mk.in | 1 + configure | 24 +- configure.ac | 16 +- src/macports1.0/macports.tcl | 25 +- src/macports1.0/macports_autoconf.tcl.in | 1 + src/pextlib1.0/xinstall.c | 83 ++- src/port1.0/portconfigure.tcl | 2 +- src/port1.0/portdestroot.tcl | 14 +- src/programs/cp/Makefile.in | 36 + src/programs/cp/cp.c | 26 + src/programs/daemondo/Makefile.in | 2 + vendor/Makefile.in | 37 +- vendor/install/Makefile.in | 38 + vendor/install/tests/Kyuafile | 3 + vendor/install/tests/install_test.sh | 448 ++++++++++++ vendor/install/xinstall.c | 878 +++++++++++++++++++++++ vendor/patches/install/xinstall.c.patch | 129 ++++ vendor/patches/tcl/clonefile.patch | 112 +++ vendor/tcl8.6.14/unix/configure | 4 +- vendor/tcl8.6.14/unix/tclUnixFCmd.c | 59 +- 21 files changed, 1910 insertions(+), 31 deletions(-) create mode 100644 src/programs/cp/Makefile.in create mode 100644 src/programs/cp/cp.c create mode 100644 vendor/install/Makefile.in create mode 100644 vendor/install/tests/Kyuafile create mode 100755 vendor/install/tests/install_test.sh create mode 100644 vendor/install/xinstall.c create mode 100644 vendor/patches/install/xinstall.c.patch create mode 100644 vendor/patches/tcl/clonefile.patch diff --git a/.gitignore b/.gitignore index bcc07c440..3c783ba87 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,7 @@ src/darwintracelib1.0/sip_copy_proc.[ch] src/machista1.0/tests/libmachista-test src/port/* !src/port/*.tcl -src/programs/daemondo/build/ +src/programs/*/build/ src/**/*.o src/**/*.a src/**/*.dylib @@ -44,6 +44,7 @@ tests/test/trace/test.tcl vendor/vendor-destroot/ vendor/tclsh +vendor/install/build/ vendor/**/*.o vendor/**/*.a vendor/**/*.dylib diff --git a/Mk/macports.autoconf.mk.in b/Mk/macports.autoconf.mk.in index bbf3ef972..29cbeaa2b 100644 --- a/Mk/macports.autoconf.mk.in +++ b/Mk/macports.autoconf.mk.in @@ -37,6 +37,7 @@ XCODEBUILD = @XCODEBUILD@ INTREE_TCLSH = @INTREE_TCLSH@ TCLSH = @TCLSH@ TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@ +TCL_PREFIX = @TCL_PREFIX@ DSCL = @DSCL@ DSEDITGROUP = @DSEDITGROUP@ diff --git a/configure b/configure index 39ab3f4e3..550ac0e11 100755 --- a/configure +++ b/configure @@ -672,6 +672,7 @@ VENDOR_TCLX_SUBDIR VENDOR_DESTROOT VENDOR_TCL_INSTALL VENDOR_TCL_SUBDIR +CLONEBIN_PATH localstatedir_expanded MPCONFIGDIR_EXPANDED DARWINTRACE_SIP_WORKAROUND_PATH @@ -689,6 +690,8 @@ HAVE_STRLCPY HAVE_STRLCAT HAVE_GETLINE HAVE_GETDELIM +EXTRA_PROGS +HAVE_CLONEFILE HAVE_TIMINGSAFE_BCMP HAVE_GETENTROPY HAVE_ARC4RANDOM_BUF @@ -704,7 +707,6 @@ RUNUSR DSTGRP DSTUSR MPCONFIGDIR -EXTRA_PROGS HAVE_FUNCTION_CFNOTIFICATIONCENTERGETDARWINNOTIFYCENTER HAVE_FRAMEWORK_IOKIT HAVE_FRAMEWORK_SYSTEMCONFIGURATION @@ -7012,8 +7014,6 @@ printf %s "checking for whether we will build daemondo... " >&6; } printf "%s\n" "${result}" >&6; } - - # Check for paths @@ -7729,6 +7729,17 @@ HAVE_GETENTROPY=$ac_cv_func_getentropy HAVE_TIMINGSAFE_BCMP=$ac_cv_func_timingsafe_bcmp +# For vendor/install +HAVE_CLONEFILE=$ac_cv_func_clonefile + +# For programs/cp +if test x$ac_cv_func_clonefile = xyes; then +EXTRA_PROGS="$EXTRA_PROGS cp" +ac_config_files="$ac_config_files src/programs/cp/Makefile vendor/install/Makefile" + +fi + + # Check for library functions, replacements are in pextlib1.0/compat/ ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline" if test "x$ac_cv_func_getline" = xyes @@ -8660,6 +8671,11 @@ eval "macports_libexec_dir=$macports_libexec_dir" prefix=$oldprefix exec_prefix=$oldexec_prefix +if test x$ac_cv_func_clonefile = xyes; then + CLONEBIN_PATH="${macports_libexec_dir}/clonebin" +fi + + ## Tcl package mp_fn_var_prepend() { eval "$1=\"$2\$$1\"" @@ -10448,6 +10464,8 @@ do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "src/programs/daemondo/Makefile") CONFIG_FILES="$CONFIG_FILES src/programs/daemondo/Makefile" ;; + "src/programs/cp/Makefile") CONFIG_FILES="$CONFIG_FILES src/programs/cp/Makefile" ;; + "vendor/install/Makefile") CONFIG_FILES="$CONFIG_FILES vendor/install/Makefile" ;; "Doxyfile") CONFIG_FILES="$CONFIG_FILES Doxyfile" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "Mk/macports.autoconf.mk") CONFIG_FILES="$CONFIG_FILES Mk/macports.autoconf.mk" ;; diff --git a/configure.ac b/configure.ac index 6c64060bd..1d9691565 100644 --- a/configure.ac +++ b/configure.ac @@ -201,8 +201,6 @@ MP_COMPILER_ATTRIBUTE_UNUSED MP_PROG_DAEMONDO -AC_SUBST(EXTRA_PROGS) - # Check for paths AC_PREFIX_DEFAULT(/opt/local) AC_ARG_WITH( @@ -269,6 +267,15 @@ AC_SUBST(HAVE_ARC4RANDOM_BUF, $ac_cv_func_arc4random_buf) AC_SUBST(HAVE_GETENTROPY, $ac_cv_func_getentropy) AC_SUBST(HAVE_TIMINGSAFE_BCMP, $ac_cv_func_timingsafe_bcmp) +# For vendor/install +AC_SUBST(HAVE_CLONEFILE, $ac_cv_func_clonefile) +# For programs/cp +if test x$ac_cv_func_clonefile = xyes; then +EXTRA_PROGS="$EXTRA_PROGS cp" +AC_CONFIG_FILES([src/programs/cp/Makefile vendor/install/Makefile]) +fi +AC_SUBST(EXTRA_PROGS) + # Check for library functions, replacements are in pextlib1.0/compat/ AC_CHECK_FUNCS([getline getdelim strlcat strlcpy]) AC_SUBST(HAVE_GETDELIM, $ac_cv_func_getdelim) @@ -347,6 +354,11 @@ eval "macports_libexec_dir=$macports_libexec_dir" prefix=$oldprefix exec_prefix=$oldexec_prefix +if test x$ac_cv_func_clonefile = xyes; then + CLONEBIN_PATH="${macports_libexec_dir}/clonebin" +fi +AC_SUBST(CLONEBIN_PATH) + ## Tcl package MP_CONFIG_SUBDIR([vendor/tcl/unix], [--prefix=${macports_libexec_dir} --enable-threads]) VENDOR_TCL_SUBDIR=tcl/unix diff --git a/src/macports1.0/macports.tcl b/src/macports1.0/macports.tcl index e324c2ee0..5b9dd9d3f 100644 --- a/src/macports1.0/macports.tcl +++ b/src/macports1.0/macports.tcl @@ -49,7 +49,7 @@ package require mpcommon 1.0 namespace eval macports { variable bootstrap_options [dict create] # Config file options with no special handling - foreach opt [list binpath auto_path extra_env portdbformat \ + foreach opt [list binpath auto_path clonebin_path extra_env portdbformat \ portarchivetype portimage_mode hfscompression portautoclean \ porttrace portverbose keeplogs destroot_umask release_urls release_version_urls \ rsync_server rsync_options rsync_dir \ @@ -87,6 +87,9 @@ namespace eval macports { macosx_deployment_target packagemaker_path default_compilers sandbox_enable sandbox_network \ delete_la_files cxx_stdlib pkg_post_unarchive_deletions {*}$user_options] + # Options set in the portfile interpreter but only in system_options + variable portinterp_private_options [list clonebin_path] + # deferred options are only computed when needed. # they are not exported to the trace thread. # they are not exported to the interpreter in system_options array. @@ -985,6 +988,7 @@ proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} { macports::portarchive_hfscompression \ macports::host_cache \ macports::porturl_prefix_map \ + macports::clonebin_path \ macports::ui_options \ macports::global_options \ macports::global_variations @@ -1486,6 +1490,15 @@ match macports.conf.default." set env(PATH) $binpath } + if {![info exists clonebin_path]} { + if {![catch {fs_clone_capable [file join $portdbpath build]} result] && $result + && [file executable ${macports::autoconf::clonebin_path}/install]} { + set clonebin_path ${macports::autoconf::clonebin_path} + } else { + set clonebin_path {} + } + } + # Set startupitem default type (can be overridden by portfile) if {![info exists startupitem_type]} { set startupitem_type default @@ -1896,6 +1909,7 @@ proc macports::copy_xcode_plist {target_homedir} { proc macports::worker_init {workername portpath porturl portbuildpath options variations} { variable portinterp_options; variable portinterp_deferred_options + variable portinterp_private_options variable ui_priorities; variable ui_options # Hide any Tcl commands that should be inaccessible to port1.0 and Portfiles @@ -2044,6 +2058,15 @@ proc macports::worker_init {workername portpath porturl portbuildpath options va $workername eval [list set $opt ?] } + foreach opt $portinterp_private_options { + if {![info exists $opt]} { + variable $opt + } + if {[info exists $opt]} { + $workername eval [list set system_options($opt) [set $opt]] + } + } + foreach {opt val} $options { $workername eval [list set user_options($opt) $val] $workername eval [list set $opt $val] diff --git a/src/macports1.0/macports_autoconf.tcl.in b/src/macports1.0/macports_autoconf.tcl.in index c2444bfdb..5cd3813c6 100644 --- a/src/macports1.0/macports_autoconf.tcl.in +++ b/src/macports1.0/macports_autoconf.tcl.in @@ -35,6 +35,7 @@ namespace eval macports::autoconf { variable aa_path "@AA@" variable bzip2_path "@BZIP2_BIN@" variable chown_path "@CHOWN@" + variable clonebin_path "@CLONEBIN_PATH@" variable gzip_path "@GZIP_BIN@" variable macports_conf_path "@MPCONFIGDIR_EXPANDED@" variable macports_keys_base "@prefix_expanded@/share/macports/keys/base" diff --git a/src/pextlib1.0/xinstall.c b/src/pextlib1.0/xinstall.c index bc990178a..3cb59fe03 100644 --- a/src/pextlib1.0/xinstall.c +++ b/src/pextlib1.0/xinstall.c @@ -47,6 +47,14 @@ #include #endif +/* For clonefile */ +#ifdef HAVE_SYS_ATTR_H +#include +#endif +#ifdef HAVE_SYS_CLONEFILE_H +#include +#endif + #include #include #include @@ -131,6 +139,9 @@ static char *funcname; static int safecopy, docompare, dostrip, dobackup, dopreserve, nommap; static mode_t mode; +#ifdef HAVE_CLONEFILE +static int clone(const char *, const char *, int, char *, size_t); +#endif static int copy(Tcl_Interp *interp, int, const char *, int, const char *, off_t); static int compare(int, const char *, size_t, int, const char *, size_t); static int create_newfile(Tcl_Interp *interp, const char *, int, struct stat *); @@ -139,6 +150,7 @@ static int install(Tcl_Interp *interp, const char *, const char *, u_long, u_int static int install_dir(Tcl_Interp *interp, char *); static u_long numeric_id(Tcl_Interp *interp, const char *, const char *, int *rval); static void strip(const char *); +static void tempfile_template(const char *, char *, size_t); static int trymmap(int); static void usage(Tcl_Interp *interp); @@ -432,9 +444,14 @@ install(Tcl_Interp *interp, const char *from_name, const char *to_name, u_long f { struct stat from_sb, temp_sb, to_sb; struct timeval tvb[2]; - int devnull, files_match, from_fd = 0, serrno, target; - int tempcopy, temp_fd, to_fd = 0; + int devnull, files_match, from_fd = -1, serrno, target; + int tempcopy, temp_fd = -1, to_fd = -1; char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; +#ifdef HAVE_CLONEFILE + const int tryclone = 1; +#else + const int tryclone = 0; +#endif files_match = 0; @@ -484,7 +501,7 @@ install(Tcl_Interp *interp, const char *from_name, const char *to_name, u_long f /* Only copy safe if the target exists. */ tempcopy = safecopy && target; - if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) { + if ((!tryclone || dostrip || docompare) && !devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) { char errmsg[255]; snprintf(errmsg, sizeof errmsg, "%s: Unable to open: %s, %s", @@ -517,6 +534,26 @@ install(Tcl_Interp *interp, const char *from_name, const char *to_name, u_long f } if (!files_match) { + int done_clone = 0; +#ifdef HAVE_CLONEFILE + if (tryclone && !devnull && !dostrip) { + done_clone = (clone(from_name, to_name, tempcopy, tempfile, sizeof(tempfile)) == 0); + } + if (done_clone && + (((to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0)) < 0) + || (!tempcopy && fstat(to_fd, &to_sb) == -1))) { + (void)unlink(tempcopy ? tempfile : to_name); + done_clone = 0; + } + if (!done_clone && from_fd < 0 && (from_fd = open(from_name, O_RDONLY, 0)) < 0) { + char errmsg[255]; + snprintf(errmsg, sizeof errmsg, "%s: Unable to open: %s, %s", + funcname, from_name, strerror(errno)); + Tcl_SetResult(interp, errmsg, TCL_VOLATILE); + return TCL_ERROR; + } +#endif + if (!done_clone) { if (tempcopy) { to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); @@ -539,8 +576,9 @@ install(Tcl_Interp *interp, const char *from_name, const char *to_name, u_long f } ui_info(interp, "%s: %s -> %s", funcname, from_name, to_name); } + } /* !done_clone */ if (!devnull) { - if (copy(interp, from_fd, from_name, to_fd, + if (!done_clone && copy(interp, from_fd, from_name, to_fd, tempcopy ? tempfile : to_name, from_sb.st_size) != TCL_OK) return TCL_ERROR; #if HAVE_COPYFILE @@ -838,12 +876,11 @@ compare(int from_fd, const char *from_name UNUSED, size_t from_len, return rv; } -/* - * create_tempfile -- - * create a temporary file based on path and open it +/* tempfile_template -- + * prepare a template filename for use with mktemp/mkstemp. */ -static int -create_tempfile(const char *path, char *temp, size_t tsize) +static void +tempfile_template(const char *path, char *temp, size_t tsize) { char *p; @@ -855,6 +892,16 @@ create_tempfile(const char *path, char *temp, size_t tsize) p = temp; (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); temp[tsize - 1] = '\0'; +} + +/* + * create_tempfile -- + * create a temporary file based on path and open it + */ +static int +create_tempfile(const char *path, char *temp, size_t tsize) +{ + tempfile_template(path, temp, tsize); return (mkstemp(temp)); } @@ -993,6 +1040,24 @@ copy(Tcl_Interp *interp, int from_fd, const char *from_name, int to_fd, const ch return TCL_OK; } +/* + * clone -- + * create a clone of a file + */ +#ifdef HAVE_CLONEFILE +static int +clone(const char *from_name, const char *to_name, + int use_temp, char *temp_name, size_t tsize) +{ + if (use_temp) { + tempfile_template(to_name, temp_name, tsize); + mktemp(temp_name); + to_name = temp_name; + } + return clonefile(from_name, to_name, CLONE_NOFOLLOW|CLONE_NOOWNERCOPY); +} +#endif + /* * strip -- * use strip(1) to strip the target file diff --git a/src/port1.0/portconfigure.tcl b/src/port1.0/portconfigure.tcl index ecca5f81d..6a89c7b62 100644 --- a/src/port1.0/portconfigure.tcl +++ b/src/port1.0/portconfigure.tcl @@ -291,7 +291,7 @@ options configure.perl configure.python configure.ruby \ default configure.perl {} default configure.python {} default configure.ruby {} -default configure.install {${portutil::autoconf::install_command}} +default configure.install {[expr {$system_options(clonebin_path) ne "" ? [file join $system_options(clonebin_path) install] : ${portutil::autoconf::install_command}}]} default configure.awk {} default configure.bison {} default configure.pkg_config {} diff --git a/src/port1.0/portdestroot.tcl b/src/port1.0/portdestroot.tcl index 0ebf48732..1b4e9bf81 100644 --- a/src/port1.0/portdestroot.tcl +++ b/src/port1.0/portdestroot.tcl @@ -126,7 +126,19 @@ proc portdestroot::destroot_start {args} { } proc portdestroot::destroot_main {args} { - command_exec -callback portprogress::target_progress_callback destroot + global system_options + if {$system_options(clonebin_path) ne ""} { + global env + set saved_path $env(PATH) + set env(PATH) $system_options(clonebin_path):$env(PATH) + } + try { + command_exec -callback portprogress::target_progress_callback destroot + } finally { + if {[info exists saved_path]} { + set env(PATH) $saved_path + } + } return 0 } diff --git a/src/programs/cp/Makefile.in b/src/programs/cp/Makefile.in new file mode 100644 index 000000000..ea1dc8665 --- /dev/null +++ b/src/programs/cp/Makefile.in @@ -0,0 +1,36 @@ + +srcdir = @srcdir@ +VPATH = @srcdir@ + +include ../../../Mk/macports.autoconf.mk + +SRCS = cp.c +OBJS = cp.o +PROGRAM = build/cp + +.PHONY: cp mkdirs + +all: cp + +cp: mkdirs ${PROGRAM} + +${PROGRAM}: ${OBJS} + ${CC} ${CFLAGS} -o ${PROGRAM} ${LIBS} ${OBJS} + +mkdirs: + mkdir -p build + +clean: + rm -Rf build ${PROGRAM} ${OBJS} + +distclean: clean + rm -f Makefile + +install: cp + ${INSTALL} -d -m 755 "${DESTDIR}${TCL_PREFIX}/clonebin" + ${INSTALL} -o "${DSTUSR}" -g "${DSTGRP}" -m 555 "${PROGRAM}" "${DESTDIR}${TCL_PREFIX}/clonebin" + +test: + +codesign:: ${PROGRAM} + ../../codesign.sh $? diff --git a/src/programs/cp/cp.c b/src/programs/cp/cp.c new file mode 100644 index 000000000..24c9703ef --- /dev/null +++ b/src/programs/cp/cp.c @@ -0,0 +1,26 @@ +/* cp wrapper to add -c (clone) option */ + +#include +#include +#include +#include + +extern char **environ; + +int main(int argc, char *argv[]) +{ + const char *cp_path = "/bin/cp"; + const char *clone_arg = "-c"; + const char **new_argv = malloc(sizeof(char *) * (argc+1)); + if (new_argv) { + new_argv[0] = cp_path; + new_argv[1] = clone_arg; + for (int i = 1; i < argc; i++) { + new_argv[i+1] = argv[i]; + } + execve(cp_path, new_argv, environ); + } + /* something failed */ + perror("cp"); + return errno; +} diff --git a/src/programs/daemondo/Makefile.in b/src/programs/daemondo/Makefile.in index e22036871..d5733c2a5 100644 --- a/src/programs/daemondo/Makefile.in +++ b/src/programs/daemondo/Makefile.in @@ -17,6 +17,8 @@ LIBS = -framework CoreFoundation \ -framework IOKit PROGRAM = build/daemondo +.PHONY: daemondo mkdirs + all: daemondo daemondo: mkdirs ${PROGRAM} diff --git a/vendor/Makefile.in b/vendor/Makefile.in index 44371ddd9..14aed0cf2 100644 --- a/vendor/Makefile.in +++ b/vendor/Makefile.in @@ -2,14 +2,15 @@ TCL_SUBDIR= @VENDOR_TCL_SUBDIR@ TCLX_SUBDIR= @VENDOR_TCLX_SUBDIR@ TCLLIB_SUBDIR= @VENDOR_TCLLIB_SUBDIR@ SIGNIFY_SUBDIR= signify-osx +INSTALL_SUBDIR= install PREFIX= @prefix@ DESTROOT= @abs_top_builddir@/vendor/vendor-destroot .PHONY: all clean distclean install destroot test -.PHONY: destroot-tcl destroot-tclx destroot-tcllib destroot-signify -.PHONY: install-tcl install-tclx install-tcllib install-signify -.PHONY: %-tcl %-tclx %-tcllib %-signify +.PHONY: destroot-tcl destroot-tclx destroot-tcllib destroot-signify destroot-install +.PHONY: install-tcl install-tclx install-tcllib install-signify install-install +.PHONY: %-tcl %-tclx %-tcllib %-signify %-install # for make all, run destroot (where destroot will depend on all of each # subpackage) @@ -37,33 +38,47 @@ all-tcllib: all-tcl @echo ===\> making $(@:%-signify=%) in ${DIRPRFX}${SIGNIFY_SUBDIR} @umask 0022; $(MAKE) -C ${SIGNIFY_SUBDIR} $(@:%-signify=%) +%-install: + @echo ===\> making $(@:%-install=%) in ${DIRPRFX}${INSTALL_SUBDIR} + @umask 0022; $(MAKE) -C ${INSTALL_SUBDIR} $(@:%-install=%) + DESTROOT_TARGETS= destroot-tcl destroot-tclx destroot-tcllib ifneq (@OS_PLATFORM@,linux) DESTROOT_TARGETS+= destroot-signify endif +ifeq (@HAVE_CLONEFILE@,yes) +DESTROOT_TARGETS+= destroot-install +endif destroot: $(DESTROOT_TARGETS) destroot-tcl: all-tcl @echo ===\> staging to destroot in ${DIRPRFX}@VENDOR_TCL_SUBDIR@ - @umask 0022; $(MAKE) -C @VENDOR_TCL_SUBDIR@ DESTDIR=${DESTROOT} @VENDOR_TCL_INSTALL@ + @umask 0022; $(MAKE) -C @VENDOR_TCL_SUBDIR@ "DESTDIR=${DESTROOT}" @VENDOR_TCL_INSTALL@ destroot-tclx: all-tclx @echo ===\> staging to destroot in ${DIRPRFX}@VENDOR_TCLX_SUBDIR@ - @umask 0022; $(MAKE) -C @VENDOR_TCLX_SUBDIR@ DESTDIR=${DESTROOT} @VENDOR_TCLX_INSTALL@ + @umask 0022; $(MAKE) -C @VENDOR_TCLX_SUBDIR@ "DESTDIR=${DESTROOT}" @VENDOR_TCLX_INSTALL@ destroot-tcllib: all-tcllib @echo ===\> staging to destroot in ${DIRPRFX}@VENDOR_TCLLIB_SUBDIR@ - @umask 0022; $(MAKE) -C @VENDOR_TCLLIB_SUBDIR@ DESTDIR=${DESTROOT} TCLSH_PROG=@INTREE_TCLSH@ @VENDOR_TCLLIB_INSTALL@ + @umask 0022; $(MAKE) -C @VENDOR_TCLLIB_SUBDIR@ "DESTDIR=${DESTROOT}" TCLSH_PROG=@INTREE_TCLSH@ @VENDOR_TCLLIB_INSTALL@ @chmod -R ugo+rX ${DESTROOT}${PREFIX}/libexec/macports/lib/tcllib* destroot-signify: @echo ===\> staging to destroot in ${DIRPRFX}${SIGNIFY_SUBDIR} - @umask 0022; $(MAKE) -C ${SIGNIFY_SUBDIR} install PREFIX=${DESTROOT}${PREFIX}/libexec/macports + @umask 0022; $(MAKE) -C ${SIGNIFY_SUBDIR} install "PREFIX=${DESTROOT}${PREFIX}/libexec/macports" + +destroot-install: + @echo ===\> staging to destroot in ${DIRPRFX}${INSTALL_SUBDIR} + @umask 0022; $(MAKE) -C ${INSTALL_SUBDIR} install "DESTDIR=${DESTROOT}" INSTALL_TARGETS= install-tcl install-tclx install-tcllib ifneq (@OS_PLATFORM@,linux) INSTALL_TARGETS+= install-signify endif +ifeq (@HAVE_CLONEFILE@,yes) +INSTALL_TARGETS+= install-install +endif install: $(INSTALL_TARGETS) @@ -106,12 +121,16 @@ install-signify: @echo ===\> making $(@:%-signify=%) in ${DIRPRFX}${SIGNIFY_SUBDIR} @umask 0022; $(MAKE) -C ${SIGNIFY_SUBDIR} install PREFIX=$(DESTDIR)${PREFIX}/libexec/macports +install-install: + @echo ===\> making $(@:%-install=%) in ${DIRPRFX}${INSTALL_SUBDIR} + @umask 0022; $(MAKE) -C ${INSTALL_SUBDIR} install + test: -clean: clean-tcl clean-tcllib clean-tclx clean-signify +clean: clean-tcl clean-tcllib clean-tclx clean-signify clean-install rm -rf ${DESTROOT} -distclean: distclean-tcl distclean-tcllib distclean-tclx distclean-signify +distclean: distclean-tcl distclean-tcllib distclean-tclx distclean-signify distclean-install rm -f Makefile rm -rf vendor-destroot rm -f tclsh diff --git a/vendor/install/Makefile.in b/vendor/install/Makefile.in new file mode 100644 index 000000000..6b0e32046 --- /dev/null +++ b/vendor/install/Makefile.in @@ -0,0 +1,38 @@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include ../../Mk/macports.autoconf.mk + +SRCS = xinstall.c +OBJS = xinstall.o +PROGRAM = build/install + +.PHONY: xinstall mkdirs + +all: xinstall + +xinstall: mkdirs ${PROGRAM} + +${PROGRAM}: ${OBJS} + ${CC} ${CFLAGS} -o ${PROGRAM} ${LIBS} ${OBJS} + +mkdirs: + mkdir -p build + +clean: + rm -Rf build ${PROGRAM} ${OBJS} + +distclean: clean + rm -f Makefile + +install: xinstall + ${INSTALL} -d -m 755 "${DESTDIR}${TCL_PREFIX}/clonebin" + ${INSTALL} -m 555 "${PROGRAM}" "${DESTDIR}${TCL_PREFIX}/clonebin" + +test: xinstall + if which -s kyua > /dev/null ; then \ + cd tests && env "PATH=../build:$$PATH" kyua test ; \ + fi + +codesign:: ${PROGRAM} + ../../codesign.sh $? diff --git a/vendor/install/tests/Kyuafile b/vendor/install/tests/Kyuafile new file mode 100644 index 000000000..040b0e8a6 --- /dev/null +++ b/vendor/install/tests/Kyuafile @@ -0,0 +1,3 @@ +syntax(2) +test_suite('install') +atf_test_program{name='install_test.sh'} diff --git a/vendor/install/tests/install_test.sh b/vendor/install/tests/install_test.sh new file mode 100755 index 000000000..a98e34911 --- /dev/null +++ b/vendor/install/tests/install_test.sh @@ -0,0 +1,448 @@ +#! /usr/bin/env atf-sh +# +# Copyright (c) 2016 Jilles Tjoelker +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +copy_to_nonexistent_with_opts() { + printf 'test\n123\r456\r\n789\0z' >testf + atf_check install "$@" testf copyf + cmp testf copyf || atf_fail "bad copy" + [ ! testf -nt copyf ] || atf_fail "bad timestamp" + [ ! -e copyf.bak ] || atf_fail "no backup expected" +} + +atf_test_case copy_to_nonexistent +copy_to_nonexistent_body() { + copy_to_nonexistent_with_opts +} + +atf_test_case copy_to_nonexistent_safe +copy_to_nonexistent_safe_body() { + copy_to_nonexistent_with_opts -S +} + +atf_test_case copy_to_nonexistent_comparing +copy_to_nonexistent_comparing_body() { + copy_to_nonexistent_with_opts -C +} + +atf_test_case copy_to_nonexistent_safe_comparing +copy_to_nonexistent_safe_comparing_body() { + copy_to_nonexistent_with_opts -S -C +} + +atf_test_case copy_to_nonexistent_backup +copy_to_nonexistent_backup_body() { + copy_to_nonexistent_with_opts -b -B.bak +} + +atf_test_case copy_to_nonexistent_backup_safe +copy_to_nonexistent_backup_safe_body() { + copy_to_nonexistent_with_opts -b -B.bak -S +} + +atf_test_case copy_to_nonexistent_preserving +copy_to_nonexistent_preserving_body() { + copy_to_nonexistent_with_opts -p + [ ! testf -ot copyf ] || atf_fail "bad timestamp 2" +} + +copy_self_with_opts() { + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0z' >testf2 + atf_check -s not-exit:0 -o empty -e match:. install "$@" testf testf + cmp testf testf2 || atf_fail "file changed after self-copy attempt" +} + +atf_test_case copy_self +copy_self_body() { + copy_self_with_opts +} + +atf_test_case copy_self_safe +copy_self_safe_body() { + copy_self_with_opts -S +} + +atf_test_case copy_self_comparing +copy_self_comparing_body() { + copy_self_with_opts -C +} + +atf_test_case copy_self_safe_comparing +copy_self_safe_comparing_body() { + copy_self_with_opts -S -C +} + +overwrite_with_opts() { + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0w' >otherf + atf_check install "$@" testf otherf + cmp testf otherf || atf_fail "bad overwrite" + [ ! testf -nt otherf ] || atf_fail "bad timestamp" +} + +atf_test_case overwrite +overwrite_body() { + overwrite_with_opts +} + +atf_test_case overwrite_safe +overwrite_safe_body() { + overwrite_with_opts -S +} + +atf_test_case overwrite_comparing +overwrite_comparing_body() { + overwrite_with_opts -C +} + +atf_test_case overwrite_safe_comparing +overwrite_safe_comparing_body() { + overwrite_with_opts -S -C +} + +overwrite_eq_with_opts() { + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0z' >otherf + atf_check install "$@" testf otherf + cmp testf otherf || atf_fail "bad overwrite" + [ ! testf -nt otherf ] || atf_fail "bad timestamp" +} + +atf_test_case overwrite_eq +overwrite_eq_body() { + overwrite_eq_with_opts +} + +atf_test_case overwrite_eq_safe +overwrite_eq_safe_body() { + overwrite_eq_with_opts -S +} + +atf_test_case overwrite_eq_comparing +overwrite_eq_comparing_body() { + overwrite_eq_with_opts -C +} + +atf_test_case overwrite_eq_safe_comparing +overwrite_eq_safe_comparing_body() { + overwrite_eq_with_opts -S -C +} + +overwrite_backup_with_opts() { + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0w' >otherf + printf 'test\n123\r456\r\n789\0w' >otherf2 + atf_check install -b -B.bak "$@" testf otherf + cmp testf otherf || atf_fail "bad overwrite" + [ ! testf -nt otherf ] || atf_fail "bad timestamp" + cmp otherf.bak otherf2 || atf_fail "bad backup" +} + +atf_test_case overwrite_backup +overwrite_backup_body() { + overwrite_backup_with_opts +} + +atf_test_case overwrite_backup_safe +overwrite_backup_safe_body() { + overwrite_backup_with_opts -S +} + +atf_test_case overwrite_backup_comparing +overwrite_backup_comparing_body() { + overwrite_backup_with_opts -C +} + +atf_test_case overwrite_backup_safe_comparing +overwrite_backup_safe_comparing_body() { + overwrite_backup_with_opts -S -C +} + +setup_stripbin() { + cat <<\STRIPBIN >stripbin +#!/bin/sh +[ "$1" = "-o" ] && dst="$2" && shift 2 +[ "$1" = "--" ] && shift +[ -z "$dst" ] && dst="$1" +STRIPBIN + [ "$1" = "true" ] && cmd="cat" || cmd="tr z @" + echo $cmd '<"$1" >"$1.new" && mv -- "$1.new" "$dst"' >>stripbin + chmod 755 stripbin + export STRIPBIN="$PWD/stripbin" +} + +strip_changing_with_opts() { + setup_stripbin + printf 'test\n123\r456\r\n789\0z' >testf + atf_check install -s "$@" testf copyf + [ ! testf -nt copyf ] || atf_fail "bad timestamp" + printf 'test\n123\r456\r\n789\0@' >otherf + cmp otherf copyf || atf_fail "bad stripped copy" +} + +# atf_test_case strip_changing +# strip_changing_body() { +# strip_changing_with_opts +# } +# +# atf_test_case strip_changing_comparing +# strip_changing_comparing_body() { +# strip_changing_with_opts -C +# } + +strip_changing_overwrite_with_opts() { + setup_stripbin + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0w' >copyf + atf_check install -s "$@" testf copyf + [ ! testf -nt copyf ] || atf_fail "bad timestamp" + printf 'test\n123\r456\r\n789\0@' >otherf + cmp otherf copyf || atf_fail "bad stripped copy" +} + +# atf_test_case strip_changing_overwrite +# strip_changing_overwrite_body() { +# strip_changing_overwrite_with_opts +# } +# +# atf_test_case strip_changing_overwrite_comparing +# strip_changing_overwrite_comparing_body() { +# strip_changing_overwrite_with_opts -C +# } + +strip_changing_overwrite_eq_with_opts() { + setup_stripbin + printf 'test\n123\r456\r\n789\0z' >testf + printf 'test\n123\r456\r\n789\0@' >copyf + atf_check install -s "$@" testf copyf + [ ! testf -nt copyf ] || atf_fail "bad timestamp" + printf 'test\n123\r456\r\n789\0@' >otherf + cmp otherf copyf || atf_fail "bad stripped copy" +} + +# atf_test_case strip_changing_overwrite_eq +# strip_changing_overwrite_eq_body() { +# strip_changing_overwrite_eq_with_opts +# } +# +# atf_test_case strip_changing_overwrite_eq_comparing +# strip_changing_overwrite_eq_comparing_body() { +# strip_changing_overwrite_eq_with_opts -C +# } + +# atf_test_case strip_noop +# strip_noop_body() { +# setup_stripbin true +# printf 'test\n123\r456\r\n789\0z' >testf +# atf_check install -s testf copyf +# [ ! testf -nt copyf ] || atf_fail "bad timestamp" +# printf 'test\n123\r456\r\n789\0z' >otherf +# cmp otherf copyf || atf_fail "bad stripped copy" +# } + +#atf_test_case hard_link +#hard_link_body() { +# printf 'test\n123\r456\r\n789\0z' >testf +# atf_check install -l h testf copyf +# [ testf -ef copyf ] || atf_fail "not same file" +# [ ! -L copyf ] || atf_fail "copy is symlink" +#} + +#atf_test_case symbolic_link +#symbolic_link_body() { +# printf 'test\n123\r456\r\n789\0z' >testf +# atf_check install -l s testf copyf +# [ testf -ef copyf ] || atf_fail "not same file" +# [ -L copyf ] || atf_fail "copy is not symlink" +#} + +#atf_test_case symbolic_link_absolute +#symbolic_link_absolute_body() { +# printf 'test\n123\r456\r\n789\0z' >testf +# atf_check install -l sa testf copyf +# [ testf -ef copyf ] || atf_fail "not same file" +# [ -L copyf ] || atf_fail "copy is not symlink" +# copyf_path=$(readlink copyf) +# testf_path="$(pwd -P)/testf" +# if [ "$copyf_path" != "$testf_path" ]; then +# atf_fail "unexpected symlink contents ('$copyf_path' != '$testf_path')" +# fi +#} + +#atf_test_case symbolic_link_relative +#symbolic_link_relative_body() { +# printf 'test\n123\r456\r\n789\0z' >testf +# atf_check install -l sr testf copyf +# [ testf -ef copyf ] || atf_fail "not same file" +# [ -L copyf ] || atf_fail "copy is not symlink" +# copyf_path=$(readlink copyf) +# testf_path="testf" +# if [ "$copyf_path" != "$testf_path" ]; then +# atf_fail "unexpected symlink contents ('$copyf_path' != '$testf_path')" +# fi +#} + +# atf_test_case symbolic_link_relative_absolute_source_and_dest1 +# symbolic_link_relative_absolute_source_and_dest1_head() { +# atf_set "descr" "Verify -l rs with absolute paths (.../copyf -> .../a/b/c/testf)" +# } +# symbolic_link_relative_absolute_source_and_dest1_body() { +# src_path=a/b/c/testf +# src_path_prefixed=$PWD/$src_path +# dest_path=$PWD/copyf +# +# atf_check mkdir -p a/b/c +# atf_check touch $src_path +# atf_check install -l sr $src_path_prefixed $dest_path +# [ $src_path_prefixed -ef $dest_path ] || atf_fail "not same file" +# [ -L $dest_path ] || atf_fail "copy is not symlink" +# dest_path_relative=$(readlink $dest_path) +# src_path_relative="$src_path" +# if [ "$src_path_relative" != "$dest_path_relative" ]; then +# atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" +# fi +# } + +# atf_test_case symbolic_link_relative_absolute_source_and_dest1_double_slash +# symbolic_link_relative_absolute_source_and_dest1_double_slash_head() { +# atf_set "descr" "Verify -l rs with absolute paths (.../copyf -> .../a/b/c/testf), using double-slashes" +# } +# symbolic_link_relative_absolute_source_and_dest1_double_slash_body() { +# src_path=a//b//c//testf +# src_path_prefixed=$PWD/$src_path +# dest_path=$PWD/copyf +# +# atf_check mkdir -p a/b/c +# atf_check touch $src_path +# atf_check install -l sr $src_path_prefixed $dest_path +# [ $src_path_prefixed -ef $dest_path ] || atf_fail "not same file" +# [ -L $dest_path ] || atf_fail "copy is not symlink" +# dest_path_relative=$(readlink $dest_path) +# src_path_relative="$(echo $src_path | sed -e 's,//,/,g')" +# if [ "$src_path_relative" != "$dest_path_relative" ]; then +# atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" +# fi +# } + +# atf_test_case symbolic_link_relative_absolute_source_and_dest2 +# symbolic_link_relative_absolute_source_and_dest2_head() { +# atf_set "descr" "Verify -l rs with absolute paths (.../a/b/c/copyf -> .../testf)" +# } +# symbolic_link_relative_absolute_source_and_dest2_body() { +# src_path=testf +# src_path_prefixed=$PWD/$src_path +# dest_path=$PWD/a/b/c/copyf +# +# atf_check mkdir -p a/b/c +# atf_check touch $src_path +# atf_check install -l sr $src_path_prefixed $dest_path +# [ $src_path_prefixed -ef $dest_path ] || atf_fail "not same file" +# [ -L $dest_path ] || atf_fail "copy is not symlink" +# dest_path_relative=$(readlink $dest_path) +# src_path_relative="../../../$src_path" +# if [ "$src_path_relative" != "$dest_path_relative" ]; then +# atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" +# fi +# } + +atf_test_case mkdir_simple +mkdir_simple_body() { + atf_check install -d dir1/dir2 + [ -d dir1 ] || atf_fail "dir1 missing" + [ -d dir1/dir2 ] || atf_fail "dir2 missing" + atf_check install -d dir1/dir2/dir3 + [ -d dir1/dir2/dir3 ] || atf_fail "dir3 missing" + atf_check install -d dir1 + atf_check install -d dir1/dir2/dir3 +} + +# atf_test_case symbolic_link_relative_absolute_common +# symbolic_link_relative_absolute_common_head() { +# atf_set "descr" "Verify -l rs with absolute paths having common components" +# } +# symbolic_link_relative_absolute_common_body() { +# filename=foo.so +# src_path=lib +# src_path_prefixed=$PWD/$src_path +# dest_path=$PWD/libexec/ +# src_file=$src_path_prefixed/$filename +# dest_file=$dest_path/$filename +# +# atf_check mkdir $src_path_prefixed $dest_path +# atf_check touch $src_file +# atf_check install -l sr $src_file $dest_path +# +# dest_path_relative=$(readlink $dest_file) +# src_path_relative="../lib/$filename" +# if [ "$src_path_relative" != "$dest_path_relative" ]; then +# atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" +# fi +# } + +atf_init_test_cases() { + atf_add_test_case copy_to_nonexistent + atf_add_test_case copy_to_nonexistent_safe + atf_add_test_case copy_to_nonexistent_comparing + atf_add_test_case copy_to_nonexistent_safe_comparing + atf_add_test_case copy_to_nonexistent_backup + atf_add_test_case copy_to_nonexistent_backup_safe + atf_add_test_case copy_to_nonexistent_preserving + atf_add_test_case copy_self + atf_add_test_case copy_self_safe + atf_add_test_case copy_self_comparing + atf_add_test_case copy_self_safe_comparing + atf_add_test_case overwrite + atf_add_test_case overwrite_safe + atf_add_test_case overwrite_comparing + atf_add_test_case overwrite_safe_comparing + atf_add_test_case overwrite_eq + atf_add_test_case overwrite_eq_safe + atf_add_test_case overwrite_eq_comparing + atf_add_test_case overwrite_eq_safe_comparing + atf_add_test_case overwrite_backup + atf_add_test_case overwrite_backup_safe + atf_add_test_case overwrite_backup_comparing + atf_add_test_case overwrite_backup_safe_comparing + #atf_add_test_case strip_changing + #atf_add_test_case strip_changing_comparing + #atf_add_test_case strip_changing_overwrite + #atf_add_test_case strip_changing_overwrite_comparing + #atf_add_test_case strip_changing_overwrite_eq + #atf_add_test_case strip_changing_overwrite_eq_comparing + #atf_add_test_case strip_noop + #atf_add_test_case hard_link + #atf_add_test_case symbolic_link + #atf_add_test_case symbolic_link_absolute + #atf_add_test_case symbolic_link_relative + #atf_add_test_case symbolic_link_relative_absolute_source_and_dest1 + #atf_add_test_case symbolic_link_relative_absolute_source_and_dest1_double_slash + #atf_add_test_case symbolic_link_relative_absolute_source_and_dest2 + #atf_add_test_case symbolic_link_relative_absolute_common + atf_add_test_case mkdir_simple +} diff --git a/vendor/install/xinstall.c b/vendor/install/xinstall.c new file mode 100644 index 000000000..6f543f17f --- /dev/null +++ b/vendor/install/xinstall.c @@ -0,0 +1,878 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.43 2001/05/30 07:08:49 ru Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif /* __APPLE__ */ + +/* Bootstrap aid - this doesn't exist in most older releases */ +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) /* from */ +#endif + +#define DIRECTORY 0x01 /* Tell install it's a directory. */ +#define SETFLAGS 0x02 /* Tell install to set flags. */ +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) +#define BACKUP_SUFFIX ".old" + +struct passwd *pp; +struct group *gp; +gid_t gid; +uid_t uid; +int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose; +mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; +char *suffix = BACKUP_SUFFIX; + +static int clone __P((const char *, const char *, int, char *, size_t)); +void copy __P((int, char *, int, char *, off_t)); +int compare __P((int, const char *, size_t, int, const char *, size_t)); +int create_newfile __P((char *, int, struct stat *)); +int create_tempfile __P((char *, char *, size_t)); +void install __P((char *, char *, u_long, u_int)); +void install_dir __P((char *)); +u_long numeric_id __P((char *, char *)); +void strip __P((char *)); +static void tempfile_template __P((const char *, char *, size_t)); +int trymmap __P((int)); +void usage __P((void)); + +int +main(int argc, + char *argv[]) +{ + struct stat from_sb, to_sb; + mode_t *set; + u_long fset = 0; + int ch, no_target; + u_int iflags; + char *flags, *group, *owner, *to_name; + + iflags = 0; + group = owner = NULL; + while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1) + switch((char)ch) { + case 'B': + suffix = optarg; + /* FALLTHROUGH */ + case 'b': + dobackup = 1; + break; + case 'C': + docompare = 1; + break; + case 'c': + /* For backwards compatibility. */ + break; + case 'd': + dodir = 1; + break; + case 'f': + flags = optarg; + if (strtofflags(&flags, &fset, NULL)) + errx(EX_USAGE, "%s: invalid flag", flags); + iflags |= SETFLAGS; + break; + case 'g': + group = optarg; + break; + case 'M': + nommap = 1; + break; + case 'm': + if (!(set = setmode(optarg))) + errx(EX_USAGE, "invalid file mode: %s", + optarg); + mode = getmode(set, 0); + free(set); + break; + case 'o': + owner = optarg; + break; + case 'p': + docompare = dopreserve = 1; + break; + case 'S': + safecopy = 1; + break; + case 's': + dostrip = 1; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* some options make no sense when creating directories */ + if ((safecopy || dostrip) && dodir) + usage(); + + /* + * Older versions allowed -d -C combo. Issue a warning + * for now, but turn this into an error before 4.5-RELEASE. + */ + if (docompare && dodir) + warnx("the -d and -C options may not be specified together"); + + /* must have at least two arguments, except when creating directories */ + if (argc < 2 && !dodir) + usage(); + + /* need to make a temp copy so we can compare stripped version */ + if (docompare && dostrip) + safecopy = 1; + + /* get group and owner id's */ + if (group != NULL) { + if ((gp = getgrnam(group)) != NULL) + gid = gp->gr_gid; + else + gid = (uid_t)numeric_id(group, "group"); + } else + gid = (gid_t)-1; + + if (owner != NULL) { + if ((pp = getpwnam(owner)) != NULL) + uid = pp->pw_uid; + else + uid = (uid_t)numeric_id(owner, "user"); + } else + uid = (uid_t)-1; + + if (dodir) { + for (; *argv != NULL; ++argv) + install_dir(*argv); + exit(EX_OK); + /* NOTREACHED */ + } + + no_target = stat(to_name = argv[argc - 1], &to_sb); + if (!no_target && S_ISDIR(to_sb.st_mode)) { + for (; *argv != to_name; ++argv) + install(*argv, to_name, fset, iflags | DIRECTORY); + exit(EX_OK); + /* NOTREACHED */ + } + + /* can't do file1 file2 directory/file */ + if (argc != 2) + usage(); + + if (!no_target) { + if (stat(*argv, &from_sb)) + err(EX_OSERR, "%s", *argv); + if (!S_ISREG(to_sb.st_mode)) { + errno = EFTYPE; + err(EX_OSERR, "%s", to_name); + } + if (to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(EX_USAGE, + "%s and %s are the same file", *argv, to_name); + } + install(*argv, to_name, fset, iflags); + exit(EX_OK); + /* NOTREACHED */ +} + +u_long +numeric_id(char *name, char *type) +{ + u_long val; + char *ep; + + /* + * XXX + * We know that uid_t's and gid_t's are unsigned longs. + */ + errno = 0; + val = strtoul(name, &ep, 10); + if (errno) + err(EX_NOUSER, "%s", name); + if (*ep != '\0') + errx(EX_NOUSER, "unknown %s %s", type, name); + return (val); +} + +/* + * install -- + * build a path name and install the file + */ +void +install(char *from_name, char *to_name, + u_long fset, + u_int flags) +{ + struct stat from_sb, temp_sb, to_sb; + struct utimbuf utb; + int devnull, files_match, from_fd=-1, serrno, target; + int tempcopy, temp_fd=-1, to_fd=-1; + char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; + const int tryclone = 1; + + files_match = 0; + + /* If try to install NULL file to a directory, fails. */ + if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { + if (stat(from_name, &from_sb)) + err(EX_OSERR, "%s", from_name); + if (!S_ISREG(from_sb.st_mode)) { + errno = EFTYPE; + err(EX_OSERR, "%s", from_name); + } + /* Build the target path. */ + if (flags & DIRECTORY) { + (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + to_name, + (p = strrchr(from_name, '/')) ? ++p : from_name); + to_name = pathbuf; + } + devnull = 0; + } else { + devnull = 1; + } + + target = stat(to_name, &to_sb) == 0; + + /* Only install to regular files. */ + if (target && !S_ISREG(to_sb.st_mode)) { + errno = EFTYPE; + warn("%s", to_name); + return; + } + + /* Only copy safe if the target exists. */ + tempcopy = safecopy && target; + + if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", from_name); + + /* If we don't strip, we can compare first. */ + if (docompare && !dostrip && target) { + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + if (devnull) + files_match = to_sb.st_size == 0; + else + files_match = !(compare(from_fd, from_name, + (size_t)from_sb.st_size, to_fd, + to_name, (size_t)to_sb.st_size)); + + /* Close "to" file unless we match. */ + if (!files_match) + (void)close(to_fd); + } + + if (!files_match) { + int done_clone = 0; + if (tryclone && !devnull && !dostrip) { + done_clone = (clone(from_name, to_name, tempcopy, tempfile, sizeof(tempfile)) == 0); + } + if (done_clone && + (((to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0)) < 0) + || (!tempcopy && fstat(to_fd, &to_sb) == -1))) { + (void)unlink(tempcopy ? tempfile : to_name); + done_clone = 0; + } + if (!done_clone) { + if (tempcopy) { + to_fd = create_tempfile(to_name, tempfile, + sizeof(tempfile)); + if (to_fd < 0) + err(EX_OSERR, "%s", tempfile); + } else { + if ((to_fd = create_newfile(to_name, target, + &to_sb)) < 0) + err(EX_OSERR, "%s", to_name); + if (verbose) + (void)printf("install: %s -> %s\n", + from_name, to_name); + } + if (!devnull) + copy(from_fd, from_name, to_fd, + tempcopy ? tempfile : to_name, from_sb.st_size); + } /* !done_clone */ + } + + if (dostrip) { + strip(tempcopy ? tempfile : to_name); + + /* + * Re-open our fd on the target, in case we used a strip + * that does not work in-place -- like GNU binutils strip. + */ + close(to_fd); + to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); + if (to_fd < 0) + err(EX_OSERR, "stripping %s", to_name); + } + + /* + * Compare the stripped temp file with the target. + */ + if (docompare && dostrip && target) { + temp_fd = to_fd; + + /* Re-open to_fd using the real target name. */ + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + + if (fstat(temp_fd, &temp_sb)) { + serrno = errno; + (void)unlink(tempfile); + errno = serrno; + err(EX_OSERR, "%s", tempfile); + } + + if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, + to_name, (size_t)to_sb.st_size) == 0) { + /* + * If target has more than one link we need to + * replace it in order to snap the extra links. + * Need to preserve target file times, though. + */ + if (to_sb.st_nlink != 1) { + utb.actime = to_sb.st_atime; + utb.modtime = to_sb.st_mtime; + (void)utime(tempfile, &utb); + } else { + files_match = 1; + (void)unlink(tempfile); + } + (void) close(temp_fd); + } + } + + /* + * Move the new file into place if doing a safe copy + * and the files are different (or just not compared). + */ + if (tempcopy && !files_match) { + /* Try to turn off the immutable bits. */ + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); + if (dobackup) { + if (snprintf(backup, MAXPATHLEN, "%s%s", to_name, + suffix) != strlen(to_name) + strlen(suffix)) { + unlink(tempfile); + errx(EX_OSERR, "%s: backup filename too long", + to_name); + } + if (verbose) + (void)printf("install: %s -> %s\n", to_name, backup); + if (rename(to_name, backup) < 0) { + serrno = errno; + unlink(tempfile); + errno = serrno; + err(EX_OSERR, "rename: %s to %s", to_name, + backup); + } + } + if (verbose) + (void)printf("install: %s -> %s\n", from_name, to_name); + if (rename(tempfile, to_name) < 0) { + serrno = errno; + unlink(tempfile); + errno = serrno; + err(EX_OSERR, "rename: %s to %s", + tempfile, to_name); + } + + /* Re-open to_fd so we aren't hosed by the rename(2). */ + (void) close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + } + +#ifdef __APPLE__ + /* in case mtime is modified */ + if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) && + fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0) { + warn("%s: unable to copy extended attributes from %s", to_name, from_name); + } +#endif /* __APPLE__ */ + /* + * Preserve the timestamp of the source file if necessary. + */ + if (dopreserve && !files_match && !devnull) { + utb.actime = from_sb.st_atime; + utb.modtime = from_sb.st_mtime; + (void)utime(to_name, &utb); + } + + if (fstat(to_fd, &to_sb) == -1) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s", to_name); + } + + /* + * Set owner, group, mode for target; do the chown first, + * chown may lose the setuid bits. + */ + if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || + (uid != (uid_t)-1 && uid != to_sb.st_uid) || + (mode != to_sb.st_mode)) { + /* Try to turn off the immutable bits. */ + if (to_sb.st_flags & NOCHANGEBITS) + (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); + } + + if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || + (uid != (uid_t)-1 && uid != to_sb.st_uid)) + if (fchown(to_fd, uid, gid) == -1) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR,"%s: chown/chgrp", to_name); + } + + if (mode != to_sb.st_mode) + if (fchmod(to_fd, mode)) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s: chmod", to_name); + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + * NFS does not support flags. Ignore ENOTSUP flags if we're just + * trying to turn off UF_NODUMP. If we're trying to set real flags, + * then warn if the the fs doesn't support it, otherwise fail. + */ + if (!devnull && fchflags(to_fd, + flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { + if (flags & SETFLAGS) { + if (errno == ENOTSUP) + warn("%s: chflags", to_name); + else { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s: chflags", to_name); + } + } + } +#ifdef __APPLE__ + /* the ACL could prevent credential/permission system calls later on... */ + if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) && + (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0)) { + warn("%s: unable to copy ACL from %s", to_name, from_name); + } +#endif /* __APPLE__ */ + + (void)close(to_fd); + if (!devnull) + (void)close(from_fd); +} + +/* + * compare -- + * compare two files; non-zero means files differ + */ +int +compare(int from_fd, const char *from_name, size_t from_len, + int to_fd, const char *to_name, size_t to_len) +{ + char *p, *q; + int rv; + int done_compare; + + rv = 0; + if (from_len != to_len) + return 1; + + if (from_len <= 8 * 1024 * 1024) { + done_compare = 0; + if (trymmap(from_fd) && trymmap(to_fd)) { + p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0); + if (p == (char *)MAP_FAILED) + goto out; + q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0); + if (q == (char *)MAP_FAILED) { + munmap(p, from_len); + goto out; + } + + rv = memcmp(p, q, from_len); + munmap(p, from_len); + munmap(q, from_len); + done_compare = 1; + } + out: + if (!done_compare) { + char buf1[MAXBSIZE]; + char buf2[MAXBSIZE]; + int n1, n2; + + rv = 0; + lseek(from_fd, 0, SEEK_SET); + lseek(to_fd, 0, SEEK_SET); + while (rv == 0) { + n1 = read(from_fd, buf1, sizeof(buf1)); + if (n1 == 0) + break; /* EOF */ + else if (n1 > 0) { + n2 = read(to_fd, buf2, n1); + if (n2 == n1) + rv = memcmp(buf1, buf2, n1); + else + rv = 1; /* out of sync */ + } else + rv = 1; /* read failure */ + } + lseek(from_fd, 0, SEEK_SET); + lseek(to_fd, 0, SEEK_SET); + } + } else + rv = 1; /* don't bother in this case */ + + return rv; +} + +/* + * tempfile_template -- + * prepare a template filename for use with mktemp/mkstemp. + */ +static void +tempfile_template(const char *path, + char *temp, + size_t tsize) +{ + char *p; + + (void)strncpy(temp, path, tsize); + temp[tsize - 1] = '\0'; + if ((p = strrchr(temp, '/')) != NULL) + p++; + else + p = temp; + (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); + temp[tsize - 1] = '\0'; +} + +/* + * create_tempfile -- + * create a temporary file based on path and open it + */ +int +create_tempfile(char *path, + char *temp, + size_t tsize) +{ + tempfile_template(path, temp, tsize); + return (mkstemp(temp)); +} + +/* + * create_newfile -- + * create a new file, overwriting an existing one if necessary + */ +int +create_newfile(char *path, + int target, + struct stat *sbp) +{ + char backup[MAXPATHLEN]; + + if (target) { + /* + * Unlink now... avoid ETXTBSY errors later. Try to turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ + if (sbp->st_flags & NOCHANGEBITS) + (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); + + if (dobackup) { + if (snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix) != strlen(path) + strlen(suffix)) + errx(EX_OSERR, "%s: backup filename too long", + path); + (void)snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix); + if (verbose) + (void)printf("install: %s -> %s\n", + path, backup); + if (rename(path, backup) < 0) + err(EX_OSERR, "rename: %s to %s", path, backup); + } else + (void)unlink(path); + } + + return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)); +} + +/* Memory strategy threshold, in pages: if physmem is larger then this, use a + * large buffer */ +#define PHYSPAGES_THRESHOLD (32*1024) + +/* Maximum buffer size in bytes - do not allow it to grow larger than this */ +#define BUFSIZE_MAX (2*1024*1024) + +/* Small (default) buffer size in bytes. It's inefficient for this to be + * smaller than MAXPHYS */ +#define BUFSIZE_SMALL (MAXPHYS) + +/* + * copy -- + * copy from one file to another + */ +void +copy(int from_fd, char *from_name, + int to_fd, char *to_name, + off_t size) +{ + register int nr, nw; + int serrno; + char *p; + int done_copy; + + static char *buf = NULL; + static size_t bufsize = 0; + + /* Rewind file descriptors. */ + if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) + err(EX_OSERR, "lseek: %s", from_name); + if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) + err(EX_OSERR, "lseek: %s", to_name); + + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ + done_copy = 0; + if (size <= 8 * 1048576 && trymmap(from_fd) && + (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, + from_fd, (off_t)0)) != (char *)MAP_FAILED) { + if ((nw = write(to_fd, p, size)) != size) { + serrno = errno; + (void)unlink(to_name); + errno = nw > 0 ? EIO : serrno; + err(EX_OSERR, "%s", to_name); + } + done_copy = 1; + } + if (!done_copy) { + if (buf == NULL) { + /* + * Note that buf and bufsize are static. If + * malloc() fails, it will fail at the start + * and not copy only some files. + */ + if (sysconf(_SC_PHYS_PAGES) > + PHYSPAGES_THRESHOLD) + bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); + else + bufsize = BUFSIZE_SMALL; + buf = malloc(bufsize); + if (buf == NULL) + err(1, "Not enough memory"); + } + while ((nr = read(from_fd, buf, bufsize)) > 0) + if ((nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errno = nw > 0 ? EIO : serrno; + err(EX_OSERR, "%s", to_name); + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s", from_name); + } + } +} + +/* + * clone -- + * create a clone of a file + */ +static int +clone(const char *from_name, const char *to_name, + int use_temp, char *temp_name, size_t tsize) +{ + if (use_temp) { + tempfile_template(to_name, temp_name, tsize); + mktemp(temp_name); + to_name = temp_name; + } + return clonefile(from_name, to_name, CLONE_NOFOLLOW|CLONE_NOOWNERCOPY); +} + +/* + * strip -- + * use strip(1) to strip the target file + */ +void +strip(char *to_name) +{ + pid_t pid; + int error; + extern char** environ; + char *const argv[] = { "xcrun", "strip", "-", to_name, NULL }; + + if (0 == (error = posix_spawnp(&pid, "xcrun", NULL, NULL, argv, environ))) { + int status = 0; + pid_t child = waitpid(pid, &status, 0); + if ((child == -1) || status) { + unlink(to_name); + errx(EX_SOFTWARE, "child process failed: xcrun strip - %s", to_name); + } + } else { + errno = error; + err(EX_OSERR, "xcrun strip - %s", to_name); + } +} + +/* + * install_dir -- + * build directory heirarchy + */ +void +install_dir(char *path) +{ + register char *p; + struct stat sb; + int ch; + + for (p = path;; ++p) + if (!*p || (p != path && *p == '/')) { + ch = *p; + *p = '\0'; + if (stat(path, &sb)) { + if (errno != ENOENT || mkdir(path, 0755) < 0) { + err(EX_OSERR, "mkdir %s", path); + /* NOTREACHED */ + } else if (verbose) + (void)printf("install: mkdir %s\n", + path); + } else if (!S_ISDIR(sb.st_mode)) + errx(EX_OSERR, "%s exists but is not a directory", path); + if (!(*p = ch)) + break; + } + + if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) + warn("chown %u:%u %s", uid, gid, path); + if (chmod(path, mode)) + warn("chmod %o %s", mode, path); +} + +/* + * usage -- + * print a usage message and die + */ +void +usage(void) +{ + (void)fprintf(stderr, "\ +usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\ + [-o owner] file1 file2\n\ + install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\ + [-o owner] file1 ... fileN directory\n\ + install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"); + exit(EX_USAGE); + /* NOTREACHED */ +} + +/* + * trymmap -- + * return true (1) if mmap should be tried, false (0) if not. + */ +int +trymmap(int fd) +{ +/* + * The ifdef is for bootstrapping - f_fstypename doesn't exist in + * pre-Lite2-merge systems. + */ +#ifdef MFSNAMELEN + struct statfs stfs; + + if (nommap || fstatfs(fd, &stfs) != 0) + return (0); + if (strcmp(stfs.f_fstypename, "ufs") == 0 || + strcmp(stfs.f_fstypename, "cd9660") == 0) + return (1); +#endif + return (0); +} diff --git a/vendor/patches/install/xinstall.c.patch b/vendor/patches/install/xinstall.c.patch new file mode 100644 index 000000000..1e940d074 --- /dev/null +++ b/vendor/patches/install/xinstall.c.patch @@ -0,0 +1,129 @@ +Applied to original from file_cmds-430.140.2 + +--- xinstall.c.orig 2024-08-20 16:07:42 ++++ xinstall.c 2024-08-27 16:58:56 +@@ -70,9 +70,9 @@ + #ifdef __APPLE__ + #include + #include ++#include ++#include + #endif /* __APPLE__ */ +- +-#include "pathnames.h" + + /* Bootstrap aid - this doesn't exist in most older releases */ + #ifndef MAP_FAILED +@@ -92,6 +92,7 @@ + mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + char *suffix = BACKUP_SUFFIX; + ++static int clone __P((const char *, const char *, int, char *, size_t)); + void copy __P((int, char *, int, char *, off_t)); + int compare __P((int, const char *, size_t, int, const char *, size_t)); + int create_newfile __P((char *, int, struct stat *)); +@@ -100,6 +101,7 @@ + void install_dir __P((char *)); + u_long numeric_id __P((char *, char *)); + void strip __P((char *)); ++static void tempfile_template __P((const char *, char *, size_t)); + int trymmap __P((int)); + void usage __P((void)); + +@@ -276,9 +278,10 @@ + { + struct stat from_sb, temp_sb, to_sb; + struct utimbuf utb; +- int devnull, files_match, from_fd=0, serrno, target; +- int tempcopy, temp_fd, to_fd=0; ++ int devnull, files_match, from_fd=-1, serrno, target; ++ int tempcopy, temp_fd=-1, to_fd=-1; + char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; ++ const int tryclone = 1; + + files_match = 0; + +@@ -334,6 +337,17 @@ + } + + if (!files_match) { ++ int done_clone = 0; ++ if (tryclone && !devnull && !dostrip) { ++ done_clone = (clone(from_name, to_name, tempcopy, tempfile, sizeof(tempfile)) == 0); ++ } ++ if (done_clone && ++ (((to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0)) < 0) ++ || (!tempcopy && fstat(to_fd, &to_sb) == -1))) { ++ (void)unlink(tempcopy ? tempfile : to_name); ++ done_clone = 0; ++ } ++ if (!done_clone) { + if (tempcopy) { + to_fd = create_tempfile(to_name, tempfile, + sizeof(tempfile)); +@@ -350,6 +364,7 @@ + if (!devnull) + copy(from_fd, from_name, to_fd, + tempcopy ? tempfile : to_name, from_sb.st_size); ++ } /* !done_clone */ + } + + if (dostrip) { +@@ -592,11 +607,11 @@ + } + + /* +- * create_tempfile -- +- * create a temporary file based on path and open it ++ * tempfile_template -- ++ * prepare a template filename for use with mktemp/mkstemp. + */ +-int +-create_tempfile(char *path, ++static void ++tempfile_template(const char *path, + char *temp, + size_t tsize) + { +@@ -610,6 +625,18 @@ + p = temp; + (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); + temp[tsize - 1] = '\0'; ++} ++ ++/* ++ * create_tempfile -- ++ * create a temporary file based on path and open it ++ */ ++int ++create_tempfile(char *path, ++ char *temp, ++ size_t tsize) ++{ ++ tempfile_template(path, temp, tsize); + return (mkstemp(temp)); + } + +@@ -736,6 +763,22 @@ + } + + /* ++ * clone -- ++ * create a clone of a file ++ */ ++static int ++clone(const char *from_name, const char *to_name, ++ int use_temp, char *temp_name, size_t tsize) ++{ ++ if (use_temp) { ++ tempfile_template(to_name, temp_name, tsize); ++ mktemp(temp_name); ++ to_name = temp_name; ++ } ++ return clonefile(from_name, to_name, CLONE_NOFOLLOW|CLONE_NOOWNERCOPY); ++} ++ ++/* + * strip -- + * use strip(1) to strip the target file + */ diff --git a/vendor/patches/tcl/clonefile.patch b/vendor/patches/tcl/clonefile.patch new file mode 100644 index 000000000..769a0bfff --- /dev/null +++ b/vendor/patches/tcl/clonefile.patch @@ -0,0 +1,112 @@ +diff --git a/vendor/tcl8.6.14/unix/configure b/vendor/tcl8.6.14/unix/configure +index 87dc84d1e..a161fed43 100755 +--- a/vendor/tcl8.6.14/unix/configure ++++ b/vendor/tcl8.6.14/unix/configure +@@ -17439,7 +17439,7 @@ fi + done + + +-for ac_header in copyfile.h ++for ac_header in sys/clonefile.h copyfile.h + do + as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` + if eval "test \"\${$as_ac_Header+set}\" = set"; then +@@ -17589,7 +17589,7 @@ fi + done + + +-for ac_func in copyfile ++for ac_func in clonefile copyfile + do + as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` + echo "$as_me:$LINENO: checking for $ac_func" >&5 +diff --git a/vendor/tcl8.6.14/unix/tclUnixFCmd.c b/vendor/tcl8.6.14/unix/tclUnixFCmd.c +index 26429df73..82da6d433 100644 +--- a/vendor/tcl8.6.14/unix/tclUnixFCmd.c ++++ b/vendor/tcl8.6.14/unix/tclUnixFCmd.c +@@ -51,6 +51,18 @@ + #ifdef HAVE_FTS + #include + #endif ++#ifdef HAVE_SYS_CLONEFILE_H ++#include ++#endif ++#ifdef HAVE_CLONEFILE ++#if defined(__APPLE__) && \ ++ defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ ++ MAC_OS_X_VERSION_MIN_REQUIRED < 101200 ++#define MayUseCloneFile() (clonefile != NULL) ++#else ++#define MayUseCloneFile() (1) ++#endif ++#endif /* HAVE_CLONEFILE */ + + /* + * The following constants specify the type of callback when +@@ -526,14 +538,50 @@ DoCopyFile( + } + return TCL_OK; + } +- ++ ++/* ++ *---------------------------------------------------------------------- ++ * ++ * TclUnixCloneFile - ++ * ++ * Helper function for TclUnixCopyFile. Clones one regular file, using ++ * clonefile(). ++ * ++ * Results: ++ * A standard Tcl result. ++ * ++ * Side effects: ++ * A file is cloned. ++ * ++ *---------------------------------------------------------------------- ++ */ ++#ifdef HAVE_CLONEFILE ++int ++TclUnixCloneFile( ++ const char *src, /* Pathname of file to clone (native). */ ++ const char *dst, /* Pathname of file to create (native). */ ++ const Tcl_StatBuf *statBufPtr, ++ /* Used to copy attributes. */ ++ int dontCopyAtts) /* If flag set, don't copy attributes. */ ++{ ++ if (clonefile(src, dst, CLONE_NOFOLLOW|CLONE_NOOWNERCOPY) == 0) { ++ if (dontCopyAtts || CopyFileAtts(src, dst, statBufPtr) == TCL_OK) { ++ return TCL_OK; ++ } ++ } ++ unlink(dst); /* INTL: Native. */ ++ return TCL_ERROR; ++} ++#endif ++ + /* + *---------------------------------------------------------------------- + * + * TclUnixCopyFile - + * + * Helper function for TclpCopyFile. Copies one regular file, using +- * read() and write(). ++ * read() and write(), or clones it with TclUnixCloneFile() if ++ * supported. + * + * Results: + * A standard Tcl result. +@@ -558,6 +606,13 @@ TclUnixCopyFile( + char *buffer; /* Data buffer for copy */ + size_t nread; + ++#ifdef HAVE_CLONEFILE ++ if (MayUseCloneFile() && ++ TclUnixCloneFile(src, dst, statBufPtr, dontCopyAtts) == TCL_OK) { ++ return TCL_OK; ++ } ++#endif ++ + #ifdef DJGPP + #define BINMODE |O_BINARY + #else diff --git a/vendor/tcl8.6.14/unix/configure b/vendor/tcl8.6.14/unix/configure index 87dc84d1e..a161fed43 100755 --- a/vendor/tcl8.6.14/unix/configure +++ b/vendor/tcl8.6.14/unix/configure @@ -17439,7 +17439,7 @@ fi done -for ac_header in copyfile.h +for ac_header in sys/clonefile.h copyfile.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -17589,7 +17589,7 @@ fi done -for ac_func in copyfile +for ac_func in clonefile copyfile do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 diff --git a/vendor/tcl8.6.14/unix/tclUnixFCmd.c b/vendor/tcl8.6.14/unix/tclUnixFCmd.c index 26429df73..470c56337 100644 --- a/vendor/tcl8.6.14/unix/tclUnixFCmd.c +++ b/vendor/tcl8.6.14/unix/tclUnixFCmd.c @@ -51,6 +51,18 @@ #ifdef HAVE_FTS #include #endif +#ifdef HAVE_SYS_CLONEFILE_H +#include +#endif +#ifdef HAVE_CLONEFILE +#if defined(__APPLE__) && \ + defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ + MAC_OS_X_VERSION_MIN_REQUIRED < 101200 +#define MayUseCloneFile() (clonefile != NULL) +#else +#define MayUseCloneFile() (1) +#endif +#endif /* HAVE_CLONEFILE */ /* * The following constants specify the type of callback when @@ -526,14 +538,50 @@ DoCopyFile( } return TCL_OK; } - + +/* + *---------------------------------------------------------------------- + * + * TclUnixCloneFile - + * + * Helper function for TclUnixCopyFile. Clones one regular file, using + * clonefile(). + * + * Results: + * A standard Tcl result. + * + * Side effects: + * A file is cloned. + * + *---------------------------------------------------------------------- + */ +#ifdef HAVE_CLONEFILE +int +TclUnixCloneFile( + const char *src, /* Pathname of file to clone (native). */ + const char *dst, /* Pathname of file to create (native). */ + const Tcl_StatBuf *statBufPtr, + /* Used to copy attributes. */ + int dontCopyAtts) /* If flag set, don't copy attributes. */ +{ + if (clonefile(src, dst, CLONE_NOFOLLOW|CLONE_NOOWNERCOPY) == 0) { + if (dontCopyAtts || CopyFileAtts(src, dst, statBufPtr) == TCL_OK) { + return TCL_OK; + } + } + unlink(dst); /* INTL: Native. */ + return TCL_ERROR; +} +#endif + /* *---------------------------------------------------------------------- * * TclUnixCopyFile - * * Helper function for TclpCopyFile. Copies one regular file, using - * read() and write(). + * read() and write(), or clones it with TclUnixCloneFile() if + * supported. * * Results: * A standard Tcl result. @@ -558,6 +606,13 @@ TclUnixCopyFile( char *buffer; /* Data buffer for copy */ size_t nread; +#ifdef HAVE_CLONEFILE + if (MayUseCloneFile() && + TclUnixCloneFile(src, dst, statBufPtr, dontCopyAtts) == TCL_OK) { + return TCL_OK; + } +#endif + #ifdef DJGPP #define BINMODE |O_BINARY #else