blob: 8ac3f988ac2f73c8e45f372755406e2b7e29e7a6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013 Red Hat, Inc.
* All Rights Reserved.
*/
#include "command.h"
#include "input.h"
#include "init.h"
#include "io.h"
#include <sys/types.h>
#include <dirent.h>
#ifndef _DIRENT_HAVE_D_RECLEN
#include <string.h>
#endif
static struct cmdinfo readdir_cmd;
static const char *d_type_str(unsigned int type)
{
const char *str;
switch (type) {
case DT_UNKNOWN:
str = "DT_UNKNOWN";
break;
case DT_FIFO:
str = "DT_FIFO";
break;
case DT_CHR:
str = "DT_CHR";
break;
case DT_DIR:
str = "DT_DIR";
break;
case DT_BLK:
str = "DT_BLK";
break;
case DT_REG:
str = "DT_REG";
break;
case DT_LNK:
str = "DT_LNK";
break;
case DT_SOCK:
str = "DT_SOCK";
break;
case DT_WHT:
str = "DT_WHT";
break;
default:
str = "ERROR!";
break;
}
return str;
}
static void
dump_dirent(
long long offset,
struct dirent *dirent)
{
printf("%08llx: d_ino: 0x%08llx", offset,
(unsigned long long)dirent->d_ino);
#ifdef _DIRENT_HAVE_D_OFF
printf(" d_off: 0x%08llx", (unsigned long long)dirent->d_off);
#endif
#ifdef _DIRENT_HAVE_D_RECLEN
printf(" d_reclen: 0x%x", dirent->d_reclen);
#endif
#ifdef _DIRENT_HAVE_D_TYPE
printf(" d_type: %s", d_type_str(dirent->d_type));
#endif
printf(" d_name: %s\n", dirent->d_name);
}
static int
read_directory(
DIR *dir,
long long offset,
unsigned long long length,
int dump,
unsigned long long *total)
{
struct dirent *dirent;
int count = 0;
seekdir(dir, offset);
*total = 0;
while (*total < length) {
dirent = readdir(dir);
if (!dirent)
break;
#ifdef _DIRENT_HAVE_D_RECLEN
*total += dirent->d_reclen;
#else
*total += strlen(dirent->d_name) + sizeof(*dirent);
#endif
count++;
if (dump) {
dump_dirent(offset, dirent);
#ifdef _DIRENT_HAVE_D_OFF
offset = dirent->d_off;
#else
/* Some platforms don't have dirent->d_off, but because
* it is used only for dumping the value, it should be
* safe to only set it to zero in such case.
*/
offset = 0;
#endif
}
}
return count;
}
static int
readdir_f(
int argc,
char **argv)
{
int cnt;
unsigned long long total;
int c;
size_t fsblocksize, fssectsize;
struct timeval t1, t2;
char s1[64], s2[64], ts[64];
long long offset = -1;
unsigned long long length = -1; /* max length limit */
int verbose = 0;
DIR *dir;
int dfd;
init_cvtnum(&fsblocksize, &fssectsize);
while ((c = getopt(argc, argv, "l:o:v")) != EOF) {
switch (c) {
case 'l':
length = cvtnum(fsblocksize, fssectsize, optarg);
break;
case 'o':
offset = cvtnum(fsblocksize, fssectsize, optarg);
break;
case 'v':
verbose = 1;
break;
default:
exitcode = 1;
return command_usage(&readdir_cmd);
}
}
dfd = dup(file->fd);
if (dfd < 0) {
exitcode = 1;
return 0;
}
dir = fdopendir(dfd);
if (!dir) {
close(dfd);
exitcode = 1;
return 0;
}
if (offset == -1) {
rewinddir(dir);
offset = telldir(dir);
}
gettimeofday(&t1, NULL);
cnt = read_directory(dir, offset, length, verbose, &total);
gettimeofday(&t2, NULL);
closedir(dir);
close(dfd);
t2 = tsub(t2, t1);
timestr(&t2, ts, sizeof(ts), 0);
cvtstr(total, s1, sizeof(s1));
cvtstr(tdiv(total, t2), s2, sizeof(s2));
printf(_("read %llu 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));
return 0;
}
void
readdir_init(void)
{
readdir_cmd.name = "readdir";
readdir_cmd.cfunc = readdir_f;
readdir_cmd.argmax = 5;
readdir_cmd.flags = CMD_NOMAP_OK|CMD_FOREIGN_OK;
readdir_cmd.args = _("[-v][-o offset][-l length]");
readdir_cmd.oneline = _("read directory entries");
add_command(&readdir_cmd);
}