summaryrefslogtreecommitdiff
path: root/corebinutils/getfacl/tests/test.sh
blob: ae22edbb816201581da8838ef77d4e24ba44b992 (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
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/bin/sh
set -eu

: "${GETFACL_BIN:?GETFACL_BIN is required}"

ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
TMPBASE="$ROOT/.tmp-tests"
mkdir -p "$TMPBASE"
WORKDIR=$(mktemp -d "$TMPBASE/getfacl-test.XXXXXX")
STDOUT_FILE="$WORKDIR/stdout"
STDERR_FILE="$WORKDIR/stderr"
trap 'rm -rf "$WORKDIR"' EXIT INT TERM

fail() {
	printf '%s\n' "FAIL: $1" >&2
	exit 1
}

assert_match() {
	pattern=$1
	text=$2
	message=$3
	printf '%s\n' "$text" | grep -Eq "$pattern" || fail "$message"
}

assert_not_match() {
	pattern=$1
	text=$2
	message=$3
	if printf '%s\n' "$text" | grep -Eq "$pattern"; then
		fail "$message"
	fi
}

assert_empty() {
	text=$1
	message=$2
	[ -z "$text" ] || fail "$message"
}

assert_eq() {
	expected=$1
	actual=$2
	message=$3
	[ "$expected" = "$actual" ] || {
		printf '%s\n' "FAIL: $message" >&2
		printf '%s\n' "--- expected ---" >&2
		printf '%s\n' "$expected" >&2
		printf '%s\n' "--- actual ---" >&2
		printf '%s\n' "$actual" >&2
		exit 1
	}
}

run_cmd() {
	: >"$STDOUT_FILE"
	: >"$STDERR_FILE"
	set +e
	"$@" >"$STDOUT_FILE" 2>"$STDERR_FILE"
	CMD_STATUS=$?
	set -e
	CMD_STDOUT=$(cat "$STDOUT_FILE")
	CMD_STDERR=$(cat "$STDERR_FILE")
}

run_stdin_cmd() {
	input=$1
	shift
	: >"$STDOUT_FILE"
	: >"$STDERR_FILE"
	set +e
	printf '%s' "$input" | "$@" >"$STDOUT_FILE" 2>"$STDERR_FILE"
	CMD_STATUS=$?
	set -e
	CMD_STDOUT=$(cat "$STDOUT_FILE")
	CMD_STDERR=$(cat "$STDERR_FILE")
}

acl_body() {
	printf '%s\n' "$1" | sed '/^#/d;/^$/d'
}

normalize_default_body() {
	printf '%s\n' "$1" | sed 's/^default://'
}

[ -x "$GETFACL_BIN" ] || fail "missing binary: $GETFACL_BIN"

run_cmd "$GETFACL_BIN" -z
[ "$CMD_STATUS" -eq 1 ] || fail "invalid option should exit 1"
assert_match '^usage: getfacl ' "$CMD_STDERR" "usage output missing for invalid option"

touch "$WORKDIR/file"
chmod 640 "$WORKDIR/file"

run_cmd "$GETFACL_BIN" "$WORKDIR/file"
[ "$CMD_STATUS" -eq 0 ] || fail "base ACL read failed"
assert_match "^# file: $WORKDIR/file$" "$CMD_STDOUT" "missing file header"
assert_match '^# owner: ' "$CMD_STDOUT" "missing owner header"
assert_match '^# group: ' "$CMD_STDOUT" "missing group header"
assert_match '^user::rw-$' "$CMD_STDOUT" "missing user base entry"
assert_match '^group::r--$' "$CMD_STDOUT" "missing group base entry"
assert_match '^other::---$' "$CMD_STDOUT" "missing other base entry"
base_output=$CMD_STDOUT

run_cmd "$GETFACL_BIN" -h "$WORKDIR/file"
[ "$CMD_STATUS" -eq 0 ] || fail "-h should work on regular files"
assert_eq "$(acl_body "$base_output")" "$(acl_body "$CMD_STDOUT")" "-h should not change regular-file ACL body"

run_cmd "$GETFACL_BIN" -q "$WORKDIR/file"
[ "$CMD_STATUS" -eq 0 ] || fail "quiet ACL read failed"
assert_not_match '^# file:' "$CMD_STDOUT" "header was not omitted"
assert_match '^user::rw-$' "$CMD_STDOUT" "quiet output lost ACL data"

run_cmd "$GETFACL_BIN" -s -q "$WORKDIR/file"
[ "$CMD_STATUS" -eq 0 ] || fail "skip-base should not fail on trivial ACL"
assert_empty "$CMD_STDOUT" "skip-base should suppress trivial ACLs"
assert_empty "$CMD_STDERR" "skip-base should not warn for trivial ACL"

run_cmd "$GETFACL_BIN" -n "$WORKDIR/file"
[ "$CMD_STATUS" -eq 0 ] || fail "numeric ACL read failed"
assert_match "^# owner: $(id -u)$" "$CMD_STDOUT" "numeric owner header missing"
assert_match "^# group: $(id -g)$" "$CMD_STDOUT" "numeric group header missing"

run_stdin_cmd "$(printf '%s\n%s\n' "$WORKDIR/file" "$WORKDIR/file")" "$GETFACL_BIN" -q
[ "$CMD_STATUS" -eq 0 ] || fail "stdin path processing failed"
count=$(printf '%s\n' "$CMD_STDOUT" | grep -c '^user::rw-$')
[ "$count" -eq 2 ] || fail "stdin path processing did not emit two ACL blocks"
assert_match '^$' "$CMD_STDOUT" "multiple stdin paths should be separated by a blank line"

run_stdin_cmd "$(printf '%s\n\n%s\n' "$WORKDIR/file" "$WORKDIR/file")" "$GETFACL_BIN" -q
[ "$CMD_STATUS" -eq 1 ] || fail "empty stdin pathname should fail"
assert_match '^getfacl: stdin: empty pathname$' "$CMD_STDERR" "empty stdin pathname error missing"
count=$(printf '%s\n' "$CMD_STDOUT" | grep -c '^user::rw-$')
[ "$count" -eq 2 ] || fail "stdin processing should continue after empty pathname"

run_cmd "$GETFACL_BIN" "$WORKDIR/missing"
[ "$CMD_STATUS" -eq 1 ] || fail "missing file should exit 1"
assert_match "^getfacl: $WORKDIR/missing: " "$CMD_STDERR" "missing file error not reported"

run_cmd "$GETFACL_BIN" "$WORKDIR/file" "$WORKDIR/missing"
[ "$CMD_STATUS" -eq 1 ] || fail "mixed success/failure operands should exit 1"
assert_match '^user::rw-$' "$CMD_STDOUT" "successful operand output missing in mixed run"
assert_match "^getfacl: $WORKDIR/missing: " "$CMD_STDERR" "mixed operand error missing"

run_cmd "$GETFACL_BIN" -i "$WORKDIR/file"
[ "$CMD_STATUS" -eq 1 ] || fail "unsupported -i should exit 1"
assert_eq "getfacl: option -i is not supported on Linux" "$CMD_STDERR" "unsupported -i check missing"

run_cmd "$GETFACL_BIN" -v "$WORKDIR/file"
[ "$CMD_STATUS" -eq 1 ] || fail "unsupported -v should exit 1"
assert_eq "getfacl: option -v is not supported on Linux" "$CMD_STDERR" "unsupported -v check missing"

ln -s "$WORKDIR/file" "$WORKDIR/link"
run_cmd "$GETFACL_BIN" -h "$WORKDIR/link"
[ "$CMD_STATUS" -eq 1 ] || fail "symlink -h should fail on Linux"
assert_eq "getfacl: $WORKDIR/link: symbolic link ACLs are not supported on Linux" "$CMD_STDERR" "symlink -h error missing"

mkdir "$WORKDIR/plain-dir"
run_cmd "$GETFACL_BIN" -d "$WORKDIR/plain-dir"
[ "$CMD_STATUS" -eq 0 ] || fail "default ACL query without default xattr should succeed"
assert_match "^# file: $WORKDIR/plain-dir$" "$CMD_STDOUT" "default ACL header missing"
assert_not_match '^default:' "$CMD_STDOUT" "unexpected default ACL entries on plain dir"

run_cmd "$GETFACL_BIN" -d -q "$WORKDIR/plain-dir"
[ "$CMD_STATUS" -eq 0 ] || fail "quiet default ACL query without xattr should succeed"
assert_empty "$CMD_STDOUT" "quiet default ACL output without xattr should be empty"

run_cmd "$GETFACL_BIN" -d -s -q "$WORKDIR/plain-dir"
[ "$CMD_STATUS" -eq 0 ] || fail "skip-base default ACL query without xattr should succeed"
assert_empty "$CMD_STDOUT" "skip-base should suppress missing default ACL output"

acl_supported=0
if command -v setfacl >/dev/null 2>&1; then
	if setfacl -m m::r-- "$WORKDIR/file" 2>/dev/null; then
		acl_supported=1
	fi
fi

if [ "$acl_supported" -eq 1 ]; then
	run_cmd "$GETFACL_BIN" "$WORKDIR/file"
	[ "$CMD_STATUS" -eq 0 ] || fail "extended ACL read failed"
	assert_match '^mask::r--$' "$CMD_STDOUT" "extended ACL mask missing"

	setfacl -m "u:$(id -u):rw-" "$WORKDIR/file"
	setfacl -m "g:$(id -g):r--" "$WORKDIR/file"

	run_cmd "$GETFACL_BIN" -n "$WORKDIR/file"
	[ "$CMD_STATUS" -eq 0 ] || fail "numeric named ACL read failed"
	assert_match "^user:$(id -u):rw-$" "$CMD_STDOUT" "named user ACL missing"
	assert_match "^group:$(id -g):r--$" "$CMD_STDOUT" "named group ACL missing"
	assert_match '^mask::rw-$' "$CMD_STDOUT" "mask was not updated after named ACL"

	run_cmd "$GETFACL_BIN" "$WORKDIR/file"
	[ "$CMD_STATUS" -eq 0 ] || fail "named ACL read failed"
	assert_match '^user:[^:][^:]*:rw-$' "$CMD_STDOUT" "named user should resolve to a name"
	assert_match '^group:[^:][^:]*:r--$' "$CMD_STDOUT" "named group should resolve to a name"

	mkdir "$WORKDIR/dir"
	setfacl -d -m u::rwx,g::r-x,o::---,m::r-x,u:$(id -u):rwx "$WORKDIR/dir"

	run_cmd "$GETFACL_BIN" -d "$WORKDIR/dir"
	[ "$CMD_STATUS" -eq 0 ] || fail "default ACL read failed"
	assert_match '^default:user::rwx$' "$CMD_STDOUT" "default user entry missing"
	assert_match "^default:user:[^:][^:]*:rwx$" "$CMD_STDOUT" "default named user entry missing"
	assert_match '^default:group::r-x$' "$CMD_STDOUT" "default group entry missing"
	assert_match '^default:mask::r-x$' "$CMD_STDOUT" "default mask entry missing"
	assert_match '^default:other::---$' "$CMD_STDOUT" "default other entry missing"

	run_cmd "$GETFACL_BIN" -n -d "$WORKDIR/dir"
	[ "$CMD_STATUS" -eq 0 ] || fail "numeric default ACL read failed"
	assert_match "^default:user:$(id -u):rwx$" "$CMD_STDOUT" "numeric default named user entry missing"

	if command -v getfacl >/dev/null 2>&1; then
		run_cmd "$GETFACL_BIN" -n -q "$WORKDIR/file"
		ours_access=$(acl_body "$CMD_STDOUT")
		system_access=$(getfacl -E -p -n "$WORKDIR/file" 2>/dev/null | sed '/^#/d;/^$/d')
		assert_eq "$system_access" "$ours_access" "access ACL body diverges from system getfacl"

		run_cmd "$GETFACL_BIN" -n -q -d "$WORKDIR/dir"
		ours_default=$(normalize_default_body "$(acl_body "$CMD_STDOUT")")
		system_default=$(getfacl -E -p -n -d "$WORKDIR/dir" 2>/dev/null | sed '/^#/d;/^$/d')
		assert_eq "$system_default" "$ours_default" "default ACL body diverges from system getfacl"
	fi
else
	printf '%s\n' "SKIP: extended ACL checks (setfacl unavailable or filesystem lacks ACL support)" >&2
fi

printf '%s\n' "PASS"