Whamcloud - gitweb
LU-16977 utils: access_log_reader accesses beyond batch array
[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 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 int verify_chunk(char *chunk_buf, const size_t chunksize,
173                  unsigned long long chunk_off, const unsigned long long time_st,
174                  const unsigned long long inode_st, const char *file)
175 {
176         struct block_data *bd;
177         char *chunk_end;
178
179         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
180              (char *)chunk_buf < chunk_end;
181              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
182                 bd = (struct block_data *)chunk_buf;
183                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
184                     (bd->bd_inode == inode_st))
185                         continue;
186                 fprintf(stderr, "\n%s: verify %s failed offset/timestamp/inode "
187                         "%llu/%llu/%llu: found %llu/%llu/%llu instead\n",
188                         progname, file, chunk_off, time_st, inode_st,
189                         bd->bd_offset, bd->bd_time, bd->bd_inode);
190                 return 1;
191         }
192         return 0;
193 }
194
195 /*
196  * fill_chunk: Fills the chunk with current or user specified timestamp
197  * and offset. The test pattern is filled at the beginning of
198  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
199  */
200 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
201                 const time_t time_st, const ino_t inode_st)
202 {
203         struct block_data *bd;
204         char *chunk_end;
205
206         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
207              (char *)chunk_buf < chunk_end;
208              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
209                 bd = (struct block_data *)chunk_buf;
210                 bd->bd_offset = chunk_off;
211                 bd->bd_time = time_st;
212                 bd->bd_inode = inode_st;
213         }
214 }
215
216 /*
217  * Write a chunk to disk, handling errors, interrupted writes, etc.
218  *
219  * If there is an IO error hit during the write, it is possible that
220  * this will just show up as a short write, and a subsequent write
221  * will return the actual error.  We want to continue in the face of
222  * minor media errors so that we can validate the whole device if
223  * possible, but if there are many errors we don't want to loop forever.
224  *
225  * The error count will be returned upon exit to ensure that the
226  * media errors are detected even if nobody is looking at the output.
227  *
228  * Returns 0 on success, or -ve errno on failure.
229  */
230 int write_retry(int fd, const char *chunk_buf, size_t nrequested,
231                 unsigned long long offset, const char *file)
232 {
233         long nwritten;
234
235 retry:
236         nwritten = write(fd, chunk_buf, nrequested);
237         if (nwritten < 0) {
238                 if (errno != ENOSPC) {
239                         fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
240                                 progname, file, offset, nrequested,
241                                 strerror(errno));
242                         if (error_count++ < 100)
243                                 return 0;
244                 }
245                 return -errno;
246         }
247         if (nwritten < nrequested) {
248                 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld written\n",
249                         progname, file, offset, nrequested, nwritten);
250                 offset += nwritten;
251                 chunk_buf += nwritten;
252                 nrequested -= nwritten;
253                 goto retry;
254         }
255
256         return 0;
257 }
258
259 /*
260  * write_chunks: write the chunk_buf on the device. The number of write
261  * operations are based on the parameters write_end, offset, and chunksize.
262  *
263  * Returns 0 on success, or -ve error number on failure.
264  */
265 int write_chunks(int fd, unsigned long long offset,unsigned long long write_end,
266                  char *chunk_buf, size_t chunksize, const time_t time_st,
267                  const ino_t inode_st, const char *file)
268 {
269         unsigned long long stride;
270
271         stride = full ? chunksize : (ONE_GB - chunksize);
272         for (offset = offset & ~(chunksize - 1); offset < write_end;
273              offset += stride) {
274                 int ret;
275
276                 if (stride != chunksize && lseek64(fd, offset, SEEK_SET) < 0) {
277                         fprintf(stderr, "\n%s: lseek66(%s+%llu) failed: %s\n",
278                                 progname, file, offset, strerror(errno));
279                         return -errno;
280                 }
281                 if (offset + chunksize > write_end)
282                         chunksize = write_end - offset;
283                 if (!full && offset > chunksize) {
284                         fill_chunk(chunk_buf, chunksize, offset, time_st,
285                                    inode_st);
286                         ret = write_retry(fd, chunk_buf, chunksize,offset,file);
287                         if (ret < 0)
288                                 return ret;
289                         offset += chunksize;
290                         if (offset + chunksize > write_end)
291                                 chunksize = write_end - offset;
292                 }
293                 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
294                 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
295                 if (ret < 0)
296                         return ret;
297         }
298         return 0;
299 }
300
301 /*
302  * read_chunk: reads the chunk_buf from the device. The number of read
303  * operations are based on the parameters read_end, offset, and chunksize.
304  */
305 int read_chunks(int fd, unsigned long long offset, unsigned long long read_end,
306                 char *chunk_buf, size_t chunksize, const time_t time_st,
307                 const ino_t inode_st, const char *file)
308 {
309         unsigned long long stride;
310
311         stride = full ? chunksize : (ONE_GB - chunksize);
312         for (offset = offset & ~(chunksize - 1); offset < read_end;
313              offset += stride) {
314                 ssize_t nread;
315
316                 if (stride != chunksize && lseek64(fd, offset, SEEK_SET) < 0) {
317                         fprintf(stderr, "\n%s: lseek64(%s+%llu) failed: %s\n",
318                                 progname, file, offset, strerror(errno));
319                         return 1;
320                 }
321                 if (offset + chunksize > read_end)
322                         chunksize = read_end - offset;
323
324                 if (!full && offset > chunksize) {
325                         nread = read(fd, chunk_buf, chunksize);
326                         if (nread < 0) {
327                                 fprintf(stderr,"\n%s: read %s@%llu+%zi failed: "
328                                         "%s\n", progname, file, offset,
329                                         chunksize, strerror(errno));
330                                 error_count++;
331                                 return 1;
332                         }
333                         if (nread < chunksize) {
334                                 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
335                                         "%zi read\n", progname, file, offset,
336                                         chunksize, nread);
337                                 error_count++;
338                         }
339                         if (verify_chunk(chunk_buf, nread, offset, time_st,
340                                          inode_st, file) != 0) {
341                                 return 1;
342                         }
343                         offset += chunksize;
344
345                         /* Need to reset position after read error */
346                         if (nread < chunksize &&
347                             lseek64(fd, offset, SEEK_SET) == -1) {
348                                 fprintf(stderr,
349                                         "\n%s: lseek64(%s@%llu) failed: %s\n",
350                                         progname, file, offset,strerror(errno));
351                                 return 1;
352                         }
353                         if (offset + chunksize >= read_end)
354                                 chunksize = read_end - offset;
355                 }
356                 nread = read(fd, chunk_buf, chunksize);
357                 if (nread < 0) {
358                         fprintf(stderr, "\n%s: read %s@%llu+%zi failed: %s\n",
359                                 progname, file, offset, chunksize,
360                                 strerror(errno));
361                         error_count++;
362                         return 1;
363                 }
364                 if (nread < chunksize) {
365                         fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
366                                 "%zi read\n", progname, file, offset,
367                                 chunksize, nread);
368                         error_count++;
369                 }
370
371                 if (verify_chunk(chunk_buf, nread, offset, time_st,
372                                  inode_st, file) != 0) {
373                         return 1;
374                 }
375         }
376         return 0;
377 }
378
379 /*
380  * new_file: prepares new filename using file counter and current dir.
381  */
382 char *new_file(char *tempfile, char *cur_dir, int file_num)
383 {
384         snprintf(tempfile, PATH_MAX, "%s/file%03d", cur_dir, file_num);
385         return tempfile;
386 }
387
388 /*
389  * new_dir: prepares new dir name using dir counters.
390  */
391 char *new_dir(char *tempdir, int dir_num)
392 {
393         snprintf(tempdir, PATH_MAX, "%s/llverfs_dir%05d", testdir, dir_num);
394         return tempdir;
395 }
396
397 /*
398  * calc_total_bytes: calculates total bytes that need to be
399  * written into or read from the filesystem.
400  */
401 static unsigned long long calc_total_bytes(const char *op)
402 {
403         unsigned long long total_bytes = 0;
404         struct statfs64 statbuf;
405
406         if (full) {
407                 if (statfs64(testdir, &statbuf) == 0) {
408                         if (strcmp(op, "write") == 0)
409                                 total_bytes = (unsigned long long)
410                                         (statbuf.f_bavail * statbuf.f_bsize);
411                         else if (strcmp(op, "read") == 0)
412                                 total_bytes = (unsigned long long)
413                                         (statbuf.f_blocks * statbuf.f_bsize);
414                         else {
415                                 fprintf(stderr, "\n%s: invalid operation: %s\n",
416                                         progname, op);
417                                 return -1;
418                         }
419                 } else {
420                         fprintf(stderr, "\n%s: unable to stat %s: %s\n",
421                                 progname, testdir, strerror(errno));
422                         return -errno;
423                 }
424         } else {
425                 total_bytes = num_dirs * files_in_dir * file_size;
426         }
427
428         return total_bytes;
429 }
430
431 /*
432  * show_rate: displays the current read/write file name and performance,
433  * along with an estimate of how long the whole read/write operation
434  * will continue.
435  */
436 void show_rate(char *op, char *filename, const struct timeval *start_time,
437                const unsigned long long total_bytes,
438                const unsigned long long curr_bytes)
439 {
440         static struct timeval last_time;
441         static unsigned long long last_bytes;
442         static char last_op;
443         struct timeval curr_time;
444         double curr_delta, overall_delta, curr_rate, overall_rate;
445         double remain_time;
446         int remain_hours, remain_minutes, remain_seconds;
447
448         if (last_op != op[0]) {
449                 last_bytes = 0;
450                 last_time = *start_time;
451                 last_op = op[0];
452         }
453
454         gettimeofday(&curr_time, NULL);
455
456         curr_delta = (curr_time.tv_sec - last_time.tv_sec) +
457                 (double)(curr_time.tv_usec - last_time.tv_usec) / 1000000;
458
459         overall_delta = (curr_time.tv_sec - start_time->tv_sec) +
460                 (double)(curr_time.tv_usec - start_time->tv_usec) / 1000000;
461
462         curr_rate = (curr_bytes - last_bytes) / curr_delta;
463         overall_rate = curr_bytes / overall_delta;
464
465         if (curr_rate == 0) {
466                 last_time = curr_time;
467                 return;
468         }
469         remain_time = (total_bytes - curr_bytes) / curr_rate;
470
471         remain_hours = remain_time / 3600;
472         remain_minutes = (remain_time - remain_hours * 3600) / 60;
473         remain_seconds = (remain_time - remain_hours * 3600 -
474                 remain_minutes * 60);
475
476         if (curr_delta > 4 || verbose > 2) {
477                 if (isatty_flag)
478                         printf("\r");
479
480                 printf("%s: %s, current: %5g MB/s, overall: %5g MB/s, "
481                        "ETA: %u:%02u:%02u", op, filename,
482                        curr_rate / ONE_MB, overall_rate / ONE_MB,
483                        remain_hours, remain_minutes, remain_seconds);
484
485                 if (isatty_flag)
486                         fflush(stdout);
487                 else
488                         printf("\n");
489
490                 last_time = curr_time;
491                 last_bytes = curr_bytes;
492         }
493 }
494
495 /*
496  * dir_write: This function writes directories and files on device.
497  * it works for both full and partial modes.
498  */
499 static int dir_write(char *chunk_buf, size_t chunksize,
500                      time_t time_st, unsigned long dir_num)
501 {
502         char tempfile[PATH_MAX];
503         char tempdir[PATH_MAX];
504         FILE *countfile;
505         struct stat64 file;
506         int file_num = 999999999;
507         ino_t inode_st = 0;
508         struct timeval start_time;
509         unsigned long long total_bytes;
510         unsigned long long curr_bytes = 0;
511         int rc = 0;
512
513         if (!full && fsetflags(testdir, EXT2_TOPDIR_FL))
514                 fprintf(stderr,
515                         "\n%s: can't set TOPDIR_FL on %s: %s (ignoring)",
516                         progname, testdir, strerror(errno));
517
518         countfile = fopen(filecount, "w");
519         if (countfile == NULL) {
520                 fprintf(stderr, "\n%s: creating %s failed :%s\n",
521                         progname, filecount, strerror(errno));
522                 return 5;
523         }
524         /* reserve space for the countfile */
525         if (fprintf(countfile, "%lu", num_files) < 1 ||
526             fflush(countfile) != 0) {
527                 fprintf(stderr, "\n%s: writing %s failed :%s\n",
528                         progname, filecount, strerror(errno));
529                 rc = 6;
530                 goto out;
531         }
532
533         /* calculate total bytes that need to be written */
534         total_bytes = calc_total_bytes("write");
535         if (total_bytes <= 0) {
536                 fprintf(stderr, "\n%s: unable to calculate total bytes\n",
537                         progname);
538                 rc = 7;
539                 goto out;
540         }
541
542         if (!full && (dir_num != 0))
543                 total_bytes -= dir_num * files_in_dir * file_size;
544
545         gettimeofday(&start_time, NULL);
546         for (; dir_num < num_dirs; num_files++, file_num++) {
547                 int fd, ret;
548
549                 if (file_num >= files_in_dir) {
550                         file_num = 0;
551                         if (mkdir(new_dir(tempdir, dir_num), dirmode) < 0) {
552                                 if (errno == ENOSPC)
553                                         break;
554                                 if (errno != EEXIST) {
555                                         fprintf(stderr, "\n%s: mkdir %s : %s\n",
556                                                 progname, tempdir,
557                                                 strerror(errno));
558                                         rc = 1;
559                                         goto out;
560                                 }
561                         }
562                         dir_num++;
563                 }
564
565                 fd = open_file(new_file(tempfile, tempdir, file_num),
566                                O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE);
567                 if (fd >= 0) {
568                         if (fstat64(fd, &file) == 0) {
569                                 inode_st = file.st_ino;
570                         } else {
571                                 fprintf(stderr, "\n%s: write stat '%s': %s",
572                                         progname, tempfile, strerror(errno));
573                                 close(fd);
574                                 break;
575                         }
576                 } else {
577                         break;
578                 }
579
580                 ret = write_chunks(fd, 0, file_size, chunk_buf, chunksize,
581                                    time_st, inode_st, tempfile);
582                 close(fd);
583                 if (ret < 0) {
584                         if (ret != -ENOSPC) {
585                                 rc = 1;
586                                 goto out;
587                         }
588                         curr_bytes = total_bytes;
589                         break;
590                 }
591
592                 curr_bytes += file_size;
593                 if (verbose > 1)
594                         show_rate("write", tempfile, &start_time,
595                                   total_bytes, curr_bytes);
596
597                 fseek(countfile, 0, SEEK_SET);
598                 if (fprintf(countfile, "%lu", num_files) < 1 ||
599                     fflush(countfile) != 0) {
600                         fprintf(stderr, "\n%s: writing %s failed :%s\n",
601                                 progname, filecount, strerror(errno));
602                 }
603         }
604
605         verbose += 2;
606         show_rate("write_done", tempfile, &start_time, total_bytes, curr_bytes);
607         printf("\n");
608         verbose -= 2;
609
610 out:
611         fclose(countfile);
612
613         return rc;
614 }
615
616 /*
617  * dir_read: This function reads directories and files on device.
618  * it works for both full and partial modes.
619  */
620 static int dir_read(char *chunk_buf, size_t chunksize,
621                     time_t time_st, unsigned long dir_num)
622 {
623         char tempfile[PATH_MAX];
624         char tempdir[PATH_MAX];
625         unsigned long count = 0;
626         struct stat64 file;
627         int file_num = 0;
628         ino_t inode_st = 0;
629         struct timeval start_time;
630         unsigned long long total_bytes;
631         unsigned long long curr_bytes = 0;
632
633         /* calculate total bytes that need to be read */
634         total_bytes = calc_total_bytes("read");
635         if (total_bytes <= 0) {
636                 fprintf(stderr, "\n%s: unable to calculate total bytes\n",
637                         progname);
638                 return 1;
639         }
640
641         if (dir_num != 0)
642                 total_bytes -= dir_num * files_in_dir * file_size;
643
644         gettimeofday(&start_time, NULL);
645         for (count = 0; count < num_files && dir_num < num_dirs; count++) {
646                 int fd, ret;
647
648                 if (file_num == 0) {
649                         new_dir(tempdir, dir_num);
650                         dir_num++;
651                 }
652
653                 fd = open_file(new_file(tempfile, tempdir, file_num),
654                                O_RDONLY | O_LARGEFILE);
655                 if (fd >= 0) {
656                         if (fstat64(fd, &file) == 0) {
657                                 inode_st = file.st_ino;
658                         } else {
659                                 fprintf(stderr, "\n%s: read stat '%s': %s\n",
660                                         progname, tempfile, strerror(errno));
661                                 close(fd);
662                                 return 1;
663                         }
664                 } else {
665                         break;
666                 }
667
668                 if (count == num_files)
669                         file_size = file.st_size;
670                 ret = read_chunks(fd, 0, file_size, chunk_buf, chunksize,
671                                   time_st, inode_st, tempfile);
672                 close(fd);
673                 if (ret)
674                         return 1;
675
676                 curr_bytes += file_size;
677                 if (verbose > 1)
678                         show_rate("read", tempfile, &start_time,
679                                   total_bytes, curr_bytes);
680
681                 if (++file_num >= files_in_dir)
682                         file_num = 0;
683         }
684         verbose += 2;
685         show_rate("read_done", tempfile, &start_time, total_bytes, curr_bytes);
686         printf("\n");
687         verbose -= 2;
688
689         return 0;
690 }
691
692 int main(int argc, char **argv)
693 {
694         time_t time_st = 0;             /* Default timestamp */
695         size_t chunksize = ONE_MB;      /* IO chunk size(defailt=1MB) */
696         char *chunk_buf;                /* chunk buffer */
697         int error = 0;
698         FILE *countfile = NULL;
699         unsigned long dir_num = 0, dir_num_orig = 0;/* starting directory */
700         int c;
701
702         progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
703         while ((c = getopt_long(argc, argv, "c:hln:o:pqrs:t:vw",
704                                       long_opts, NULL)) != -1) {
705                 switch (c) {
706                 case 'c':
707                         chunksize = strtoul(optarg, NULL, 0) * ONE_MB;
708                         if (chunksize == 0) {
709                                 fprintf(stderr, "%s: bad chunk size '%s'\n",
710                                         optarg, progname);
711                                 return -1;
712                         }
713                         break;
714                 case 'l':
715                         full = 1;
716                         break;
717                 case 'n':
718                         num_dirs = strtoul(optarg, NULL, 0);
719                         break;
720                 case 'o': /* offset */
721                         dir_num = strtoul(optarg, NULL, 0);
722                         break;
723                 case 'p':
724                         file_size = ONE_MB;
725                         chunksize = ONE_MB;
726                         files_in_dir = 1;
727                         full = 0;
728                         break;
729                 case 'q':
730                         verbose = 0;
731                         break;
732                 case 'r':
733                         readoption = 1;
734                         break;
735                 case 's':
736                         file_size = strtoul(optarg, NULL, 0) * ONE_MB;
737                         if (file_size == 0) {
738                                 fprintf(stderr, "%s: bad file size '%s'\n",
739                                         optarg, progname);
740                                 return -1;
741                         }
742                         break;
743                 case 't':
744                         time_st = (time_t)strtoul(optarg, NULL, 0);
745                         break;
746                 case 'v':
747                         verbose++;
748                         break;
749                 case 'w':
750                         writeoption = 1;
751                         break;
752
753                 case 'h':
754                 default:
755                         usage(1);
756                         return 0;
757                 }
758         }
759         testdir = argv[optind];
760
761         if (!testdir) {
762                 fprintf(stderr, "%s: pathname not given\n", progname);
763                 usage(1);
764                 return -1;
765         }
766         if (!readoption && !writeoption) {
767                 readoption = 1;
768                 writeoption = 1;
769         }
770         if (!time_st)
771                 (void) time(&time_st);
772         printf("Timestamp: %lu\n", (unsigned long )time_st);
773         isatty_flag = isatty(STDOUT_FILENO);
774
775         if (!full && !num_dirs) {
776 #ifdef HAVE_EXT2FS_EXT2FS_H
777                 struct mntent *tempmnt;
778                 FILE *fp = NULL;
779                 ext2_filsys fs;
780
781                 if ((fp = setmntent("/etc/mtab", "r")) == NULL) {
782                         fprintf(stderr, "%s: fail to open /etc/mtab in read mode :%s\n",
783                                 progname, strerror(errno));
784                         goto guess;
785                 }
786
787                 /* find device name using filesystem */
788                 while ((tempmnt = getmntent(fp)) != NULL) {
789                         if (strcmp(tempmnt->mnt_dir, testdir) == 0)
790                                 break;
791                 }
792
793                 if (tempmnt == NULL) {
794                         fprintf(stderr, "%s: no device found for '%s'\n",
795                                 progname, testdir);
796                         endmntent(fp);
797                         goto guess;
798                 }
799
800                 if (ext2fs_open(tempmnt->mnt_fsname, 0, 0, 0,
801                                 unix_io_manager, &fs)) {
802                         fprintf(stderr, "%s: unable to open ext3 fs on '%s'\n",
803                                 progname, testdir);
804                         endmntent(fp);
805                         goto guess;
806                 }
807                 endmntent(fp);
808
809                 num_dirs = (fs->super->s_blocks_count +
810                             fs->super->s_blocks_per_group - 1) /
811                         fs->super->s_blocks_per_group;
812                 if (verbose)
813                         printf("ext3 block groups: %u, fs blocks: %u "
814                                "blocks per group: %u\n",
815                                num_dirs, fs->super->s_blocks_count,
816                                fs->super->s_blocks_per_group);
817                 ext2fs_close(fs);
818 #else
819                 goto guess;
820 #endif
821         }
822         if (!num_dirs) {
823                 struct statfs64 statbuf;
824 guess:
825                 /*
826                  * Most extN filesystems are formatted with 128MB/group
827                  * (32k bitmap = 4KB blocksize * 8 bits/block) * 4KB,
828                  * so this is a relatively safe default (somewhat more
829                  * or less doesn't make a huge difference for testing).
830                  *
831                  * We want to create one directory per group, together
832                  * with the "TOPDIR" feature, so that the directories
833                  * are spread across the whole block device.
834                  */
835                 if (statfs64(testdir, &statbuf) == 0) {
836                         num_dirs = 1 + (long long)statbuf.f_blocks *
837                                                   statbuf.f_bsize /
838                                 (full ? files_in_dir * file_size : 128*ONE_MB);
839                         if (verbose)
840                                 printf("dirs: %u, fs blocks: %llu\n",
841                                        num_dirs, (long long)statbuf.f_blocks);
842                 } else {
843                         fprintf(stderr, "%s: unable to stat '%s': %s\n",
844                                 progname, testdir, strerror(errno));
845                         if (!num_dirs)
846                                 num_dirs = 30000;
847                         if (verbose)
848                                 printf("dirs: %u\n", num_dirs);
849                 }
850         }
851         chunk_buf = (char *)calloc(chunksize, 1);
852         if (chunk_buf == NULL) {
853                 fprintf(stderr, "Memory allocation failed for chunk_buf\n");
854                 return 4;
855         }
856         snprintf(filecount, sizeof(filecount), "%s/%s.filecount",
857                  testdir, progname);
858         if (writeoption) {
859                 (void)mkdir(testdir, dirmode);
860
861                 unlink(filecount);
862                 if (dir_num != 0) {
863                         num_files = dir_num * files_in_dir;
864                         if (verbose)
865                                 printf("\n%s: %lu files already written\n",
866                                        progname, num_files);
867                 }
868                 if (dir_write(chunk_buf, chunksize, time_st, dir_num)) {
869                         error = 3;
870                         goto out;
871                 }
872                 dir_num = dir_num_orig;
873         }
874         if (readoption) {
875                 if (!writeoption) {
876                         countfile = fopen(filecount, "r");
877                         if (countfile == NULL ||
878                             fscanf(countfile, "%lu", &num_files) != 1 ||
879                             num_files == 0) {
880                                 fprintf(stderr, "\n%s: reading %s failed :%s\n",
881                                         progname, filecount, strerror(errno));
882                                 num_files = num_dirs * files_in_dir;
883                         } else {
884                                 num_files -= (dir_num * files_in_dir);
885                         }
886                         if (countfile)
887                                 fclose(countfile);
888                 }
889                 if (dir_read(chunk_buf, chunksize, time_st, dir_num)) {
890                         fprintf(stderr, "\n%s: Data verification failed\n",
891                                 progname) ;
892                         error = 2;
893                         goto out;
894                 }
895         }
896         error = error_count;
897 out:
898         free(chunk_buf);
899         return error;
900 }