summaryrefslogtreecommitdiff
path: root/pre-commit.d/30store-metadata
blob: 2e35e6c081a974ce688702977d5fc5cbb5665a92 (plain)
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
#!/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
		echo "$(stat --format="$1" "$statfile") '$statfile'"
	done
}

generate_metadata() {
	# This function generates the script commands to fix any files
	# that aren't owner=root, group=root, or mode=0644 or 0755.
	# 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 maintain the permissions on 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 | sed -e "s/^/mkdir -p '/" -e "s/\$/'/"
	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" "$dest" "$link"
		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

	# Find all directories that aren't 0755
	find $NOVCS -type d \! -perm 0755 -print | statf "maybe chmod %a" | sort

	if [ "$VCS" = darcs ]; then
		# Find all files that aren't 0644 (darcs doesn't maintain
		# the executable bit).
		find $NOVCS -type f \! -perm 0644 -print | statf "maybe chmod %a" | sort
	else
		# Find all files that aren't 0644 or 0755 (we can assume the VCS will
		# maintain the executable bit).
		find $NOVCS -type f \! -perm 0644 \! -perm 0755 -print | statf "maybe chmod %a" | sort
	fi

	# 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