]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
fincore: add recursive directory scanning
authorMatteo Croce <teknoraver@meta.com>
Sat, 19 Apr 2025 01:09:25 +0000 (03:09 +0200)
committerMatteo Croce <teknoraver@meta.com>
Wed, 21 May 2025 02:21:23 +0000 (04:21 +0200)
Add a --recursive flag to fincore which allows to recursively scan
directories.

Co-authored-by: Karel Zak <kzak@redhat.com>
Signed-off-by: Matteo Croce <teknoraver@meta.com>
18 files changed:
bash-completion/fincore
configure.ac
meson.build
misc-utils/fincore.1.adoc
misc-utils/fincore.c
tests/expected/fincore/count.16384 [new file with mode: 0644]
tests/expected/fincore/count.16384.recursive [new file with mode: 0644]
tests/expected/fincore/count.4096
tests/expected/fincore/count.4096.recursive [new file with mode: 0644]
tests/expected/fincore/count.65536
tests/expected/fincore/count.65536.recursive [new file with mode: 0644]
tests/expected/fincore/count.err [moved from tests/expected/fincore/count.err.4096 with 100% similarity]
tests/expected/fincore/count.err.65536 [deleted file]
tests/expected/fincore/count.err.nosize [deleted file]
tests/expected/fincore/count.nosize
tests/expected/fincore/count.nosize.recursive [new file with mode: 0644]
tests/helpers/test_sysinfo.c
tests/ts/fincore/count

index f0f9c45f17e547803b975381d5e04aa98ad61ebc..5f073b4ec013a542151fbfd494d956c3737bab08 100644 (file)
@@ -32,6 +32,7 @@ _fincore_module()
                                --output
                                --output-all
                                --raw
+                               --recursive
                                --help
                                --version
                        "
index 68a8f54d923ca9e142a2a94f66dcae75bee7a928..b04ec74db11ae8a78fa35fefd26548820bf6887d 100644 (file)
@@ -822,6 +822,10 @@ AS_IF([test x"$have_dirfd" = xno], [
 
 AM_CONDITIONAL([HAVE_DIRFD], [test "x$have_dirfd" = xyes || test "x$have_ddfd" = xyes])
 
+have_fts_open=no
+AC_CHECK_FUNCS([fts_open], [have_fts_open=yes], [have_fts_open=no])
+AM_CONDITIONAL([HAVE_FTS_OPEN], [test "x$have_fts_open" = xyes])
+
 MQ_LIBS=
 AC_CHECK_LIB([rt], [mq_open], [MQ_LIBS="-lrt"])
 AC_SUBST([MQ_LIBS])
index e225454d708d067b9ca28f4434af22b2bea3320b..ec471f4b59d43745053d9fa9772bb13a602be1e0 100644 (file)
@@ -683,6 +683,7 @@ funcs = '''
         open_memstream
         reboot
         getusershell
+        fts_open
 '''.split()
 
 foreach func: funcs
index 2ee43435f843b174590541dbf0c9b135111f6dc2..7d0c8fca3718cca93f9943f2b0d593f421ddc628 100644 (file)
@@ -47,6 +47,9 @@ Produce output in raw format. All potentially unsafe characters are hex-escaped
 *-J*, *--json*::
 Use JSON output format.
 
+*-R*, *--recursive*::
+Recursively check all files in directories.
+
 include::man-common/help-version.adoc[]
 
 == AUTHORS
index a2f2400521d1db61413aba89689bac7f742c98fc..160dd8e0bf09c64d76500e89e7a9808ef0cd5e01 100644 (file)
 #include <stdio.h>
 #include <string.h>
 
+#ifdef HAVE_FTS_OPEN
+#include <fts.h>
+#endif
+
 #include "c.h"
 #include "cctype.h"
 #include "nls.h"
@@ -125,12 +129,13 @@ struct fincore_control {
        unsigned int bytes : 1,
                     noheadings : 1,
                     raw : 1,
-                    json : 1;
+                    json : 1,
+                    recursive : 1;
 
 };
 
 struct fincore_state {
-       const char * const name;
+       const char * name;
        long long unsigned int file_size;
 
        struct cachestat cstat;
@@ -357,30 +362,34 @@ static int fincore_fd (struct fincore_control *ctl,
  * Returns: <0 on error, 0 success, 1 ignore.
  */
 static int fincore_name(struct fincore_control *ctl,
-                       struct fincore_state *st)
+                       const char *filename,
+                       const char *showname,
+                       struct stat *statp)
 {
        int fd;
        int rc = 0;
-       struct stat sb;
+       struct stat _sb, *sb = statp ?: &_sb;
+       struct fincore_state _st = { .name = filename }, *st = &_st;
 
-       if ((fd = open (st->name, O_RDONLY)) < 0) {
-               warn(_("failed to open: %s"), st->name);
+       if ((fd = open(filename, O_RDONLY)) < 0) {
+               warn(_("failed to open: %s"), showname);
                return -errno;
        }
 
-       if (fstat (fd, &sb) < 0) {
-               warn(_("failed to do fstat: %s"), st->name);
-               close (fd);
-               return -errno;
+       if (!statp) {
+               if (fstat (fd, sb) < 0) {
+                       warn(_("failed to do fstat: %s"), showname);
+                       close (fd);
+                       return -errno;
+               }
        }
-       st->file_size = sb.st_size;
 
-       if (S_ISBLK(sb.st_mode)) {
+       if (S_ISBLK(sb->st_mode)) {
                rc = blkdev_get_size(fd, &st->file_size);
                if (rc)
-                       warn(_("failed ioctl to get size: %s"), st->name);
-       } else if (S_ISREG(sb.st_mode)) {
-               st->file_size = sb.st_size;
+                       warn(_("failed ioctl to get size: %s"), showname);
+       } else if (S_ISREG(sb->st_mode)) {
+               st->file_size = sb->st_size;
        } else {
                rc = 1;                 /* ignore things like symlinks
                                         * and directories*/
@@ -390,6 +399,12 @@ static int fincore_name(struct fincore_control *ctl,
                rc = fincore_fd(ctl, fd, st);
 
        close (fd);
+
+       if (!rc) {
+               st->name = showname;
+               rc = add_output_data(ctl, st);
+       }
+
        return rc;
 }
 
@@ -408,6 +423,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -o, --output <list>   output columns\n"), out);
        fputs(_("     --output-all      output all columns\n"), out);
        fputs(_(" -r, --raw             use raw output format\n"), out);
+       fputs(_(" -R, --recursive       recursively check all files in directories\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fprintf(out, USAGE_HELP_OPTIONS(23));
@@ -445,6 +461,7 @@ int main(int argc, char ** argv)
                { "help",       no_argument, NULL, 'h' },
                { "json",       no_argument, NULL, 'J' },
                { "raw",        no_argument, NULL, 'r' },
+               { "recursive",  no_argument, NULL, 'R' },
                { NULL, 0, NULL, 0 },
        };
 
@@ -453,7 +470,7 @@ int main(int argc, char ** argv)
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while ((c = getopt_long (argc, argv, "bno:JrVh", longopts, NULL)) != -1) {
+       while ((c = getopt_long (argc, argv, "bno:JrRVh", longopts, NULL)) != -1) {
                switch (c) {
                case 'b':
                        ctl.bytes = 1;
@@ -474,6 +491,12 @@ int main(int argc, char ** argv)
                case 'r':
                        ctl.raw = 1;
                        break;
+               case 'R':
+#ifndef HAVE_FTS_OPEN
+                       errx(EXIT_FAILURE, _("recursive option is not supported"));
+#endif
+                       ctl.recursive = 1;
+                       break;
                case 'V':
                        print_version(EXIT_SUCCESS);
                case 'h':
@@ -537,25 +560,32 @@ int main(int argc, char ** argv)
                }
        }
 
-       for(; optind < argc; optind++) {
-               struct fincore_state st = {
-                       .name = argv[optind],
-               };
+       if (ctl.recursive) {
+#ifdef HAVE_FTS_OPEN
+               FTS *fts = fts_open(argv + optind, FTS_PHYSICAL, NULL);
+               FTSENT *ent;
 
-               switch (fincore_name(&ctl, &st)) {
-               case 0:
-                       add_output_data(&ctl, &st);
-                       break;
-               case 1:
-                       break; /* ignore */
-               default:
+               if (!fts) {
+                       warn(_("failed to iterate tree"));
                        rc = EXIT_FAILURE;
-                       break;
+               } else {
+                       while ((ent = fts_read(fts)) != NULL) {
+                               if (ent->fts_info == FTS_F || ent->fts_info == FTS_DEFAULT) {
+                                       /* fts changes directory when iterating,
+                                        * so we need to use .fts_accpath to access
+                                        * the file named .fts_path */
+                                       rc |= fincore_name(&ctl, ent->fts_accpath, ent->fts_path, ent->fts_statp);
+                               }
+                       }
                }
+#endif
+       } else {
+               for(; optind < argc; optind++)
+                       rc |= fincore_name(&ctl, argv[optind], argv[optind], NULL);
        }
 
        scols_print_table(ctl.tb);
        scols_unref_table(ctl.tb);
 
-       return rc;
+       return rc ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/tests/expected/fincore/count.16384 b/tests/expected/fincore/count.16384
new file mode 100644 (file)
index 0000000..cf4dbf7
--- /dev/null
@@ -0,0 +1,53 @@
+[ NO EXCITING FILE ]
+return value: 1
+0 0 i_EMPTY_FILE
+return value: 0
+1 16383 i_PAGESIZE_-1__incore_
+return value: 0
+1 16384 i_JUST_PAGESIZE_incore_
+return value: 0
+0 16384 i_JUST_PAGESIZE_directio_
+return value: 0
+2 32768 i_TWO_PAGES_incore_
+return value: 0
+0 32768 i_TWO_PAGES_directio_
+return value: 0
+1 32768 i_TWO_PAGES_mixed_directio_incore_
+return value: 0
+1 32768 i_TWO_PAGES_mixed_incore_directio_
+return value: 0
+2 536854528 i_WINDOW_SIZE_incore-sparse-incore_
+return value: 0
+0 536854528 i_WINDOW_SIZE_directio-sparse-directio_
+return value: 0
+1 536854528 i_WINDOW_SIZE_incore-sparse-directio_
+return value: 0
+1 536854528 i_WINDOW_SIZE_directio-sparse-incore_
+return value: 0
+2 536870912 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
+return value: 0
+0 536870912 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
+return value: 0
+1 536870912 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
+return value: 0
+1 536870912 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
+return value: 0
+[ MULTIPLE FILES ]
+PAGES SIZE FILE
+0 0 i_EMPTY_FILE
+1 16383 i_PAGESIZE_-1__incore_
+1 16384 i_JUST_PAGESIZE_incore_
+0 16384 i_JUST_PAGESIZE_directio_
+2 32768 i_TWO_PAGES_incore_
+0 32768 i_TWO_PAGES_directio_
+1 32768 i_TWO_PAGES_mixed_directio_incore_
+1 32768 i_TWO_PAGES_mixed_incore_directio_
+2 536854528 i_WINDOW_SIZE_incore-sparse-incore_
+0 536854528 i_WINDOW_SIZE_directio-sparse-directio_
+1 536854528 i_WINDOW_SIZE_incore-sparse-directio_
+1 536854528 i_WINDOW_SIZE_directio-sparse-incore_
+2 536870912 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
+0 536870912 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
+1 536870912 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
+1 536870912 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
+return value: 1
diff --git a/tests/expected/fincore/count.16384.recursive b/tests/expected/fincore/count.16384.recursive
new file mode 100644 (file)
index 0000000..048a2d2
--- /dev/null
@@ -0,0 +1,12 @@
+0 0 i_dir/EMPTY_FILE
+return value: 0
+1 16383 i_dir/PAGESIZE_-1__incore_
+return value: 0
+1 16384 i_dir/JUST_PAGESIZE_incore_
+return value: 0
+[ RECURSIVE SCAN ]
+0 0 i_dir/EMPTY_FILE
+1 16383 i_dir/PAGESIZE_-1__incore_
+1 16384 i_dir/JUST_PAGESIZE_incore_
+PAGES SIZE FILE
+return value: 0
index a5d764f5813668fa0cadc06e0031548b65c78fd6..51f807868696b6474aebcd03ff3b0a44f26b9825 100644 (file)
@@ -1,20 +1,20 @@
 [ NO EXCITING FILE ]
 return value: 1
-0     0 i_EMPTY_FILE
+0 0 i_EMPTY_FILE
 return value: 0
-1  4095 i_PAGESIZE_-1__incore_
+1 4095 i_PAGESIZE_-1__incore_
 return value: 0
-1  4096 i_JUST_PAGESIZE_incore_
+1 4096 i_JUST_PAGESIZE_incore_
 return value: 0
-0  4096 i_JUST_PAGESIZE_directio_
+0 4096 i_JUST_PAGESIZE_directio_
 return value: 0
-2  8192 i_TWO_PAGES_incore_
+2 8192 i_TWO_PAGES_incore_
 return value: 0
-0  8192 i_TWO_PAGES_directio_
+0 8192 i_TWO_PAGES_directio_
 return value: 0
-1  8192 i_TWO_PAGES_mixed_directio_incore_
+1 8192 i_TWO_PAGES_mixed_directio_incore_
 return value: 0
-1  8192 i_TWO_PAGES_mixed_incore_directio_
+1 8192 i_TWO_PAGES_mixed_incore_directio_
 return value: 0
 2 134213632 i_WINDOW_SIZE_incore-sparse-incore_
 return value: 0
@@ -33,21 +33,21 @@ return value: 0
 1 134217728 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
 return value: 0
 [ MULTIPLE FILES ]
-PAGES      SIZE FILE
-    0         0 i_EMPTY_FILE
-    1      4095 i_PAGESIZE_-1__incore_
-    1      4096 i_JUST_PAGESIZE_incore_
-    0      4096 i_JUST_PAGESIZE_directio_
-    2      8192 i_TWO_PAGES_incore_
-    0      8192 i_TWO_PAGES_directio_
-    1      8192 i_TWO_PAGES_mixed_directio_incore_
-    1      8192 i_TWO_PAGES_mixed_incore_directio_
-    2 134213632 i_WINDOW_SIZE_incore-sparse-incore_
-    0 134213632 i_WINDOW_SIZE_directio-sparse-directio_
-    1 134213632 i_WINDOW_SIZE_incore-sparse-directio_
-    1 134213632 i_WINDOW_SIZE_directio-sparse-incore_
-    2 134217728 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
-    0 134217728 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
-    1 134217728 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
-    1 134217728 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
+PAGES SIZE FILE
+0 0 i_EMPTY_FILE
+1 4095 i_PAGESIZE_-1__incore_
+1 4096 i_JUST_PAGESIZE_incore_
+0 4096 i_JUST_PAGESIZE_directio_
+2 8192 i_TWO_PAGES_incore_
+0 8192 i_TWO_PAGES_directio_
+1 8192 i_TWO_PAGES_mixed_directio_incore_
+1 8192 i_TWO_PAGES_mixed_incore_directio_
+2 134213632 i_WINDOW_SIZE_incore-sparse-incore_
+0 134213632 i_WINDOW_SIZE_directio-sparse-directio_
+1 134213632 i_WINDOW_SIZE_incore-sparse-directio_
+1 134213632 i_WINDOW_SIZE_directio-sparse-incore_
+2 134217728 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
+0 134217728 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
+1 134217728 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
+1 134217728 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
 return value: 1
diff --git a/tests/expected/fincore/count.4096.recursive b/tests/expected/fincore/count.4096.recursive
new file mode 100644 (file)
index 0000000..d1b5846
--- /dev/null
@@ -0,0 +1,12 @@
+0 0 i_dir/EMPTY_FILE
+return value: 0
+1 4095 i_dir/PAGESIZE_-1__incore_
+return value: 0
+1 4096 i_dir/JUST_PAGESIZE_incore_
+return value: 0
+[ RECURSIVE SCAN ]
+0 0 i_dir/EMPTY_FILE
+1 4095 i_dir/PAGESIZE_-1__incore_
+1 4096 i_dir/JUST_PAGESIZE_incore_
+PAGES SIZE FILE
+return value: 0
index 07a97d2e293fddf614d7bf04e624149bebd08451..d66c78bd79334800d59ae900043994627ee9d1c2 100644 (file)
@@ -1,6 +1,6 @@
 [ NO EXCITING FILE ]
 return value: 1
-0     0 i_EMPTY_FILE
+0 0 i_EMPTY_FILE
 return value: 0
 1 65535 i_PAGESIZE_-1__incore_
 return value: 0
@@ -33,21 +33,21 @@ return value: 0
 1 2147483648 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
 return value: 0
 [ MULTIPLE FILES ]
-PAGES       SIZE FILE
-    0          0 i_EMPTY_FILE
-    1      65535 i_PAGESIZE_-1__incore_
-    1      65536 i_JUST_PAGESIZE_incore_
-    0      65536 i_JUST_PAGESIZE_directio_
-    2     131072 i_TWO_PAGES_incore_
-    0     131072 i_TWO_PAGES_directio_
-    1     131072 i_TWO_PAGES_mixed_directio_incore_
-    1     131072 i_TWO_PAGES_mixed_incore_directio_
-    2 2147418112 i_WINDOW_SIZE_incore-sparse-incore_
-    0 2147418112 i_WINDOW_SIZE_directio-sparse-directio_
-    1 2147418112 i_WINDOW_SIZE_incore-sparse-directio_
-    1 2147418112 i_WINDOW_SIZE_directio-sparse-incore_
-    2 2147483648 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
-    0 2147483648 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
-    1 2147483648 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
-    1 2147483648 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
+PAGES SIZE FILE
+0 0 i_EMPTY_FILE
+1 65535 i_PAGESIZE_-1__incore_
+1 65536 i_JUST_PAGESIZE_incore_
+0 65536 i_JUST_PAGESIZE_directio_
+2 131072 i_TWO_PAGES_incore_
+0 131072 i_TWO_PAGES_directio_
+1 131072 i_TWO_PAGES_mixed_directio_incore_
+1 131072 i_TWO_PAGES_mixed_incore_directio_
+2 2147418112 i_WINDOW_SIZE_incore-sparse-incore_
+0 2147418112 i_WINDOW_SIZE_directio-sparse-directio_
+1 2147418112 i_WINDOW_SIZE_incore-sparse-directio_
+1 2147418112 i_WINDOW_SIZE_directio-sparse-incore_
+2 2147483648 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
+0 2147483648 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
+1 2147483648 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
+1 2147483648 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
 return value: 1
diff --git a/tests/expected/fincore/count.65536.recursive b/tests/expected/fincore/count.65536.recursive
new file mode 100644 (file)
index 0000000..3a881e5
--- /dev/null
@@ -0,0 +1,12 @@
+0 0 i_dir/EMPTY_FILE
+return value: 0
+1 65535 i_dir/PAGESIZE_-1__incore_
+return value: 0
+1 65536 i_dir/JUST_PAGESIZE_incore_
+return value: 0
+[ RECURSIVE SCAN ]
+0 0 i_dir/EMPTY_FILE
+1 65535 i_dir/PAGESIZE_-1__incore_
+1 65536 i_dir/JUST_PAGESIZE_incore_
+PAGES SIZE FILE
+return value: 0
diff --git a/tests/expected/fincore/count.err.65536 b/tests/expected/fincore/count.err.65536
deleted file mode 100644 (file)
index e3fad01..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[ NO EXCITING FILE ]
-fincore: failed to open: no_such_file: No such file or directory
-[ MULTIPLE FILES ]
-fincore: failed to open: no_such_file: No such file or directory
diff --git a/tests/expected/fincore/count.err.nosize b/tests/expected/fincore/count.err.nosize
deleted file mode 100644 (file)
index e3fad01..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[ NO EXCITING FILE ]
-fincore: failed to open: no_such_file: No such file or directory
-[ MULTIPLE FILES ]
-fincore: failed to open: no_such_file: No such file or directory
index ec2654295793e48a019366afeb71f61728038aff..0429fc309be1e3012b5e633f63b7c6a2317f195c 100644 (file)
@@ -34,20 +34,20 @@ return value: 0
 return value: 0
 [ MULTIPLE FILES ]
 PAGES FILE
-    0 i_EMPTY_FILE
-    1 i_PAGESIZE_-1__incore_
-    1 i_JUST_PAGESIZE_incore_
-    0 i_JUST_PAGESIZE_directio_
-    2 i_TWO_PAGES_incore_
-    0 i_TWO_PAGES_directio_
-    1 i_TWO_PAGES_mixed_directio_incore_
-    1 i_TWO_PAGES_mixed_incore_directio_
-    2 i_WINDOW_SIZE_incore-sparse-incore_
-    0 i_WINDOW_SIZE_directio-sparse-directio_
-    1 i_WINDOW_SIZE_incore-sparse-directio_
-    1 i_WINDOW_SIZE_directio-sparse-incore_
-    2 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
-    0 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
-    1 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
-    1 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
+0 i_EMPTY_FILE
+1 i_PAGESIZE_-1__incore_
+1 i_JUST_PAGESIZE_incore_
+0 i_JUST_PAGESIZE_directio_
+2 i_TWO_PAGES_incore_
+0 i_TWO_PAGES_directio_
+1 i_TWO_PAGES_mixed_directio_incore_
+1 i_TWO_PAGES_mixed_incore_directio_
+2 i_WINDOW_SIZE_incore-sparse-incore_
+0 i_WINDOW_SIZE_directio-sparse-directio_
+1 i_WINDOW_SIZE_incore-sparse-directio_
+1 i_WINDOW_SIZE_directio-sparse-incore_
+2 i_WINDOW_SIZE_+_1_page_incore-sparse-incore_
+0 i_WINDOW_SIZE_+_1_page_directio-sparse-directio_
+1 i_WINDOW_SIZE_+_1_page_incore-sparse-directio_
+1 i_WINDOW_SIZE_+_1_page_directio-sparse-incore_
 return value: 1
diff --git a/tests/expected/fincore/count.nosize.recursive b/tests/expected/fincore/count.nosize.recursive
new file mode 100644 (file)
index 0000000..4c8c832
--- /dev/null
@@ -0,0 +1,12 @@
+0 i_dir/EMPTY_FILE
+return value: 0
+1 i_dir/PAGESIZE_-1__incore_
+return value: 0
+1 i_dir/JUST_PAGESIZE_incore_
+return value: 0
+[ RECURSIVE SCAN ]
+0 i_dir/EMPTY_FILE
+1 i_dir/JUST_PAGESIZE_incore_
+1 i_dir/PAGESIZE_-1__incore_
+PAGES FILE
+return value: 0
index b4a601e3bc030590eae301b6bbcde21f319ea37d..001f8599eb0407ba4e5c63ee0d44d49bde703c8d 100644 (file)
 # endif
 #endif
 
+#ifdef HAVE_FTS_OPEN
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <fts.h>
+#endif
+
 #include "xalloc.h"
 #include "namespace.h"
 
@@ -218,6 +224,89 @@ static int hlp_hostname(void)
        return 0;
 }
 
+static int hlp_fts(void)
+{
+#ifdef HAVE_FTS_OPEN
+       char template[NAME_MAX];
+       char *paths[] = { NULL, NULL };
+       char path[PATH_MAX];
+       FTSENT *node;
+       FTS *fts;
+       int r;
+       int files = 0, dirs = 0;
+
+       snprintf(template, sizeof(template), "%s/fts_checkXXXXXX", getenv("TMPDIR") ?: "/tmp");
+
+       paths[0] = mkdtemp(template);
+       if (paths[0] == NULL)
+               return 0;
+
+       r = mkdir(template, 0755);
+       if (r < 0 && errno != EEXIST)
+               return 0;
+       dirs++;
+
+       snprintf(path, sizeof(path), "%s/subdir", template);
+       r = mkdir(path, 0755);
+       if (r < 0 && errno != EEXIST)
+               return 0;
+       dirs++;
+
+       snprintf(path, sizeof(path), "%s/file1.txt", template);
+       r = creat(path, 0644);
+       if (r < 0)
+               return 0;
+       close(r);
+       files++;
+
+       snprintf(path, sizeof(path), "%s/file2.txt", template);
+       r = creat(path, 0644);
+       if (r < 0)
+               return 0;
+       close(r);
+       files++;
+
+       snprintf(path, sizeof(path), "%s/subdir/file3.txt", template);
+       r = creat(path, 0644);
+       if (r < 0)
+               return 0;
+       close(r);
+       files++;
+
+       snprintf(path, sizeof(path), "%s/subdir/file4.txt", template);
+       r = creat(path, 0644);
+       if (r < 0)
+               return 0;
+       close(r);
+       files++;
+
+       fts = fts_open(paths, FTS_NOCHDIR | FTS_PHYSICAL, NULL);
+       if (fts == NULL)
+               return 0;
+
+       while ((node = fts_read(fts)) != NULL) {
+               switch (node->fts_info) {
+               case FTS_F:
+                       files--;
+                       break;
+               case FTS_D:
+                       dirs--;
+                       break;
+               case FTS_ERR:
+                       return 0;
+               default:
+                       break;
+               }
+       }
+
+       if (fts_close(fts) < 0 || files != 0 || dirs != 0)
+               return 0;
+
+       puts("FTS");
+#endif
+       return 0;
+}
+
 static const mntHlpfnc hlps[] =
 {
        { "WORDSIZE",   hlp_wordsize    },
@@ -238,6 +327,7 @@ static const mntHlpfnc hlps[] =
        { "ns-gettype-ok", hlp_get_nstype_ok },
        { "ns-getuserns-ok", hlp_get_userns_ok },
        { "hostname", hlp_hostname, },
+       { "fts", hlp_fts, },
        { NULL, NULL }
 };
 
index 8a5fffe364aa4229503af43e5ec966cdcbe762d4..e132b99026903f7beda9b33eef5deb6a8bb6d70a 100755 (executable)
@@ -14,6 +14,11 @@ if [[ "$FS" = "tmpfs" || "$FS" = "overlay" || "$FS" = "" ]]; then
        ts_skip "fincore does not work on tmpfs or unknown fs"
 fi
 
+# https://github.com/util-linux/util-linux/pull/3529
+if [ "$($TS_HELPER_SYSINFO fts)" != FTS ]; then
+    ts_skip "fts_open() is unreliable on this setup"
+fi
+
 function footer
 {
     echo "return value: $1"
@@ -23,7 +28,7 @@ function make_input_name
 {
     header=$1
     prefix=i_
-    echo ${prefix}$(sed -e "s/[^-+a-zA-Z0-9_]/_/g"<<<"$header")
+    echo ${prefix}$(sed -e "s/[^-+a-zA-Z0-9_/]/_/g"<<<"$header")
 }
 
 function _dd
@@ -71,7 +76,7 @@ function run_dd_test
                _dd if=/dev/zero of=$input count=1 bs=$bs $flags || return
     fi
 
-    $TS_CMD_FINCORE --output $OUT_COLUMNS  --bytes --noheadings $input
+    $TS_CMD_FINCORE --raw --output $OUT_COLUMNS  --bytes --noheadings $input
 
     footer "$?"
 }
@@ -91,7 +96,7 @@ function run_dd_dd_test
     _dd if=/dev/zero of=$input count=1 bs=$bs $flags0 || return
     _dd if=/dev/zero of=$input count=1 bs=$bs $flags1 || return
 
-    $TS_CMD_FINCORE --output $OUT_COLUMNS --bytes --noheadings $input
+    $TS_CMD_FINCORE --raw --output $OUT_COLUMNS --bytes --noheadings $input
 
     footer "$?"
 }
@@ -100,17 +105,32 @@ function run_dd_dd_test
 PAGE_SIZE=$($TS_HELPER_SYSINFO pagesize)
 WINDOW_SIZE=$(( 32 * 1024 * PAGE_SIZE ))
 
+AGG_OUT="$TS_OUTDIR/count.aggregate"
+AGG_ERR="$TS_OUTDIR/count.err.aggregate"
+
 # we use PAGE_SIZE dependent output for a few systems
 if test -f "$TS_EXPECTED.$PAGE_SIZE"; then
-       TS_EXPECTED+=".$PAGE_SIZE"
-       TS_EXPECTED_ERR+=".$PAGE_SIZE"
+       cat "$TS_EXPECTED.$PAGE_SIZE" >"$AGG_OUT"
        OUT_COLUMNS="PAGES,SIZE,FILE"
 else
-       TS_EXPECTED+=".nosize"
-       TS_EXPECTED_ERR+=".nosize"
+       cat "$TS_EXPECTED.nosize" >"$AGG_OUT"
        OUT_COLUMNS="PAGES,FILE"
 fi
 
+cat "$TS_EXPECTED_ERR" >"$AGG_ERR"
+
+if ! $TS_CMD_FINCORE --recursive |& grep -q 'recursive option is not supported' ; then
+       RECURSIVE=1
+       if test -f "$TS_EXPECTED.$PAGE_SIZE"; then
+               cat "$TS_EXPECTED.$PAGE_SIZE.recursive" >> "$AGG_OUT"
+       else
+               cat "$TS_EXPECTED.nosize.recursive" >> "$AGG_OUT"
+       fi
+       echo '[ RECURSIVE SCAN ]' >> "$AGG_ERR"
+fi
+
+TS_EXPECTED="$AGG_OUT"
+TS_EXPECTED_ERR="$AGG_ERR"
 
 ts_check_test_command "$TS_CMD_FINCORE"
 ts_cd "$TS_OUTDIR"
@@ -125,7 +145,7 @@ ts_log_both "[ NO EXCITING FILE ]"
     input=no_such_file
     INPUT="${INPUT} ${input}"
 
-    $TS_CMD_FINCORE --output $OUT_COLUMNS --bytes --noheadings $input
+    $TS_CMD_FINCORE --raw --output $OUT_COLUMNS --bytes --noheadings $input
     footer "$?"
 } >> $TS_OUTPUT 2>> $TS_ERRLOG
 
@@ -154,13 +174,13 @@ ts_log_both "[ NO EXCITING FILE ]"
 } >> $TS_OUTPUT 2>> $TS_ERRLOG
 
 {
-    run_dd_dd_test "TWO PAGES(mixed directio/incore)" \
+    run_dd_dd_test "TWO PAGES(mixed directio,incore)" \
                            oflag=direct \
                            "oflag=append seek=1"
 } >> $TS_OUTPUT 2>> $TS_ERRLOG
 
 {
-    run_dd_dd_test "TWO PAGES(mixed incore/directio)" \
+    run_dd_dd_test "TWO PAGES(mixed incore,directio)" \
                   "" \
                   "oflag=direct,append seek=1"
 } >> $TS_OUTPUT 2>> $TS_ERRLOG
@@ -223,9 +243,32 @@ ts_log_both "[ NO EXCITING FILE ]"
 
 ts_log_both "[ MULTIPLE FILES ]"
 {
-    $TS_CMD_FINCORE --output $OUT_COLUMNS --bytes $INPUT
+    $TS_CMD_FINCORE --raw --output $OUT_COLUMNS --bytes $INPUT
     footer "$?"
 } >> $TS_OUTPUT 2>> $TS_ERRLOG
 
+if [ -n "$RECURSIVE" ]; then
+       dir=$(make_input_name dir)
+       mkdir -p "$dir"
+
+       {
+           run_dd_test "dir/EMPTY FILE" 0
+       } >> $TS_OUTPUT 2>> $TS_ERRLOG
+
+       {
+           run_dd_test "dir/PAGESIZE -1 (incore)" $(( PAGE_SIZE - 1 ))
+       } >> $TS_OUTPUT 2>> $TS_ERRLOG
+
+       {
+           run_dd_test "dir/JUST PAGESIZE(incore)" $(( PAGE_SIZE ))
+       } >> $TS_OUTPUT 2>> $TS_ERRLOG
+
+       ts_log_both "[ RECURSIVE SCAN ]"
+       {
+           $TS_CMD_FINCORE --raw --output $OUT_COLUMNS --bytes --recursive --raw "$dir" |sort
+           footer "$?"
+       } >> $TS_OUTPUT 2>> $TS_ERRLOG
+fi
+
 rm -f $INPUT
 ts_finalize