getdents reads the directory entries from an open directory from the provided offset (or 0 if not specified). On completion, getdents prints summary information regarding the number of operations and bytes transferred. Options are available to specify the starting offset, buffer size and verbose mode to dump directory entry information from the buffer. Signed-off-by: Brian Foster <bfoster@xxxxxxxxxx> --- Hi all, This is something I hacked together for testing something for gluster. Thoughts appreciated! Brian io/Makefile | 4 ++ io/getdents.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ io/init.c | 1 + io/io.h | 1 + 4 files changed, 181 insertions(+) create mode 100644 io/getdents.c diff --git a/io/Makefile b/io/Makefile index 50edf91..cfd1b11 100644 --- a/io/Makefile +++ b/io/Makefile @@ -80,6 +80,10 @@ ifeq ($(HAVE_PREADV),yes) LCFLAGS += -DHAVE_PREADV -DHAVE_PWRITEV endif +ifeq ($(PKG_PLATFORM),linux) +CFILES += getdents.c +endif + default: depend $(LTCOMMAND) include $(BUILDRULES) diff --git a/io/getdents.c b/io/getdents.c new file mode 100644 index 0000000..2b167f2 --- /dev/null +++ b/io/getdents.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2013 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 + */ + +#include <xfs/xfs.h> +#include <xfs/command.h> +#include <xfs/input.h> +#include "init.h" +#include "io.h" + +#include <sys/syscall.h> + +static struct cmdinfo getdents_cmd; + +static char *gd_buf; +static size_t gd_bufsz; + +struct linux_dirent { + unsigned long d_ino; /* Inode number */ + unsigned long d_off; /* Offset to next linux_dirent */ + unsigned short d_reclen; /* Length of this linux_dirent */ + char d_name[]; /* Filename (null-terminated) */ +}; + +static void +dump_dir_buffer( + struct linux_dirent *dirp, + long long offset, + long long length) +{ + while (length > 0) { + printf("%08llx: 0x%lx (%s)\n", + offset, dirp->d_ino, dirp->d_name); + + offset = dirp->d_off; + length -= dirp->d_reclen; + + dirp = (struct linux_dirent *) ((char *) dirp + dirp->d_reclen); + } +} + +static int +do_getdents(char *buf, unsigned int len) +{ + return syscall(SYS_getdents, file->fd, buf, len); +} + +static int +read_directory( + long long offset, + long long length, + int dump, + long long *total) +{ + int bytes; + int count = 0; + + offset = lseek(file->fd, offset, SEEK_SET); + if (offset < 0) + perror("lseek"); + + *total = 0; + while (1) { + bytes = do_getdents(gd_buf, gd_bufsz); + if (!bytes) + break; + if (bytes < 0) + perror("getdents"); + + if (dump) + dump_dir_buffer((struct linux_dirent *) gd_buf, offset, bytes); + + *total += bytes; + count++; + + /* + * Keep filling the buffer until we've read at least as much + * data as requested. + */ + if (length > 0 && *total >= length) + break; + } + + return count; +} + +static int +getdents_f( + int argc, + char **argv) +{ + int cnt; + long long total; + int c; + size_t fsblocksize, fssectsize; + struct timeval t1, t2; + char s1[64], s2[64], ts[64]; + long long offset = -1; + long long length = -1; + int verbose = 0; + + init_cvtnum(&fsblocksize, &fssectsize); + gd_bufsz = fsblocksize; + + while ((c = getopt(argc, argv, "b:l:o:v")) != EOF) { + switch (c) { + case 'b': + gd_bufsz = cvtnum(fsblocksize, fssectsize, optarg); + break; + case 'l': + length = cvtnum(fsblocksize, fssectsize, optarg); + break; + case 'o': + offset = cvtnum(fsblocksize, fssectsize, optarg); + break; + case 'v': + verbose = 1; + break; + default: + return command_usage(&getdents_cmd); + } + } + + gd_buf = malloc(gd_bufsz); + if (!gd_buf) + return -1; + + if (offset == -1) + offset = lseek(file->fd, 0, SEEK_CUR); + + gettimeofday(&t1, NULL); + cnt = read_directory(offset, length, verbose, &total); + gettimeofday(&t2, NULL); + + t2 = tsub(t2, t1); + timestr(&t2, ts, sizeof(ts), 0); + + cvtstr(total, s1, sizeof(s1)); + cvtstr(tdiv(total, t2), s2, sizeof(s2)); + + printf(_("read %lld bytes from offset %lld\n"), total, offset); + printf(_("%s, %d ops, %s (%s/sec and %.4f ops/sec)\n"), + s1, cnt, ts, s2, tdiv(cnt, t2)); + + free(gd_buf); + + return 0; +} + +void +getdents_init(void) +{ + getdents_cmd.name = "getdents"; + getdents_cmd.cfunc = getdents_f; + getdents_cmd.argmax = 6; + getdents_cmd.flags = CMD_NOMAP_OK|CMD_FOREIGN_OK; + getdents_cmd.args = _("[-v][-b buffer size][-o offset][-l length]"); + getdents_cmd.oneline = _("read directory entries"); + + add_command(&getdents_cmd); +} diff --git a/io/init.c b/io/init.c index ca3055a..44cce44 100644 --- a/io/init.c +++ b/io/init.c @@ -60,6 +60,7 @@ init_commands(void) file_init(); freeze_init(); fsync_init(); + getdents_init(); getrusage_init(); help_init(); imap_init(); diff --git a/io/io.h b/io/io.h index 91f0e3e..5658b13 100644 --- a/io/io.h +++ b/io/io.h @@ -94,6 +94,7 @@ extern void bmap_init(void); extern void file_init(void); extern void freeze_init(void); extern void fsync_init(void); +extern void getdents_init(void); extern void getrusage_init(void); extern void help_init(void); extern void imap_init(void); -- 1.8.1.4 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs