/* $Id: fsperf3.c,v 1.5 2003/11/26 19:01:52 ca Exp $ */
/*
This program can be used to test the performance of
creation (and deletion) of many files (including writing and reading data)
in nested subdirectories
with fstat()
with fchown()
*/
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define PERLEVEL 100
#define FNAMEFMT0 "%06d"
#define FNAMEFMT1 "%02d/%06d"
#define FNAMEFMT2 "%02d/%02d/%06d"
#define DNAMEFMT1 "%02d"
#define DNAMEFMT2 "%02d/%02d"
#if BUFSIZ < 16
# error BUFSIZ too small
#endif
static gid_t gid = 0;
static int do_stat = 0;
static void
fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
static void
filename(char *fn, int n, int level)
{
switch(level)
{
case 0:
sprintf(fn, FNAMEFMT0, n);
break;
case 1:
sprintf(fn, FNAMEFMT1, n % PERLEVEL, n);
break;
case 2:
sprintf(fn, FNAMEFMT2, (n / PERLEVEL) % PERLEVEL, n % PERLEVEL,
n);
break;
default:
fatal("filename: wrong level %d", level);
/* NOTREACHED */
}
}
/* rename_file - rename a file */
static void
rename_file(int old, int new, int level)
{
char new_path[BUFSIZ];
char old_path[BUFSIZ];
filename(new_path, new, level);
filename(old_path, old, level);
/* printf("/: %s -> %s\n", old_path, new_path); */
if (rename(old_path, new_path))
fatal("rename %s to %s: %d", old_path, new_path, errno);
}
/* make_file - create a little file and use it */
static void
make_file(int seqno, int size, int level)
{
char path[BUFSIZ];
char buf[1024];
int fd;
int i;
struct stat st;
filename(path, seqno, level);
/* printf("+: %s\n", path); */
if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
fatal("open(%s): %d", path, errno);
if (do_stat)
{
if (fstat(fd, &st) < 0)
fatal("stat(%d): %d", fd, errno);
}
else
st.st_gid = gid;
if (gid != 0 && gid != st.st_gid && (fchown(fd, -1, gid) < 0))
fatal("fchown(%s): %d", path, errno);
memset(buf, 'x', sizeof(buf));
for (i = 0; i < size; i++)
if (write(fd, buf, sizeof(buf)) <= 0)
fatal("write: i=%d, fd=%d, errno=%d", i, fd, errno);
if (fsync(fd))
fatal("fsync(%s): %d", path, errno);
if (close(fd))
fatal("close(%s): %d", path, errno);
if ((fd = open(path, O_RDONLY, 0660)) == 0)
fatal("open(%s): %d", path, errno);
while (read(fd, buf, sizeof(buf)) > 0)
/* void */ ;
if (close(fd))
fatal("close(%s): %d", path, errno);
}
/* remove_file - delete specified file */
static void
remove_file(int n, int level)
{
char path[BUFSIZ];
filename(path, n, level);
/* printf("-: %s\n", path); */
if (remove(path))
fatal("remove %s: %d", path, errno);
}
/* remove_silent - delete specified file, silently */
static void
remove_silent(int n, int level)
{
char path[BUFSIZ];
filename(path, n, level);
(void) remove(path);
}
/* usage - explain */
static void usage(char *myname)
{
fprintf(stderr, "usage: %s [-r] [-C concurrency] [-g gid] [-h hash] [-l lines] [-s size] messages directory_entries\n",
myname);
fprintf(stderr, "\t-C n\tkeep n messages open concurrently\n");
fprintf(stderr, "\t-g gid\tchgrp\n");
fprintf(stderr, "\t-h hashlevel\t0, 1, 2: number of directory levels\n");
fprintf(stderr, "\t-l lines\tlines for file to use\n");
fprintf(stderr, "\t-p\tpopulate directory with files before start\n");
fprintf(stderr, "\t-r\trename file twice\n");
fprintf(stderr, "\t-s size\tsize of file\n");
exit(1);
}
int
main(int argc, char **argv)
{
int op_count;
int max_file;
time_t start;
int do_rename = 0;
int populate = 0;
int seq, h;
int o, n;
int ch;
int size = 2;
int lines = 400;
int level = 0;
int concurrent = 0;
char path[BUFSIZ];
while ((ch = getopt(argc, argv, "C:cfg:h:l:prs:")) != EOF)
{
switch (ch)
{
case 'C':
if ((concurrent = atoi(optarg)) < 0)
usage(argv[0]);
break;
case 'f':
do_stat = 1;
break;
case 'g':
if ((gid = atoi(optarg)) <= 0)
usage(argv[0]);
break;
case 'h':
if ((level = atoi(optarg)) < 0)
usage(argv[0]);
break;
case 'l':
if ((lines = atoi(optarg)) <= 0)
usage(argv[0]);
break;
case 'p':
populate = 1;
break;
case 'r':
do_rename = 1;
break;
case 's':
if ((size = atoi(optarg)) <= 0)
usage(argv[0]);
break;
default:
usage(argv[0]);
}
}
if (argc != optind + 2)
usage(argv[0]);
if ((op_count = atoi(argv[optind])) <= 0)
usage(argv[0]);
if ((max_file = atoi(argv[optind + 1])) <= 0)
usage(argv[0]);
if (concurrent >= max_file)
usage(argv[0]);
if (op_count < max_file)
usage(argv[0]);
if (level > 0)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT1, seq);
if (mkdir(path, 0750))
fatal("mkdir(%s): %d", path, errno);
}
}
if (level > 1)
{
for (h = 0; h < PERLEVEL; h++)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT2, h, seq);
if (mkdir(path, 0750))
fatal("mkdir(%s): %d", path, errno);
}
}
}
/* Populate the directory with little files. */
if (populate)
for (seq = 0; seq < max_file; seq++)
make_file(seq, size, level);
/* Simulate arrival and delivery of mail messages. */
seq = 0;
start = time((time_t *) 0);
if (concurrent > 0)
{
for (h = 0; h < concurrent; h++)
{
seq %= max_file;
make_file(seq, size, level);
seq++;
}
}
while (op_count > 0)
{
seq %= max_file;
make_file(seq, size, level);
o = seq - concurrent;
if (o < 0)
o += max_file;
if (do_rename)
{
n = o + max_file;
rename_file(o, n, level);
rename_file(n, o, level);
}
remove_file(o, level);
seq++;
op_count--;
}
if (concurrent > 0)
{
for (h = 0; h < concurrent; h++)
{
o = seq - concurrent;
if (o < 0)
o += max_file;
remove_file(o, level);
seq++;
}
}
printf("elapsed time: %ld\n", (long) time((time_t *) 0) - start);
/* Clean up directory fillers. */
if (populate)
for (seq = 0; seq < max_file; seq++)
remove_silent(seq, level);
if (level > 1)
{
for (h = 0; h < PERLEVEL; h++)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT2, h, seq);
(void) rmdir(path);
}
}
}
if (level > 0)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT1, seq);
(void) rmdir(path);
}
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1