Whamcloud - gitweb
LU-17175 gss: start lsvcgssd from l_getauth
[fs/lustre-release.git] / lustre / utils / llverfs.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
5  * Use is subject to license terms.
6  *
7  * Copyright (c) 2011, Intel Corporation.
8  *
9  */
10
11 /*
12  * This file is part of Lustre, http://www.lustre.org/
13  *
14  * lustre/utils/llverfs.c
15  *
16  * Filesystem Verification Tool: this program tests the correct operation of
17  * large filesystems and the underlying block storage device(s). For more
18  * information, see the llverfs.8 man page.
19  *
20  */
21
22 #ifndef _GNU_SOURCE
23 #define _GNU_SOURCE
24 #endif
25 #ifndef LUSTRE_UTILS
26 #define LUSTRE_UTILS
27 #endif
28 #ifndef _LARGEFILE64_SOURCE
29 #define _LARGEFILE64_SOURCE
30 #endif
31 #ifndef _FILE_OFFSET_BITS
32 #define _FILE_OFFSET_BITS 64
33 #endif
34
35 #include <features.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <limits.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <getopt.h>
46 #include <time.h>
47 #include <dirent.h>
48 #include <mntent.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/vfs.h>
52 #include <sys/time.h>
53 #include <gnu/stubs.h>
54 #include <gnu/stubs.h>
55
56 #ifdef HAVE_EXT2FS_EXT2FS_H
57 #  include <e2p/e2p.h>
58 #  include <ext2fs/ext2fs.h>
59 #else
60 #  ifndef EXT2_TOPDIR_FL
61 #    define EXT2_TOPDIR_FL              0x00020000 /* Top of directory tree */
62 #  endif
63 static int fsetflags(const char *path, unsigned int flag)
64 {
65         char cmd[PATH_MAX + 128];
66         int rc;
67
68         if (flag != EXT2_TOPDIR_FL) {
69                 rc = EOPNOTSUPP;
70                 goto out;
71         }
72
73         snprintf(cmd, sizeof(cmd), "chattr +T %s", path);
74
75         rc = system(cmd);
76         if (rc > 0) {
77                 rc = WEXITSTATUS(rc);
78 out:
79                 errno = rc;
80         }
81
82         return rc;
83 }
84 #endif
85
86 #define ONE_MB (1024 * 1024)
87 #define ONE_GB ((unsigned long long)(1024 * 1024 * 1024))
88 #define BLOCKSIZE 4096
89
90 /* Structure for writing test pattern */
91 struct block_data {
92         unsigned long long bd_offset;
93         unsigned long long bd_time;
94         unsigned long long bd_inode;
95 };
96 static char *progname;              /* name by which this program was run. */
97 static unsigned verbose = 1;        /* prints offset in kB, operation rate */
98 static int readoption;              /* run test in read-only (verify) mode */
99 static int writeoption;             /* run test in write_only mode */
100 char *testdir;                      /* name of device to be tested. */
101 static unsigned full = 1;           /* flag to full check */
102 static int error_count;             /* number of IO errors hit during run */
103 char filecount[PATH_MAX];           /* file with total number of files written*/
104 static unsigned long num_files;     /* Total number of files for read/write */
105 static loff_t file_size = 4*ONE_GB; /* Size of each file */
106 static unsigned files_in_dir = 32;  /* number of files in each directioy */
107 static unsigned int num_dirs;       /* total number of directories */
108 const int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
109 static int isatty_flag;
110 static int perms =  S_IRWXU | S_IRGRP | S_IROTH;
111
112 static struct option const long_opts[] = {
113         { .val = 'c',   .name = "chunksize",    .has_arg = required_argument },
114         { .val = 'h',   .name = "help",         .has_arg = no_argument },
115         { .val = 'l',   .name = "long",         .has_arg = no_argument },
116         { .val = 'l',   .name = "full",         .has_arg = no_argument },
117         { .val = 'n',   .name = "num_dirs",     .has_arg = required_argument },
118         { .val = 'o',   .name = "offset",       .has_arg = required_argument },
119         { .val = 'p',   .name = "partial",      .has_arg = required_argument },
120         { .val = 'q',   .name = "quiet",        .has_arg = required_argument },
121         { .val = 'r',   .name = "read",         .has_arg = no_argument },
122         { .val = 's',   .name = "filesize",     .has_arg = no_argument },
123         { .val = 't',   .name = "timestamp",    .has_arg = required_argument },
124         { .val = 'v',   .name = "verbose",      .has_arg = no_argument },
125         { .val = 'w',   .name = "write",        .has_arg = no_argument },
126         { .name = NULL } };
127
128 /*
129  * Usages: displays help information, whenever user supply --help option in
130  * command or enters incorrect command line.
131  */
132 static void usage(int status)
133 {
134         if (status != 0) {
135                 printf("\nUsage: %s [OPTION]... <filesystem path> ...\n",
136                        progname);
137                 printf("Filesystem verification tool.\n"
138                        "\t-t {seconds}, --timestamp,  set test time"
139                        "(default=current time())\n"
140                        "\t-o {offset}, --offset, directory starting offset"
141                        " from which tests should start\n"
142                        "\t-r, --read, run in verify mode\n"
143                        "\t-w, --write, run in test-pattern mode, default=rw\n"
144                        "\t-v, --verbose\n"
145                        "\t-p, --partial, for partial check (1MB files)\n"
146                        "\t-l, --long, --full check (4GB file with 4k blocks)\n"
147                        "\t-n, --num_dirs, number of directories to create\n"
148                        "\t-c, --chunksize, IO chunk size in MB (default=1)\n"
149                        "\t-s, --filesize, file size in MB (default=4096)\n"
150                        "\t-h, --help, display this help and exit\n");
151         }
152         exit(status);
153 }
154
155 /*
156  * open_file: Opens file in specified mode and returns fd.
157  */
158 static int open_file(const char *file, int flag)
159 {
160         int fd = open(file, flag, perms);
161         if (fd < 0) {
162                 fprintf(stderr, "\n%s: Open '%s' failed:%s\n",
163                         progname, file, strerror(errno));
164         }
165         return (fd);
166 }
167
168 /*
169  * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
170  * Returns 0 if test offset and timestamp is correct otherwise 1.
171  */
172 static int verify_chunk(char *chunk_buf, const size_t chunksize,
173                         unsigned long long chunk_off,
174                         const unsigned long long time_st,
175                         const unsigned long long inode_st,
176                         const char *file)
177 {
178         struct block_data *bd;
179         char *chunk_end;
180
181         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
182              (char *)chunk_buf < chunk_end;
183              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
184                 bd = (struct block_data *)chunk_buf;
185                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
186                     (bd->bd_inode == inode_st))
187                         continue;
188                 fprintf(stderr, "\n%s: verify %s failed offset/timestamp/inode "
189                         "%llu/%llu/%llu: found %llu/%llu/%llu instead\n",
190                         progname, file, chunk_off, time_st, inode_st,
191                         bd->bd_offset, bd->bd_time, bd->bd_inode);
192                 return 1;
193         }
194         return 0;
195 }
196
197 /*
198  * fill_chunk: Fills the chunk with current or user specified timestamp
199  * and offset. The test pattern is filled at the beginning of
200  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
201  */
202 static void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
203                        const time_t time_st, const ino_t inode_st)
204 {
205         struct block_data *bd;
206         char *chunk_end;
207
208         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
209              (char *)chunk_buf < chunk_end;
210              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
211                 bd = (struct block_data *)chunk_buf;
212                 bd->bd_offset = chunk_off;
213                 bd->bd_time = time_st;
214                 bd->bd_inode = inode_st;
215         }
216 }
217
218 /*
219  * Write a chunk to disk, handling errors, interrupted writes, etc.
220  *
221  * If there is an IO error hit during the write, it is possible that
222  * this will just show up as a short write, and a subsequent write
223  * will return the actual error.  We want to continue in the face of
224  * minor media errors so that we can validate the whole device if
225  * possible, but if there are many errors we don't want to loop forever.
226  *
227  * The error count will be returned upon exit to ensure that the
228  * media errors are detected even if nobody is looking at the output.
229  *
230  * Returns 0 on success, or -ve errno on failure.
231  */
232 static int write_retry(int fd, const char *chunk_buf, size_t nrequested,
233                        unsigned long long offset, const char *file)
234 {
235         long nwritten;
236
237 retry:
238         nwritten = write(fd, chunk_buf, nrequested);
239         if (nwritten < 0) {
240                 if (errno != ENOSPC) {
241                         fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
242                                 progname, file, offset, nrequested,
243                                 strerror(errno));
244                         if (error_count++ < 100)
245                                 return 0;
246                 }
247                 return -errno;
248         }
249         if (nwritten < nrequested) {
250                 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld written\n",
251                         progname, file, offset, nrequested, nwritten);
252                 offset += nwritten;
253                 chunk_buf += nwritten;
254                 nrequested -= nwritten;
255                 goto retry;
256         }
257
258         return 0;
259 }
260
261 /*
262  * write_chunks: write the chunk_buf on the device. The number of write
263  * operations are based on the parameters write_end, offset, and chunksize.
264  *
265  * Returns 0 on success, or -ve error number on failure.
266  */
267 static int write_chunks(int fd, unsigned long long offset,
268                         unsigned long long write_end, char *chunk_buf,
269                         size_t chunksize, const time_t time_st,
270                         const ino_t inode_st, const char *file)
271 {
272         unsigned long long stride;
273
274         stride = full ? chunksize : (ONE_GB - chunksize);
275         for (offset = offset & ~(chunksize - 1); offset < write_end;
276              offset += stride) {
277                 int ret;
278
279                 if (stride != chunksize && lseek64(fd, offset, SEEK_SET) < 0) {
280                         fprintf(stderr, "\n%s: lseek66(%s+%llu) failed: %s\n",
281                                 progname, file, offset, strerror(errno));
282                         return -errno;
283                 }
284                 if (offset + chunksize > write_end)
285                         chunksize = write_end - offset;
286                 if (!full && offset > chunksize) {
287                         fill_chunk(chunk_buf, chunksize, offset, time_st,
288                                    inode_st);
289                         ret = write_retry(fd, chunk_buf, chunksize,offset,file);
290                         if (ret < 0)
291                                 return ret;
292                         offset += chunksize;
293                         if (offset + chunksize > write_end)
294                                 chunksize = write_end - offset;
295                 }
296                 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
297                 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
298                 if (ret < 0)
299                         return ret;
300         }
301         return 0;
302 }
303
304 /*
305  * read_chunk: reads the chunk_buf from the device. The number of read
306  * operations are based on the parameters read_end, offset, and chunksize.
307  */
308 static int read_chunks(int fd, unsigned long long offset,
309                        unsigned long long read_end, char *chunk_buf,
310                        size_t chunksize, const time_t time_st,
311                        const ino_t inode_st, const char *file)
312 {
313         unsigned long long stride;
314
315         stride = full ? chunksize : (ONE_GB - chunksize);
316         for (offset = offset & ~(chunksize - 1); offset < read_end;
317              offset += stride) {
318                 ssize_t nread;
319
320                 if (stride != chunksize && lseek64(fd, offset, SEEK_SET) < 0) {
321                         fprintf(stderr, "\n%s: lseek64(%s+%llu) failed: %s\n",
322                                 progname, file, offset, strerror(errno));
323                         return 1;
324                 }
325                 if (offset + chunksize > read_end)
326                         chunksize = read_end - offset;
327
328                 if (!full && offset > chunksize) {
329                         nread = read(fd, chunk_buf, chunksize);
330                         if (nread < 0) {
331                                 fprintf(stderr,"\n%s: read %s@%llu+%zi failed: "
332                                         "%s\n", progname, file, offset,
333                                         chunksize, strerror(errno));
334                                 error_count++;
335                                 return 1;
336                         }
337                         if (nread < chunksize) {
338                                 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
339                                         "%zi read\n", progname, file, offset,
340                                         chunksize, nread);
341                                 error_count++;
342                         }
343                         if (verify_chunk(chunk_buf, nread, offset, time_st,
344                                          inode_st, file) != 0) {
345                                 return 1;
346                         }
347                         offset += chunksize;
348
349                         /* Need to reset position after read error */
350                         if (nread < chunksize &&
351                             lseek64(fd, offset, SEEK_SET) == -1) {
352                                 fprintf(stderr,
353                                         "\n%s: lseek64(%s@%llu) failed: %s\n",
354                                         progname, file, offset,strerror(errno));
355                                 return 1;
356                         }
357                         if (offset + chunksize >= read_end)
358                                 chunksize = read_end - offset;
359                 }
360                 nread = read(fd, chunk_buf, chunksize);
361                 if (nread < 0) {
362                         fprintf(stderr, "\n%s: read %s@%llu+%zi failed: %s\n",
363                                 progname, file, offset, chunksize,
364                                 strerror(errno));
365                         error_count++;
366                         return 1;
367                 }
368                 if (nread < chunksize) {
369                         fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
370                                 "%zi read\n", progname, file, offset,
371                                 chunksize, nread);
372                         error_count++;
373                 }
374
375                 if (verify_chunk(chunk_buf, nread, offset, time_st,
376                                  inode_st, file) != 0) {
377                         return 1;
378                 }
379         }
380         return 0;
381 }
382
383 /*
384  * new_file: prepares new filename using file counter and current dir.
385  */
386 static char *new_file(char *tempfile, char *cur_dir, int file_num)
387 {
388         int rc = 0;
389
390         rc = snprintf(tempfile, PATH_MAX, "%s/file%03d", cur_dir, file_num);
391         if (rc >= PATH_MAX || rc < 0)
392                 return NULL;
393
394         return tempfile;
395 }
396
397 /*
398  * new_dir: prepares new dir name using dir counters.
399  */
400 static char *new_dir(char *tempdir, int dir_num)
401 {
402         int rc = 0;
403
404         rc = snprintf(tempdir, PATH_MAX, "%s/llverfs_dir%05d", testdir, dir_num);
405         if (rc >= PATH_MAX || rc < 0)
406                 return NULL;
407
408         return tempdir;
409 }
410
411 /*
412  * calc_total_bytes: calculates total bytes that need to be
413  * written into or read from the filesystem.
414  */
415 static unsigned long long calc_total_bytes(const char *op)
416 {
417         unsigned long long total_bytes = 0;
418         struct statfs64 statbuf;
419
420         if (full) {
421                 if (statfs64(testdir, &statbuf) == 0) {
422                         if (strcmp(op, "write") == 0)
423                                 total_bytes = (unsigned long long)
424                                         (statbuf.f_bavail * statbuf.f_bsize);
425                         else if (strcmp(op, "read") == 0)
426                                 total_bytes = (unsigned long long)
427                                         (statbuf.f_blocks * statbuf.f_bsize);
428                         else {
429                                 fprintf(stderr, "\n%s: invalid operation: %s\n",
430                                         progname, op);
431                                 return -1;
432                         }
433                 } else {
434                         fprintf(stderr, "\n%s: unable to stat %s: %s\n",
435                                 progname, testdir, strerror(errno));
436                         return -errno;
437                 }
438         } else {
439                 total_bytes = num_dirs * files_in_dir * file_size;
440         }
441
442         return total_bytes;
443 }
444
445 /*
446  * show_rate: displays the current read/write file name and performance,
447  * along with an estimate of how long the whole read/write operation
448  * will continue.
449  */
450 static void show_rate(char *op, char *filename,
451                       const struct timeval *start_time,
452                       const unsigned long long total_bytes,
453                       const unsigned long long curr_bytes)
454 {
455         static struct timeval last_time;
456         static unsigned long long last_bytes;
457         static char last_op;
458         struct timeval curr_time;
459         double curr_delta, overall_delta, curr_rate, overall_rate;
460         double remain_time;
461         int remain_hours, remain_minutes, remain_seconds;
462
463         if (last_op != op[0]) {
464                 last_bytes = 0;
465                 last_time = *start_time;
466                 last_op = op[0];
467         }
468
469         gettimeofday(&curr_time, NULL);
470
471         curr_delta = (curr_time.tv_sec - last_time.tv_sec) +
472                 (double)(curr_time.tv_usec - last_time.tv_usec) / 1000000;
473
474         overall_delta = (curr_time.tv_sec - start_time->tv_sec) +
475                 (double)(curr_time.tv_usec - start_time->tv_usec) / 1000000;
476
477         curr_rate = (curr_bytes - last_bytes) / curr_delta;
478         overall_rate = curr_bytes / overall_delta;
479
480         if (curr_rate == 0) {
481                 last_time = curr_time;
482                 return;
483         }
484         remain_time = (total_bytes - curr_bytes) / curr_rate;
485
486         remain_hours = remain_time / 3600;
487         remain_minutes = (remain_time - remain_hours * 3600) / 60;
488         remain_seconds = (remain_time - remain_hours * 3600 -
489                 remain_minutes * 60);
490
491         if (curr_delta > 4 || verbose > 2) {
492                 if (isatty_flag)
493                         printf("\r");
494
495                 printf("%s: %s, current: %5g MB/s, overall: %5g MB/s, "
496                        "ETA: %u:%02u:%02u", op, filename,
497                        curr_rate / ONE_MB, overall_rate / ONE_MB,
498                        remain_hours, remain_minutes, remain_seconds);
499
500                 if (isatty_flag)
501                         fflush(stdout);
502                 else
503                         printf("\n");
504
505                 last_time = curr_time;
506                 last_bytes = curr_bytes;
507         }
508 }
509
510 /*
511  * dir_write: This function writes directories and files on device.
512  * it works for both full and partial modes.
513  */
514 static int dir_write(char *chunk_buf, size_t chunksize,
515                      time_t time_st, unsigned long dir_num)
516 {
517         char tempfile[PATH_MAX];
518         char tempdir[PATH_MAX];
519         FILE *countfile;
520         struct stat64 file;
521         int file_num = 999999999;
522         ino_t inode_st = 0;
523         struct timeval start_time;
524         unsigned long long total_bytes;
525         unsigned long long curr_bytes = 0;
526         int rc = 0;
527
528         if (!full && fsetflags(testdir, EXT2_TOPDIR_FL))
529                 fprintf(stderr,
530                         "\n%s: can't set TOPDIR_FL on %s: %s (ignoring)",
531                         progname, testdir, strerror(errno));
532
533         countfile = fopen(filecount, "w");
534         if (countfile == NULL) {
535                 fprintf(stderr, "\n%s: creating %s failed :%s\n",
536                         progname, filecount, strerror(errno));
537                 return 5;
538         }
539         /* reserve space for the countfile */
540         if (fprintf(countfile, "%lu", num_files) < 1 ||
541             fflush(countfile) != 0) {
542                 fprintf(stderr, "\n%s: writing %s failed :%s\n",
543                         progname, filecount, strerror(errno));
544                 rc = 6;
545                 goto out;
546         }
547
548         /* calculate total bytes that need to be written */
549         total_bytes = calc_total_bytes("write");
550         if (total_bytes <= 0) {
551                 fprintf(stderr, "\n%s: unable to calculate total bytes\n",
552                         progname);
553                 rc = 7;
554                 goto out;
555         }
556
557         if (!full && (dir_num != 0))
558                 total_bytes -= dir_num * files_in_dir * file_size;
559
560         gettimeofday(&start_time, NULL);
561         for (; dir_num < num_dirs; num_files++, file_num++) {
562                 int fd, ret;
563
564                 if (file_num >= files_in_dir) {
565                         file_num = 0;
566                         if (mkdir(new_dir(tempdir, dir_num), dirmode) < 0) {
567                                 if (errno == ENOSPC)
568                                         break;
569                                 if (errno != EEXIST) {
570                                         fprintf(stderr, "\n%s: mkdir %s : %s\n",
571                                                 progname, tempdir,
572                                                 strerror(errno));
573                                         rc = 1;
574                                         goto out;
575                                 }
576                         }
577                         dir_num++;
578                 }
579
580                 fd = open_file(new_file(tempfile, tempdir, file_num),
581                                O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE);
582                 if (fd >= 0) {
583                         if (fstat64(fd, &file) == 0) {
584                                 inode_st = file.st_ino;
585                         } else {
586                                 fprintf(stderr, "\n%s: write stat '%s': %s",
587                                         progname, tempfile, strerror(errno));
588                                 close(fd);
589                                 break;
590                         }
591                 } else {
592                         break;
593                 }
594
595                 ret = write_chunks(fd, 0, file_size, chunk_buf, chunksize,
596                                    time_st, inode_st, tempfile);
597                 close(fd);
598                 if (ret < 0) {
599                         if (ret != -ENOSPC) {
600                                 rc = 1;
601                                 goto out;
602                         }
603                         curr_bytes = total_bytes;
604                         break;
605                 }
606
607                 curr_bytes += file_size;
608                 if (verbose > 1)
609                         show_rate("write", tempfile, &start_time,
610                                   total_bytes, curr_bytes);
611
612                 fseek(countfile, 0, SEEK_SET);
613                 if (fprintf(countfile, "%lu", num_files) < 1 ||
614                     fflush(countfile) != 0) {
615                         fprintf(stderr, "\n%s: writing %s failed :%s\n",
616                                 progname, filecount, strerror(errno));
617                 }
618         }
619
620         verbose += 2;
621         show_rate("write_done", tempfile, &start_time, total_bytes, curr_bytes);
622         printf("\n");
623         verbose -= 2;
624
625 out:
626         fclose(countfile);
627
628         return rc;
629 }
630
631 /*
632  * dir_read: This function reads directories and files on device.
633  * it works for both full and partial modes.
634  */
635 static int dir_read(char *chunk_buf, size_t chunksize,
636                     time_t time_st, unsigned long dir_num)
637 {
638         char tempfile[PATH_MAX];
639         char tempdir[PATH_MAX];
640         unsigned long count = 0;
641         struct stat64 file;
642         int file_num = 0;
643         ino_t inode_st = 0;
644         struct timeval start_time;
645         unsigned long long total_bytes;
646         unsigned long long curr_bytes = 0;
647
648         /* calculate total bytes that need to be read */
649         total_bytes = calc_total_bytes("read");
650         if (total_bytes <= 0) {
651                 fprintf(stderr, "\n%s: unable to calculate total bytes\n",
652                         progname);
653                 return 1;
654         }
655
656         if (dir_num != 0)
657                 total_bytes -= dir_num * files_in_dir * file_size;
658
659         gettimeofday(&start_time, NULL);
660         for (count = 0; count < num_files && dir_num < num_dirs; count++) {
661                 int fd, ret;
662
663                 if (file_num == 0) {
664                         new_dir(tempdir, dir_num);
665                         dir_num++;
666                 }
667
668                 fd = open_file(new_file(tempfile, tempdir, file_num),
669                                O_RDONLY | O_LARGEFILE);
670                 if (fd >= 0) {
671                         if (fstat64(fd, &file) == 0) {
672                                 inode_st = file.st_ino;
673                         } else {
674                                 fprintf(stderr, "\n%s: read stat '%s': %s\n",
675                                         progname, tempfile, strerror(errno));
676                                 close(fd);
677                                 return 1;
678                         }
679                 } else {
680                         break;
681                 }
682
683                 if (count == num_files)
684                         file_size = file.st_size;
685                 ret = read_chunks(fd, 0, file_size, chunk_buf, chunksize,
686                                   time_st, inode_st, tempfile);
687                 close(fd);
688                 if (ret)
689                         return 1;
690
691                 curr_bytes += file_size;
692                 if (verbose > 1)
693                         show_rate("read", tempfile, &start_time,
694                                   total_bytes, curr_bytes);
695
696                 if (++file_num >= files_in_dir)
697                         file_num = 0;
698         }
699         verbose += 2;
700         show_rate("read_done", tempfile, &start_time, total_bytes, curr_bytes);
701         printf("\n");
702         verbose -= 2;
703
704         return 0;
705 }
706
707 int main(int argc, char **argv)
708 {
709         time_t time_st = 0;             /* Default timestamp */
710         size_t chunksize = ONE_MB;      /* IO chunk size(defailt=1MB) */
711         char *chunk_buf;                /* chunk buffer */
712         int error = 0;
713         FILE *countfile = NULL;
714         unsigned long dir_num = 0, dir_num_orig = 0;/* starting directory */
715         int c;
716
717         progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
718         while ((c = getopt_long(argc, argv, "c:hln:o:pqrs:t:vw",
719                                       long_opts, NULL)) != -1) {
720                 unsigned long val;    /* Staging value for num_dirs */
721
722                 switch (c) {
723                 case 'c':
724                         chunksize = strtoul(optarg, NULL, 0) * ONE_MB;
725                         if (chunksize == 0) {
726                                 fprintf(stderr, "%s: bad chunk size '%s'\n",
727                                         optarg, progname);
728                                 return -1;
729                         }
730                         break;
731                 case 'l':
732                         full = 1;
733                         break;
734                 case 'n':
735                         /* num_dirs cannot be negative */
736                         if (optarg[0] == '-')
737                                 goto out_num_dirs;
738                         val = strtoul(optarg, NULL, 0);
739                         if (val == 0 || val > INT_MAX || errno == ERANGE)
740                                 goto out_num_dirs;
741                         num_dirs = val;
742                         break;
743 out_num_dirs:
744                         fprintf(stderr, "%s: bad directory count '%s'", optarg,
745                                 progname);
746                         usage(1);
747                         return -1;
748                 case 'o': /* offset */
749                         dir_num = strtoul(optarg, NULL, 0);
750                         break;
751                 case 'p':
752                         file_size = ONE_MB;
753                         chunksize = ONE_MB;
754                         files_in_dir = 1;
755                         full = 0;
756                         break;
757                 case 'q':
758                         verbose = 0;
759                         break;
760                 case 'r':
761                         readoption = 1;
762                         break;
763                 case 's':
764                         file_size = strtoul(optarg, NULL, 0) * ONE_MB;
765                         if (file_size == 0) {
766                                 fprintf(stderr, "%s: bad file size '%s'\n",
767                                         optarg, progname);
768                                 return -1;
769                         }
770                         break;
771                 case 't':
772                         time_st = (time_t)strtoul(optarg, NULL, 0);
773                         break;
774                 case 'v':
775                         verbose++;
776                         break;
777                 case 'w':
778                         writeoption = 1;
779                         break;
780
781                 case 'h':
782                 default:
783                         usage(1);
784                         return 0;
785                 }
786         }
787         testdir = argv[optind];
788
789         if (!testdir) {
790                 fprintf(stderr, "%s: pathname not given\n", progname);
791                 usage(1);
792                 return -1;
793         }
794         if (!readoption && !writeoption) {
795                 readoption = 1;
796                 writeoption = 1;
797         }
798         if (!time_st)
799                 (void) time(&time_st);
800         printf("Timestamp: %lu\n", (unsigned long )time_st);
801         isatty_flag = isatty(STDOUT_FILENO);
802
803         if (!full && !num_dirs) {
804 #ifdef HAVE_EXT2FS_EXT2FS_H
805                 struct mntent *tempmnt;
806                 FILE *fp = NULL;
807                 ext2_filsys fs;
808
809                 if ((fp = setmntent("/etc/mtab", "r")) == NULL) {
810                         fprintf(stderr, "%s: fail to open /etc/mtab in read mode :%s\n",
811                                 progname, strerror(errno));
812                         goto guess;
813                 }
814
815                 /* find device name using filesystem */
816                 while ((tempmnt = getmntent(fp)) != NULL) {
817                         if (strcmp(tempmnt->mnt_dir, testdir) == 0)
818                                 break;
819                 }
820
821                 if (tempmnt == NULL) {
822                         fprintf(stderr, "%s: no device found for '%s'\n",
823                                 progname, testdir);
824                         endmntent(fp);
825                         goto guess;
826                 }
827
828                 if (ext2fs_open(tempmnt->mnt_fsname, 0, 0, 0,
829                                 unix_io_manager, &fs)) {
830                         fprintf(stderr, "%s: unable to open ext3 fs on '%s'\n",
831                                 progname, testdir);
832                         endmntent(fp);
833                         goto guess;
834                 }
835                 endmntent(fp);
836
837                 num_dirs = (fs->super->s_blocks_count +
838                             fs->super->s_blocks_per_group - 1) /
839                         fs->super->s_blocks_per_group;
840                 if (verbose)
841                         printf("ext3 block groups: %u, fs blocks: %u "
842                                "blocks per group: %u\n",
843                                num_dirs, fs->super->s_blocks_count,
844                                fs->super->s_blocks_per_group);
845                 ext2fs_close(fs);
846 #else
847                 goto guess;
848 #endif
849         }
850         if (!num_dirs) {
851                 struct statfs64 statbuf;
852 guess:
853                 /*
854                  * Most extN filesystems are formatted with 128MB/group
855                  * (32k bitmap = 4KB blocksize * 8 bits/block) * 4KB,
856                  * so this is a relatively safe default (somewhat more
857                  * or less doesn't make a huge difference for testing).
858                  *
859                  * We want to create one directory per group, together
860                  * with the "TOPDIR" feature, so that the directories
861                  * are spread across the whole block device.
862                  */
863                 if (statfs64(testdir, &statbuf) == 0) {
864                         num_dirs = 1 + (long long)statbuf.f_blocks *
865                                                   statbuf.f_bsize /
866                                 (full ? files_in_dir * file_size : 128*ONE_MB);
867                         if (verbose)
868                                 printf("dirs: %u, fs blocks: %llu\n",
869                                        num_dirs, (long long)statbuf.f_blocks);
870                 } else {
871                         fprintf(stderr, "%s: unable to stat '%s': %s\n",
872                                 progname, testdir, strerror(errno));
873                         if (!num_dirs)
874                                 num_dirs = 30000;
875                         if (verbose)
876                                 printf("dirs: %u\n", num_dirs);
877                 }
878         }
879         chunk_buf = (char *)calloc(chunksize, 1);
880         if (chunk_buf == NULL) {
881                 fprintf(stderr, "Memory allocation failed for chunk_buf\n");
882                 return 4;
883         }
884         snprintf(filecount, sizeof(filecount), "%s/%s.filecount",
885                  testdir, progname);
886         if (writeoption) {
887                 (void)mkdir(testdir, dirmode);
888
889                 unlink(filecount);
890                 if (dir_num != 0) {
891                         num_files = dir_num * files_in_dir;
892                         if (verbose)
893                                 printf("\n%s: %lu files already written\n",
894                                        progname, num_files);
895                 }
896                 if (dir_write(chunk_buf, chunksize, time_st, dir_num)) {
897                         error = 3;
898                         goto out;
899                 }
900                 dir_num = dir_num_orig;
901         }
902         if (readoption) {
903                 if (!writeoption) {
904                         countfile = fopen(filecount, "r");
905                         if (countfile == NULL ||
906                             fscanf(countfile, "%lu", &num_files) != 1 ||
907                             num_files == 0) {
908                                 fprintf(stderr, "\n%s: reading %s failed :%s\n",
909                                         progname, filecount, strerror(errno));
910                                 num_files = num_dirs * files_in_dir;
911                         } else {
912                                 num_files -= (dir_num * files_in_dir);
913                         }
914                         if (countfile)
915                                 fclose(countfile);
916                 }
917                 if (dir_read(chunk_buf, chunksize, time_st, dir_num)) {
918                         fprintf(stderr, "\n%s: Data verification failed\n",
919                                 progname) ;
920                         error = 2;
921                         goto out;
922                 }
923         }
924         error = error_count;
925 out:
926         free(chunk_buf);
927         return error;
928 }