_cleanup()
{
cd /
- umount $SCRATCH_MNT/test2 > /dev/null 2>&1
- umount $SCRATCH_MNT/test > /dev/null 2>&1
+ umount -d $SCRATCH_MNT/test2 > /dev/null 2>&1
+ umount -d $SCRATCH_MNT/test > /dev/null 2>&1
rm -f $tmp.*
if [ -w $seq.full ]
|| _fail "!!! clean failed"
_log "umount ext2 on xfs"
-umount $SCRATCH_MNT/test2 >> $seq.full 2>&1 \
+umount -d $SCRATCH_MNT/test2 >> $seq.full 2>&1 \
|| _fail "!!! umount ext2 failed"
_log "umount xfs"
-umount $SCRATCH_MNT/test >> $seq.full 2>&1 \
+umount -d $SCRATCH_MNT/test >> $seq.full 2>&1 \
|| _fail "!!! umount xfs failed"
echo "--- mounts at end (before cleanup)" >> $seq.full
echo unmounting and removing new image
umount $source_dir
- umount -d $target_dir
+ umount -d $target_dir > /dev/null 2>&1
rm -f $target
}
{
cd /
rm -f $tmp.*
- umount $LOOP_MNT 2>/dev/null
+ umount -d $LOOP_MNT 2>/dev/null
rmdir $LOOP_MNT
_cleanup_testdir
}
$XFS_GROWFS_PROG $LOOP_MNT 2>&1 | _filter_growfs 2>&1
echo "*** unmount"
- umount $LOOP_MNT
+ umount -d $LOOP_MNT > /dev/null 2>&1
# Large grows takes forever to check..
if [ "$check" -gt "0" ]
let I=$I+1
[ $[$I % 1000] -eq 0 ] && echo "Created $I/$E"
done
+wait
sync
-d name=$LOOP_DEV,size=${i}g |grep log
mount -o loop -t xfs $LOOP_DEV $LOOP_MNT
echo "test write" > $LOOP_MNT/test
- umount $LOOP_MNT
+ umount -d $LOOP_MNT > /dev/null 2>&1
done
}
# make large holey file
-d name=$LOOP_DEV,size=${i}g |grep log
mount -o loop -t xfs $LOOP_DEV $LOOP_MNT
echo "test write" > $LOOP_MNT/test
- umount $LOOP_MNT
+ umount -d $LOOP_MNT > /dev/null 2>&1
done
}
# make large holey file
_supported_os Linux
_require_scratch
+rm -f $seq.full
+
[ "$XFS_FSR_PROG" = "" ] && _notrun "xfs_fsr not found"
# create freespace holes of 1-3 blocks in length
# (say 5 extents) and lots of variations around that dependent on the
# number of attributes in the files being defragmented.
#
+# We have to make sure there are enough free inodes for the test to
+# pass without needing to allocate new clusters during the test.
+# With such fragemented free space, that will fail.
+#
fragment_freespace()
{
_file="$SCRATCH_MNT/not_free"
+ _dir="$SCRATCH_MNT/saved"
- for i in `seq 0 1 10000`; do
- echo foo > $_file.$i
+ # allocate inode space
+ mkdir -p $_dir
+ for i in `seq 0 1 1000`; do
+ touch $_file.$i
done
- sync
-
- for i in `seq 0 2 10000`; do
- rm -f $_file.$i
+ for i in `seq 0 63 1000`; do
+ mv $_file.$i $_dir
done
- for i in `seq 0 7 10000`; do
+ for i in `seq 0 1 1000`; do
rm -f $_file.$i
done
+
+ $XFS_IO_PROG -fs -c "resvsp 0 40000k" $_file > /dev/null 2>&1
+
+ for i in `seq 0 8 40000`; do
+ $XFS_IO_PROG -f -c "unresvsp ${i}k 4k" $_file \
+ > /dev/null 2>&1
+ done
+ for i in `seq 0 28 40000`; do
+ $XFS_IO_PROG -f -c "unresvsp ${i}k 4k" $_file \
+ > /dev/null 2>&1
+ done
sync
# and now use up all the remaining extents larger than 3 blocks
- dd if=/dev/zero of=$_file.large bs=4k count=1024 > /dev/null 2>&1
- sync
+ $XFS_IO_PROG -fs -c "resvsp 0 4m" $_file.large > /dev/null 2>&1
}
create_attrs()
create_data()
{
+ size=`expr \( $1 + 1 \) \* 4096`
+ $XFS_IO_PROG -f -c "truncate $size" $2 > /dev/null 2>&1
for foo in `seq $1 -1 0`; do
let offset=$foo*4096
- $XFS_IO_PROG -f -c "pwrite $offset 4096" -c "fsync" $2 > /dev/null 2>&1
+ $XFS_IO_PROG -f -c "resvsp $offset 4096" $2 > /dev/null 2>&1
done
- xfs_bmap -vp $2
}
# create the designated file with a certain number of attributes and a certain
target=$3
rm -f $target
- echo > $target
+ touch $target
create_attrs $nattrs $target
create_data $file_blocks $target
}
target=$3
rm -f $target
- echo > $target
+ touch $target
create_data $file_blocks $target
create_attrs $nattrs $target
}
for j in `seq 5 1 20`; do
create_target_attr_first $i $j $targ.$i.$j >> $seq.full 2>&1
done
+ xfs_bmap -vp $targ.$i.* >> $seq.full 2>&1
FSRXFSTEST=true xfs_fsr -d -v -C $n $targ.$i.* >> $seq.full 2>&1
xfs_bmap -vp $targ.$i.* >> $seq.full 2>&1
for j in `seq 5 1 20`; do
create_target_attr_last $i $j $targ.$i.$j >> $seq.full 2>&1
done
+ xfs_bmap -vp $targ.$i.* >> $seq.full 2>&1
FSRXFSTEST=true xfs_fsr -d -v -C $n $targ.$i.* >> $seq.full 2>&1
xfs_bmap -vp $targ.$i.* >> $seq.full 2>&1
done
_cleanup()
{
cd /
- umount $LOOP_MNT 2>/dev/null
+ umount -d $LOOP_MNT 2>/dev/null
rm -f $LOOP_DEV
rmdir $LOOP_MNT
_cleanup_testdir
xfs_io -f -c "resvsp 0 $fsize" $LOOP_MNT/foo | _filter_io
echo "*** unmount loop filesystem"
- umount $LOOP_MNT
+ umount -d $LOOP_MNT > /dev/null 2>&1
echo "*** check loop filesystem"
_check_xfs_filesystem $LOOP_DEV none none
#! /bin/bash
# FS QA Test No. 285
#
-# Test to verify project quota xfs_admin, xfsdump/xfsrestore and
-# xfs_db functionality
+# SEEK_DATA/SEEK_HOLE sanity tests.
+#
+# Improved by Jeff.liu@oracle.com
+# Creater: josef@redhat.com
#
#-----------------------------------------------------------------------
-# Copyright (c) 2012 Red Hat, Inc. All Rights Reserved.
+# Copyright (c) 2011 Oracle Inc. All Rights Reserved.
+# Copyright (c) 2011 Red Hat. All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
#-----------------------------------------------------------------------
#
# creator
-owner=ranto.boris@gmail.com
+owner=jeff.liu@oracle.com
seq=`basename $0`
echo "QA output created by $seq"
-tmp=/tmp/$$
+
here=`pwd`
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
-rm -f $seq.full
# get standard environment, filters and checks
. ./common.rc
-. ./common.quota
-
-_cleanup()
-{
- cd /
- umount $SCRATCH_MNT 2>/dev/null
- rm -rf $tmp.*
-}
-
-_print_projid()
-{
- $XFS_DB_PROG -c "inode $1" \
- -c "print core.projid_lo" \
- -c "print core.projid_hi" \
- $SCRATCH_DEV
-}
-
-# real QA test starts here
-_supported_fs xfs
-_require_xfs_quota
-_require_scratch
-_require_projid32bit
-
-# create xfs fs without projid32bit ability, will be gained by xfs_admin
-_scratch_mkfs_xfs -i projid32bit=0 -d size=200m >> $seq.full \
- || _fail "mkfs failed"
-_qmount_option "pquota"
-_qmount
-# require project quotas
-_require_prjquota $SCRATCH_DEV
-
-dir=$SCRATCH_MNT/pquota
-
-status=1
+. ./common.filter
-mkdir -p $dir
-touch $dir/{16,32}bit
-inode16a=$(ls -i $dir/16bit | cut -d ' ' -f 1)
-inode32a=$(ls -i $dir/32bit | cut -d ' ' -f 1)
-$XFS_QUOTA_PROG -x -c "project -s -p $dir/16bit 1234" $SCRATCH_DEV \
- >> $seq.full
-$XFS_QUOTA_PROG -x -c "project -s -p $dir/32bit 2123456789" $SCRATCH_DEV \
- >> $seq.full 2>&1
+_supported_fs generic
+_supported_os Linux
-echo "No 32bit project quotas:"
-$XFS_IO_PROG -r -c "lsproj" $dir/16bit
-$XFS_IO_PROG -r -c "lsproj" $dir/32bit
+BASE_TEST_FILE=$TEST_DIR/seek_sanity_testfile
-umount $SCRATCH_MNT
+[ -x $here/src/seek_sanity_test ] || _notrun "seek_sanitfy_tester not built"
-# Now, enable projid32bit support by xfs_admin
-xfs_admin -p $SCRATCH_DEV >> $seq.full 2>&1 || _fail "xfs_admin failed"
-
-# Now mount the fs, 32bit project quotas shall be supported, now
-_qmount_option "pquota"
-_qmount
-$XFS_QUOTA_PROG -x -c "project -s -p $dir/32bit 2123456789" $SCRATCH_DEV \
- >> $seq.full
-
-# These will be checked by $seq.out
-echo "With 32bit project quota support:"
-$XFS_IO_PROG -r -c "lsproj" $dir/16bit
-$XFS_IO_PROG -r -c "lsproj" $dir/32bit
-
-# Dump the fs to a temporary file
-rm -f $tmp.dump.img
-$XFSDUMP_PROG -f $tmp.dump -L label -M media -l 0 $SCRATCH_MNT >> $seq.full \
- || _fail "dump failed"
-
-# Prepare the device to restore the dumped file system
-restore_dir=$SCRATCH_MNT/restore/pquota
-
-# Just make the restore dir, the pquota dir will be created by xfsrestore
-mkdir -p $SCRATCH_MNT/restore
-
-# Restore
-$XFSRESTORE_PROG -f $tmp.dump $SCRATCH_MNT/restore >> $seq.full 2>&1 \
- || _fail "xfsrestore failed"
-
-# Check that they are the same
-diff -urpN $SCRATCH_MNT/{,restore}/pquota || _fail "diff failed"
-
-touch $restore_dir/32bitv2
-inode16b=$(ls -i $restore_dir/16bit | cut -d ' ' -f 1)
-inode32b=$(ls -i $restore_dir/32bit | cut -d ' ' -f 1)
-inode32v2=$(ls -i $restore_dir/32bitv2 | cut -d ' ' -f 1)
-$XFS_QUOTA_PROG -x -c "project -s -p $restore_dir/32bitv2 2123456789" \
- $SCRATCH_MNT >> $seq.full
-echo "The restored file system + one additional file:"
-$XFS_IO_PROG -r -c "lsproj" $restore_dir/16bit
-$XFS_IO_PROG -r -c "lsproj" $restore_dir/32bit
-$XFS_IO_PROG -r -c "lsproj" $restore_dir/32bitv2
-
-umount $SCRATCH_MNT
-
-# Now, we can examine the file systems with xfs_db
-echo "These two values of 16bit project quota ids shall be the same"
-_print_projid $inode16a
-_print_projid $inode16b
+_cleanup()
+{
+ eval "rm -f $BASE_TEST_FILE.*"
+}
-echo "These three values of 32bit project quota ids shall be the same"
-_print_projid $inode32b
-_print_projid $inode32a
-_print_projid $inode32v2
+$here/src/seek_sanity_test $BASE_TEST_FILE > $seq.full 2>&1 ||
+ _fail "seek sanity check failed!"
+# success, all done
status=0
exit
--- /dev/null
+File system supports the default behavior.
+File system magic#: 0x58465342
+Allocation size: 4096
+
+01. Test empty file
+01.01 SEEK_DATA expected -1 with errno -6, got -6. succ
+01.02 SEEK_HOLE expected -1 with errno -6, got -6. succ
+01.03 SEEK_HOLE expected -1 with errno -6, got -6. succ
+
+02. Test a tiny full file
+02.01 SEEK_HOLE expected 8 or 8, got 8. succ
+02.02 SEEK_DATA expected 0 or 0, got 0. succ
+02.03 SEEK_DATA expected 1 or 1, got 1. succ
+02.04 SEEK_HOLE expected 8 or 8, got 8. succ
+02.05 SEEK_DATA expected 7 or 7, got 7. succ
+02.06 SEEK_HOLE expected -1 with errno -6, got -6. succ
+02.07 SEEK_DATA expected -1 with errno -6, got -6. succ
+02.08 SEEK_HOLE expected -1 with errno -6, got -6. succ
+02.09 SEEK_DATA expected -1 with errno -6, got -6. succ
+
+03. Test a larger full file
+03.01 SEEK_HOLE expected 8292 or 8292, got 8292. succ
+03.02 SEEK_HOLE expected 8292 or 8292, got 8292. succ
+03.03 SEEK_DATA expected 0 or 0, got 0. succ
+03.04 SEEK_DATA expected 1 or 1, got 1. succ
+03.05 SEEK_HOLE expected 8292 or 8292, got 8292. succ
+03.06 SEEK_DATA expected 8291 or 8291, got 8291. succ
+03.07 SEEK_HOLE expected -1 with errno -6, got -6. succ
+03.08 SEEK_DATA expected -1 with errno -6, got -6. succ
+03.09 SEEK_HOLE expected -1 with errno -6, got -6. succ
+03.10 SEEK_DATA expected -1 with errno -6, got -6. succ
+
+04. Test file hole at beg, data at end
+04.01 SEEK_HOLE expected 0 or 8200, got 0. succ
+04.02 SEEK_HOLE expected 1 or 8200, got 1. succ
+04.03 SEEK_DATA expected 8192 or 0, got 8192. succ
+04.04 SEEK_DATA expected 8192 or 1, got 8192. succ
+04.05 SEEK_HOLE expected 8191 or 8200, got 8191. succ
+04.06 SEEK_DATA expected 8192 or 8191, got 8192. succ
+04.07 SEEK_HOLE expected 8200 or 8200, got 8200. succ
+04.08 SEEK_DATA expected 8192 or 8192, got 8192. succ
+04.09 SEEK_HOLE expected 8200 or 8200, got 8200. succ
+04.10 SEEK_DATA expected 8193 or 8193, got 8193. succ
+04.11 SEEK_HOLE expected 8200 or 8200, got 8200. succ
+04.12 SEEK_DATA expected 8199 or 8199, got 8199. succ
+04.13 SEEK_HOLE expected -1 with errno -6, got -6. succ
+04.14 SEEK_DATA expected -1 with errno -6, got -6. succ
+04.15 SEEK_HOLE expected -1 with errno -6, got -6. succ
+04.16 SEEK_DATA expected -1 with errno -6, got -6. succ
+
+05. Test file data at beg, hole at end
+05.01 SEEK_HOLE expected 4096 or 16384, got 4096. succ
+05.02 SEEK_HOLE expected 4096 or 16384, got 4096. succ
+05.03 SEEK_DATA expected 0 or 0, got 0. succ
+05.04 SEEK_DATA expected 1 or 1, got 1. succ
+05.05 SEEK_HOLE expected 4096 or 16384, got 4096. succ
+05.06 SEEK_DATA expected 4095 or 4095, got 4095. succ
+05.07 SEEK_HOLE expected 4096 or 16384, got 4096. succ
+05.08 SEEK_DATA expected -1 with errno -6, got -6. succ
+05.09 SEEK_HOLE expected 4097 or 16384, got 4097. succ
+05.10 SEEK_DATA expected -1 with errno -6, got -6. succ
+05.11 SEEK_HOLE expected 16383 or 16384, got 16383. succ
+05.12 SEEK_DATA expected -1 with errno -6, got -6. succ
+05.13 SEEK_HOLE expected -1 with errno -6, got -6. succ
+05.14 SEEK_DATA expected -1 with errno -6, got -6. succ
+05.15 SEEK_HOLE expected -1 with errno -6, got -6. succ
+05.16 SEEK_DATA expected -1 with errno -6, got -6. succ
+
+06. Test file hole data hole data
+06.01 SEEK_HOLE expected 0 or 16384, got 0. succ
+06.02 SEEK_HOLE expected 1 or 16384, got 1. succ
+06.03 SEEK_DATA expected 4096 or 0, got 4096. succ
+06.04 SEEK_DATA expected 4096 or 1, got 4096. succ
+06.05 SEEK_HOLE expected 4095 or 16384, got 4095. succ
+06.06 SEEK_DATA expected 4096 or 4095, got 4096. succ
+06.07 SEEK_HOLE expected 8192 or 16384, got 16384. succ
+06.08 SEEK_DATA expected 4096 or 4096, got 4096. succ
+06.09 SEEK_HOLE expected 8192 or 16384, got 16384. succ
+06.10 SEEK_DATA expected 4097 or 4097, got 4097. succ
+06.11 SEEK_HOLE expected 8192 or 16384, got 16384. succ
+06.12 SEEK_DATA expected 8191 or 8191, got 8191. succ
+06.13 SEEK_HOLE expected 8192 or 16384, got 16384. succ
+06.14 SEEK_DATA expected 12288 or 8192, got 8192. succ
+06.15 SEEK_HOLE expected 8193 or 16384, got 16384. succ
+06.16 SEEK_DATA expected 12288 or 8193, got 8193. succ
+06.17 SEEK_HOLE expected 12287 or 16384, got 16384. succ
+06.18 SEEK_DATA expected 12288 or 12287, got 12287. succ
+06.19 SEEK_HOLE expected 16384 or 16384, got 16384. succ
+06.20 SEEK_DATA expected 12288 or 12288, got 12288. succ
+06.21 SEEK_HOLE expected 16384 or 16384, got 16384. succ
+06.22 SEEK_DATA expected 12289 or 12289, got 12289. succ
+06.23 SEEK_HOLE expected 16384 or 16384, got 16384. succ
+06.24 SEEK_DATA expected 16383 or 16383, got 16383. succ
+06.25 SEEK_HOLE expected -1 with errno -6, got -6. succ
+06.26 SEEK_DATA expected -1 with errno -6, got -6. succ
+06.27 SEEK_HOLE expected -1 with errno -6, got -6. succ
+06.28 SEEK_DATA expected -1 with errno -6, got -6. succ
+
+07. Test file with unwritten extents, only have dirty pages
+07.01 SEEK_HOLE expected 0 or 4194304, got 4194304. succ
+07.02 SEEK_HOLE expected 1 or 4194304, got 4194304. succ
+07.03 SEEK_DATA expected 40960 or 0, got 0. succ
+07.04 SEEK_DATA expected 40960 or 1, got 1. succ
+
+08. Test file with unwritten extents, only have unwritten pages
+08.01 SEEK_HOLE expected 0 or 4194304, got 4194304. succ
+08.02 SEEK_HOLE expected 1 or 4194304, got 4194304. succ
+08.03 SEEK_DATA expected 40960 or 0, got 0. succ
+08.04 SEEK_DATA expected 40960 or 1, got 1. succ
+
+09. Test file with unwritten extents, have both dirty && unwritten pages
+09.01 SEEK_HOLE expected 0 or 8388608, got 8388608. succ
+09.02 SEEK_HOLE expected 1 or 8388608, got 8388608. succ
+09.03 SEEK_DATA expected 40960 or 0, got 0. succ
+09.04 SEEK_DATA expected 40960 or 1, got 1. succ
+
QA output created by 285
-No 32bit project quotas:
-projid = 1234
-projid = 0
-With 32bit project quota support:
-projid = 1234
-projid = 2123456789
-The restored file system + one additional file:
-projid = 1234
-projid = 2123456789
-projid = 2123456789
-These two values of 16bit project quota ids shall be the same
-core.projid_lo = 1234
-core.projid_hi = 0
-core.projid_lo = 1234
-core.projid_hi = 0
-These three values of 32bit project quota ids shall be the same
-core.projid_lo = 24853
-core.projid_hi = 32401
-core.projid_lo = 24853
-core.projid_hi = 32401
-core.projid_lo = 24853
-core.projid_hi = 32401
--- /dev/null
+#! /bin/bash
+# FS QA Test No. 286
+#
+# SEEK_DATA/SEEK_HOLE copy tests.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2011 Oracle Inc. All Rights Reserved.
+#
+# 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.
+#
+# This program is distributed in the hope that it would 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 the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#-----------------------------------------------------------------------
+#
+# creator
+owner=jeff.liu@oracle.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+
+src=$TEST_DIR/seek_copy_testfile
+dest=$TEST_DIR/seek_copy_testfile.dest
+
+[ -x $here/src/seek_copy_test ] || _notrun "seek_copy_test not built"
+
+_cleanup()
+{
+ rm -f $src $dest
+}
+
+# seek_copy_test_01: tests file with holes and written data extents.
+# verify results:
+# 1. file size is identical.
+# 2. perform cmp(1) to compare SRC and DEST file byte by byte.
+test01()
+{
+ rm -f $src $dest
+
+ write_cmd="-c \"truncate 100m\""
+ for i in $(seq 0 5 100); do
+ offset=$(($i * $((1 << 20))))
+ write_cmd="$write_cmd -c \"pwrite $offset 1m\""
+ done
+
+ echo "*** test01() create sparse file ***" >>$seq.full
+ eval ${XFS_IO_PROG} -F -f "${write_cmd}" $src >>$seq.full 2>&1 ||
+ _fail "create sparse file failed!"
+ echo "*** test01() create sparse file done ***" >>$seq.full
+ echo >>$seq.full
+
+ $here/src/seek_copy_test $src $dest
+
+ test $(stat --printf "%s" $src) = $(stat --printf "%s" $dest) ||
+ _fail "TEST01: file size check failed"
+
+ cmp $src $dest || _fail "TEST01: file bytes check failed"
+}
+
+# seek_copy_test_02 - tests file with holes, written and unwritten extents.
+# verify results:
+# 1. file size is identical.
+# 2. perform cmp(1) to compare SRC and DEST file byte by byte.
+test02()
+{
+ rm -rf $src $dest
+
+ write_cmd="-c \"truncate 200m\""
+ for i in $(seq 0 10 100); do
+ offset=$(($((6 << 20)) + $i * $((1 << 20))))
+ write_cmd="$write_cmd -c \"falloc $offset 3m\" -c \"pwrite $offset 1m\""
+ done
+
+ echo "*** test02() create sparse file ***" >>$seq.full
+ eval ${XFS_IO_PROG} -F -f "${write_cmd}" $src >>$seq.full 2>&1 ||
+ _fail "create sparse file failed!"
+ echo "*** test02() create sparse file done ***" >>$seq.full
+ echo >>$seq.full
+
+ $here/src/seek_copy_test $src $dest
+
+ test $(stat --printf "%s" $src) = $(stat --printf "%s" $dest) ||
+ _fail "TEST02: file size check failed"
+
+ cmp $src $dest || _fail "TEST02: file bytes check failed"
+}
+
+rm -f $seq.full
+
+test01
+test02
+
+status=0
+exit
--- /dev/null
+*** test01() create sparse file ***
+wrote 1048576/1048576 bytes at offset 0
+1 MiB, 256 ops; 0.0000 sec (1.242 GiB/sec and 325699.7455 ops/sec)
+wrote 1048576/1048576 bytes at offset 5242880
+1 MiB, 256 ops; 0.0000 sec (1.160 GiB/sec and 304038.0048 ops/sec)
+wrote 1048576/1048576 bytes at offset 10485760
+1 MiB, 256 ops; 0.0000 sec (1.112 GiB/sec and 291571.7540 ops/sec)
+wrote 1048576/1048576 bytes at offset 15728640
+1 MiB, 256 ops; 0.0000 sec (1.095 GiB/sec and 286995.5157 ops/sec)
+wrote 1048576/1048576 bytes at offset 20971520
+1 MiB, 256 ops; 0.0000 sec (1.094 GiB/sec and 286674.1321 ops/sec)
+wrote 1048576/1048576 bytes at offset 26214400
+1 MiB, 256 ops; 0.0000 sec (1.085 GiB/sec and 284444.4444 ops/sec)
+wrote 1048576/1048576 bytes at offset 31457280
+1 MiB, 256 ops; 0.0000 sec (1.065 GiB/sec and 279171.2105 ops/sec)
+wrote 1048576/1048576 bytes at offset 36700160
+1 MiB, 256 ops; 0.0000 sec (1.064 GiB/sec and 278867.1024 ops/sec)
+wrote 1048576/1048576 bytes at offset 41943040
+1 MiB, 256 ops; 0.0000 sec (1.068 GiB/sec and 280087.5274 ops/sec)
+wrote 1048576/1048576 bytes at offset 47185920
+1 MiB, 256 ops; 0.0000 sec (1.058 GiB/sec and 277356.4464 ops/sec)
+wrote 1048576/1048576 bytes at offset 52428800
+1 MiB, 256 ops; 0.0000 sec (1.067 GiB/sec and 279781.4208 ops/sec)
+wrote 1048576/1048576 bytes at offset 57671680
+1 MiB, 256 ops; 0.0000 sec (1.071 GiB/sec and 280701.7544 ops/sec)
+wrote 1048576/1048576 bytes at offset 62914560
+1 MiB, 256 ops; 0.0000 sec (1.071 GiB/sec and 280701.7544 ops/sec)
+wrote 1048576/1048576 bytes at offset 68157440
+1 MiB, 256 ops; 0.0000 sec (1.074 GiB/sec and 281628.1628 ops/sec)
+wrote 1048576/1048576 bytes at offset 73400320
+1 MiB, 256 ops; 0.0000 sec (1.071 GiB/sec and 280701.7544 ops/sec)
+wrote 1048576/1048576 bytes at offset 78643200
+1 MiB, 256 ops; 0.0000 sec (1.068 GiB/sec and 280087.5274 ops/sec)
+wrote 1048576/1048576 bytes at offset 83886080
+1 MiB, 256 ops; 0.0000 sec (1.073 GiB/sec and 281318.6813 ops/sec)
+wrote 1048576/1048576 bytes at offset 89128960
+1 MiB, 256 ops; 0.0000 sec (1.068 GiB/sec and 280087.5274 ops/sec)
+wrote 1048576/1048576 bytes at offset 94371840
+1 MiB, 256 ops; 0.0000 sec (1.085 GiB/sec and 284444.4444 ops/sec)
+wrote 1048576/1048576 bytes at offset 99614720
+1 MiB, 256 ops; 0.0000 sec (1.055 GiB/sec and 276457.8834 ops/sec)
+wrote 1048576/1048576 bytes at offset 104857600
+1 MiB, 256 ops; 0.0000 sec (1.140 GiB/sec and 298716.4527 ops/sec)
+*** test01() create sparse file done ***
+
+*** test02() create sparse file ***
+wrote 1048576/1048576 bytes at offset 6291456
+1 MiB, 256 ops; 0.0000 sec (1.360 GiB/sec and 356545.9610 ops/sec)
+wrote 1048576/1048576 bytes at offset 16777216
+1 MiB, 256 ops; 0.0000 sec (1.385 GiB/sec and 363120.5674 ops/sec)
+wrote 1048576/1048576 bytes at offset 27262976
+1 MiB, 256 ops; 0.0000 sec (1.340 GiB/sec and 351165.9808 ops/sec)
+wrote 1048576/1048576 bytes at offset 37748736
+1 MiB, 256 ops; 0.0000 sec (1.309 GiB/sec and 343163.5389 ops/sec)
+wrote 1048576/1048576 bytes at offset 48234496
+1 MiB, 256 ops; 0.0000 sec (1.267 GiB/sec and 332036.3165 ops/sec)
+wrote 1048576/1048576 bytes at offset 58720256
+1 MiB, 256 ops; 0.0000 sec (1.285 GiB/sec and 336842.1053 ops/sec)
+wrote 1048576/1048576 bytes at offset 69206016
+1 MiB, 256 ops; 0.0000 sec (1.288 GiB/sec and 337730.8707 ops/sec)
+wrote 1048576/1048576 bytes at offset 79691776
+1 MiB, 256 ops; 0.0000 sec (1.278 GiB/sec and 335078.5340 ops/sec)
+wrote 1048576/1048576 bytes at offset 90177536
+1 MiB, 256 ops; 0.0000 sec (1.293 GiB/sec and 339072.8477 ops/sec)
+wrote 1048576/1048576 bytes at offset 100663296
+1 MiB, 256 ops; 0.0000 sec (1.280 GiB/sec and 335517.6933 ops/sec)
+wrote 1048576/1048576 bytes at offset 111149056
+1 MiB, 256 ops; 0.0000 sec (1.285 GiB/sec and 336842.1053 ops/sec)
+*** test02() create sparse file done ***
+
--- /dev/null
+QA output created by 286
--- /dev/null
+#! /bin/bash
+# FS QA Test No. 285
+#
+# Test to verify project quota xfs_admin, xfsdump/xfsrestore and
+# xfs_db functionality
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2012 Red Hat, Inc. All Rights Reserved.
+#
+# 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.
+#
+# This program is distributed in the hope that it would 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 the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#-----------------------------------------------------------------------
+#
+# creator
+owner=ranto.boris@gmail.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+tmp=/tmp/$$
+here=`pwd`
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+rm -f $seq.full
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.quota
+
+_cleanup()
+{
+ cd /
+ umount $SCRATCH_MNT 2>/dev/null
+ rm -rf $tmp.*
+}
+
+_print_projid()
+{
+ $XFS_DB_PROG -c "inode $1" \
+ -c "print core.projid_lo" \
+ -c "print core.projid_hi" \
+ $SCRATCH_DEV
+}
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_quota
+_require_scratch
+_require_projid32bit
+
+# create xfs fs without projid32bit ability, will be gained by xfs_admin
+_scratch_mkfs_xfs -i projid32bit=0 -d size=200m >> $seq.full \
+ || _fail "mkfs failed"
+_qmount_option "pquota"
+_qmount
+# require project quotas
+_require_prjquota $SCRATCH_DEV
+
+dir=$SCRATCH_MNT/pquota
+
+status=1
+
+mkdir -p $dir
+touch $dir/{16,32}bit
+inode16a=$(ls -i $dir/16bit | cut -d ' ' -f 1)
+inode32a=$(ls -i $dir/32bit | cut -d ' ' -f 1)
+$XFS_QUOTA_PROG -x -c "project -s -p $dir/16bit 1234" $SCRATCH_DEV \
+ >> $seq.full
+$XFS_QUOTA_PROG -x -c "project -s -p $dir/32bit 2123456789" $SCRATCH_DEV \
+ >> $seq.full 2>&1
+
+echo "No 32bit project quotas:"
+$XFS_IO_PROG -r -c "lsproj" $dir/16bit
+$XFS_IO_PROG -r -c "lsproj" $dir/32bit
+
+umount $SCRATCH_MNT
+
+# Now, enable projid32bit support by xfs_admin
+xfs_admin -p $SCRATCH_DEV >> $seq.full 2>&1 || _fail "xfs_admin failed"
+
+# Now mount the fs, 32bit project quotas shall be supported, now
+_qmount_option "pquota"
+_qmount
+$XFS_QUOTA_PROG -x -c "project -s -p $dir/32bit 2123456789" $SCRATCH_DEV \
+ >> $seq.full
+
+# These will be checked by $seq.out
+echo "With 32bit project quota support:"
+$XFS_IO_PROG -r -c "lsproj" $dir/16bit
+$XFS_IO_PROG -r -c "lsproj" $dir/32bit
+
+# Dump the fs to a temporary file
+rm -f $tmp.dump.img
+$XFSDUMP_PROG -f $tmp.dump -L label -M media -l 0 $SCRATCH_MNT >> $seq.full \
+ || _fail "dump failed"
+
+# Prepare the device to restore the dumped file system
+restore_dir=$SCRATCH_MNT/restore/pquota
+
+# Just make the restore dir, the pquota dir will be created by xfsrestore
+mkdir -p $SCRATCH_MNT/restore
+
+# Restore
+$XFSRESTORE_PROG -f $tmp.dump $SCRATCH_MNT/restore >> $seq.full 2>&1 \
+ || _fail "xfsrestore failed"
+
+# Check that they are the same
+diff -urpN $SCRATCH_MNT/{,restore}/pquota || _fail "diff failed"
+
+touch $restore_dir/32bitv2
+inode16b=$(ls -i $restore_dir/16bit | cut -d ' ' -f 1)
+inode32b=$(ls -i $restore_dir/32bit | cut -d ' ' -f 1)
+inode32v2=$(ls -i $restore_dir/32bitv2 | cut -d ' ' -f 1)
+$XFS_QUOTA_PROG -x -c "project -s -p $restore_dir/32bitv2 2123456789" \
+ $SCRATCH_MNT >> $seq.full
+echo "The restored file system + one additional file:"
+$XFS_IO_PROG -r -c "lsproj" $restore_dir/16bit
+$XFS_IO_PROG -r -c "lsproj" $restore_dir/32bit
+$XFS_IO_PROG -r -c "lsproj" $restore_dir/32bitv2
+
+umount $SCRATCH_MNT
+
+# Now, we can examine the file systems with xfs_db
+echo "These two values of 16bit project quota ids shall be the same"
+_print_projid $inode16a
+_print_projid $inode16b
+
+echo "These three values of 32bit project quota ids shall be the same"
+_print_projid $inode32b
+_print_projid $inode32a
+_print_projid $inode32v2
+
+status=0
+exit
--- /dev/null
+QA output created by 287
+No 32bit project quotas:
+projid = 1234
+projid = 0
+With 32bit project quota support:
+projid = 1234
+projid = 2123456789
+The restored file system + one additional file:
+projid = 1234
+projid = 2123456789
+projid = 2123456789
+These two values of 16bit project quota ids shall be the same
+core.projid_lo = 1234
+core.projid_hi = 0
+core.projid_lo = 1234
+core.projid_hi = 0
+These three values of 32bit project quota ids shall be the same
+core.projid_lo = 24853
+core.projid_hi = 32401
+core.projid_lo = 24853
+core.projid_hi = 32401
+core.projid_lo = 24853
+core.projid_hi = 32401
{
# Sync to get delalloc to disk
sync
+
+ # kill caches to guarantee removal speculative delalloc
+ # XXX: really need an ioctl instead of this big hammer
+ echo 3 > /proc/sys/vm/drop_caches
+
VFS_QUOTA=0
case $FSTYP in
ext2|ext3|ext4|ext4dev|reiserfs)
quotacheck -u -g $SCRATCH_MNT 2>/dev/null
else
# use XFS method to force quotacheck
- mount -o remount,noquota $SCRATCH_DEV
- mount -o remount,usrquota,grpquota $SCRATCH_DEV
+ xfs_quota -x -c "off -ug" $SCRATCH_MNT
+ _scratch_unmount
+ _scratch_mount "-o usrquota,grpquota"
fi
repquota -u -n $SCRATCH_MNT | grep -v "^#0" | _filter_scratch |
sort >$tmp.user.checked
282 dump ioctl auto quick
283 dump ioctl auto quick
284 auto
-285 auto dump quota quick
+285 auto rw
+286 other
+287 auto dump quota quick
install: default
$(INSTALL) -m 755 -d $(PKG_LIB_DIR)/ltp
- $(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/ltp
+ $(LTINSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/ltp
$(INSTALL) -m 755 $(SCRIPTS) $(PKG_LIB_DIR)/ltp
-include .dep
exit 1
])
libacl="-lacl"
- test -f `pwd`/../acl/libacl/libacl.la && \
- libacl="`pwd`/../acl/libacl/libacl.la"
test -f ${libexecdir}${libdirsuffix}/libacl.la && \
libacl="${libexecdir}${libdirsuffix}/libacl.la"
AC_SUBST(libacl)
exit 1
])
libattr="-lattr"
- test -f `pwd`/../attr/libattr/libattr.la && \
- libattr="`pwd`/../attr/libattr/libattr.la"
test -f ${libexecdir}${libdirsuffix}/libattr.la && \
libattr="${libexecdir}${libdirsuffix}/libattr.la"
AC_SUBST(libattr)
exit 1
])
libattr="-lattr"
- test -f `pwd`/../attr/libattr/libattr.la && \
- libattr="`pwd`/../attr/libattr/libattr.la"
test -f ${libexecdir}${libdirsuffix}/libattr.la && \
libattr="${libexecdir}${libdirsuffix}/libattr.la"
AC_SUBST(libattr)
echo
])
libdm="-ldm"
- test -f `pwd`/../dmapi/libdm/libdm.la && \
- libdm="`pwd`/../dmapi/libdm/libdm.la"
test -f ${libexecdir}${libdirsuffix}/libdm.la && \
libdm="${libexecdir}${libdirsuffix}/libdm.la"
AC_SUBST(libdm)
exit 1
])
libxfs="-lxfs"
- test -f `pwd`/../xfsprogs/libxfs/libxfs.la && \
- libxfs="`pwd`/../xfsprogs/libxfs/libxfs.la"
test -f ${libexecdir}${libdirsuffix}/libxfs.la && \
libxfs="${libexecdir}${libdirsuffix}/libxfs.la"
AC_SUBST(libxfs)
exit 1
])
libhdl="-lhandle"
- test -f `pwd`/../xfsprogs/libhandle/libhandle.la && \
- libhdl="`pwd`/../xfsprogs/libhandle/libhandle.la"
test -f ${libexecdir}${libdirsuffix}/libhandle.la && \
libhdl="${libexecdir}${libdirsuffix}/libhandle.la"
AC_SUBST(libhdl)
exit 1
])
libhdl="-lhandle"
- test -f `pwd`/../xfsprogs/libhandle/libhandle.la && \
- libhdl="`pwd`/../xfsprogs/libhandle/libhandle.la"
test -f ${libexecdir}${libdirsuffix}/libhandle.la && \
libhdl="${libexecdir}${libdirsuffix}/libhandle.la"
AC_SUBST(libhdl)
preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
locktest unwritten_mmap bulkstat_unlink_test t_stripealign \
bulkstat_unlink_test_modified t_dir_offset t_futimens t_immutable \
- stale_handle pwrite_mmap_blocked fstrim t_dir_offset2
+ stale_handle pwrite_mmap_blocked fstrim t_dir_offset2 seek_sanity_test \
+ seek_copy_test
SUBDIRS =
install: default $(addsuffix -install,$(SUBDIRS))
$(INSTALL) -m 755 -d $(PKG_LIB_DIR)/src
- $(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src
- $(INSTALL) -m 755 fill2attr fill2fs fill2fs_check scaleread.sh $(PKG_LIB_DIR)/src
- $(INSTALL) -m 644 dumpfile $(PKG_LIB_DIR)/src
+ $(LTINSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src
+ $(LTINSTALL) -m 755 fill2attr fill2fs fill2fs_check scaleread.sh $(PKG_LIB_DIR)/src
+ $(LTINSTALL) -m 644 dumpfile $(PKG_LIB_DIR)/src
%-install:
$(MAKE) -C $* install
--- /dev/null
+/*
+ * Copyright (C) 2011 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#define _XOPEN_SOURCE 500
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef SEEK_DATA
+#define SEEK_DATA 3
+#define SEEK_HOLE 4
+#endif
+
+#define BUF_SIZE 4096
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+static void
+error(const char *fmt, ...)
+{
+ char buf[256];
+ va_list args;
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, "ERROR: [%s:%d] %s:%s\n", __func__, __LINE__,
+ buf, strerror(errno));
+}
+
+static size_t
+full_write(int fd, const void *buf, size_t count)
+{
+ size_t total = 0;
+ const char *ptr = (const char *) buf;
+
+ while (count > 0) {
+ ssize_t n = write(fd, ptr, count);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ error("failed as %s", strerror(errno));
+ break;
+ }
+
+ if (n == 0) {
+ error("%zu bytes transferred. Aborting.",
+ total);
+ break;
+ }
+
+ total += n;
+ ptr += n;
+ count -= n;
+ }
+
+ return total;
+}
+
+/*
+ * Copy a data extent from source file to dest file.
+ * @data_off: data offset
+ * @hole_off: hole offset
+ * The length of this extent is (hole_off - data_off).
+ */
+static int
+do_extent_copy(int src_fd, int dest_fd, off_t data_off, off_t hole_off)
+{
+ uint64_t len = (uint64_t)(hole_off - data_off);
+ char buf[BUF_SIZE];
+ int ret;
+
+ /* Seek to data_off for data reading */
+ ret = lseek(src_fd, data_off, SEEK_SET);
+ if (ret < 0) {
+ error("seek source file to %llu failed as %s",
+ (uint64_t)data_off, strerror(errno));
+ return ret;
+ }
+
+ /* Seek to data_off for data writing, make holes as well */
+ ret = lseek(dest_fd, data_off, SEEK_SET);
+ if (ret < 0) {
+ error("seek dest file to %llu failed as %s",
+ (uint64_t)data_off, strerror(errno));
+ return ret;
+ }
+
+ while (len > 0) {
+ ssize_t nr_read = read(src_fd, buf, BUF_SIZE);
+ if (nr_read < 0) {
+ if (errno == EINTR)
+ continue;
+ error("read source file extent failed as %s",
+ strerror(errno));
+ ret = -1;
+ break;
+ }
+
+ if (nr_read == 0) {
+ error("reached EOF");
+ break;
+ }
+
+ if (full_write(dest_fd, buf, nr_read) != nr_read) {
+ error("write data to dest file failed as %s",
+ strerror(errno));
+ ret = -1;
+ break;
+ }
+
+ len -= nr_read;
+ }
+
+ return ret;
+}
+
+/*
+ * If lseek(2) failed and the errno is set to ENXIO, for
+ * SEEK_DATA there are no more data regions past the supplied
+ * offset. For SEEK_HOLE, there are no more holes past the
+ * supplied offset. Set scan->hit_final_extent to true for
+ * either case.
+ */
+static int
+copy_extents(int src_fd, int dest_fd, off_t src_total_size)
+{
+ int ret = 0;
+ off_t seek_start = 0;
+ off_t dest_pos = 0;
+ off_t data_pos, hole_pos;
+
+ do {
+ data_pos = lseek(src_fd, seek_start, SEEK_DATA);
+ if (data_pos < 0) {
+ if (errno == ENXIO)
+ ret = 0;
+ else {
+ error("SEEK_DATA failed due to %s",
+ strerror(errno));
+ ret = -1;
+ }
+ break;
+ }
+
+ hole_pos = lseek(src_fd, data_pos, SEEK_HOLE);
+ if (hole_pos < 0) {
+ if (errno == ENXIO)
+ ret = 0;
+ else {
+ error("SEEK_HOLE failed due to %s\n",
+ strerror(errno));
+ ret = -1;
+ }
+ break;
+ }
+
+ /* do extent copy */
+ ret = do_extent_copy(src_fd, dest_fd, data_pos, hole_pos);
+ if (ret < 0) {
+ error("copy extent failed");
+ break;
+ }
+
+ dest_pos += (hole_pos - data_pos);
+ seek_start = hole_pos;
+ } while (seek_start < src_total_size);
+
+ if (dest_pos < src_total_size) {
+ ret = ftruncate(dest_fd, src_total_size);
+ if (ret < 0) {
+ error("truncate dest file to %lld bytes failed as %s",
+ (long long)src_total_size, strerror(errno));
+ }
+ }
+
+ return ret;
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0;
+ int src_fd;
+ int dest_fd;
+ struct stat st;
+ size_t src_total_size;
+
+ if (argc != 3) {
+ fprintf(stdout, "Usage: %s source dest\n", argv[0]);
+ return 1;
+ }
+
+ src_fd = open(argv[1], O_RDONLY, 0644);
+ if (src_fd < 0) {
+ error("create %s failed", argv[1]);
+ return -1;
+ }
+
+ dest_fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0644);
+ if (dest_fd < 0) {
+ error("create %s failed", argv[2]);
+ ret = -errno;
+ goto close_src_fd;
+ }
+
+ ret = fstat(src_fd, &st);
+ if (ret < 0) {
+ error("get file %s staticis failed", argv[1]);
+ ret = -errno;
+ goto close_dest_fd;
+ }
+
+ src_total_size = st.st_size;
+ ret = copy_extents(src_fd, dest_fd, src_total_size);
+ if (ret < 0)
+ error("extents copy failed");
+
+close_dest_fd:
+ close(dest_fd);
+close_src_fd:
+ close(src_fd);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Oracle. All rights reserved.
+ * Copyright (C) 2011 Red Hat. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#define _XOPEN_SOURCE 500
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef SEEK_DATA
+#define SEEK_DATA 3
+#define SEEK_HOLE 4
+#endif
+
+static blksize_t alloc_size;
+int default_behavior = 0;
+char *base_file_path;
+
+static void get_file_system(int fd)
+{
+ struct statfs buf;
+
+ if (!fstatfs(fd, &buf))
+ fprintf(stdout, "File system magic#: 0x%lx\n", buf.f_type);
+}
+
+static int get_io_sizes(int fd)
+{
+ struct stat buf;
+ int ret;
+
+ ret = fstat(fd, &buf);
+ if (ret)
+ fprintf(stderr, " ERROR %d: Failed to find io blocksize\n",
+ errno);
+
+ /* st_blksize is typically also the allocation size */
+ alloc_size = buf.st_blksize;
+ fprintf(stdout, "Allocation size: %ld\n", alloc_size);
+
+ return ret;
+}
+
+#define do_free(x) do { if(x) free(x); } while(0);
+
+static void *do_malloc(size_t size)
+{
+ void *buf;
+
+ buf = malloc(size);
+ if (!buf)
+ fprintf(stderr, " ERROR: Unable to allocate %ld bytes\n",
+ (long)size);
+
+ return buf;
+}
+
+static int do_truncate(int fd, off_t length)
+{
+ int ret;
+
+ ret = ftruncate(fd, length);
+ if (ret)
+ fprintf(stderr, " ERROR %d: Failed to extend file "
+ "to %ld bytes\n", errno, (long)length);
+ return ret;
+}
+
+static int do_fallocate(int fd, off_t offset, off_t length, int mode)
+{
+ int ret;
+
+ ret = fallocate(fd, mode, offset, length);
+ if (ret)
+ fprintf(stderr, " ERROR %d: Failed to preallocate "
+ "space to %ld bytes\n", errno, (long) length);
+
+ return ret;
+}
+
+/*
+ * Synchnorize all dirty pages in the file range starting from
+ * offset to nbytes length.
+ */
+static int do_sync_dirty_pages(int fd, off64_t offset, off64_t nbytes)
+{
+ int ret;
+
+ ret = sync_file_range(fd, offset, nbytes, SYNC_FILE_RANGE_WRITE);
+ if (ret)
+ fprintf(stderr, " ERROR %d: Failed to sync out dirty "
+ "pages\n", errno);
+
+ return ret;
+}
+
+static ssize_t do_pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+ ssize_t ret, written = 0;
+
+ while (count > written) {
+ ret = pwrite(fd, buf + written, count - written, offset + written);
+ if (ret < 0) {
+ fprintf(stderr, " ERROR %d: Failed to write %ld "
+ "bytes\n", errno, (long)count);
+ return ret;
+ }
+ written += ret;
+ }
+
+ return 0;
+}
+
+#define do_close(x) do { if ((x) > -1) close(x); } while(0);
+
+static int do_create(const char *filename)
+{
+ int fd;
+
+ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
+ if (fd < 0)
+ fprintf(stderr, " ERROR %d: Failed to create file '%s'\n",
+ errno, filename);
+
+ return fd;
+}
+
+static int do_lseek(int testnum, int subtest, int fd, int filsz, int origin,
+ off_t set, off_t exp)
+{
+ off_t pos, exp2;
+ int x, ret;
+
+ assert(!(origin != SEEK_HOLE && origin != SEEK_DATA));
+
+ /*
+ * The file pointer can be set to different values depending
+ * on the implementation. For SEEK_HOLE, EOF could be a valid
+ * value. For SEEK_DATA, supplied offset could be the valid
+ * value.
+ */
+ exp2 = exp;
+ if (origin == SEEK_HOLE && exp2 != -1)
+ exp2 = filsz;
+ if (origin == SEEK_DATA && default_behavior && set < filsz)
+ exp2 = set;
+
+ pos = lseek(fd, set, origin);
+
+ if (pos == -1 && exp == -1) {
+ x = fprintf(stdout, "%02d.%02d %s expected -1 with errno %d, got %d. ",
+ testnum, subtest,
+ (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
+ -ENXIO, -errno);
+ ret = !(errno == ENXIO);
+ } else {
+
+ x = fprintf(stdout, "%02d.%02d %s expected %ld or %ld, got %ld. ",
+ testnum, subtest,
+ (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
+ (long)exp, (long)exp2, (long)pos);
+ ret = !(pos == exp || pos == exp2);
+ }
+
+ fprintf(stdout, "%*s\n", (70 - x), ret ? "FAIL" : "succ");
+
+ return ret;
+}
+
+/*
+ * test file with unwritten extents, have both dirty and
+ * writeback pages in page cache.
+ */
+static int test09(int fd, int testnum)
+{
+ int ret = 0;
+ char *buf = NULL;
+ int bufsz = alloc_size;
+ int filsz = 8 << 20;
+
+ /*
+ * HOLE - unwritten DATA in dirty page - HOLE -
+ * unwritten DATA in writeback page
+ */
+
+ /* Each unit is bufsz */
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ /* preallocate 8M space to file */
+ ret = do_fallocate(fd, 0, filsz, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
+ if (!ret) {
+ ret = do_pwrite(fd, buf, bufsz, bufsz * 100);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * Sync out dirty pages from bufsz * 100, this will convert
+ * the dirty page to writeback.
+ */
+ ret = do_sync_dirty_pages(fd, bufsz * 100, 0);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, 0);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, 1);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
+
+out:
+ do_free(buf);
+ return ret;
+}
+
+/* test file with unwritten extent, only have writeback page */
+static int test08(int fd, int testnum)
+{
+ int ret = 0;
+ char *buf = NULL;
+ int bufsz = alloc_size;
+ int filsz = 4 << 20;
+
+ /* HOLE - unwritten DATA in writeback page */
+ /* Each unit is bufsz */
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ /* preallocate 4M space to file */
+ ret = do_fallocate(fd, 0, filsz, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
+ if (ret)
+ goto out;
+
+ /* Sync out all file */
+ ret = do_sync_dirty_pages(fd, 0, 0);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, 0);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, 1);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
+
+out:
+ do_free(buf);
+ return ret;
+}
+
+/*
+ * test file with unwritten extents, only have dirty pages
+ * in page cache.
+ */
+static int test07(int fd, int testnum)
+{
+ int ret = 0;
+ char *buf = NULL;
+ int bufsz = alloc_size;
+ int filsz = 4 << 20;
+
+ /* HOLE - unwritten DATA in dirty page */
+ /* Each unit is bufsz */
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ /* preallocate 4M space to file */
+ ret = do_fallocate(fd, 0, filsz, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, 0);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, 1);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
+
+out:
+ do_free(buf);
+ return ret;
+}
+
+/* test hole data hole data */
+static int test06(int fd, int testnum)
+{
+ int ret = -1;
+ char *buf = NULL;
+ int bufsz = alloc_size;
+ int filsz = bufsz * 4;
+ int off;
+
+ /* HOLE - DATA - HOLE - DATA */
+ /* Each unit is bufsz */
+
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+
+ memset(buf, 'a', bufsz);
+
+ ret = do_pwrite(fd, buf, bufsz, bufsz);
+ if (!ret)
+ do_pwrite(fd, buf, bufsz, bufsz * 3);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, 0);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, 1);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, bufsz);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, bufsz);
+
+ /* offset around first hole-data boundary */
+ off = bufsz;
+ ret += do_lseek(testnum, 5, fd, filsz, SEEK_HOLE, off - 1, off - 1);
+ ret += do_lseek(testnum, 6, fd, filsz, SEEK_DATA, off - 1, off);
+ ret += do_lseek(testnum, 7, fd, filsz, SEEK_HOLE, off, bufsz * 2);
+ ret += do_lseek(testnum, 8, fd, filsz, SEEK_DATA, off, off);
+ ret += do_lseek(testnum, 9, fd, filsz, SEEK_HOLE, off + 1, bufsz * 2);
+ ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, off + 1, off + 1);
+
+ /* offset around data-hole boundary */
+ off = bufsz * 2;
+ ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, off - 1, off);
+ ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, off - 1, off - 1);
+ ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, off, off);
+ ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, off, bufsz * 3);
+ ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, off + 1, off + 1);
+ ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, off + 1, bufsz * 3);
+
+ /* offset around second hole-data boundary */
+ off = bufsz * 3;
+ ret += do_lseek(testnum, 17, fd, filsz, SEEK_HOLE, off - 1, off - 1);
+ ret += do_lseek(testnum, 18, fd, filsz, SEEK_DATA, off - 1, off);
+ ret += do_lseek(testnum, 19, fd, filsz, SEEK_HOLE, off, filsz);
+ ret += do_lseek(testnum, 20, fd, filsz, SEEK_DATA, off, off);
+ ret += do_lseek(testnum, 21, fd, filsz, SEEK_HOLE, off + 1, filsz);
+ ret += do_lseek(testnum, 22, fd, filsz, SEEK_DATA, off + 1, off + 1);
+
+ /* offset around the end of file */
+ off = filsz;
+ ret += do_lseek(testnum, 23, fd, filsz, SEEK_HOLE, off - 1, filsz);
+ ret += do_lseek(testnum, 24, fd, filsz, SEEK_DATA, off - 1, filsz - 1);
+ ret += do_lseek(testnum, 25, fd, filsz, SEEK_HOLE, off, -1);
+ ret += do_lseek(testnum, 26, fd, filsz, SEEK_DATA, off, -1);
+ ret += do_lseek(testnum, 27, fd, filsz, SEEK_HOLE, off + 1, -1);
+ ret += do_lseek(testnum, 28, fd, filsz, SEEK_DATA, off + 1, -1);
+
+out:
+ do_free(buf);
+ return ret;
+}
+
+/* test file with data at the beginning and a hole at the end */
+static int test05(int fd, int testnum)
+{
+ int ret = -1;
+ char *buf = NULL;
+ int bufsz = alloc_size;
+ int filsz = bufsz * 4;
+
+ /* |- DATA -|- HOLE -|- HOLE -|- HOLE -| */
+
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ ret = do_truncate(fd, filsz);
+ if (!ret)
+ ret = do_pwrite(fd, buf, bufsz, 0);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, bufsz);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, bufsz);
+
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, 0);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, 1);
+
+ /* offset around data-hole boundary */
+ ret += do_lseek(testnum, 5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
+ ret += do_lseek(testnum, 6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
+
+ ret += do_lseek(testnum, 7, fd, filsz, SEEK_HOLE, bufsz, bufsz);
+ ret += do_lseek(testnum, 8, fd, filsz, SEEK_DATA, bufsz, -1);
+ ret += do_lseek(testnum, 9, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
+ ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
+
+ /* offset around eof */
+ ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz - 1);
+ ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, -1);
+ ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz, -1);
+ ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz, -1);
+ ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
+ ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
+out:
+ do_free(buf);
+ return ret;
+}
+/* test hole begin and data end */
+static int test04(int fd, int testnum)
+{
+ int ret;
+ char *buf = "ABCDEFGH";
+ int bufsz, holsz, filsz;
+
+ bufsz = strlen(buf);
+ holsz = alloc_size * 2;
+ filsz = holsz + bufsz;
+
+ /* |- HOLE -|- HOLE -|- DATA -| */
+
+ ret = do_pwrite(fd, buf, bufsz, holsz);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, 0);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, 1);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, holsz);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, holsz);
+ /* offset around hole-data boundary */
+ ret += do_lseek(testnum, 5, fd, filsz, SEEK_HOLE, holsz - 1, holsz - 1);
+ ret += do_lseek(testnum, 6, fd, filsz, SEEK_DATA, holsz - 1, holsz);
+ ret += do_lseek(testnum, 7, fd, filsz, SEEK_HOLE, holsz, filsz);
+ ret += do_lseek(testnum, 8, fd, filsz, SEEK_DATA, holsz, holsz);
+ ret += do_lseek(testnum, 9, fd, filsz, SEEK_HOLE, holsz + 1, filsz);
+ ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, holsz + 1, holsz + 1);
+
+ /* offset around eof */
+ ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz);
+ ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, filsz - 1);
+ ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz, -1);
+ ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz, -1);
+ ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
+ ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
+out:
+ return ret;
+}
+
+/* test a larger full file */
+static int test03(int fd, int testnum)
+{
+ char *buf = NULL;
+ int bufsz = alloc_size * 2 + 100;
+ int filsz = bufsz;
+ int ret = -1;
+
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ ret = do_pwrite(fd, buf, bufsz, 0);
+ if (ret)
+ goto out;
+
+ /* offset at the beginning */
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, bufsz);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 1, bufsz);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 0, 0);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_DATA, 1, 1);
+
+ /* offset around eof */
+ ret += do_lseek(testnum, 5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
+ ret += do_lseek(testnum, 6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
+ ret += do_lseek(testnum, 7, fd, filsz, SEEK_HOLE, bufsz, -1);
+ ret += do_lseek(testnum, 8, fd, filsz, SEEK_DATA, bufsz, -1);
+ ret += do_lseek(testnum, 9, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
+ ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
+
+out:
+ do_free(buf);
+ return ret;
+}
+
+/* test tiny full file */
+static int test02(int fd, int testnum)
+{
+ int ret;
+ char buf[] = "ABCDEFGH";
+ int bufsz, filsz;
+
+ bufsz = strlen(buf);
+ filsz = bufsz;
+
+ /* |- DATA -| */
+
+ ret = do_pwrite(fd, buf, bufsz, 0);
+ if (ret)
+ goto out;
+
+ ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, filsz);
+ ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 0, 0);
+ ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 1, 1);
+ ret += do_lseek(testnum, 4, fd, filsz, SEEK_HOLE, bufsz - 1, filsz);
+ ret += do_lseek(testnum, 5, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
+ ret += do_lseek(testnum, 6, fd, filsz, SEEK_HOLE, bufsz, -1);
+ ret += do_lseek(testnum, 7, fd, filsz, SEEK_DATA, bufsz, -1);
+ ret += do_lseek(testnum, 8, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
+ ret += do_lseek(testnum, 9, fd, filsz, SEEK_DATA, bufsz + 1, -1);
+
+out:
+ return ret;
+}
+
+/* test empty file */
+static int test01(int fd, int testnum)
+{
+ int ret = 0;
+
+ ret += do_lseek(testnum, 1, fd, 0, SEEK_DATA, 0, -1);
+ ret += do_lseek(testnum, 2, fd, 0, SEEK_HOLE, 0, -1);
+ ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, 1, -1);
+
+ return ret;
+}
+
+struct testrec {
+ int test_num;
+ int (*test_func)(int fd, int testnum);
+ char *test_desc;
+};
+
+struct testrec seek_tests[] = {
+ { 1, test01, "Test empty file" },
+ { 2, test02, "Test a tiny full file" },
+ { 3, test03, "Test a larger full file" },
+ { 4, test04, "Test file hole at beg, data at end" },
+ { 5, test05, "Test file data at beg, hole at end" },
+ { 6, test06, "Test file hole data hole data" },
+ { 7, test07, "Test file with unwritten extents, only have dirty pages" },
+ { 8, test08, "Test file with unwritten extents, only have unwritten pages" },
+ { 9, test09, "Test file with unwritten extents, have both dirty && unwritten pages" },
+};
+
+static int run_test(struct testrec *tr)
+{
+ int ret = 0, fd = -1;
+ char filename[255];
+
+ snprintf(filename, sizeof(filename), "%s%02d", base_file_path, tr->test_num);
+
+ fd = do_create(filename);
+ if (fd > -1) {
+ printf("%02d. %-50s\n", tr->test_num, tr->test_desc);
+ ret = tr->test_func(fd, tr->test_num);
+ printf("\n");
+ }
+
+ do_close(fd);
+ return ret;
+}
+
+static int test_basic_support(void)
+{
+ int ret = -1, fd;
+ off_t pos;
+ char *buf = NULL;
+ int bufsz, filsz;
+
+ fd = do_create(base_file_path);
+ if (fd == -1)
+ goto out;
+
+ get_file_system(fd);
+
+ ret = get_io_sizes(fd);
+ if (ret)
+ goto out;
+
+ bufsz = alloc_size * 2;
+ filsz = bufsz * 2;
+
+ buf = do_malloc(bufsz);
+ if (!buf)
+ goto out;
+ memset(buf, 'a', bufsz);
+
+ /* File with 2 allocated blocks.... */
+ ret = do_pwrite(fd, buf, bufsz, 0);
+ if (ret)
+ goto out;
+
+ /* followed by a hole... */
+ ret = do_truncate(fd, filsz);
+ if (ret)
+ goto out;
+
+ /* Is SEEK_DATA and SEEK_HOLE supported in the kernel? */
+ pos = lseek(fd, 0, SEEK_DATA);
+ if (pos != -1)
+ pos = lseek(fd, 0, SEEK_HOLE);
+ if (pos == -1) {
+ fprintf(stderr, "Kernel does not support llseek(2) extensions "
+ "SEEK_HOLE and/or SEEK_DATA. Aborting.\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (pos == filsz) {
+ default_behavior = 1;
+ fprintf(stderr, "File system supports the default behavior.\n");
+ }
+
+ printf("\n");
+
+out:
+ do_free(buf);
+ do_close(fd);
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = -1;
+ int i = 0;
+ int numtests = sizeof(seek_tests) / sizeof(struct testrec);
+
+ if (argc != 2) {
+ fprintf(stdout, "Usage: %s base_file_path\n", argv[0]);
+ return ret;
+ }
+
+ base_file_path = (char *)strdup(argv[1]);
+
+ ret = test_basic_support();
+ if (ret)
+ goto out;
+
+ for (i = 0; i < numtests; ++i) {
+ if (ret)
+ goto out;
+ run_test(&seek_tests[i]);
+ }
+
+out:
+ free(base_file_path);
+ return ret;
+}