1#!/bin/sh
2#
3# Automated tests for utimensat()
4#
5# Copyright (C) 2008, Linux Foundation
6# Written by Michael Kerrisk <mtk.manpages@gmail.com>
7# Licensed under GPLv2 or later
8#
9# Not (yet) included in this automated test set:
10# * AT_SYMLINK_NOFOLLOW in flags: If pathname specifies a symbolic link,
11#   then update the timestamps of the link, rather than the file to which
12#   it refers.
13# * Setting of nanosecond components of timestamps (support for
14#   nanosecond timestamps is file-system-dependent)
15# * "Updated file timestamps are set to the greatest value supported
16#   by the file system that is not greater than the specified time."
17#   (i.e., if we set timestamp to {0, 999999999}, then the setting
18#   is rounded down, rather than up, to unit of timestamp resolution.
19# * Privileged processes should be able to bypass permission checks.
20#   (except when file is marked with the "Immutable" EFA).
21
22#=====================================================================
23
24export TCID=utimensat01
25export TST_TOTAL=99
26export TST_COUNT=0
27. test.sh
28
29if tst_kvercmp 2 6 22 ; then
30	tst_brkm TCONF "System kernel version is less than 2.6.22,cannot execute test"
31fi
32
33RESULT_FILE=$TMPDIR/utimensat.result
34
35TEST_DIR=$TMPDIR/utimensat_tests
36FILE=$TEST_DIR/utimensat.test_file
37
38TEST_PROG=utimensat01
39
40if [ ! -f $LTPROOT/testcases/bin/$TEST_PROG ]; then
41	tst_brkm TWARN "$LTPROOT/testcases/bin/$TEST_PROG is missing (please check install)"
42fi
43
44# Summary counters of all test results
45
46test_num=0
47failed_cnt=0
48passed_cnt=0
49failed_list=""
50
51#=====================================================================
52
53setup_file()
54{
55# $1 is test file pathname
56# $2 is owner for test file (chown(1))
57# $3 is permissions for test file (chmod(1))
58# $4 is "ext2" extended file attributes for test file (chattr(1))
59
60    FILE=$1
61
62    # Make sure any old version of file is deleted
63
64    if test -e $FILE; then
65        sudo $s_arg chattr -ai $FILE || return $?
66        sudo $s_arg rm -f $FILE || return $?
67    fi
68
69    # Create file and make atime and mtime zero.
70
71    sudo $s_arg -u $test_user touch $FILE || return $?
72    if ! $TEST_PROG -q $FILE 0 0 0 0 > $RESULT_FILE; then
73        echo "Failed to set up test file $FILE" 1>&2
74        exit 1
75    fi
76
77    read res atime mtime < $RESULT_FILE
78    if test "X$res" != "XSUCCESS" ||
79                test $atime -ne 0 || test $mtime != 0; then
80        echo "Failed to set correct times on test file $FILE" 1>&2
81        exit 1
82    fi
83
84    # Set owner, permissions, and EFAs for file.
85
86    if test -n "$2"; then
87        sudo $s_arg chown $2 $FILE || return $?
88    fi
89
90    sudo $s_arg chmod $3 $FILE || return $?
91
92    if test -n "$4"; then
93        sudo $s_arg chattr $4 $FILE || return $?
94    fi
95
96    # Display file setup, for visual verification
97
98    ls -l $FILE | awk '{ printf "Owner=%s; perms=%s; ", $3, $1}'
99    if ! sudo $s_arg lsattr -l $FILE | sed 's/, /,/g' | awk '{print "EFAs=" $2}'
100    then
101        return $?
102    fi
103
104}
105
106test_failed()
107{
108    tst_resm TFAIL "FAILED test $test_num"
109
110    failed_cnt=$(expr $failed_cnt + 1)
111    failed_list="$failed_list $test_num"
112}
113
114check_result()
115{
116    STATUS=$1                   # Exit status from test program
117    EXPECTED_RESULT=$2          # SUCCESS / EACCES / EPERM / EINVAL
118    EXPECT_ATIME_CHANGED=$3     # Should be 'y' or 'n' (only for SUCCESS)
119    EXPECT_MTIME_CHANGED=$4     # Should be 'y' or 'n' (only for SUCCESS)
120
121    test_num=$(expr $test_num + 1)
122
123    # If our test setup failed, stop immediately
124
125    if test $STATUS -gt 1; then
126        echo "FAILED (bad test setup)"
127        exit 1
128    fi
129
130    read res atime mtime < $RESULT_FILE
131
132    echo "EXPECTED: $EXPECTED_RESULT $EXPECT_ATIME_CHANGED "\
133         "$EXPECT_MTIME_CHANGED"
134    echo "RESULT:   $res $atime $mtime"
135
136    if test "$res" != "$EXPECTED_RESULT"; then
137        test_failed
138        return
139    fi
140
141    passed=1
142
143    # If the test program exited successfully, then check that atime and
144    # and mtime were updated / not updated, as expected.
145
146    if test $EXPECTED_RESULT = "SUCCESS"; then
147        if test $EXPECT_ATIME_CHANGED = "y"; then
148            if test $atime -eq 0; then
149                echo "atime should have changed, but did not"
150                passed=0
151            fi
152        else
153            if test $atime -ne 0; then
154                echo "atime should not have changed, but did"
155                passed=0
156            fi
157        fi
158
159        if test $EXPECT_MTIME_CHANGED = "y"; then
160            if test $mtime -eq 0; then
161                echo "mtime should have changed, but did not"
162                passed=0
163            fi
164        else
165            if test $mtime -ne 0; then
166                echo "mtime should not have changed, but did"
167                passed=0
168            fi
169        fi
170
171        if test $passed -eq 0; then
172            test_failed
173            return
174        fi
175    fi
176
177    passed_cnt=$(expr $passed_cnt + 1)
178    tst_resm TPASS "PASSED test $test_num"
179}
180
181run_test()
182{
183    # By default, we do three types of test:
184    # a) pathname (pathname != NULL)
185    # b) readable file descriptor (pathname == NULL, dirfd opened O_RDONLY)
186    # c) writable file descriptor (pathname == NULL, dirfd opened O_RDWR).
187    #    For this case we also include O_APPEND in open flags, since that
188    #    is needed if testing with a file that has the Append-only
189    #    attribute enabled.
190
191    # -R says don't do tests with readable file descriptor
192    # -W says don't do tests with writable file descriptor
193
194    OPTIND=1
195
196    do_read_fd_test=1
197    do_write_fd_test=1
198    while getopts "RW" opt; do
199        case "$opt" in
200        R) do_read_fd_test=0
201           ;;
202        W) do_write_fd_test=0
203           ;;
204        *) echo "run_test: bad usage"
205           exit 1
206           ;;
207        esac
208    done
209    shift `expr $OPTIND - 1`
210
211    echo "Pathname test"
212    setup_file $FILE "$1" "$2" "$3"
213    cp $LTPROOT/testcases/bin/$TEST_PROG ./
214    CMD="./$TEST_PROG -q $FILE $4"
215    echo "$CMD"
216    sudo $s_arg -u $test_user $CMD > $RESULT_FILE
217    check_result $? $5 $6 $7
218    echo
219
220    if test $do_read_fd_test -ne 0; then
221        echo "Readable file descriptor (futimens(3)) test"
222        setup_file $FILE "$1" "$2" "$3"
223        CMD="./$TEST_PROG -q -d $FILE NULL $4"
224        echo "$CMD"
225        sudo $s_arg -u $test_user $CMD > $RESULT_FILE
226        check_result $? $5 $6 $7
227        echo
228    fi
229
230    # Can't do the writable file descriptor test for immutable files
231    # (even root can't open an immutable file for writing)
232
233    if test $do_write_fd_test -ne 0; then
234        echo "Writable file descriptor (futimens(3)) test"
235        setup_file $FILE "$1" "$2" "$3"
236        CMD="./$TEST_PROG -q -w -d $FILE NULL $4"
237        echo "$CMD"
238        sudo $s_arg -u $test_user $CMD > $RESULT_FILE
239        check_result $? $5 $6 $7
240        echo
241    fi
242
243    sudo $s_arg chattr -ai $FILE
244    sudo $s_arg rm -f $FILE
245}
246
247#=====================================================================
248
249# Since some automated testing systems have no tty while testing,
250# comment this line in /etc/sudoers to avoid the error message:
251# `sudo: sorry, you must have a tty to run sudo'
252# Use trap to restore this line after program terminates.
253sudoers=/etc/sudoers
254if [ ! -r $sudoers ]; then
255	tst_brkm TBROK "can't read $sudoers"
256fi
257pattern="[[:space:]]*Defaults[[:space:]]*requiretty.*"
258if grep -q "^${pattern}" $sudoers; then
259	tst_resm TINFO "Comment requiretty in $sudoers for automated testing systems"
260	if ! sed -r -i.$$ -e "s/^($pattern)/#\1/" $sudoers; then
261		tst_brkm TBROK "failed to mangle $sudoers properly"
262	fi
263	trap 'trap "" EXIT; restore_sudoers' EXIT
264fi
265
266restore_sudoers()
267{
268	tst_resm TINFO "Restore requiretty in $sudoers"
269	mv /etc/sudoers.$$ /etc/sudoers
270}
271
272test_user=nobody
273echo "test sudo for -n option, non-interactive"
274if sudo -h | grep -q -- -n; then
275	s_arg="-n"
276	echo "sudo supports -n"
277else
278	s_arg=
279	echo "sudo does not support -n"
280fi
281
282if ! sudo $s_arg true; then
283	tst_brkm TBROK "sudo cannot be run by user non-interactively"
284fi
285if test ! -f $sudoers
286then
287	echo "root    ALL=(ALL)    ALL" > $sudoers || exit
288	chmod 440 $sudoers
289	trap 'trap "" EXIT; nuke_sudoers' EXIT
290fi
291
292nuke_sudoers()
293{
294	sudo rm -f $sudoers
295}
296
297sudo $s_arg -u $test_user mkdir -p $TEST_DIR
298
299# Make sure chattr command is supported
300touch $TEST_DIR/tmp_file
301chattr +a $TEST_DIR/tmp_file
302if [ $? -ne 0 ] ; then
303	rm -rf $TEST_DIR
304	tst_brkm TCONF "chattr not supported"
305fi
306chattr -a $TEST_DIR/tmp_file
307
308cd $TEST_DIR
309chown root $LTPROOT/testcases/bin/$TEST_PROG
310chmod ugo+x,u+s $LTPROOT/testcases/bin/$TEST_PROG
311
312#=====================================================================
313
314
315echo "============================================================"
316
317echo
318echo "Testing read-only file, owned by self"
319echo
320
321echo "***** Testing times==NULL case *****"
322run_test -W "" 400 "" "" SUCCESS y y
323
324echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
325run_test -W "" 400 "" "0 n 0 n" SUCCESS y y
326
327echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
328run_test -W "" 400 "" "0 o 0 o" SUCCESS n n
329
330echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
331run_test -W "" 400 "" "0 n 0 o" SUCCESS y n
332
333echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
334run_test -W "" 400 "" "0 o 0 n" SUCCESS n y
335
336echo "***** Testing times=={ x, y } case *****"
337run_test -W "" 400 "" "1 1 1 1" SUCCESS y y
338
339echo "============================================================"
340
341echo
342echo "Testing read-only file, not owned by self"
343echo
344
345echo "***** Testing times==NULL case *****"
346run_test -RW root 400 "" "" EACCES
347
348echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
349run_test -RW root 400 "" "0 n 0 n" EACCES
350
351echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
352run_test -RW root 400 "" "0 o 0 o" SUCCESS n n
353
354echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
355run_test -RW root 400 "" "0 n 0 o" EPERM
356
357echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
358run_test -RW root 400 "" "0 o 0 n" EPERM
359
360echo "***** Testing times=={ x, y } case *****"
361run_test -RW root 400 "" "1 1 1 1" EPERM
362
363echo "============================================================"
364
365echo
366echo "Testing writable file, not owned by self"
367echo
368
369echo "***** Testing times==NULL case *****"
370run_test root 666 "" "" SUCCESS y y
371
372echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
373run_test root 666 "" "0 n 0 n" SUCCESS y y
374
375echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
376run_test root 666 "" "0 o 0 o" SUCCESS n n
377
378echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
379run_test root 666 "" "0 n 0 o" EPERM
380
381echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
382run_test root 666 "" "0 o 0 n" EPERM
383
384echo "***** Testing times=={ x, y } case *****"
385run_test root 666 "" "1 1 1 1" EPERM
386
387echo "============================================================"
388
389echo
390echo "Testing append-only file, owned by self"
391echo
392
393echo "***** Testing times==NULL case *****"
394run_test "" 600 "+a" "" SUCCESS y y
395
396echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
397run_test "" 600 "+a" "0 n 0 n" SUCCESS y y
398
399echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
400run_test "" 600 "+a" "0 o 0 o" SUCCESS n n
401
402echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
403run_test "" 600 "+a" "0 n 0 o" EPERM
404
405echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
406run_test "" 600 "+a" "0 o 0 n" EPERM
407
408echo "***** Testing times=={ x, y } case *****"
409run_test "" 600 "+a" "1 1 1 1" EPERM
410
411echo "============================================================"
412
413echo
414echo "Testing immutable file, owned by self"
415echo
416
417echo "***** Testing times==NULL case *****"
418run_test -W "" 600 "+i" "" EACCES
419
420echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
421run_test -W "" 600 "+i" "0 n 0 n" EACCES
422
423echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
424run_test -W "" 600 "+i" "0 o 0 o" SUCCESS n n
425
426echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
427run_test -W "" 600 "+i" "0 n 0 o" EPERM
428
429echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
430run_test -W "" 600 "+i" "0 o 0 n" EPERM
431
432echo "***** Testing times=={ x, y } case *****"
433run_test -W "" 600 "+i" "1 1 1 1" EPERM
434
435echo "============================================================"
436
437# Immutable+append-only should have same results as immutable
438
439echo
440echo "Testing immutable append-only file, owned by self"
441echo
442
443echo "***** Testing times==NULL case *****"
444run_test -W "" 600 "+ai" "" EACCES
445
446echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
447run_test -W "" 600 "+ai" "0 n 0 n" EACCES
448
449echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
450run_test -W "" 600 "+ai" "0 o 0 o" SUCCESS n n
451
452echo "***** Testing times=={ UTIME_NOW, UTIME_OMIT } case *****"
453run_test -W "" 600 "+ai" "0 n 0 o" EPERM
454
455echo "***** Testing times=={ UTIME_OMIT, UTIME_NOW } case *****"
456run_test -W "" 600 "+ai" "0 o 0 n" EPERM
457
458echo "***** Testing times=={ x, y } case *****"
459run_test -W "" 600 "+ai" "1 1 1 1" EPERM
460
461echo "============================================================"
462
463echo
464
465# EINVAL should result, if pathname is NULL, dirfd is not
466# AT_FDCWD, and flags contains AT_SYMLINK_NOFOLLOW.
467
468echo "***** Testing pathname==NULL, dirfd!=AT_FDCWD, flags has" \
469     "AT_SYMLINK_NOFOLLOW *****"
470setup_file $FILE "" 600 ""
471CMD="$TEST_PROG -q -n -d $FILE NULL $4"
472echo "$CMD"
473$CMD > $RESULT_FILE
474check_result $? EINVAL
475echo
476
477echo "============================================================"
478
479echo
480
481# If UTIME_NOW / UTIME_OMIT in tv_nsec field, the tv_sec should
482# be ignored.
483
484echo "tv_sec should be ignored if tv_nsec is UTIME_OMIT or UTIME_NOW"
485
486echo "***** Testing times=={ UTIME_NOW, UTIME_NOW } case *****"
487run_test -RW "" 600 "" "1 n 1 n" SUCCESS y y
488
489echo "***** Testing times=={ UTIME_OMIT, UTIME_OMIT } case *****"
490run_test -RW "" 600 "" "1 o 1 o" SUCCESS n n
491
492echo "============================================================"
493
494echo
495
496rm -rf "$TEST_DIR"
497uname -a
498date
499echo "Total tests: $test_num; passed: $passed_cnt; failed: $failed_cnt"
500if test $failed_cnt -gt 0; then
501    echo "Failed tests: $failed_list"
502fi
503
504tst_exit
505