1 #
2 # CDDL HEADER START
3 #
4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
7 #
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
12 #
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
18 #
19 # CDDL HEADER END
20 #
21
22 #
23 # Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 # Use is subject to license terms.
25 #
26
27 #
28 # Copyright (c) 2013 by Delphix. All rights reserved.
29 #
30
31 . $STF_SUITE/include/libtest.shlib
32 . $STF_SUITE/tests/functional/history/history.cfg
33
34 function run_and_verify
35 {
36 typeset user pool
37 while getopts "p:u:" opt; do
38 case $opt in
39 p)
40 pool=$OPTARG
41 ;;
42 u)
43 user=$OPTARG
44 ;;
45 esac
46 done
47 shift $(($OPTIND - 1))
48
49 pool=${pool:-$TESTPOOL}
50 user=${user:-"root"}
51 fullcmd="$1"
52 flags="$2"
53
54 histcmd=$($ECHO $fullcmd | $SED 's/\/usr\/sbin\///g')
55 cmd=$($ECHO $histcmd | $AWK '{print $1}')
56 subcmd=$($ECHO $histcmd | $AWK '{print $2}')
57
58 # If we aren't running zpool or zfs, something is wrong
59 [[ $cmd == "zpool" || $cmd == "zfs" ]] || \
60 log_fail "run_and_verify called with \"$cmd ($fullcmd)\""
61
62 # If this is a 'zfs receive' truncate the stdin redirect
63 [[ $subcmd == "receive" || $subcmd == "recv" ]] && \
64 histcmd=${histcmd%% <*}
65
66 # Run the command as the specified user, and find the new history.
67 $ZPOOL history $flags $pool > $OLD_HISTORY 2>/dev/null
68 if [[ $user == "root" ]]; then
69 log_must eval "$fullcmd"
70 else
71 log_must $SU $user -c "eval $fullcmd"
72 fi
73 $ZPOOL history $flags $pool > $TMP_HISTORY 2>/dev/null
74 $DIFF $OLD_HISTORY $TMP_HISTORY | $GREP "^> " | $SED 's/^> //g' \
75 > $NEW_HISTORY
76
77 # Verify what's common to every case, regardless of zpool history flags.
78 $GREP "$histcmd" $NEW_HISTORY >/dev/null 2>&1 || \
79 log_fail "Didn't find \"$histcmd\" in pool history"
80
81 # If 'zpool history' was called without any flags, then we're done.
82 [[ -z $flags ]] && return
83
84 # Verify the new history in cases that are more interesting because
85 # additional information is logged with -i or -l.
86
87 [[ $flags =~ "i" ]] && log_must verify_$subcmd "$histcmd" "$subcmd" \
88 "$flags"
89 [[ $flags =~ "l" ]] && log_must verify_long "$histcmd" "$user" "$flags"
90 }
91
92 function verify_long
93 {
94 typeset cmd=$1
95 typeset user=$2
96 typeset flags=$3
97
98 [[ $flags =~ "l" ]] || return 1
99
100 typeset uid=$($ID -u $user)
101 typeset hname=$($HOSTNAME)
102 if ! is_global_zone; then
103 hname=$hname:$($ZONENAME)
104 fi
105
106 $GREP "$cmd \[user $uid ($user) on $hname\]" $NEW_HISTORY >/dev/null \
107 2>&1
108 if [[ $? != 0 ]]; then
109 log_note "Couldn't find long information for \"$cmd\""
110 return 1
111 fi
112
113 return 0
114 }
115
116 function verify_hold
117 {
118 typeset cmd=$1
119 typeset subcmd=$2
120 typeset flags=$3
121
122 [[ $flags =~ "i" ]] || return 1
123
124 typeset tag=$($ECHO $cmd | $AWK '{print $4}')
125 typeset fullname=${cmd##* }
126 typeset dsname=${fullname%%@*}
127 typeset snapname=${fullname##*@}
128
129 # This works whether or not the hold was recursive
130 for ds in $($ZFS list -r -Ho name -t snapshot $dsname | \
131 $GREP "@$snapname"); do
132 $GREP "$subcmd $ds ([0-9]*) tag=$tag" $NEW_HISTORY \
133 >/dev/null 2>&1
134 if [[ $? != 0 ]]; then
135 log_note "Didn't find hold on $ds with $tag"
136 return 1
137 fi
138 done
139
140 return 0
141 }
142
143 function verify_release
144 {
145 # hold and release formats only differ by the subcommand name, so
146 # simply reuse the hold function.
147 verify_hold "$1" "release" "$3"
148 }
149
150 function verify_rollback
151 {
152 typeset cmd=$1
153 typeset flags=$3
154
155 [[ $flags =~ "i" ]] || return 1
156
157 typeset fullname=${cmd##* }
158 typeset dsname=${fullname%%@*}
159 typeset parent_fs=${dsname##*/}
160 typeset rb_fs=${dsname}/%rollback
161 typeset snapname=${fullname##*@}
162
163 $GREP "clone swap $rb_fs ([0-9]*) parent=$parent_fs" $NEW_HISTORY \
164 >/dev/null 2>&1
165 if [[ $? != 0 ]]; then
166 log_note "Didn't find rollback clone swap in pool history"
167 return 1
168 fi
169
170 $GREP "destroy $rb_fs" $NEW_HISTORY >/dev/null 2>&1
171 if [[ $? != 0 ]]; then
172 log_note "Didn't find rollback destroy in pool history"
173 return 1
174 fi
175
176 return 0
177 }
178
179 function verify_inherit
180 {
181 typeset cmd=$1
182 typeset flags=$3
183
184 [[ $flags =~ "i" ]] || return 1
185
186 typeset dsname=${cmd##* }
187 typeset prop=${cmd% *}
188 prop=${prop##* }
189
190 # This works whether or not the inherit was recursive
191 for ds in $($ZFS list -r -Ho name -t filesystem $dsname); do
192 $GREP "$subcmd $ds ([0-9]*) ${prop}=" $NEW_HISTORY >/dev/null \
193 2>&1
194 if [[ $? != 0 ]]; then
195 log_note "Didn't find inherit history for $ds"
196 return 1
197 fi
198 done
199
200 return 0
201 }
202
203 function verify_allow
204 {
205 typeset cmd=$1
206 typeset subcmd=$2
207 typeset flags=$3
208
209 [[ $flags =~ "i" ]] || return 1
210 [[ $subcmd == "allow" ]] && subcmd="update"
211 [[ $subcmd == "unallow" ]] && subcmd="remove"
212 typeset is_set lflag dflag dsname gname gid uname uid opt str code tmp
213
214 #
215 # Here, we determine three things:
216 # - Whether we're operating on a set or an indivdual permission (which
217 # dictates the case of the first character in the code)
218 # - The name of the dataset we're operating on.
219 # - Whether the operation applies locally or to descendent datasets (or
220 # both)
221 #
222 $ECHO $cmd | $AWK '{i = NF - 1; print $i}' | $GREP '@' >/dev/null \
223 2>&1 && is_set=1
224 dsname=${cmd##* }
225 [[ $cmd =~ "-l " ]] && lflag=1
226 [[ $cmd =~ "-d " ]] && dflag=1
227 if [[ -z $lflag && -z $dflag ]]; then
228 lflag=1
229 dflag=1
230 fi
231
232 #
233 # For each of the five cases below, the operation is essentially the
234 # same. First, use the command passed in to determine what the code at
235 # the end of the pool history will be. The specifics of the code are
236 # described in a block comment at the top of dsl_deleg.c. Once that's
237 # been assembled, check for its presence in the history, and return
238 # success or failure accordingly.
239 #
240 if [[ $cmd =~ "-s " ]]; then
241 str="s-\$@"
242 [[ -n $is_set ]] && str="S-\$@"
243 tmp=${cmd#*@}
244 code="$str${tmp% *}"
245 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
246 $NEW_HISTORY >/dev/null 2>&1
247 if [[ $? != 0 ]]; then
248 log_note "Couldn't find $code in $NEW_HISTORY"
249 return 1
250 fi
251 elif [[ $cmd =~ "-c " ]]; then
252 str="c-\$"
253 [[ -n $is_set ]] && str="C-\$"
254 tmp=${cmd#*-c}
255 code="$str${tmp% *}"
256 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
257 $NEW_HISTORY >/dev/null 2>&1
258 if [ $? != 0 ]]; then
259 log_note "Couldn't find $code in $NEW_HISTORY"
260 return 1
261 fi
262 elif [[ $cmd =~ "-u " ]]; then
263 str="u"
264 [[ -n $is_set ]] && str="U"
265 tmp=${cmd##*-u }
266 opt=$($ECHO $tmp | $AWK '{print $2}')
267 uid=$($ID -u ${tmp%% *})
268 if [[ -n $lflag ]]; then
269 code="${str}l\$$uid $opt"
270 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
271 $NEW_HISTORY >/dev/null 2>&1
272 if [ $? != 0 ]]; then
273 log_note "Couldn't find $code in $NEW_HISTORY"
274 return 1
275 fi
276 fi
277 if [[ -n $dflag ]]; then
278 code="${str}d\$$uid $opt"
279 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
280 $NEW_HISTORY >/dev/null 2>&1
281 if [ $? != 0 ]]; then
282 log_note "Couldn't find $code in $NEW_HISTORY"
283 return 1
284 fi
285 fi
286 elif [[ $cmd =~ "-g " ]]; then
287 str="g"
288 [[ -n $is_set ]] && str="G"
289 tmp=${cmd##*-g }
290 opt=$($ECHO $tmp | $AWK '{print $2}')
291 gid=$($AWK -F: "/^${tmp%% *}:/ {print \$3}" /etc/group)
292 if [[ -n $lflag ]]; then
293 code="${str}l\$$gid $opt"
294 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
295 $NEW_HISTORY >/dev/null 2>&1
296 if [ $? != 0 ]]; then
297 log_note "Couldn't find $code in $NEW_HISTORY"
298 return 1
299 fi
300 fi
301 if [[ -n $dflag ]]; then
302 code="${str}d\$$gid $opt"
303 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
304 $NEW_HISTORY >/dev/null 2>&1
305 if [ $? != 0 ]]; then
306 log_note "Couldn't find $code in $NEW_HISTORY"
307 return 1
308 fi
309 fi
310 elif [[ $cmd =~ "-e " ]]; then
311 str="e"
312 [[ -n $is_set ]] && str="E"
313 opt=${cmd##*-e }
314 opt=${opt%% *}
315 if [[ -n $lflag ]]; then
316 code="${str}l\$ $opt"
317 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
318 $NEW_HISTORY >/dev/null 2>&1
319 if [ $? != 0 ]]; then
320 log_note "Couldn't find $code in $NEW_HISTORY"
321 return 1
322 fi
323 fi
324 if [[ -n $dflag ]]; then
325 code="${str}d\$ $opt"
326 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
327 $NEW_HISTORY >/dev/null 2>&1
328 if [ $? != 0 ]]; then
329 log_note "Couldn't find $code in $NEW_HISTORY"
330 return 1
331 fi
332 fi
333 else
334 log_note "Can't parse command \"$cmd\""
335 return 1
336 fi
337
338 return 0
339 }
340
341 function verify_unallow
342 {
343 #
344 # The unallow and allow history have the same format, except the former
345 # logs "permission removed" and the latter "permission updated" so
346 # simply reuse the allow function.
347 #
348 verify_allow "$1" "unallow" "$3"
349 }
350
351 function verify_destroy
352 {
353 typeset cmd=$1
354 typeset flags=$3
355
356 # This function doesn't currently verifiy the zpool command.
357 [[ ${cmd%% *} == "zfs" ]] || return 1
358 [[ $flags =~ "i" ]] || return 1
359
360 typeset dsname=${cmd##* }
361 [[ $dsname =~ "@" ]] && typeset is_snap=1
362
363 if [[ -n $is_snap ]]; then
364 $GREP "ioctl destroy_snaps" $NEW_HISTORY >/dev/null 2>&1
365 if [[ $? != 0 ]]; then
366 log_note "Didn't find ioctl while destroying $dsname"
367 return 1
368 fi
369 fi
370
371 # This should be present for datasets and snapshots alike
372 $GREP "destroy $dsname" $NEW_HISTORY >/dev/null 2>&1
373 if [[ $? != 0 ]]; then
374 log_note "Didn't find \"destroy\" for $dsname"
375 return 1
376 fi
377
378 return 0
379 }
380
381 function verify_snapshot
382 {
383 typeset cmd=$1
384 typeset flags=$3
385
386 [[ $flags =~ "i" ]] || return 1
387
388 typeset fullname=${cmd##* }
389 typeset dsname=${fullname%%@*}
390 typeset snapname=${fullname##*@}
391
392 $GREP "\[txg:[0-9]*\] $subcmd $fullname ([0-9]*)" $NEW_HISTORY \
393 >/dev/null 2>&1
394 if [[ $? != 0 ]]; then
395 log_note "Didn't find snapshot command for $fullname"
396 return 1
397 fi
398
399 # This works whether or not the snapshot was recursive
400 for ds in $($ZFS list -r -Ho name -t snapshot $dsname | \
401 $GREP "@$snapname"); do
402 $GREP "^[ ]* $ds$" $NEW_HISTORY >/dev/null 2>&1
403 if [[ $? != 0 ]]; then
404 log_note "Didn't find \"ioctl snapshot\" for $ds"
405 return 1
406 fi
407 done
408
409 return 0
410 }