#! /bin/sh
# Automated libuser fs function regression tester
#
# Copyright (c) 2012 Red Hat, Inc. All rights reserved.
#
# This is free software; you can redistribute it and/or modify it under
# the terms of the GNU Library General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Author: Miloslav Trmač <mitr@redhat.com>

if ! fakeroot --version &>/dev/null; then
    echo 'fakeroot not available, skipping test' >&2
    exit 77
fi

run_test() {
    fakeroot "$@"
}
# If you want to test this as real root, use the following instead, and run the
# test manually.  Make sure you have backups!
#run_test() {
#    ( "$@"; )
#}

srcdir=$srcdir/tests

workdir=$(pwd)/test_fs
export workdir

umask 002

trap 'status=$?; chmod -R u+rwx "$workdir"; rm -rf "$workdir"; exit $status' 0
trap '(exit 1); exit 1' 1 2 13 15

rm -rf "$workdir"
mkdir "$workdir"

# Set up an the environment
mkdir "$workdir"/files
> "$workdir"/files/passwd
> "$workdir"/files/shadow
> "$workdir"/files/group
> "$workdir"/files/gshadow

# Set up the client
LIBUSER_CONF=$workdir/libuser.conf
export LIBUSER_CONF
sed "s|@WORKDIR@|$workdir|g; s|@TOP_BUILDDIR@|$(pwd)|g" \
    < "$srcdir"/fs.conf.in > "$LIBUSER_CONF"
# Ugly non-portable hacks
LD_LIBRARY_PATH=$(pwd)/lib/.libs
export LD_LIBRARY_PATH
PYTHONPATH=$(pwd)/python/.libs
export PYTHONPATH

# Like ls, but filtered to only contain interesting columns and avoid
# varying data.  Expects one argument, a directory path
filtered_ls() {
    (
	cd "$1";
	LC_ALL=C ls -lnR | \
	    awk 'NF > 3 { printf("%.10s %d %4d %4d %s\n", $1, $2, $3, $4, $9); }
             NF <= 3 && !/total/ { print }';
    )
}
export -f filtered_ls


# Test lu_homedir_remove()
test_lu_homedir_remove() {
    # User's "own" content
    mkdir -p "$workdir"/rm/root/{dir,unreadable}
    touch "$workdir"/rm/{kept,root/{dir,unreadable}/f}
    mkfifo "$workdir"/rm/root/fifo
    ln -s ../kept "$workdir"/rm/root/symlink
    chown -R 555:555 "$workdir"/rm/root
    chmod 701 "$workdir"/rm/root
    chmod 000 "$workdir"/rm/root/unreadable

    # Other content in the home directory
    mknod "$workdir"/rm/root/block b 1 1
    mknod "$workdir"/rm/root/char c 1 1
    echo 'foo' > "$workdir"/rm/secret
    chmod 000 "$workdir"/rm/secret
    ln "$workdir"/rm/{secret,root/hardlink}

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --remove "$workdir/rm/root"
    if [ $? -ne 0 ]; then
	exit 1
    fi

    filtered_ls "$workdir/rm"
}
export -f test_lu_homedir_remove
run_test test_lu_homedir_remove > "$workdir"/rm_output

diff "$workdir"/rm_output - <<EOF
.:
-rw-rw-r-- 1    0    0 kept
---------- 1    0    0 secret
EOF
if [ $? -ne 0 ]; then
    exit 1
fi


# Test lu_homedir_remove_for_user_if_owned()
test_lu_homedir_remove_for_user_if_owned1() {
    # User's "own" content
    mkdir -p "$workdir"/rm_owned1/root/dir
    touch "$workdir"/rm_owned1/root/dir/f
    chown -R 555:555 "$workdir"/rm_owned1/root
    chmod 701 "$workdir"/rm_owned1/root

    # Other content in the home directory
    mkdir "$workdir"/rm_owned1/root/non-owned-dir
    touch "$workdir"/rm_owned1/root/non-owned-dir/f

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --remove-if-owned \
	"$workdir/rm_owned1/root" 555
    if [ $? -ne 0 ]; then
	exit 1
    fi

    filtered_ls "$workdir"/rm_owned1
}
export -f test_lu_homedir_remove_for_user_if_owned1
run_test test_lu_homedir_remove_for_user_if_owned1 > "$workdir"/rm_owned_output1

diff "$workdir"/rm_owned_output1 - <<EOF
.:
EOF
if [ $? -ne 0 ]; then
    exit 1
fi

# Nothing is touched if the home directory is not owned by the right UID
test_lu_homedir_remove_for_user_if_owned2() {
    # Non-owned home directory
    mkdir -p "$workdir"/rm_owned2/root/dir
    touch "$workdir"/rm_owned2/root/dir/f
    chown -R 555:555 "$workdir"/rm_owned2/root
    chown 444:444 "$workdir"/rm_owned2/root
    chmod 701 "$workdir"/rm_owned2/root

    # Other content in the home directory
    mkdir "$workdir"/rm_owned2/root/non-owned-dir
    touch "$workdir"/rm_owned2/root/non-owned-dir/f

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --remove-if-owned \
	"$workdir/rm_owned2/root" 555
    echo $?

    filtered_ls "$workdir"/rm_owned2
}
export -f test_lu_homedir_remove_for_user_if_owned2
run_test test_lu_homedir_remove_for_user_if_owned2 \
	> "$workdir"/rm_owned_output2 2>&1

diff "$workdir"/rm_owned_output2 - <<EOF
\`$workdir/rm_owned2/root' is not owned by UID \`555'
1
.:
drwx-----x 4  444  444 root

./root:
drwxrwxr-x 2  555  555 dir
drwxrwxr-x 2    0    0 non-owned-dir

./root/dir:
-rw-rw-r-- 1  555  555 f

./root/non-owned-dir:
-rw-rw-r-- 1    0    0 f
EOF
if [ $? -ne 0 ]; then
    exit 1
fi


# Prepare an "interesting" directory, to be used both directly as a home
# directory and as a skeleton.  Note: changes the current directory!
create_source_directory() {
    mkdir -p "$1"/{dir,unreadable,group-owned}
    # User's "own" content
    for i in "$1" "$1"/dir "$1"/group-owned; do
	touch "$i"/f "$i"/setuid
	mkfifo "$i"/fifo
	ln -s ../outside "$i"/symlink
    done
    mkdir "$1"/setgid
    chmod g+s "$1"/setgid
    echo 'content' > "$1"/unreadable/f
    chmod 000 "$1"/unreadable/f "$1"/unreadable
    chown -R 555:555 "$1"
    chgrp -R 444 "$1"/group-owned
    for i in "$1" "$1"/dir "$1"/group-owned; do
	chmod u+xs "$i"/setuid
    done

    # Other content in the home directory
    mknod "$1"/block b 1 1
    mknod "$1"/char c 1 1
    echo 'foo' > "$1"/secret
    chmod 000 "$1"/secret
}
export -f create_source_directory


# Test lu_homedir_move()
test_lu_homedir_move1() {
    create_source_directory "$workdir"/home1

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --move "$workdir"/{home1,home2}
    if [ $? -ne 0 ]; then
	exit 1
    fi

    filtered_ls "$workdir"/home2
}
export -f test_lu_homedir_move1
run_test test_lu_homedir_move1 > "$workdir"/mv_output

# Special files and fifos are not copied over.  Ownership and permissions are
# preserved.
diff "$workdir"/mv_output - <<EOF
.:
drwxrwxr-x 2  555  555 dir
-rw-rw-r-- 1  555  555 f
drwxrwxr-x 2  555  444 group-owned
---------- 1    0    0 secret
drwxrwsr-x 2  555  555 setgid
-rwsrw-r-- 1  555  555 setuid
lrwxrwxrwx 1  555  555 symlink
d--------- 2  555  555 unreadable

./dir:
-rw-rw-r-- 1  555  555 f
-rwsrw-r-- 1  555  555 setuid
lrwxrwxrwx 1  555  555 symlink

./group-owned:
-rw-rw-r-- 1  555  444 f
-rwsrw-r-- 1  555  444 setuid
lrwxrwxrwx 1  555  444 symlink

./setgid:

./unreadable:
---------- 1  555  555 f
EOF
if [ $? -ne 0 ]; then
    exit 1
fi

# Moving onto an existing directory is prohibited
test_lu_homedir_move2() {
    mkdir "$workdir"/mv2home{1,2}

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --move "$workdir"/mv2home{1,2}
    echo $?
}
export -f test_lu_homedir_move2
run_test test_lu_homedir_move2 > "$workdir"/mv2_output 2>&1
diff "$workdir"/mv2_output - <<EOF
Error creating \`$workdir/mv2home2': File exists
1
EOF
if [ $? -ne 0 ]; then
    exit 1
fi


# Test lu_homedir_populate()
function test_lu_homedir_populate1() {
    create_source_directory "$workdir"/skel
    chown -R 0:0 "$workdir"/skel
    chgrp -R 444 "$workdir"/skel/group-owned
    # chown/chgrp have reset permissions, fix them up
    for i in "$workdir"/skel "$workdir"/skel/dir "$workdir"/skel/group-owned; do
	chmod u+xs "$i"/setuid
    done

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --populate "$workdir"/newhome 556 557
    if [ $? -ne 0 ]; then
	exit 1
    fi

    filtered_ls "$workdir"/newhome
}
export -f test_lu_homedir_populate1
run_test test_lu_homedir_populate1 > "$workdir"/pop_output

# Special files and fifos are not copied over.  Permissions are preserved,
# ownership is changed to the desired values except for non-root groups
diff "$workdir"/pop_output - <<EOF
.:
drwxrwxr-x 2  556  557 dir
-rw-rw-r-- 1  556  557 f
drwxrwxr-x 2  556  444 group-owned
---------- 1  556  557 secret
drwxrwsr-x 2  556  557 setgid
-rwsrw-r-- 1  556  557 setuid
lrwxrwxrwx 1  556  557 symlink
d--------- 2  556  557 unreadable

./dir:
-rw-rw-r-- 1  556  557 f
-rwsrw-r-- 1  556  557 setuid
lrwxrwxrwx 1  556  557 symlink

./group-owned:
-rw-rw-r-- 1  556  444 f
-rwsrw-r-- 1  556  444 setuid
lrwxrwxrwx 1  556  444 symlink

./setgid:

./unreadable:
---------- 1  556  557 f
EOF
if [ $? -ne 0 ]; then
    exit 1
fi

# Populating an existing directory is prohibited
function test_lu_homedir_populate2() {
    rm -rf "$workdir"/skel
    mkdir "$workdir"/{skel,pop2}

    $VALGRIND $PYTHON "$srcdir"/fs_test.py --populate "$workdir"/pop2 556 557
    echo $?
}
export -f test_lu_homedir_populate2
run_test test_lu_homedir_populate2 > "$workdir"/pop2_output 2>&1
diff "$workdir"/pop2_output - <<EOF
Error creating \`$workdir/pop2': File exists
1
EOF
if [ $? -ne 0 ]; then
    exit 1
fi
