diff options
Diffstat (limited to 'pre-commit.d/30store-metadata')
-rwxr-xr-x | pre-commit.d/30store-metadata | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/pre-commit.d/30store-metadata b/pre-commit.d/30store-metadata new file mode 100755 index 0000000..edec06b --- /dev/null +++ b/pre-commit.d/30store-metadata @@ -0,0 +1,153 @@ +#!/bin/sh +set -e + +# Filters out UNKNOWN users and groups, prints a warning on stderr. +filter_unknown() { + CMD=$1 + while read line; do + # if the first n chars of $line equal "$CMD UNKNOWN "... + if [ "$(printf %.$((9+${#CMD}))s "$line")" = "$CMD UNKNOWN " ]; then + echo Bad "$2" for "$line" >&2 + else + echo "$line" + fi + done +} + +filter_ignore() { + case "$VCS" in + darcs) ignorefile=.darcsignore ;; + git) ignorefile=.gitignore ;; + esac + + if [ -n "$ignorefile" ] && [ -e "$ignorefile" ]; then + listfile="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )" + case "$VCS" in + darcs) + grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$listfile" || true + grep -Evf "$listfile" + ;; + git) + (git ls-files -oi --exclude-standard; git ls-files -oi --exclude-standard --directory) | sort | uniq > "$listfile" || true + sed 's/^\.\///' | grep -xFvf "$listfile" + ;; + esac + rm -f "$listfile" + unset listfile + else + cat - + fi +} + +shellquote() { + # Single quotes text, escaping existing single quotes. + sed -e "s/'/'\"'\"'/g" -e "s/^/'/" -e "s/$/'/" +} + +generate_metadata() { + # This function generates the script commands to fix any file + # ownerships that aren't owner=root, group=root, as well as to + # store the permissions of files. + # The script is produced on stdout. Errors go to stderr. + # + # The script can use a 'maybe' function, which only runs a command + # if the file in its last argument exists. + + # We want files in the directory containing VCS data + # but we want find to ignore the VCS files themselves. + # + # (Note that when using this, the find expression must end with + # -print or -exec, else the excluded directories will actually be + # printed!) + NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o' + + # Keep the sort order the same at all times. + LC_COLLATE=C + export LC_COLLATE + + if [ "$VCS" = git ] || [ "$VCS" = hg ]; then + # These version control systems do not track directories, + # so empty directories must be stored specially. + find $NOVCS -type d -empty -print | + sort | shellquote | sed -e "s/^/mkdir -p /" + fi + + if [ "$VCS" = darcs ]; then + # This version control system does not track symlinks, + # so they must be stored specially. + find $NOVCS -type l -print | sort | filter_ignore | while read link; do + dest=$( readlink "$link" ) + printf "ln -sf '%s' '%s'\n" "$(echo "$dest" | shellquote)" "$(echo "$link" | shellquote)" + done + fi + + # Store things that don't have the default user or group. + # Store all file modes, in case the user has an unusual umask. + find $NOVCS \( -type f -or -type d \) -print | filter_ignore | sort | perl -ne ' + BEGIN { $q=chr(39) } + sub uidname { + my $want=shift; + if (exists $uidcache{$want}) { + return $uidcache{$want}; + } + my $name=scalar getpwuid($want); + return $uidcache{$want}=defined $name ? $name : $want; + } + sub gidname { + my $want=shift; + if (exists $gidcache{$want}) { + return $gidcache{$want}; + } + my $name=scalar getgrgid($want); + return $gidcache{$want}=defined $name ? $name : $want; + } + chomp; + my @stat=stat($_); + my $mode = $stat[2]; + my $uid = $stat[4]; + my $gid = $stat[5]; + s/$q/$q"$q"$q/g; # escape single quotes + s/^/$q/; + s/$/$q/; + if ($uid != $>) { + printf "maybe chown $q%s$q %s\n", uidname($uid), $_; + } + if ($gid != $)) { + printf "maybe chgrp $q%s$q %s\n", gidname($gid), $_; + } + printf "maybe chmod %04o %s\n", $mode & 07777, $_; + ' + + # We don't handle xattrs. + # Maybe check for getfattr/setfattr and use them if they're available? +} + +if [ "$VCS" = git ] || [ "$VCS" = hg ] || [ "$VCS" = bzr ] || [ "$VCS" = darcs ]; then + if [ -f .metadata ]; then + # remove obsolete .metadata file + # git allows fully deleting it at this point, other VCS + # may not (the repo is locked for hg). + if [ "$VCS" = git ]; then + $VCS rm .metadata + else + rm -f .metadata + fi + fi + + echo "# Generated by etckeeper. Do not edit." > .etckeeper + echo >> .etckeeper + + # Make sure the file is not readable by others, since it can leak + # information about contents of non-readable directories in /etc. + chmod 700 .etckeeper + + generate_metadata >> .etckeeper + + # stage the file as part of the current commit + if [ "$VCS" = git ]; then + # this will do nothing if the metadata file is unchanged. + git add .etckeeper + fi + # hg, bzr and darcs add not done, they will automatically + # include the file in the current commit +fi |