1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#!/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() {
if [ "$VCS" = darcs ]; then
ignorefile=.darcsignore
fi
if [ "$VCS" = darcs ] && [ -e "$ignorefile" ]; then
patternsfile="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )"
grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$patternsfile" || true
grep -Evf "$patternsfile"
rm -f "$patternsfile"
unset patternsfile
else
cat -
fi
}
statf() {
while read statfile; do
printf "$(stat --format="$1" "$statfile") "
echo "$statfile" | shellquote
done
}
shellquote() {
# Single quotes text, escaping existing single quotes.
sed -e "s/'/'\"'\"'/" -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
# Find all files and directories that don't have the current user as the owner
find $NOVCS \! -user "$(id -u)" -print | statf "maybe chown %U" | sort | filter_unknown 'maybe chown' owner
# Find all files and directories that don't have root as the group
find $NOVCS \! -group $(id -g) -print | statf "maybe chgrp %G" | sort | filter_unknown 'maybe chgrp' group
# Store all file modes, in case the user has an unusual umask.
find $NOVCS \( -type f -or -type d \) -print | sort | perl -ne '
BEGIN { $q=chr(39) }
chomp;
my $mode = (stat($_))[2];
s/$q/$q"$q"$q/g; # escape single quotes
printf "maybe chmod %04o %s\n", $mode & 07777, "$q$_$q";
'
# 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
|