File: 30store-metadata

package info (click to toggle)
etckeeper 1.18.23-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,640 kB
  • sloc: sh: 1,267; python: 111; makefile: 109; ansic: 81
file content (179 lines) | stat: -rwxr-xr-x 4,991 bytes parent folder | download | duplicates (2)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/bin/sh
set -e

# Keep the sort order the same at all times.
LC_COLLATE=C
export LC_COLLATE
unset LC_ALL

filter_ignore() {
	case "$VCS" in
		darcs) ignorefile=.darcsignore ;;
		git)   ignorefile=.gitignore ;;
	esac

	if [ -n "$ignorefile" ] && [ -e "$ignorefile" ]; then
		if command -v mktemp >/dev/null; then
			tempfile="mktemp -t etckeeper-$VCS.XXXXXXXXXX"
		elif command -v tempfile >/dev/null; then
			tempfile="tempfile"
		else
			echo "etckeeper warning: can't find tempfile or mktemp" >&2
			exit 1
		fi
		listfile="$($tempfile)"
		case "$VCS" in
			darcs)
				LC_CTYPE=C grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$listfile" || true
				LC_CTYPE=C grep -Evf "$listfile"
				;;
			git)
				(git ls-files -oi --exclude-standard; git ls-files -oi --exclude-standard --directory) | sort | uniq > "$listfile" || true
				if [ -s "$listfile" ]; then
					sed 's/^\.\///' | LC_CTYPE=C grep -xFvf "$listfile"
				else
					cat -
				fi
				;;
		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'

	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 | maybe_chmod_chown

	# We don't handle xattrs.
	# Maybe check for getfattr/setfattr and use them if they're available?
}

maybe_chmod_chown() {
	if command -v perl >/dev/null; then
		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, $_;
		'
		return $?
	else
		# fallback if perl isn't present
		euid=$(id -u)
		egid=$(id -g)
		q="'"
		while read x; do
			stat=$(stat -c "%f:%u:%g:%a:%U:%G" "$x")
			IFS=":" read mode uid gid perm uname gname <<EOF
$stat
EOF
			x=$q$(echo $x | sed "s/$q/$q\"$q\"$q/")$q
			if [ $uid -ne $euid ]; then
				echo maybe chown "'$uname'" $x
			fi
			if [ $gid -ne $egid ]; then
				echo maybe chgrp "'$gname'" $x
			fi
			echo maybe chmod 0$perm $x
		done
	fi
}

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