diff options
author | Rich Felker <dalias@aerifal.cx> | 2013-08-17 22:21:11 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2013-08-17 22:21:11 -0400 |
commit | e678fc6f32e5e5c013ab4f99c38f5703b15ff6cd (patch) | |
tree | de7c3f0b61f5da227bc978c5f3fc0f53180773ca /tools | |
parent | 109bd65acf5180f0a48ea4c4e5f2bc6884be202d (diff) | |
download | musl-e678fc6f32e5e5c013ab4f99c38f5703b15ff6cd.tar.gz musl-e678fc6f32e5e5c013ab4f99c38f5703b15ff6cd.tar.xz musl-e678fc6f32e5e5c013ab4f99c38f5703b15ff6cd.zip |
replace system's install command with a shell script
the historical (non-standardized) install command is really inappropriate for installing binaries/libraries on a system that utilizes memory-mapped executable files. rather than replacing an existing file atomically, it overwrites the existing file. this can cause running programs to see a partially-modified version of the file, resulting in unpredictable behavior, or SIGBUS. a MAP_COPY mode for mmap would get around this problem, but Linux lacks MAP_COPY. the shell script added with this commit works around the problem by writing temporary files and moving them into place. unlike the historical install utility, it also support a -l option for installing a symbolic link atomically, via the same method.
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/install.sh | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 00000000..7dcea333 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# +# This is an actually-safe install command which installs the new +# file atomically in the new location, rather than overwriting +# existing files. +# + +usage() { +printf "usage: %s [-D] [-l] [-m mode] src dest\n" "$0" 1>&2 +exit 1 +} + +mkdirp= +symlink= +mode=755 + +while getopts Dlm: name ; do +case "$name" in +D) mkdirp=yes ;; +l) symlink=yes ;; +m) mode=$OPTARG ;; +?) usage ;; +esac +done +shift $(($OPTIND - 1)) + +test "$#" -eq 2 || usage +src=$1 +dst=$2 +tmp="$dst.tmp.$$" + +case "$dst" in +*/) printf "%s: %s ends in /\n", "$0" "$dst" 1>&2 ; exit 1 ;; +esac + +set -C +set -e + +if test "$mkdirp" ; then +umask 022 +case "$2" in +*/*) mkdir -p "${dst%/*}" ;; +esac +fi + +trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP + +umask 077 + +if test "$symlink" ; then +ln -s "$1" "$tmp" +else +cat < "$1" > "$tmp" +fi + +mv "$tmp" "$2" +test -d "$2" && { +rm -f "$2/$tmp" +printf "%s: %s is a directory\n" "$0" "$dst" 1>&2 +exit 1 +} + +test "$symlink" || chmod "$mode" "$2" + +exit 0 |