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