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