[LTP] [PATCH] Add read_all01 test
Richard Palethorpe
rpalethorpe@suse.com
Tue Jan 9 16:30:28 CET 2018
While using a shell script I wrote which dumps some of the contents of /sys
and /proc (submitted in a separate patch) I found some minor kernel
bugs. There is already a test specifically for /proc however it is attempting
to verify the behavior of particular files, where as this test is just doing
a basic sanity check and thus can be applied more generally.
Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com>
---
runtest/fs | 4 +
testcases/kernel/fs/read_all/.gitignore | 1 +
testcases/kernel/fs/read_all/Makefile | 22 +++
testcases/kernel/fs/read_all/read_all01.c | 288 ++++++++++++++++++++++++++++++
4 files changed, 315 insertions(+)
create mode 100644 testcases/kernel/fs/read_all/.gitignore
create mode 100644 testcases/kernel/fs/read_all/Makefile
create mode 100644 testcases/kernel/fs/read_all/read_all01.c
diff --git a/runtest/fs b/runtest/fs
index 3fa210a9f..b8bf54f24 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -69,6 +69,10 @@ fs_di fs_di -d $TMPDIR
# Was not sure why it should reside in runtest/crashme and won´t get tested ever
proc01 proc01 -m 128
+read_all01_dev read_all01 -d /dev
+read_all01_proc read_all01 -d /proc
+read_all01_sys read_all01
+
#Run the File System Race Condition Check tests as well
fs_racer fs_racer.sh -t 5
diff --git a/testcases/kernel/fs/read_all/.gitignore b/testcases/kernel/fs/read_all/.gitignore
new file mode 100644
index 000000000..4b405760c
--- /dev/null
+++ b/testcases/kernel/fs/read_all/.gitignore
@@ -0,0 +1 @@
+read_all01
diff --git a/testcases/kernel/fs/read_all/Makefile b/testcases/kernel/fs/read_all/Makefile
new file mode 100644
index 000000000..f51fbe591
--- /dev/null
+++ b/testcases/kernel/fs/read_all/Makefile
@@ -0,0 +1,22 @@
+# Copyright (c) 2017 Linux Test Project
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+CFLAGS += -D_GNU_SOURCE
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/read_all/read_all01.c b/testcases/kernel/fs/read_all/read_all01.c
new file mode 100644
index 000000000..f746db33b
--- /dev/null
+++ b/testcases/kernel/fs/read_all/read_all01.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*
+ * Perform a small read on every file in a directory tree.
+ *
+ * Useful for testing file systems like proc, sysfs and debugfs or anything
+ * which exposes a file like API so long as it respects O_NONBLOCK. This test
+ * is not concerned if a particular file in one of these file systems conforms
+ * exactly to its specific documented behavior. Just whether reading from that
+ * file causes a serious error such as a NULL pointer dereference.
+ *
+ * It is not required to run this as root, but test coverage will be much
+ * higher with full privileges.
+ *
+ * The reads are preformed by worker processes which are given file paths by a
+ * single parent process. The parent process recursively scans a given
+ * directory and passes the file paths it finds to the child processes through
+ * a pipe. The test will use a maximum of 15 processes, depending on the
+ * number of CPU cores available, under the assumption that while parallelism
+ * is good we don't want to spend too much time creating processes,
+ * distributing data and waiting for locks.
+ *
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <fnmatch.h>
+
+#include "tst_test.h"
+#include "tst_minmax.h"
+
+#define BUFFER_SIZE 1024
+#define MAX_PATH 4096
+
+struct worker {
+ pid_t pid;
+ int in;
+ int out;
+};
+
+static char *verbose;
+static char *root_dir = "/sys";
+static char *exclude;
+static long worker_count;
+static struct worker *workers;
+
+static struct tst_option options[] = {
+ {"v", &verbose,
+ "-v Print information about successful reads"},
+ {"d:", &root_dir,
+ "-d path Path to the directory to read from, defaults to /sys"},
+ {"e:", &exclude,
+ "-e pattern Ignore files which match an 'extended' pattern, see fnmatch(3)"},
+ {NULL, NULL, NULL}
+};
+
+static void read_test(const char *path)
+{
+ char buf[BUFFER_SIZE];
+ int fd;
+ ssize_t count;
+
+ if (exclude && !fnmatch(exclude, path, FNM_EXTMATCH)) {
+ if (verbose)
+ tst_res(TINFO, "Ignoring %s", path);
+ return;
+ }
+
+ if (verbose)
+ tst_res(TINFO, "%s(%s)", __func__, path);
+
+ fd = open(path, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ tst_res(TINFO | TERRNO, "open(%s)", path);
+ return;
+ }
+
+ count = read(fd, buf, sizeof(buf) - 1);
+ if (count > 0 && verbose) {
+ if (count <= 20) {
+ if (buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ buf[count] = '\0';
+ } else
+ strcpy(buf + 20, "...");
+
+ tst_res(TINFO, "read(%s, buf) = %ld, buf = %s",
+ path, count, buf);
+
+ } else if (!count && verbose)
+ tst_res(TINFO, "read(%s) = EOF", path);
+ else if (count < 0)
+ tst_res(TINFO | TERRNO, "read(%s)", path);
+
+ SAFE_CLOSE(fd);
+}
+
+static int worker_run(struct worker *self)
+{
+ ssize_t i, j, ret, count;
+ char buf[PIPE_BUF];
+ struct sigaction term_sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = 0,
+ };
+
+ sigaction(SIGTTIN, &term_sa, NULL);
+
+ for (count = 0;;) {
+ ret = read(self->in, buf + count, sizeof(buf) - 1 - count);
+ if (ret < 0 && errno == EINTR)
+ continue;
+ else if (ret < 0)
+ tst_res(TBROK | TERRNO,
+ "Worker can not read from pipe");
+ else if (ret == 0)
+ break;
+
+ count += ret;
+
+ for (i = 0, j = 0; i < count; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ read_test(buf + j);
+ j = i + 1;
+ }
+ }
+
+ count = i - j;
+ memmove(buf, buf + j, count);
+ if (sizeof(buf) - 1 - count < 1)
+ tst_brk(TBROK,
+ "Worker receive buffer is too small for path");
+ }
+
+ return 0;
+}
+
+static void spawn_workers(void)
+{
+ int i, j;
+ int pipe[2];
+ struct worker *wa = workers;
+
+ bzero(workers, worker_count * sizeof(*workers));
+
+ for (i = 0; i < worker_count; i++) {
+ SAFE_PIPE(pipe);
+ wa[i].in = pipe[0];
+ wa[i].out = pipe[1];
+ wa[i].pid = SAFE_FORK();
+ if (!wa[i].pid) {
+ for (j = 0; j <= i; j++)
+ SAFE_CLOSE(wa[j].out);
+ exit(worker_run(wa + i));
+ } else {
+ SAFE_CLOSE(wa[i].in);
+ }
+ }
+}
+
+static void stop_workers(void)
+{
+ int i;
+
+ for (i = 0; workers && i < worker_count; i++) {
+ if (workers[i].out > 0)
+ SAFE_CLOSE(workers[i].out);
+ }
+}
+
+static void sched_work(const char *path)
+{
+ static long cur;
+
+ SAFE_WRITE(1, workers[cur].out, path, strlen(path));
+ cur++;
+ if (cur >= worker_count)
+ cur = 0;
+}
+
+static void setup(void)
+{
+ worker_count = MIN(MAX(SAFE_SYSCONF(_SC_NPROCESSORS_ONLN) - 1, 1), 15);
+ workers = (struct worker *)SAFE_MALLOC(worker_count * sizeof(*workers));
+}
+
+static void cleanup(void)
+{
+ stop_workers();
+
+ if (workers)
+ free(workers);
+}
+
+static void visit_dir(const char *path)
+{
+ DIR *dir;
+ struct dirent *dent;
+ struct stat dent_st;
+ char dent_path[MAX_PATH];
+
+ dir = opendir(path);
+ if (!dir) {
+ tst_res(TINFO | TERRNO, "opendir(%s)", path);
+ return;
+ }
+
+ for (dent = SAFE_READDIR(dir); dent; dent = SAFE_READDIR(dir)) {
+ if (!strcmp(dent->d_name, ".") ||
+ !strcmp(dent->d_name, ".."))
+ continue;
+
+ if (dent->d_type != DT_UNKNOWN) {
+ switch (dent->d_type) {
+ case DT_DIR:
+ snprintf(dent_path, MAX_PATH,
+ "%s/%s", path, dent->d_name);
+ visit_dir(dent_path);
+ break;
+ case DT_LNK:
+ break;
+ default:
+ snprintf(dent_path, MAX_PATH,
+ "%s/%s\n", path, dent->d_name);
+ sched_work(dent_path);
+ }
+ } else {
+ SAFE_LSTAT(dent_path, &dent_st);
+ switch (dent_st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ snprintf(dent_path, MAX_PATH,
+ "%s/%s", path, dent->d_name);
+ visit_dir(dent_path);
+ break;
+ case S_IFLNK:
+ break;
+ default:
+ snprintf(dent_path, MAX_PATH,
+ "%s/%s\n", path, dent->d_name);
+ sched_work(dent_path);
+ }
+ }
+ }
+
+ SAFE_CLOSEDIR(dir);
+}
+
+static void run(void)
+{
+ spawn_workers();
+
+ visit_dir(root_dir);
+ tst_res(TPASS, "Finished scanning directory tree");
+
+ stop_workers();
+
+}
+
+static struct tst_test test = {
+ .options = options,
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run,
+ .forks_child = 1,
+};
+
--
2.15.1
More information about the ltp
mailing list