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