gfs2-utils: RHEL7 - gfs2-utils tests: Document usage of convenience macros
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=1ecf5d89...
Commit: 1ecf5d89fa0ea0d707531e8e77305b4dc0bdc32b
Parent: 1c841a668fb0896ce3294f1700549dfa7207717e
Author: Andrew Price <anprice(a)redhat.com>
AuthorDate: Wed Jan 14 15:21:06 2015 +0000
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
gfs2-utils tests: Document usage of convenience macros
Lower the barrier to writing tests by adding some docs to clarify the m4
convenience macros in testsuite.at.
Signed-off-by: Andrew Price <anprice(a)redhat.com>
---
tests/testsuite.at | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/tests/testsuite.at b/tests/testsuite.at
index aa86966..0422b02 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -1,15 +1,18 @@
# Regenerate the sparse file used for testing and skip the test if it fails
+# Usage: GFS_TGT_REGEN
m4_define([GFS_TGT_REGEN],
[AT_CHECK([rm -f $GFS_TGT && truncate -s ${GFS_TGT_SZ}G ${GFS_TGT}], [ignore], [ignore], [ignore])
AT_SKIP_IF([test ! -f ${GFS_TGT}])])
# Regenerate, check, fsck is used a lot so combine it into one macro
+# Usage: GFS_FSCK_CHECK ([mkfs.gfs2 ... $GFS_TGT])
m4_define([GFS_FSCK_CHECK],
[GFS_TGT_REGEN
AT_CHECK($1, 0, [ignore], [ignore])
AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])])
# Regenerate, mkfs, modify fs with gfs2l, fsck
+# Usage: GFS_TGT_REGEN ([<mkfs.gfs2 command>], [<gfs2l script>])
m4_define([GFS_LANG_CHECK],
[GFS_TGT_REGEN
AT_CHECK($1, 0, [ignore], [ignore])
9 years
gfs2-utils: RHEL7 - gfs2-utils tests: Skip unit tests if check is not found
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=1c841a66...
Commit: 1c841a668fb0896ce3294f1700549dfa7207717e
Parent: d46075a179cb83dc0b17667649f9465aace8eeff
Author: Andrew Price <anprice(a)redhat.com>
AuthorDate: Wed Jan 14 15:05:25 2015 +0000
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
gfs2-utils tests: Skip unit tests if check is not found
The check package (check-devel in Fedora) is required for building the
unit tests. When the configure script doesn't find check, it prevents
the unit tests from being built, but the test suite still tries to run
the unit tests. Introduce a wrapper m4 macro GFS_UNIT_TEST to set up
unit test test groups which will be skipped if check was not found in
the configure stage.
Signed-off-by: Andrew Price <anprice(a)redhat.com>
---
configure.ac | 2 +-
tests/Makefile.am | 13 +++++++------
tests/libgfs2.at | 6 ++----
tests/testsuite.at | 8 ++++++++
4 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/configure.ac b/configure.ac
index 2341730..a523be3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,7 +97,7 @@ AC_ARG_ENABLE([gcov],
PKG_CHECK_MODULES([check], [check >= 0.9.8],
[have_check=yes],
[have_check=no])
-AM_CONDITIONAL([BUILD_TESTS], [test "x$have_check" = "xyes"])
+AM_CONDITIONAL([HAVE_CHECK], [test "x$have_check" = "xyes"])
PKG_CHECK_MODULES([zlib],[zlib])
PKG_CHECK_MODULES([blkid],[blkid])
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 475f4a7..3abb640 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,7 @@ EXTRA_DIST = $(TESTSUITE_AT) package.m4 $(TESTSUITE) atlocal atconfig
DISTCLEANFILES = atlocal atconfig
CLEANFILES = testvol
-if BUILD_TESTS
+if HAVE_CHECK
UNIT_TESTS = \
check_meta \
check_rgrp
@@ -55,19 +55,20 @@ TESTSUITE_AT = \
testsuite.at \
mkfs.at \
fsck.at \
- edit.at
+ edit.at \
+ libgfs2.at
-if BUILD_TESTS
-TESTSUITE_AT += libgfs2.at
+if HAVE_CHECK
+ENABLE_UNIT=yes
endif
TESTSUITE = $(srcdir)/testsuite
check-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) '$(TESTSUITE)' $(TOPTS)
+ $(SHELL) '$(TESTSUITE)' ENABLE_UNIT_TESTS=$(ENABLE_UNIT) $(TOPTS)
installcheck-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(sbindir):tests' $(TOPTS)
+ $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(sbindir):tests' ENABLE_UNIT_TESTS=$(ENABLE_UNIT) $(TOPTS)
clean-local:
test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean
diff --git a/tests/libgfs2.at b/tests/libgfs2.at
index a5bee87..1d2243c 100644
--- a/tests/libgfs2.at
+++ b/tests/libgfs2.at
@@ -1,11 +1,9 @@
AT_BANNER([libgfs2 unit tests])
-AT_SETUP([meta.c])
-AT_KEYWORDS(libgfs2)
+GFS_UNIT_TEST([meta.c],[libgfs2])
AT_CHECK([check_meta], 0, [ignore], [ignore])
AT_CLEANUP
-AT_SETUP([rgrp.c])
-AT_KEYWORDS(libgfs2)
+GFS_UNIT_TEST([rgrp.c],[libgfs2])
AT_CHECK([check_rgrp], 0, [ignore], [ignore])
AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index fe781a7..aa86966 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -17,6 +17,14 @@ AT_CHECK([echo "$2" | gfs2l ${GFS_TGT}], 0, [ignore], [ignore])
AT_CHECK([fsck.gfs2 -y $GFS_TGT], 1, [ignore], [ignore])
AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])])
+# Set up a unit test, skipping if unit tests are disabled
+# Usage: GFS_UNIT_TEST ([name], [keywords])
+m4_define([GFS_UNIT_TEST],
+[AT_SETUP($1)
+AT_KEYWORDS($2)
+AT_CHECK([test x"$ENABLE_UNIT_TESTS" = "xyes" || exit 77])])
+
+# Test suite begins here
AT_INIT([])
AT_COLOR_TESTS
9 years
gfs2-utils: RHEL7 - gfs2-utils tests: Improve docs
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=d46075a1...
Commit: d46075a179cb83dc0b17667649f9465aace8eeff
Parent: 806495ab5e9f83b107bcca25def28415c1dc192b
Author: Andrew Price <anprice(a)redhat.com>
AuthorDate: Wed Jan 14 12:30:04 2015 +0000
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
gfs2-utils tests: Improve docs
Add a pointer to doc/README.tests in README.build and be more consistent
about how the test suite is run in doc/README.tests. Add example usage
and mention the TOPTS argument.
Signed-off-by: Andrew Price <anprice(a)redhat.com>
---
README.build | 3 ++-
doc/README.tests | 32 ++++++++++++++++++++++++--------
2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/README.build b/README.build
index 2aad6ab..f4ebe53 100644
--- a/README.build
+++ b/README.build
@@ -29,4 +29,5 @@ To install gfs2-utils, run:
make install
-See also doc/README.contributing for details on submitting patches.
+See also doc/README.contributing for details on submitting patches and
+doc/README.tests for more details regarding the test suite.
diff --git a/doc/README.tests b/doc/README.tests
index 79f51f6..1b90a0c 100644
--- a/doc/README.tests
+++ b/doc/README.tests
@@ -1,16 +1,30 @@
Working with the gfs2-utils test suite
--------------------------------------
+Overview
+--------
The test suite in the tests directory of the gfs2-utils source tree is based on
the Autotest framework provided by Autoconf. The basic idea is that the
testsuite.at file is the main source file for the tests, written in m4 and
-generating a bourne shell script called testsuite.
+generating a bourne shell script called testsuite, which we run with 'make check'.
-When run, either using 'make check' or directly from within the tests
-directory, the testsuite script sources atconfig and atlocal for configuration
-and then runs the whole testsuite or a specified set of tests (see ./testsuite
--h for help).
+When run, the test suite sources tests/atconfig and tests/atlocal for
+configuration and then runs the whole suite or a specified set of tests,
+depending on options passed to the test suite using TOPTS. For example, to see
+a list of available options, use 'make check TOPTS=-h' and to see a numbered
+list of the available tests, use 'make check TOPTS=-l'.
+A subset of the available tests can be run using keywords and/or by specifying
+the test ID numbers in TOPTS, e.g. make check TOPTS='-k mkfs 24 25'
+
+Test output is captured and, if a test fails, a log is kept in
+tests/testsuite.dir/$n/testsuite.log where $n is the test's ID number. Failed
+tests can be re-run using make check TOPTS='--recheck' although it's better to
+re-run the entire suite after fixing tests as a fix for one test could break
+another.
+
+Writing tests
+-------------
A number of GFS2-specific convenience macros have been defined in testsuite.at
to make defining new tests quick and easy. Also, some variables have been
defined in atlocal.in so that full paths to programs do not have to be included
@@ -32,15 +46,17 @@ do not fit into an existing category, in which case AT_BANNER can be used to
group them, and they can be organised into a new .at file and sourced from
testsuite.at.
-Since the tests can be run individually (e.g. ./testsuite 15) any new tests
-which require the dummy volume $GFS_TGT to be present should call GFS_TGT_REGEN
-before attempting to use it.
+As the tests can be run individually, any new tests which require the dummy
+volume $GFS_TGT to be present should call GFS_TGT_REGEN before attempting to
+use it.
Documentation for Autotest, including the AT_* macros used to define tests, can
be found in the autoconf manual at:
http://www.gnu.org/software/autoconf/manual/index.html
+Generating coverage reports
+---------------------------
Test coverage instrumentation can be enabled using the --enable-gcov option at
the configure stage. Once the tools have been built and run with this option
enabled, coverage data will be written to files in the source directories for
9 years
gfs2-utils: RHEL7 - gfs2-utils tests: Shorten TESTSUITEFLAGS to TOPTS
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=806495ab...
Commit: 806495ab5e9f83b107bcca25def28415c1dc192b
Parent: 56c6931b22a5b2f1315a03a5c9f4992d81538d5e
Author: Andrew Price <anprice(a)redhat.com>
AuthorDate: Wed Jan 14 12:02:37 2015 +0000
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
gfs2-utils tests: Shorten TESTSUITEFLAGS to TOPTS
TESTSUITEFLAGS is too long, replace it with TOPTS
Signed-off-by: Andrew Price <anprice(a)redhat.com>
---
tests/Makefile.am | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b912dfa..475f4a7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -64,10 +64,10 @@ endif
TESTSUITE = $(srcdir)/testsuite
check-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+ $(SHELL) '$(TESTSUITE)' $(TOPTS)
installcheck-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(sbindir):tests' $(TESTSUITEFLAGS)
+ $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(sbindir):tests' $(TOPTS)
clean-local:
test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean
9 years
gfs2-utils: RHEL7 - gfs2-utils tests: Add keywords to tests
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=56c6931b...
Commit: 56c6931b22a5b2f1315a03a5c9f4992d81538d5e
Parent: cc49c36caffdffe4c2d6fe8fbde0a6813081267d
Author: Andrew Price <anprice(a)redhat.com>
AuthorDate: Wed Jan 14 11:42:50 2015 +0000
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
gfs2-utils tests: Add keywords to tests
The testsuite script generated by autotest accepts a -k <keyword> option
which limits the set of tests to be run to a specified group. Add
keywords to tests to take advantage of this feature.
Signed-off-by: Andrew Price <anprice(a)redhat.com>
---
tests/edit.at | 5 +++++
tests/fsck.at | 2 ++
tests/libgfs2.at | 2 ++
tests/mkfs.at | 16 ++++++++++++++++
4 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/tests/edit.at b/tests/edit.at
index 49f4a58..e3f02e6 100644
--- a/tests/edit.at
+++ b/tests/edit.at
@@ -2,6 +2,7 @@ AT_TESTED([gfs2_edit])
AT_BANNER([gfs2_edit tests])
AT_SETUP([Save/restoremeta, defaults])
+AT_KEYWORDS(gfs2_edit edit)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock $GFS_TGT $(($(gfs_max_blocks 4096)/2))], 0, [ignore], [ignore])
AT_CHECK([gfs2_edit savemeta $GFS_TGT test.meta > savemeta.log], 0, [ignore], [ignore])
@@ -14,6 +15,7 @@ AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Save/restoremeta, no compression])
+AT_KEYWORDS(gfs2_edit edit)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock $GFS_TGT $(($(gfs_max_blocks 4096)/2))], 0, [ignore], [ignore])
AT_CHECK([gfs2_edit savemeta -z0 $GFS_TGT test.meta], 0, [ignore], [ignore])
@@ -23,6 +25,7 @@ AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Save/restoremeta, min. block size])
+AT_KEYWORDS(gfs2_edit edit)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock -b512 $GFS_TGT], 0, [ignore], [ignore])
AT_CHECK([gfs2_edit savemeta -z0 $GFS_TGT test.meta], 0, [ignore], [ignore])
@@ -32,6 +35,7 @@ AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Save/restoremeta, 4 journals])
+AT_KEYWORDS(gfs2_edit edit)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock -j4 -J8 $GFS_TGT], 0, [ignore], [ignore])
AT_CHECK([gfs2_edit savemeta -z0 $GFS_TGT test.meta], 0, [ignore], [ignore])
@@ -41,6 +45,7 @@ AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Save/restoremeta, min. block size, 4 journals])
+AT_KEYWORDS(gfs2_edit edit)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock -b512 -j4 -J8 $GFS_TGT], 0, [ignore], [ignore])
AT_CHECK([gfs2_edit savemeta -z0 $GFS_TGT test.meta], 0, [ignore], [ignore])
diff --git a/tests/fsck.at b/tests/fsck.at
index e3b82bd..4d25f40 100644
--- a/tests/fsck.at
+++ b/tests/fsck.at
@@ -2,6 +2,7 @@ AT_TESTED([fsck.gfs2])
AT_BANNER([fsck.gfs2 tests])
AT_SETUP([Fix invalid block sizes])
+AT_KEYWORDS(fsck.gfs2 fsck)
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set sb { sb_bsize: 0 }])
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set sb { sb_bsize: 1 }])
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set sb { sb_bsize: 513 }])
@@ -10,5 +11,6 @@ GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set sb { sb_bsize: 4097
AT_CLEANUP
AT_SETUP([Fix invalid goal blocks])
+AT_KEYWORDS(fsck.gfs2 fsck)
GFS_LANG_CHECK([mkfs.gfs2 -O -p lock_nolock $GFS_TGT], [set '/' { di_goal_meta: 0 }])
AT_CLEANUP
diff --git a/tests/libgfs2.at b/tests/libgfs2.at
index 8c44cdd..a5bee87 100644
--- a/tests/libgfs2.at
+++ b/tests/libgfs2.at
@@ -1,9 +1,11 @@
AT_BANNER([libgfs2 unit tests])
AT_SETUP([meta.c])
+AT_KEYWORDS(libgfs2)
AT_CHECK([check_meta], 0, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([rgrp.c])
+AT_KEYWORDS(libgfs2)
AT_CHECK([check_rgrp], 0, [ignore], [ignore])
AT_CLEANUP
diff --git a/tests/mkfs.at b/tests/mkfs.at
index ff99bb1..438184c 100644
--- a/tests/mkfs.at
+++ b/tests/mkfs.at
@@ -2,36 +2,43 @@ AT_TESTED([mkfs.gfs2])
AT_BANNER([mkfs.gfs2 tests])
AT_SETUP([Locking protocol validation])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
AT_CHECK([$GFS_MKFS -p badprotocol $GFS_TGT], 255, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Resource group size validation])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
AT_CHECK([$GFS_MKFS -p lock_nolock -r 31 $GFS_TGT], 255, [ignore], [ignore])
AT_CHECK([$GFS_MKFS -p lock_nolock -r 2049 $GFS_TGT], 255, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Journal size validation])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
AT_CHECK([$GFS_MKFS -p lock_nolock -J 7 $GFS_TGT], 255, [ignore], [ignore])
AT_CHECK([$GFS_MKFS -p lock_nolock -J 1025 $GFS_TGT], 255, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Block count validation])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_TGT_REGEN
AT_CHECK([$GFS_MKFS -p lock_nolock -b 512 $GFS_TGT $(($(gfs_max_blocks 512)+1))], 255, [ignore], [ignore])
AT_CHECK([$GFS_MKFS -p lock_nolock -b 4096 $GFS_TGT $(($(gfs_max_blocks 4096)+1))], 255, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Quota change file size validation])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
AT_CHECK([$GFS_MKFS -p lock_nolock -c 0 $GFS_TGT], 255, [ignore], [ignore])
AT_CHECK([$GFS_MKFS -p lock_nolock -c 65 $GFS_TGT], 255, [ignore], [ignore])
AT_CLEANUP
AT_SETUP([Locking protocols])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock $GFS_TGT])
GFS_FSCK_CHECK([$GFS_MKFS -p lock_dlm -t foo:bar $GFS_TGT])
AT_CLEANUP
AT_SETUP([Valid block sizes 512-4096])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 512 $GFS_TGT])
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 1024 $GFS_TGT])
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 2048 $GFS_TGT])
@@ -39,37 +46,46 @@ GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 4096 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Max. blocks, min. block size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 512 $GFS_TGT $(gfs_max_blocks 512)])
AT_CLEANUP
AT_SETUP([Max. blocks, max. block size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -b 4096 $GFS_TGT $(util_max_blocks 4096)])
AT_CLEANUP
AT_SETUP([Max. resource group size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -r 2048 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Min. resource group size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -r 32 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Max. resource group size, min. block size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -r 2048 -b 512 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Max. journal size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -J 1024 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Min. journal size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -J 8 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Max. quota change file size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -c 64 $GFS_TGT])
AT_CLEANUP
AT_SETUP([Min. quota change file size])
+AT_KEYWORDS(mkfs.gfs2 mkfs)
GFS_FSCK_CHECK([$GFS_MKFS -p lock_nolock -c 1 $GFS_TGT])
AT_CLEANUP
9 years
gfs2-utils: RHEL7 - fsck.gfs2: fix double-free bug
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=cc49c36c...
Commit: cc49c36caffdffe4c2d6fe8fbde0a6813081267d
Parent: 3ef35f4d0deeabfe9fdbc7939ae8dc6ae7dfb11f
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Thu Jan 15 09:49:12 2015 -0600
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
fsck.gfs2: fix double-free bug
This patch fixes a double-free bug that can occur when errors are found
in an inode and later, it's freed. The problem is that function
free_metalist was using osi_list_del when removing buffer_heads from the
inode in memory. It should be using osi_list_del_init so that the list
head is reinitialized. Otherwise the list head is left with an unknown
value which gets interpreted later on as !list_empty(), so it tries to
free the list a second time.
This also fixes a minor issue with function rangecheck_jmeta, which
frees the buffer_head when it hits an error. The patch sets the returned
*bh value back to NULL to ensure it's not reused. This is just a
precaution I spotted while debugging, and it's worth doing.
---
gfs2/fsck/fs_recovery.c | 1 +
gfs2/fsck/metawalk.c | 2 +-
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index 3d8fe98..095d118 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -631,6 +631,7 @@ static int rangecheck_jmeta(struct gfs2_inode *ip, uint64_t block,
(unsigned long long)block,
(unsigned long long)block);
brelse(*bh);
+ *bh = NULL;
return meta_skip_further;
}
}
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 4d16623..289b062 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1203,7 +1203,7 @@ static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
nbh = osi_list_entry(list->next,
struct gfs2_buffer_head, b_altlist);
if (nbh == ip->i_bh)
- osi_list_del(&nbh->b_altlist);
+ osi_list_del_init(&nbh->b_altlist);
else
brelse(nbh);
}
9 years
gfs2-utils: RHEL7 - fsck.gfs2: Fix a use-after-free in pass2
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=3ef35f4d...
Commit: 3ef35f4d0deeabfe9fdbc7939ae8dc6ae7dfb11f
Parent: f0956d61f856599d1215b13f4b6be6731110edee
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 14 12:22:23 2015 -0600
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
fsck.gfs2: Fix a use-after-free in pass2
This patch fixes a rare code path that's doing use-after-free
in pass2.
---
gfs2/fsck/pass2.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 77bb888..74bcc26 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1930,13 +1930,14 @@ int pass2(struct gfs2_sbd *sdp)
ip = fsck_load_inode(sdp, dirblk);
astate_save(ip, &as);
error = check_metatree(ip, &pass2_fxns);
- fsck_inode_put(&ip);
if (error < 0) {
stack;
+ fsck_inode_put(&ip);
return error;
}
if (astate_changed(ip, &as))
reprocess_inode(ip, "current");
+ fsck_inode_put(&ip);
}
error = check_dir(sdp, dirblk, &pass2_fxns);
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
9 years
gfs2-utils: RHEL7 - fsck.gfs2: remove duplicate designation during undo
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=f0956d61...
Commit: f0956d61f856599d1215b13f4b6be6731110edee
Parent: 9dccc5b4ccb5583201b09195e17fc95c76674be0
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Fri Jan 9 07:35:46 2015 -0600
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
fsck.gfs2: remove duplicate designation during undo
This patch fixes a problem whereby fsck.gfs2's pass1 would perform this
sequence of events:
1. Metadata block X is identified as being referenced from dinode D1.
2. Metadata block X is identified as being referenced from another dinode, D2,
which makes it a duplicate reference, but so far, no serious errors were
found for that dinode.
3. Dinode D2 is found later to be irreparably damaged, and needs to be removed.
When D2 is deleted, the duplicate reference from D2 is removed and block X is
not freed because D1 still references it. However, it's still marked as a
duplicate and requires processing in pass1b.
Later, pass1b resolves the duplicate and determine's there is really only one
reference remaining, so it makes the correct decision. However, it should not
be necessary. The "undo" functions should remove the duplicate reference if
(and only if) the only reference was from D2. Note, though, that if the
corruption is found later in the cycle (after "undo" is possible) the duplicate
reference MUST remain and be resolved by pass1b.
---
gfs2/fsck/pass1.c | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 4f00316..2841b8c 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -363,6 +363,11 @@ static int undo_reference(struct gfs2_inode *ip, uint64_t block, int meta,
"from another inode; not freeing.\n"),
(unsigned long long)block,
(unsigned long long)block);
+ if (dt->refs == 1) {
+ log_err(_("This was the only duplicate "
+ "reference so far; removing it.\n"));
+ dup_delete(dt);
+ }
return 1;
}
}
@@ -1012,8 +1017,13 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
(unsigned long long)ip->i_di.di_num.no_addr);
if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
return meta_is_good;
- else
+ else {
+ log_debug(_("Inode 0x%llx bad pointer tolerance "
+ "exceeded: block 0x%llx.\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)block);
return meta_error; /* Exits check_metatree quicker */
+ }
}
return meta_is_good;
}
9 years
gfs2-utils: RHEL7 - fsck.gfs2: Revise "undo" processing
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=9dccc5b4...
Commit: 9dccc5b4ccb5583201b09195e17fc95c76674be0
Parent: e93361dabf478d66bde70dcf2a165e202eec9461
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Wed Jan 7 08:18:44 2015 -0600
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
fsck.gfs2: Revise "undo" processing
This patch has to do with "undo" processing. This is where pass1 has
processed a bunch of data blocks before encountering an error. If the
error is "fatal" processing is stopped immediately and the work done
to that point (up to the fatal error block) is backed out. If the
error is not fatal, processing may continue for the rest of the
indirect block it's processing. The problem occurs when it finds a
non-fatal error block, then later it decides it cannot continue. In
that case, the "undo" function needs to back out changes, but it
needs to finish working through the data blocks on the rest of that
indirect block. This patch allows it to finish out the rest of the
"undo" work properly.
---
gfs2/fsck/metawalk.c | 46 ++++++++++++++++++++++++++++++++++++----------
1 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 9969374..4d16623 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1446,17 +1446,28 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
pass1. Therefore the individual check_data functions
should do a range check. */
rc = pass->check_data(ip, metablock, block, pass->private);
- if (!error && rc) {
- error = rc;
+ if (rc && (!error || (rc < error))) {
log_info("\n");
if (rc < 0) {
- *error_blk = block;
+ /* A fatal error trumps a non-fatal one. */
+ if ((*error_blk == 0) || (rc < error)) {
+ log_debug(_("Fatal error on block 0x"
+ "%llx preempts non-fatal "
+ "error on block 0x%llx\n"),
+ (unsigned long long)block,
+ (unsigned long long)*error_blk);
+ *error_blk = block;
+ }
log_info(_("Unrecoverable "));
+ } else { /* nonfatal error */
+ if ((*error_blk) == 0)
+ *error_blk = block;
}
log_info(_("data block error %d on block %llu "
"(0x%llx).\n"), rc,
(unsigned long long)block,
(unsigned long long)block);
+ error = rc;
}
if (rc < 0)
return rc;
@@ -1467,10 +1478,11 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
uint64_t *ptr_start, char *ptr_end,
- uint64_t error_blk)
+ uint64_t error_blk, int error)
{
int rc = 0;
uint64_t block, *ptr;
+ int found_error_blk = 0;
/* If there isn't much pointer corruption check the pointers */
for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
@@ -1480,13 +1492,25 @@ static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
if (skip_this_pass || fsck_abort)
return 1;
block = be64_to_cpu(*ptr);
- if (block == error_blk)
- return 1;
+ if (block == error_blk) {
+ if (error < 0) { /* A fatal error that stopped it? */
+ log_debug(_("Stopping the undo process: "
+ "fatal error block 0x%llx was "
+ "found.\n"),
+ (unsigned long long)error_blk);
+ return 1;
+ }
+ found_error_blk = 1;
+ log_debug(_("The non-fatal error block 0x%llx was "
+ "found, but undo processing will continue "
+ "until the end of this metadata block.\n"),
+ (unsigned long long)error_blk);
+ }
rc = pass->undo_check_data(ip, block, pass->private);
if (rc < 0)
return rc;
}
- return 0;
+ return found_error_blk;
}
static int hdr_size(struct gfs2_buffer_head *bh, int height)
@@ -1592,9 +1616,11 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
undo_metalist:
if (!error)
goto out;
- log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors.\n"),
+ log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors at "
+ "%lld (0x%llx).\n"),
(unsigned long long)ip->i_di.di_num.no_addr,
- (unsigned long long)ip->i_di.di_num.no_addr);
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)error_blk, (unsigned long long)error_blk);
if (!query( _("Remove the invalid inode? (y/n) "))) {
free_metalist(ip, &metalist[0]);
log_err(_("Invalid inode not deleted.\n"));
@@ -1623,7 +1649,7 @@ undo_metalist:
(uint64_t *)
(bh->b_data + head_size),
(bh->b_data + ip->i_sbd->bsize),
- error_blk);
+ error_blk, error);
if (rc > 0) {
hit_error_blk = 1;
rc = 0;
9 years
gfs2-utils: RHEL7 - fsck.gfs2: Adjust when hash table is doubled
by Andrew Price
Gitweb: http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=e93361da...
Commit: e93361dabf478d66bde70dcf2a165e202eec9461
Parent: e85902814022324dda6dce30df30e30efbced2c8
Author: Bob Peterson <rpeterso(a)redhat.com>
AuthorDate: Tue Jan 6 08:51:20 2015 -0600
Committer: Andrew Price <anprice(a)redhat.com>
CommitterDate: Wed Apr 1 16:51:35 2015 +0100
fsck.gfs2: Adjust when hash table is doubled
If directory entries are found to be on the wrong leaf block, they are
moved as needed. That sometimes requires the leaf block to be split,
which, in turn, sometimes requires the hash table be doubled. The
problem was that the functions that traverse the hash tables were
not taking into account the fact that the hash table could just
unexpectly double, throwing off all the pointers. This patch attempts
to correct that by fixing up the pointers as necessary.
---
gfs2/fsck/metawalk.c | 34 +++++++++++++++++++++++++++++++---
gfs2/fsck/metawalk.h | 2 +-
gfs2/fsck/pass1.c | 2 +-
gfs2/fsck/pass2.c | 25 ++++++++++++++++---------
4 files changed, 49 insertions(+), 14 deletions(-)
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 2dc8a32..9969374 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -461,7 +461,7 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
} else {
error = pass->check_dentry(ip, dent, prev, bh,
filename, count,
- lindex,
+ &lindex,
pass->private);
if (error < 0) {
stack;
@@ -504,6 +504,7 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
uint32_t count = 0;
struct gfs2_sbd *sdp = ip->i_sbd;
const char *msg;
+ int di_depth = ip->i_di.di_depth;
/* Make sure the block number is in range. */
if (!valid_block(ip->i_sbd, *leaf_no)) {
@@ -604,6 +605,16 @@ int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
}
}
out:
+ if (di_depth < ip->i_di.di_depth) {
+ log_debug(_("Depth of directory %lld (0x%llx) changed from "
+ "%d to %d; adjusting ref_count from %d to %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ di_depth, ip->i_di.di_depth,
+ *ref_count,
+ (*ref_count) << (ip->i_di.di_depth - di_depth));
+ (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+ }
brelse(lbh);
return 0;
@@ -618,6 +629,16 @@ bad_leaf:
return fix;
}
+ if (di_depth < ip->i_di.di_depth) {
+ log_debug(_("Depth of directory %lld (0x%llx) changed from "
+ "%d to %d. Adjusting ref_count from %d to %d\n"),
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ (unsigned long long)ip->i_di.di_num.no_addr,
+ di_depth, ip->i_di.di_depth,
+ *ref_count,
+ (*ref_count) << (ip->i_di.di_depth - di_depth));
+ (*ref_count) <<= (ip->i_di.di_depth - di_depth);
+ }
return 1;
}
@@ -773,8 +794,13 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
}
error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
&ref_count);
- if (ref_count != orig_ref_count)
+ if (ref_count != orig_ref_count) {
+ log_debug(_("Ref count of leaf 0x%llx "
+ "changed from %d to %d.\n"),
+ (unsigned long long)leaf_no,
+ orig_ref_count, ref_count);
tbl_valid = 0;
+ }
if (!leaf.lf_next || error)
break;
leaf_no = leaf.lf_next;
@@ -787,6 +813,8 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
(unsigned long long)ip->i_di.di_num.no_addr,
orig_di_depth, ip->i_di.di_depth);
tbl_valid = 0;
+ lindex <<= (ip->i_di.di_depth - orig_di_depth);
+ hsize = (1 << ip->i_di.di_depth);
}
if (orig_di_height != ip->i_di.di_height) {
log_debug(_("Height of 0x%llx changed from %d to "
@@ -1666,7 +1694,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
static int remove_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh,
- char *filename, uint32_t *count, int lindex,
+ char *filename, uint32_t *count, int *lindex,
void *private)
{
/* the metawalk_fxn's private field must be set to the dentry
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 482016a..aae9121 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -132,7 +132,7 @@ struct metawalk_fxns {
struct gfs2_dirent *prev,
struct gfs2_buffer_head *bh,
char *filename, uint32_t *count,
- int lindex, void *private);
+ int *lindex, void *private);
int (*check_eattr_entry) (struct gfs2_inode *ip,
struct gfs2_buffer_head *leaf_bh,
struct gfs2_ea_header *ea_hdr,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index dbe60bd..4f00316 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -161,7 +161,7 @@ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct gfs2_dirent dentry, *de;
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 631dc56..77bb888 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -307,7 +307,7 @@ static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
* or -1 on error
*/
static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
- const char *tmp_name, int lindex, int lindex_max,
+ const char *tmp_name, int *lindex, int lindex_max,
int hash_index, struct gfs2_buffer_head *bh,
struct dir_status *ds, struct gfs2_dirent *dent,
struct gfs2_dirent *de, struct gfs2_dirent *prev_de,
@@ -318,13 +318,14 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
uint64_t planned_leaf, real_leaf;
int li, dest_ref, error;
uint64_t *tbl;
+ int di_depth;
log_err(_("Directory entry '%s' at block %lld (0x%llx) is on the "
"wrong leaf block.\n"), tmp_name,
(unsigned long long)entry->no_addr,
(unsigned long long)entry->no_addr);
log_err(_("Leaf index is: 0x%x. The range for this leaf block is "
- "0x%x - 0x%x\n"), hash_index, lindex, lindex_max);
+ "0x%x - 0x%x\n"), hash_index, *lindex, lindex_max);
if (!query( _("Move the misplaced directory entry to "
"a valid leaf block? (y/n) "))) {
log_err( _("Misplaced directory entry not moved.\n"));
@@ -339,8 +340,8 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
}
planned_leaf = be64_to_cpu(tbl[hash_index]);
log_err(_("Moving it from leaf %llu (0x%llx) to %llu (0x%llx)\n"),
- (unsigned long long)be64_to_cpu(tbl[lindex]),
- (unsigned long long)be64_to_cpu(tbl[lindex]),
+ (unsigned long long)be64_to_cpu(tbl[*lindex]),
+ (unsigned long long)be64_to_cpu(tbl[*lindex]),
(unsigned long long)planned_leaf,
(unsigned long long)planned_leaf);
/* Can't trust lf_depth; we have to count */
@@ -366,10 +367,16 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
return 1; /* nuke the dent upon return */
}
+ di_depth = ip->i_di.di_depth;
if (dir_add(ip, tmp_name, de->de_name_len, &de->de_inum,
de->de_type) == 0) {
log_err(_("The misplaced directory entry was moved to a "
"valid leaf block.\n"));
+ if (ip->i_di.di_depth > di_depth) {
+ log_err(_("Directory hash table was doubled.\n"));
+ hash_index <<= (ip->i_di.di_depth - di_depth);
+ (*lindex) <<= (ip->i_di.di_depth - di_depth);
+ }
if (lgfs2_get_leaf_ptr(ip, hash_index, &real_leaf)) {
log_err(_("Could not read leaf %d in dinode %"PRIu64": %s\n"), hash_index,
(uint64_t)ip->i_di.di_num.no_addr, strerror(errno));
@@ -389,7 +396,7 @@ static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
appear later, we'll count it has part of our normal
processing when we get to that leaf block later on in the
hash table. */
- if (hash_index > lindex) {
+ if (hash_index > *lindex) {
log_err(_("Accounting deferred.\n"));
return 1; /* nuke the dent upon return */
}
@@ -655,7 +662,7 @@ static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
struct gfs2_sbd *sdp = ip->i_sbd;
uint8_t q = 0;
@@ -811,8 +818,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
block have a hash table index that fits */
if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
hash_index = hash_table_index(de->de_hash, ip);
- lindex_max = hash_table_max(lindex, ip, bh);
- if (hash_index < lindex || hash_index > lindex_max) {
+ lindex_max = hash_table_max(*lindex, ip, bh);
+ if (hash_index < *lindex || hash_index > lindex_max) {
int nuke_dent;
nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
@@ -1024,7 +1031,7 @@ static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
static int basic_check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct gfs2_buffer_head *bh, char *filename,
- uint32_t *count, int lindex, void *priv)
+ uint32_t *count, int *lindex, void *priv)
{
uint8_t q = 0;
char tmp_name[MAX_FILENAME];
9 years