socsvn commit: r223910 -
soc2011/gk/ino64-head/tools/regression/readdir-lint
gk at FreeBSD.org
gk at FreeBSD.org
Sun Jul 3 14:07:18 UTC 2011
Author: gk
Date: Sun Jul 3 14:07:15 2011
New Revision: 223910
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=223910
Log:
Add readdir-lint tool -- test VOP_READDIR implementation
Added:
soc2011/gk/ino64-head/tools/regression/readdir-lint/
soc2011/gk/ino64-head/tools/regression/readdir-lint/Makefile (contents, props changed)
soc2011/gk/ino64-head/tools/regression/readdir-lint/readdir-lint.c (contents, props changed)
Added: soc2011/gk/ino64-head/tools/regression/readdir-lint/Makefile
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ soc2011/gk/ino64-head/tools/regression/readdir-lint/Makefile Sun Jul 3 14:07:15 2011 (r223910)
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+WARNS?= 6
+
+PROG= readdir-lint
+NO_MAN=
+DEBUG_FLAGS= -O0 -g -I${.CURDIR}/../../../sys
+
+.include <bsd.prog.mk>
Added: soc2011/gk/ino64-head/tools/regression/readdir-lint/readdir-lint.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ soc2011/gk/ino64-head/tools/regression/readdir-lint/readdir-lint.c Sun Jul 3 14:07:15 2011 (r223910)
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 2011 Gleb Kurtsou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#define HAVE_DIRENT_NAMLEN
+#define HAVE_GETPROGNAME
+#endif
+
+#define DIRSIZE_MAX (4*1024*1024)
+#define DIRSIZE_ENTRY (sizeof(struct dirent))
+#define DIRSIZE_MIN ((sizeof (struct dirent) - (MAXNAMLEN+1)) + 4)
+#define DIRSIZE_BLOCK 512
+#define DIRSIZE_PAGE 4096
+
+#define DIRENT_MAX 512
+
+#define DP_NEXT(a) ((struct dirent *)(((char *)(a)) + a->d_reclen))
+
+#define WARNX(var, fmt, ...) \
+ do { \
+ if ((var) == 0) { \
+ printf(fmt "\n", ## __VA_ARGS__ ); \
+ var = 1; \
+ } \
+ } while (0)
+
+static int opt_verbose;
+static int opt_skip;
+static char *opt_path;
+
+#ifdef HAVE_DIRENT_NAMLEN
+static int warn_nameterm;
+#endif
+static int warn_noerr;
+static int warn_seekoff;
+static int warn_zeroino;
+static int warn_zerooff;
+static int warn_reclen;
+
+struct dirbuf {
+ struct dirent *dp, *begin, *end;
+ long base;
+ int fd;
+ int eof;
+ size_t bufsize;
+};
+
+static void
+dir_init(struct dirbuf *dir, const char *path, int bufsize)
+{
+ dir->fd = open(path, O_RDONLY);
+ if (dir->fd == -1)
+ err(1, "open %s", path);
+
+ dir->begin = malloc(bufsize);
+ if (dir->begin == NULL)
+ err(1, "malloc failed");
+ dir->dp = NULL;
+ dir->end = NULL;
+ dir->base = 0;
+ dir->eof = 0;
+ dir->bufsize = bufsize;
+}
+
+static void
+dir_destroy(struct dirbuf *dir)
+{
+ free(dir->begin);
+}
+
+static off_t
+dir_offset(struct dirbuf *dir)
+{
+ return lseek(dir->fd, 0, SEEK_CUR);
+}
+
+static int
+dir_readx(struct dirbuf *dir)
+{
+ struct dirent *di;
+ off_t seekoff;
+ int rv;
+
+ rv = getdirentries(dir->fd, (char *)dir->begin, dir->bufsize, &dir->base);
+ if (opt_verbose >= 3)
+ printf("dir_read %d: len=%d base=%ld\n", dir->fd, rv, dir->base);
+ if (rv == -1)
+ return (rv);
+ if (rv == 0) {
+ dir->eof = 1;
+ dir->dp = NULL;
+ dir->end = NULL;
+ } else {
+ dir->dp = dir->begin;
+ dir->end = (struct dirent *)((char *)dir->begin + rv);
+ seekoff = dir_offset(dir);
+ for (di = dir->dp; di < dir->end; di = DP_NEXT(di)) {
+ if (di->d_reclen <= 0 ||
+ di->d_reclen > (char *)dir->end - (char *)di) {
+ WARNX(warn_reclen, "Invalid entry size: %d", di->d_reclen);
+ dir->end = di;
+ break;
+ }
+#ifdef HAVE_DIRENT_NAMLEN
+ if (di->d_namlen > MAXNAMLEN)
+ errx(1, "Ivalid name lenghth: %d", di->d_namlen);
+ if (di->d_name[di->d_namlen] != '\0') {
+ di->d_name[di->d_namlen] = '\0';
+ WARNX(warn_nameterm, "Entry names are not NUL-terminated");
+ }
+#else
+#endif
+ if (di->d_fileno == 0) {
+ WARNX(warn_zeroino, "Zero d_fileno: %08jx %s",
+ (uintmax_t)di->d_off, di->d_name);
+ }
+ if (di->d_off == 0)
+ WARNX(warn_zerooff, "Zero d_off: %ju %s",
+ (uintmax_t)di->d_fileno, di->d_name);
+ if (DP_NEXT(di) >= dir->end && di->d_off != seekoff) {
+ WARNX(warn_seekoff, "Directory(%zd) and last entry offsets mismatch: %ju -- %ju",
+ dir->bufsize, (uintmax_t)seekoff, (uintmax_t)di->d_off);
+ }
+ }
+ }
+
+ return (rv);
+}
+
+static int
+dir_read(struct dirbuf *dir)
+{
+ int rv;
+
+ rv = dir_readx(dir);
+ if (rv == -1)
+ err(1, "Directory read");
+ return (rv);
+}
+
+
+static struct dirent *
+dir_next(struct dirbuf *dir)
+{
+ if (dir->eof)
+ return NULL;
+
+ if (dir->dp == NULL || DP_NEXT(dir->dp) >= dir->end) {
+ dir_read(dir);
+ if (dir->eof)
+ return NULL;
+ } else {
+ dir->dp = DP_NEXT(dir->dp);
+ }
+
+ return dir->dp;
+}
+
+static void
+dir_seek(struct dirbuf *dir, off_t off)
+{
+ int rv;
+
+ rv = lseek(dir->fd, off, SEEK_SET);
+ if (rv == -1)
+ err(3, "seek(%jd, SEEK_SET)", (uintmax_t)off);
+ dir->dp = NULL;
+ dir->end = NULL;
+ dir->base = 0;
+ dir->eof = 0;
+}
+
+static int
+dir_cmpent(struct dirbuf *dir1, struct dirbuf *dir2)
+{
+ struct dirent *dp1, *dp2;
+
+ dp1 = dir1->dp;
+ dp2 = dir2->dp;
+
+ if (dir1->eof != dir2->eof)
+ errx(3, "Invalid EOF: %d %ld -- %d %ld",
+ dir1->eof, dir1->base, dir2->eof, dir2->base);
+ else if (dir1->eof)
+ return (1);
+ if (opt_verbose >= 2)
+ printf(" %08jx (%d bytes) %-12s -- %08jx (%d bytes) %-12s\n",
+ (uintmax_t)dp1->d_off, dp1->d_reclen, dp1->d_name,
+ (uintmax_t)dp2->d_off, dp2->d_reclen, dp2->d_name);
+ if (strcmp(dp1->d_name, dp2->d_name) != 0 ||
+ dp1->d_off != dp2->d_off)
+ printf("Entries mismatch: %08jx (%d bytes) %-12s -- %08jx (%d bytes) %-12s\n",
+ (uintmax_t)dp1->d_off, dp1->d_reclen, dp1->d_name,
+ (uintmax_t)dp2->d_off, dp2->d_reclen, dp2->d_name);
+
+ return (0);
+}
+
+static void
+dir_lint(struct dirbuf *dir1, struct dirbuf *dir2)
+{
+ dir_read(dir1);
+ dir_read(dir2);
+
+ while (dir_cmpent(dir1, dir2) == 0) {
+ dir_next(dir1);
+ dir_next(dir2);
+ }
+}
+
+#ifndef HAVE_GETPROGNAME
+#define getprogname() "readdir-lint"
+#endif
+
+static void
+test_bufsize(struct dirbuf *dir_expect, struct dirbuf *dir)
+{
+ int tests_bufsize[] = { DIRSIZE_PAGE, DIRSIZE_BLOCK, DIRSIZE_ENTRY, 0 };
+ int *ip;
+
+ for (ip = tests_bufsize; *ip != 0; ip++) {
+ if (opt_skip > 0) {
+ opt_skip--;
+ continue;
+ }
+ printf("Test buffer sizes: %d -- %d\n", DIRSIZE_MAX, *ip);
+ dir_init(dir, opt_path, *ip);
+ dir_seek(dir_expect, 0);
+ dir_lint(dir_expect, dir);
+ dir_destroy(dir);
+ }
+}
+
+static void
+test_minbufsize(struct dirbuf *dir_expect, struct dirbuf *dir)
+{
+ int len;
+
+ if (opt_skip > 0) {
+ opt_skip--;
+ return;
+ }
+
+ printf("Test minimal buffer size\n");
+ dir_init(dir, opt_path, DIRSIZE_ENTRY);
+ dir_seek(dir_expect, 0);
+ dir_read(dir_expect);
+ while(!dir_expect->eof) {
+ off_t prevoff = dir_offset(dir);
+
+ for (dir->bufsize = DIRSIZE_MIN; dir->bufsize <= DIRSIZE_ENTRY;
+ dir->bufsize += 4) {
+ len = dir_readx(dir);
+ if (len <= 0) {
+ if (prevoff != dir_offset(dir))
+ errx(2, "Directory offset changed but no data read: %jd %jd",
+ (uintmax_t)prevoff, (uintmax_t)dir_offset(dir));
+ if (len == 0) {
+ WARNX(warn_noerr, "EINVAL expected for small buffer read, 0 byte result");
+ continue;
+ }
+ if (errno == EINVAL)
+ continue;
+ err(1, "Directory read");
+ }
+ if (opt_verbose >= 1)
+ printf(" min size %08jx (%d of %zd bytes) %s\n",
+ (uintmax_t)dir->dp->d_off, dir->dp->d_reclen, dir->bufsize,
+ dir->dp->d_name);
+ break;
+ }
+ if (dir->bufsize > DIRSIZE_ENTRY) {
+ errx(2, "Couldn't read entry at offset %jd",
+ (uintmax_t)dir_offset(dir));
+ }
+ dir->eof = 0;
+ if (dir_cmpent(dir_expect, dir) != 0)
+ break;
+ dir_next(dir_expect);
+ dir_seek(dir, dir->dp->d_off);
+ }
+ dir_destroy(dir);
+}
+
+static void
+usage(int exitcode)
+{
+ fprintf(stderr, "usage: %s directory\n", getprogname());
+ exit(exitcode);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct dirbuf dir_max;
+ struct dirbuf dir_i;
+ int len, opt;
+ long prevbase;
+
+ while ((opt = getopt(argc, argv, "hs:v")) != -1) {
+ switch (opt) {
+ case 's':
+ opt_skip = atoi(optarg);
+ break;
+ case 'v':
+ opt_verbose++;
+ break;
+ case 'h':
+ usage(0);
+ break;
+ case '?':
+ default:
+ usage(-1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage(1);
+ opt_path = argv[0];
+
+ dir_init(&dir_max, opt_path, DIRSIZE_MAX);
+ dir_read(&dir_max);
+ prevbase = dir_max.base;
+ len = dir_read(&dir_max);
+ if (!dir_max.eof || len != 0)
+ errx(1, "Directory is too large");
+
+ test_bufsize(&dir_max, &dir_i);
+ test_minbufsize(&dir_max, &dir_i);
+
+ return (0);
+}
More information about the svn-soc-all
mailing list