#! /bin/sh
#
# sv-bug-16738.sh -- Test case for Savannah bug #16738.
# Copyright (C) 2006 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, 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 General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
#

# Test case for Savannah bug #16738.  The environment size is not
# subtracted from the command line length in find, though it is in
# xargs.

if [ -z "$FIND" ] ; then
    echo "Using system find utility."
    FIND=find
else 
    case "$FIND" in 
	/*) ;;
	*) FIND="$PWD"/"$FIND" ;;
    esac
    FIND=$(echo $FIND | sed -e 's_/\./_/_')
fi
echo Testing find binary "$FIND: $($FIND --version|sed -e 1q)"


# Signals to trap in order to do cleanup
badthings="0 INT TERM"

pass() { echo PASS; exit 0 ; }
fail() { echo FAIL; exit 1 ; }
miscarry() { printf "%s\n" "$1" >&2 ; exit 2 ; }

if ! ORIG_PWD=$(pwd)
then
    miscarry "Not sure what the current directory is."
fi


cleanup() {
    if [ -n "$1" ]
    then
	printf "Cleaning up %s..." "$1"
	cd "$ORIG_PWD"
	if rm -rf "$1"; then
	    echo done
	else
	    echo FAILED
	    trap "" $badthings
	    exit 2
	fi
     else
	echo "(no cleanup to do)"
     fi
}

# Let's not assume we have 'seq'.
myseq() {
    limit=${1:-1}
    yes "x" | nl | sed -e "$limit q" | tr -d x
}

if getconf POSIX2_VERSION >/dev/null
then
    if ARG_MAX=$(getconf ARG_MAX)
    then
	echo "Value of ARG_MAX is $ARG_MAX"
    else
    	miscarry "I cannot determine the value of ARG_MAX on your system.
That may mean there is no limit on your system."
    fi
else
    # No getconf on your system.
    miscarry "You appear not to have a working getconf binary on your system.
I need to use it to figure out the value of ARG_MAX on your system.
Hence I cannot continue with the test."
fi

yes() {
    while echo "$@"
    do
      true
    done
}

# First, make an environment variable > 2K long (2048 bytes is subtracted
# from the max. length of the command line, as per the functionality 
# of xargs).   
# 
# Initially we do not export this variable, so at first it wil not 
# affect things.
bigvar=$(yes "" | sed -e '8192 q' | tr '\n' 'x')

testdir=

# (Over-)estimate the number of files we need to create in order
# to generate a command line of at least ARG_MAX bytes.  We avoid
# putting too many files in any given directory because that leads
# to poor performance on some systems.
let "leaves=500"
# namelen is an underestimate the average length of the relative path 
# from the test directory to a leaf.  For example ./24/102
namelen=7
let "ndirs=1 + (ARG_MAX/namelen) / leaves"
let "filesneeded = ndirs * (leaves + 1)"

printf "We want to use %d directories, each containing %d files.\n" \
   $ndirs $leaves
printf "Therefore we need a total of %ld free inodes\n" $filesneeded

for dir in $SV_BUG_16738_TEST_DIR /var/tmp /tmp
do
  ifree=$( df -P -i $dir | sed -n '$ p' | awk '{print ($4);}')

  printf "%20s: %ld free inodes\n" "$dir" $ifree
  if [ $ifree -gt $filesneeded ]
  then
      echo "There are enough free inodes on $dir"
      testdir="$dir"
      break
  fi
done

if [ -z "$testdir" ]
then
    miscarry "I could not find a filesystem with enough free inodes."
fi

# We're gong to be creating a lot of files and then deleting them.
# If anybody else has access to the system, we'd prefer it if they 
# weren't able to do nasty things to us, so we will ensure that they can't
# read or search our temporary directory etc.
umask 077


# Make a temporary directory to do the test in.
# We don't do it directly in $testdir since there
# will be a terrible mess to clear up if there is a 
# problem.
if td=$(mktemp -d -p "$testdir" )
then
    testdir="$td"
    echo "The test will be performed in $testdir"
    trap "cleanup $testdir" $badthings
else
    # We knew there were enough free inodes (a shile ago) in $testdir
    # but we didn't check how many free blocks there were.
    miscarry "You don't have a mktemp command, or it doesn't support -d -p, or there is no space left in $testdir"
fi

# OK, now generate the files.   
cd "$testdir" || miscarry "Could not change directory to $testdir"

printf "Populating %d directories with %d files each: " $ndirs $leaves
for dirno in $(myseq $ndirs) 
do
   printf "%s" "."
   if mkdir "$testdir"/"$dirno"
   then
       cd "$testdir/$dirno"
       myseq $leaves | xargs touch
    fi
done
echo " done."


cd "$testdir" 

# Check that we created enough files.
totlen=$(find . -print | tr -dc '[0-9]/.' | wc -c)
echo "$totlen bytes of filenames; needed $ARG_MAX"
if [ $totlen -lt ARG_MAX ]
then
    miscarry "We did not create enough files.  Please edit the script and adjust the value of namelen by hand"
fi


# find . -name 702
# First run find with an empty environment.
testcmd="${FIND} . -exec echo {} +"
echo Test command is $testcmd
echo
# set -x
echo "Running test with empty environment: "
if env -i $testcmd >/dev/null
then
    echo "Running test with normal environment: "
    if $testcmd >/dev/null
    then
	echo "Running test with inflated environment: "
	export bigvar
	if $testcmd >/dev/null
	then
	    pass
	else
	    fail
	fi
    else
	miscarry "Your existing environment variables are probably large enough to trigger this bug"
    fi
else 
    miscarry "I cannot run find on that filesystem even with an empty environment."
fi

