Whamcloud - gitweb
LU-17000 coverity: llverfs: Add check for -n option
[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                 unsigned long val;    /* Staging value for num_dirs */
706
707                 switch (c) {
708                 case 'c':
709                         chunksize = strtoul(optarg, NULL, 0) * ONE_MB;
710                         if (chunksize == 0) {
711                                 fprintf(stderr, "%s: bad chunk size '%s'\n",
712                                         optarg, progname);
713                                 return -1;
714                         }
715                         break;
716                 case 'l':
717                         full = 1;
718                         break;
719                 case 'n':
720                         /* num_dirs cannot be negative */
721                         if (optarg[0] == '-')
722                                 goto out_num_dirs;
723                         val = strtoul(optarg, NULL, 0);
724                         if (val == 0 || val > INT_MAX || errno == ERANGE)
725                                 goto out_num_dirs;
726                         num_dirs = val;
727                         break;
728 out_num_dirs:
729                         fprintf(stderr, "%s: bad directory count '%s'", optarg,
730                                 progname);
731                         usage(1);
732                         return -1;
733                 case 'o': /* offset */
734                         dir_num = strtoul(optarg, NULL, 0);
735                         break;
736                 case 'p':
737                         file_size = ONE_MB;
738                         chunksize = ONE_MB;
739                         files_in_dir = 1;
740                         full = 0;
741                         break;
742                 case 'q':
743                         verbose = 0;
744                         break;
745                 case 'r':
746                         readoption = 1;
747                         break;
748                 case 's':
749                         file_size = strtoul(optarg, NULL, 0) * ONE_MB;
750                         if (file_size == 0) {
751                                 fprintf(stderr, "%s: bad file size '%s'\n",
752                                         optarg, progname);
753                                 return -1;
754                         }
755                         break;
756                 case 't':
757                         time_st = (time_t)strtoul(optarg, NULL, 0);
758                         break;
759                 case 'v':
760                         verbose++;
761                         break;
762                 case 'w':
763                         writeoption = 1;
764                         break;
765
766                 case 'h':
767                 default:
768                         usage(1);
769                         return 0;
770                 }
771         }
772         testdir = argv[optind];
773
774         if (!testdir) {
775                 fprintf(stderr, "%s: pathname not given\n", progname);
776                 usage(1);
777                 return -1;
778         }
779         if (!readoption && !writeoption) {
780                 readoption = 1;
781                 writeoption = 1;
782         }
783         if (!time_st)
784                 (void) time(&time_st);
785         printf("Timestamp: %lu\n", (unsigned long )time_st);
786         isatty_flag = isatty(STDOUT_FILENO);
787
788         if (!full && !num_dirs) {
789 #ifdef HAVE_EXT2FS_EXT2FS_H
790                 struct mntent *tempmnt;
791                 FILE *fp = NULL;
792                 ext2_filsys fs;
793
794                 if ((fp = setmntent("/etc/mtab", "r")) == NULL) {
795                         fprintf(stderr, "%s: fail to open /etc/mtab in read mode :%s\n",
796                                 progname, strerror(errno));
797                         goto guess;
798                 }
799
800                 /* find device name using filesystem */
801                 while ((tempmnt = getmntent(fp)) != NULL) {
802                         if (strcmp(tempmnt->mnt_dir, testdir) == 0)
803                                 break;
804                 }
805
806                 if (tempmnt == NULL) {
807                         fprintf(stderr, "%s: no device found for '%s'\n",
808                                 progname, testdir);
809                         endmntent(fp);
810                         goto guess;
811                 }
812
813                 if (ext2fs_open(tempmnt->mnt_fsname, 0, 0, 0,
814                                 unix_io_manager, &fs)) {
815                         fprintf(stderr, "%s: unable to open ext3 fs on '%s'\n",
816                                 progname, testdir);
817                         endmntent(fp);
818                         goto guess;
819                 }
820                 endmntent(fp);
821
822                 num_dirs = (fs->super->s_blocks_count +
823                             fs->super->s_blocks_per_group - 1) /
824                         fs->super->s_blocks_per_group;
825                 if (verbose)
826                         printf("ext3 block groups: %u, fs blocks: %u "
827                                "blocks per group: %u\n",
828                                num_dirs, fs->super->s_blocks_count,
829                                fs->super->s_blocks_per_group);
830                 ext2fs_close(fs);
831 #else
832                 goto guess;
833 #endif
834         }
835         if (!num_dirs) {
836                 struct statfs64 statbuf;
837 guess:
838                 /*
839                  * Most extN filesystems are formatted with 128MB/group
840                  * (32k bitmap = 4KB blocksize * 8 bits/block) * 4KB,
841                  * so this is a relatively safe default (somewhat more
842                  * or less doesn't make a huge difference for testing).
843                  *
844                  * We want to create one directory per group, together
845                  * with the "TOPDIR" feature, so that the directories
846                  * are spread across the whole block device.
847                  */
848                 if (statfs64(testdir, &statbuf) == 0) {
849                         num_dirs = 1 + (long long)statbuf.f_blocks *
850                                                   statbuf.f_bsize /
851                                 (full ? files_in_dir * file_size : 128*ONE_MB);
852                         if (verbose)
853                                 printf("dirs: %u, fs blocks: %llu\n",
854                                        num_dirs, (long long)statbuf.f_blocks);
855                 } else {
856                         fprintf(stderr, "%s: unable to stat '%s': %s\n",
857                                 progname, testdir, strerror(errno));
858                         if (!num_dirs)
859                                 num_dirs = 30000;
860                         if (verbose)
861                                 printf("dirs: %u\n", num_dirs);
862                 }
863         }
864         chunk_buf = (char *)calloc(chunksize, 1);
865         if (chunk_buf == NULL) {
866                 fprintf(stderr, "Memory allocation failed for chunk_buf\n");
867                 return 4;
868         }
869         snprintf(filecount, sizeof(filecount), "%s/%s.filecount",
870                  testdir, progname);
871         if (writeoption) {
872                 (void)mkdir(testdir, dirmode);
873
874                 unlink(filecount);
875                 if (dir_num != 0) {
876                         num_files = dir_num * files_in_dir;
877                         if (verbose)
878                                 printf("\n%s: %lu files already written\n",
879                                        progname, num_files);
880                 }
881                 if (dir_write(chunk_buf, chunksize, time_st, dir_num)) {
882                         error = 3;
883                         goto out;
884                 }
885                 dir_num = dir_num_orig;
886         }
887         if (readoption) {
888                 if (!writeoption) {
889                         countfile = fopen(filecount, "r");
890                         if (countfile == NULL ||
891                             fscanf(countfile, "%lu", &num_files) != 1 ||
892                             num_files == 0) {
893                                 fprintf(stderr, "\n%s: reading %s failed :%s\n",
894                                         progname, filecount, strerror(errno));
895                                 num_files = num_dirs * files_in_dir;
896                         } else {
897                                 num_files -= (dir_num * files_in_dir);
898                         }
899                         if (countfile)
900                                 fclose(countfile);
901                 }
902                 if (dir_read(chunk_buf, chunksize, time_st, dir_num)) {
903                         fprintf(stderr, "\n%s: Data verification failed\n",
904                                 progname) ;
905                         error = 2;
906                         goto out;
907                 }
908         }
909         error = error_count;
910 out:
911         free(chunk_buf);
912         return error;
913 }