1 /*
2 * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <new>
8 #include <string>
9 #include <vector>
10
11 #include <dirent.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <getopt.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20
21 #include <fs_attr.h>
22 #include <fs_volume.h>
23 #include <OS.h>
24 #include <TypeConstants.h>
25
26
27 enum file_action {
28 kCreateFile,
29 kCreateDir,
30 kRenameFile,
31 kRemoveFile,
32 kRemoveDir,
33 kAppendFile,
34 kReplaceFile,
35 kTruncateFile,
36
37 kNumActions
38 };
39
40 struct block_identifier {
41 off_t offset;
42 uint32 identifier;
43 uint16 data[0];
44 };
45
46 struct entry {
47 std::string name;
48 uint32 identifier;
49 off_t size;
50 };
51
52 typedef std::vector<entry> EntryVector;
53
54
55 const char* kDefaultBaseDir = "./random_file_temp";
56 const char* kIdentifierAttribute = "rfa:identifier";
57
58 const uint32 kDefaultDirCount = 1;
59 const uint32 kDefaultFileCount = 10;
60 const uint32 kDefaultRunCount = 100;
61 const off_t kDefaultMaxFileSize = 32768;
62
63 const uint32 kMaxFileCount = 1000000;
64 const uint32 kMaxDirCount = 100000;
65
66 const uint32 kBlockSize = 256;
67
68
69 extern const char *__progname;
70 static const char *kProgramName = __progname;
71
72 static bool sDisableFileCache = false;
73 static bool sVerbose = false;
74 static bool sCheckBeforeRemove = false;
75 static off_t sMaxFileSize = kDefaultMaxFileSize;
76 static uint32 sCount = 0;
77
78 static off_t sWriteTotal = 0;
79 static off_t sReadTotal = 0;
80 static bigtime_t sWriteTime = 0;
81 static bigtime_t sReadTime = 0;
82 static uint32 sRun = 0;
83
84
85 static off_t
string_to_size(const char * valueWithUnit)86 string_to_size(const char* valueWithUnit)
87 {
88 char* unit;
89 off_t size = strtoull(valueWithUnit, &unit, 10);
90
91 if (strchr(unit, 'G') || strchr(unit, 'g'))
92 size *= 1024 * 1024 * 1024;
93 else if (strchr(unit, 'M') || strchr(unit, 'm'))
94 size *= 1024 * 1024;
95 else if (strchr(unit, 'K') || strchr(unit, 'k'))
96 size *= 1024;
97
98 return size;
99 }
100
101
102 static std::string
size_to_string(off_t size)103 size_to_string(off_t size)
104 {
105 char buffer[256];
106
107 if (size > 10LL * 1024 * 1024 * 1024) {
108 snprintf(buffer, sizeof(buffer), "%g GB",
109 size / (1024.0 * 1024 * 1024));
110 } else if (size > 10 * 1024 * 1024)
111 snprintf(buffer, sizeof(buffer), "%g MB", size / (1024.0 * 1024));
112 else if (size > 10 * 1024)
113 snprintf(buffer, sizeof(buffer), "%g KB", size / (1024.0));
114 else
115 snprintf(buffer, sizeof(buffer), "%lld B", size);
116
117 return buffer;
118 }
119
120
121 static std::string
time_to_string(bigtime_t usecs)122 time_to_string(bigtime_t usecs)
123 {
124 static const bigtime_t kSecond = 1000000ULL;
125 static const bigtime_t kHour = 3600 * kSecond;
126 static const bigtime_t kMinute = 60 * kSecond;
127
128 uint32 hours = usecs / kHour;
129 uint32 minutes = usecs / kMinute;
130 uint32 seconds = usecs / kSecond;
131
132 char buffer[256];
133 if (usecs >= kHour) {
134 minutes %= 60;
135 seconds %= 60;
136 snprintf(buffer, sizeof(buffer), "%luh %02lum %02lus", hours, minutes,
137 seconds);
138 } else if (usecs > 100 * kSecond) {
139 seconds %= 60;
140 snprintf(buffer, sizeof(buffer), "%lum %02lus", minutes, seconds);
141 } else
142 snprintf(buffer, sizeof(buffer), "%gs", 1.0 * usecs / kSecond);
143
144 return buffer;
145 }
146
147
148 static void
usage(int status)149 usage(int status)
150 {
151 fprintf(stderr,
152 "Usage: %s [options]\n"
153 "Performs some random file actions for file system testing.\n"
154 "\n"
155 " -r, --runs=<count>\t\tThe number of actions to perform.\n"
156 "\t\t\t\tDefaults to %lu.\n"
157 " -s, --seed=<seed>\t\tThe base seed to use for the random numbers.\n"
158 " -f, --file-count=<count>\tThe maximal number of files to create.\n"
159 "\t\t\t\tDefaults to %lu.\n"
160 " -d, --dir-count=<count>\tThe maximal number of directories to create.\n"
161 "\t\t\t\tDefaults to %lu.\n"
162 " -m, --max-file-size=<size>\tThe maximal file size of the files.\n"
163 "\t\t\t\tDefaults to %lld.\n"
164 " -b, --base-dir=<path>\t\tThe base directory for the actions. "
165 "Defaults\n"
166 "\t\t\t\tto %s.\n"
167 " -c, --check-interval=<count>\tCheck after every <count> runs. "
168 "Defaults to 0,\n"
169 "\t\t\t\tmeaning only check once at the end.\n"
170 " -n, --no-cache\t\tDisables the file cache when doing I/O on\n"
171 "\t\t\t\ta file.\n"
172 " -a, --always-check\t\tAlways check contents before removing data.\n"
173 " -k, --keep-dirty\t\tDo not remove the working files on quit.\n"
174 " -i, --mount-image=<image>\tMounts an image for the actions, and "
175 "remounts\n"
176 "\t\t\t\tit before checking (each time).\n"
177 " -v, --verbose\t\t\tShow the actions as being performed\n",
178 kProgramName, kDefaultRunCount, kDefaultFileCount, kDefaultDirCount,
179 kDefaultMaxFileSize, kDefaultBaseDir);
180
181 exit(status);
182 }
183
184
185 static void
error(const char * format,...)186 error(const char* format, ...)
187 {
188 va_list args;
189 va_start(args, format);
190
191 fprintf(stderr, "%s: ", kProgramName);
192 vfprintf(stderr, format, args);
193 fputc('\n', stderr);
194
195 va_end(args);
196 fflush(stderr);
197
198 exit(1);
199 }
200
201
202 static void
warning(const char * format,...)203 warning(const char* format, ...)
204 {
205 va_list args;
206 va_start(args, format);
207
208 fprintf(stderr, "%s: ", kProgramName);
209 vfprintf(stderr, format, args);
210 fputc('\n', stderr);
211
212 va_end(args);
213 fflush(stderr);
214 }
215
216
217 static void
verbose(const char * format,...)218 verbose(const char* format, ...)
219 {
220 if (!sVerbose)
221 return;
222
223 va_list args;
224 va_start(args, format);
225
226 vprintf(format, args);
227 putchar('\n');
228
229 va_end(args);
230 fflush(stdout);
231 }
232
233
234 static void
action(const char * format,...)235 action(const char* format, ...)
236 {
237 if (!sVerbose)
238 return;
239
240 va_list args;
241 va_start(args, format);
242
243 printf("%7lu ", sRun + 1);
244 vprintf(format, args);
245 putchar('\n');
246
247 va_end(args);
248 fflush(stdout);
249 }
250
251
252 static file_action
choose_action()253 choose_action()
254 {
255 return (file_action)(rand() % kNumActions);
256 }
257
258
259 static inline int
choose_index(const EntryVector & entries)260 choose_index(const EntryVector& entries)
261 {
262 return rand() % entries.size();
263 }
264
265
266 static inline const std::string&
choose_parent(const EntryVector & entries)267 choose_parent(const EntryVector& entries)
268 {
269 return entries[choose_index(entries)].name;
270 }
271
272
273 static std::string
create_name(const std::string & parent,const char * prefix)274 create_name(const std::string& parent, const char* prefix)
275 {
276 char buffer[1024];
277 snprintf(buffer, sizeof(buffer), "%s/%s-%lu", parent.c_str(), prefix,
278 sCount++);
279
280 std::string name = buffer;
281 return name;
282 }
283
284
285 static int
open_file(const std::string & name,int mode)286 open_file(const std::string& name, int mode)
287 {
288 return open(name.c_str(), mode | (sDisableFileCache ? O_DIRECT : 0), 0666);
289 }
290
291
292 static void
generate_block(char * buffer,const struct entry & entry,off_t offset)293 generate_block(char* buffer, const struct entry& entry, off_t offset)
294 {
295 block_identifier* block = (block_identifier*)buffer;
296 block->offset = offset;
297 block->identifier = entry.identifier;
298
299 uint32 count = (kBlockSize - offsetof(block_identifier, data)) / 2;
300 offset += offsetof(block_identifier, data);
301
302 for (uint32 i = 0; i < count; i++) {
303 block->data[i] = offset + i * 2;
304 }
305 }
306
307
308 static void
write_blocks(int fd,struct entry & entry,bool append=false)309 write_blocks(int fd, struct entry& entry, bool append = false)
310 {
311 off_t size = min_c(rand() % sMaxFileSize, sMaxFileSize);
312 off_t offset = 0;
313
314 if (append) {
315 // in the append case, we need to check the file size
316 struct stat stat;
317 if (fstat(fd, &stat) != 0)
318 error("stat file failed: %s\n", strerror(errno));
319
320 if (size + stat.st_size > sMaxFileSize)
321 size = sMaxFileSize - stat.st_size;
322
323 offset = stat.st_size;
324 }
325
326 verbose("\t\twrite %lu bytes", size);
327
328 entry.size += size;
329 uint32 blockOffset = offset % kBlockSize;
330 sWriteTotal += size;
331
332 bigtime_t start = system_time();
333
334 while (size > 0) {
335 char block[kBlockSize];
336 generate_block(block, entry, offset - blockOffset);
337
338 ssize_t toWrite = min_c(size, kBlockSize - blockOffset);
339 ssize_t bytesWritten = write(fd, block + blockOffset, toWrite);
340 if (bytesWritten != toWrite)
341 error("writing failed: %s", strerror(errno));
342
343 offset += toWrite;
344 size -= toWrite;
345 blockOffset = 0;
346 }
347
348 sWriteTime += system_time() - start;
349 }
350
351
352 static void
dump_block(const char * buffer,int size,const char * prefix)353 dump_block(const char* buffer, int size, const char* prefix)
354 {
355 const int DUMPED_BLOCK_SIZE = 16;
356 int i;
357
358 for (i = 0; i < size;) {
359 int start = i;
360
361 printf("%s%04x ", prefix, i);
362 for (; i < start + DUMPED_BLOCK_SIZE; i++) {
363 if (!(i % 4))
364 printf(" ");
365
366 if (i >= size)
367 printf(" ");
368 else
369 printf("%02x", *(unsigned char*)(buffer + i));
370 }
371 printf(" ");
372
373 for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
374 if (i < size) {
375 char c = buffer[i];
376
377 if (c < 30)
378 printf(".");
379 else
380 printf("%c", c);
381 } else
382 break;
383 }
384 printf("\n");
385 }
386 }
387
388
389 static void
check_file(const struct entry & file)390 check_file(const struct entry& file)
391 {
392 int fd = open_file(file.name, O_RDONLY);
393 if (fd < 0) {
394 error("opening file \"%s\" failed: %s", file.name.c_str(),
395 strerror(errno));
396 }
397
398 // first check if size matches
399
400 struct stat stat;
401 if (fstat(fd, &stat) != 0)
402 error("stat file \"%s\" failed: %s", file.name.c_str(), strerror(errno));
403
404 if (file.size != stat.st_size) {
405 warning("size does not match for \"%s\"! Expected %lld reported %lld",
406 file.name.c_str(), file.size, stat.st_size);
407 close(fd);
408 return;
409 }
410
411 // check contents
412
413 off_t size = file.size;
414 off_t offset = 0;
415 sReadTotal += size;
416
417 bigtime_t start = system_time();
418
419 while (size > 0) {
420 // read block
421 char block[kBlockSize];
422 ssize_t toRead = min_c(size, kBlockSize);
423 ssize_t bytesRead = read(fd, block, toRead);
424 if (bytesRead != toRead) {
425 error("reading \"%s\" failed: %s", file.name.c_str(),
426 strerror(errno));
427 }
428
429 // compare with generated block
430 char generatedBlock[kBlockSize];
431 generate_block(generatedBlock, file, offset);
432
433 if (memcmp(generatedBlock, block, bytesRead) != 0) {
434 dump_block(generatedBlock, bytesRead, "generated: ");
435 dump_block(block, bytesRead, "read: ");
436 error("block at %lld differ in \"%s\"!", offset, file.name.c_str());
437 }
438
439 offset += toRead;
440 size -= toRead;
441 }
442
443 sReadTime += system_time() - start;
444
445 close(fd);
446 }
447
448
449 static void
check_files(EntryVector & files)450 check_files(EntryVector& files)
451 {
452 verbose("check all files...");
453
454 for (EntryVector::iterator i = files.begin(); i != files.end(); i++) {
455 const struct entry& file = *i;
456
457 check_file(file);
458 }
459 }
460
461
462 static void
remove_dirs(const std::string & path)463 remove_dirs(const std::string& path)
464 {
465 DIR* dir = opendir(path.c_str());
466 if (dir == NULL) {
467 warning("Could not open directory \"%s\": %s", path.c_str(),
468 strerror(errno));
469 return;
470 }
471
472 while (struct dirent* entry = readdir(dir)) {
473 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
474 continue;
475
476 std::string subPath = path + "/" + entry->d_name;
477 remove_dirs(subPath);
478 }
479
480 closedir(dir);
481 rmdir(path.c_str());
482 }
483
484
485 static void
mount_image(const char * image,const char * mountPoint)486 mount_image(const char* image, const char* mountPoint)
487 {
488 dev_t volume = fs_mount_volume(mountPoint, image, NULL, 0, NULL);
489 if (volume < 0)
490 error("mounting failed: %s", strerror(volume));
491 }
492
493
494 static void
unmount_image(const char * mountPoint)495 unmount_image(const char* mountPoint)
496 {
497 status_t status = fs_unmount_volume(mountPoint, 0);
498 if (status != B_OK)
499 error("unmounting failed: %s", strerror(status));
500 }
501
502
503 // #pragma mark - Actions
504
505
506 static void
create_dir(EntryVector & dirs)507 create_dir(EntryVector& dirs)
508 {
509 std::string parent = choose_parent(dirs);
510 std::string name = create_name(parent, "dir");
511
512 action("create dir %s (identifier %lu)", name.c_str(), sCount);
513
514 if (mkdir(name.c_str(), 0777) != 0)
515 error("creating dir \"%s\" failed: %s", name.c_str(), strerror(errno));
516
517 struct entry dir;
518 dir.name = name;
519 dir.identifier = sCount;
520 dir.size = 0;
521
522 dirs.push_back(dir);
523 }
524
525
526 static void
remove_dir(EntryVector & dirs)527 remove_dir(EntryVector& dirs)
528 {
529 if (dirs.empty())
530 return;
531
532 int index = choose_index(dirs);
533 if (index == 0)
534 return;
535
536 const std::string& name = dirs[index].name;
537
538 if (rmdir(name.c_str()) != 0) {
539 if (errno == ENOTEMPTY || errno == EEXIST) {
540 // TODO: in rare cases, we could remove all files
541 return;
542 }
543
544 error("removing dir \"%s\" failed: %s", name.c_str(), strerror(errno));
545 }
546
547 action("removed dir %s", name.c_str());
548
549 EntryVector::iterator iterator = dirs.begin();
550 dirs.erase(iterator + index);
551 }
552
553
554 static void
create_file(const EntryVector & dirs,EntryVector & files)555 create_file(const EntryVector& dirs, EntryVector& files)
556 {
557 std::string parent = choose_parent(dirs);
558 std::string name = create_name(parent, "file");
559
560 action("create file %s (identifier %lu)", name.c_str(), sCount);
561
562 int fd = open_file(name, O_RDWR | O_CREAT | O_TRUNC);
563 if (fd < 0)
564 error("creating file \"%s\" failed: %s", name.c_str(), strerror(errno));
565
566 struct entry file;
567 file.name = name;
568 file.identifier = sCount;
569 file.size = 0;
570 write_blocks(fd, file);
571
572 files.push_back(file);
573
574 fs_write_attr(fd, kIdentifierAttribute, B_UINT32_TYPE, 0, &file.identifier,
575 sizeof(uint32));
576
577 close(fd);
578 }
579
580
581 static void
remove_file(EntryVector & files)582 remove_file(EntryVector& files)
583 {
584 if (files.empty())
585 return;
586
587 int index = choose_index(files);
588 const std::string& name = files[index].name;
589
590 if (sCheckBeforeRemove)
591 check_file(files[index]);
592
593 if (remove(name.c_str()) != 0)
594 error("removing file \"%s\" failed: %s", name.c_str(), strerror(errno));
595
596 action("removed file %s", name.c_str());
597
598 EntryVector::iterator iterator = files.begin();
599 files.erase(iterator + index);
600 }
601
602
603 static void
rename_file(const EntryVector & dirs,EntryVector & files)604 rename_file(const EntryVector& dirs, EntryVector& files)
605 {
606 if (files.empty())
607 return;
608
609 std::string parent = choose_parent(dirs);
610 std::string newName = create_name(parent, "renamed-file");
611
612 int index = choose_index(files);
613 const std::string& oldName = files[index].name;
614
615 action("rename file \"%s\" to \"%s\"", oldName.c_str(), newName.c_str());
616
617 if (rename(oldName.c_str(), newName.c_str()) != 0) {
618 error("renaming file \"%s\" to \"%s\" failed: %s", oldName.c_str(),
619 newName.c_str(), strerror(errno));
620 }
621
622 files[index].name = newName;
623 }
624
625
626 static void
append_file(EntryVector & files)627 append_file(EntryVector& files)
628 {
629 if (files.empty())
630 return;
631
632 struct entry& file = files[choose_index(files)];
633
634 action("append to \"%s\"", file.name.c_str());
635
636 int fd = open_file(file.name, O_WRONLY | O_APPEND);
637 if (fd < 0) {
638 error("appending to file \"%s\" failed: %s", file.name.c_str(),
639 strerror(errno));
640 }
641
642 write_blocks(fd, file, true);
643 close(fd);
644 }
645
646
647 static void
replace_file(EntryVector & files)648 replace_file(EntryVector& files)
649 {
650 if (files.empty())
651 return;
652
653 struct entry& file = files[choose_index(files)];
654
655 action("replace \"%s\" contents", file.name.c_str());
656
657 if (sCheckBeforeRemove)
658 check_file(file);
659
660 int fd = open_file(file.name, O_CREAT | O_WRONLY | O_TRUNC);
661 if (fd < 0) {
662 error("replacing file \"%s\" failed: %s", file.name.c_str(),
663 strerror(errno));
664 }
665
666 file.size = 0;
667 write_blocks(fd, file);
668
669 close(fd);
670 }
671
672
673 static void
truncate_file(EntryVector & files)674 truncate_file(EntryVector& files)
675 {
676 if (files.empty())
677 return;
678
679 struct entry& file = files[choose_index(files)];
680
681 action("truncate \"%s\"", file.name.c_str());
682
683 if (sCheckBeforeRemove)
684 check_file(file);
685
686 int fd = open_file(file.name, O_WRONLY | O_TRUNC);
687 if (fd < 0) {
688 error("truncating file \"%s\" failed: %s", file.name.c_str(),
689 strerror(errno));
690 }
691
692 file.size = 0;
693
694 close(fd);
695 }
696
697
698 // #pragma mark -
699
700
701 int
main(int argc,char ** argv)702 main(int argc, char** argv)
703 {
704 // parse arguments
705
706 const static struct option kOptions[] = {
707 {"runs", required_argument, 0, 'r'},
708 {"seed", required_argument, 0, 's'},
709 {"file-count", required_argument, 0, 'f'},
710 {"dir-count", required_argument, 0, 'd'},
711 {"check-interval", required_argument, 0, 'c'},
712 {"max-file-size", required_argument, 0, 'm'},
713 {"base-dir", required_argument, 0, 'b'},
714 {"no-cache", no_argument, 0, 'n'},
715 {"always-check", no_argument, 0, 'a'},
716 {"keep-dirty", no_argument, 0, 'k'},
717 {"mount-image", required_argument, 0, 'i'},
718 {"verbose", no_argument, 0, 'v'},
719 {"help", no_argument, 0, 'h'},
720 {NULL}
721 };
722
723 uint32 maxFileCount = kDefaultFileCount;
724 uint32 maxDirCount = kDefaultDirCount;
725 uint32 runs = kDefaultRunCount;
726 uint32 checkInterval = 0;
727 uint32 seed = 0;
728 bool keepDirty = false;
729 const char* mountImage = NULL;
730
731 struct entry base;
732 base.name = kDefaultBaseDir;
733 base.identifier = 0;
734 base.size = 0;
735
736 int c;
737 while ((c = getopt_long(argc, argv, "r:s:f:d:c:m:b:naki:vh", kOptions,
738 NULL)) != -1) {
739 switch (c) {
740 case 0:
741 break;
742 case 'r':
743 runs = strtoul(optarg, NULL, 0);
744 if (runs < 1)
745 runs = 1;
746 break;
747 case 's':
748 // seed
749 seed = strtoul(optarg, NULL, 0);
750 break;
751 case 'f':
752 // file count
753 maxFileCount = strtoul(optarg, NULL, 0);
754 if (maxFileCount < 5)
755 maxFileCount = 5;
756 else if (maxFileCount > kMaxFileCount)
757 maxFileCount = kMaxFileCount;
758 break;
759 case 'd':
760 // directory count
761 maxDirCount = strtoul(optarg, NULL, 0);
762 if (maxDirCount < 1)
763 maxDirCount = 1;
764 else if (maxDirCount > kMaxDirCount)
765 maxDirCount = kMaxDirCount;
766 break;
767 case 'c':
768 // check interval
769 checkInterval = strtoul(optarg, NULL, 0);
770 if (checkInterval < 0)
771 checkInterval = 0;
772 break;
773 case 'm':
774 // max file size
775 sMaxFileSize = string_to_size(optarg);
776 break;
777 case 'b':
778 base.name = optarg;
779 break;
780 case 'n':
781 sDisableFileCache = true;
782 break;
783 case 'a':
784 sCheckBeforeRemove = true;
785 break;
786 case 'k':
787 keepDirty = true;
788 break;
789 case 'i':
790 mountImage = optarg;
791 break;
792 case 'v':
793 sVerbose = true;
794 break;
795 case 'h':
796 usage(0);
797 break;
798 default:
799 usage(1);
800 break;
801 }
802 }
803
804 if (mkdir(base.name.c_str(), 0777) != 0 && errno != EEXIST) {
805 fprintf(stderr, "%s: cannot create base directory: %s\n",
806 kProgramName, strerror(errno));
807 return 1;
808 }
809 if (mountImage != NULL)
810 mount_image(mountImage, base.name.c_str());
811
812 EntryVector dirs;
813 EntryVector files;
814
815 dirs.push_back(base);
816
817 srand(seed);
818
819 verbose("%lu runs, %lu files (up to %s in size), %lu dirs, seed %lu\n", runs,
820 maxFileCount, size_to_string(sMaxFileSize).c_str(), maxDirCount, seed);
821
822 for (sRun = 0; sRun < runs; sRun++) {
823 file_action action = choose_action();
824
825 switch (action) {
826 case kCreateFile:
827 if (files.size() > maxFileCount / 2) {
828 // create a single file
829 if (files.size() < maxFileCount)
830 create_file(dirs, files);
831 } else {
832 // create some more files to fill up the list (ie. 10%)
833 uint32 count
834 = min_c(maxFileCount, files.size() + maxFileCount / 10);
835 for (uint32 i = files.size(); i < count; i++) {
836 create_file(dirs, files);
837 }
838 }
839 break;
840 case kCreateDir:
841 if (dirs.size() > maxDirCount / 2) {
842 // create a single directory
843 if (dirs.size() < maxDirCount)
844 create_dir(dirs);
845 } else {
846 // create some more directories to fill up the list (ie. 10%)
847 uint32 count
848 = min_c(maxDirCount, dirs.size() + maxDirCount / 10);
849 for (uint32 i = dirs.size(); i < count; i++) {
850 create_dir(dirs);
851 }
852 }
853 break;
854 case kRenameFile:
855 rename_file(dirs, files);
856 break;
857 case kRemoveFile:
858 remove_file(files);
859 break;
860 case kRemoveDir:
861 remove_dir(dirs);
862 break;
863 case kAppendFile:
864 append_file(files);
865 break;
866 case kReplaceFile:
867 replace_file(files);
868 break;
869 case kTruncateFile:
870 truncate_file(files);
871 break;
872
873 default:
874 break;
875 }
876
877 if (checkInterval != 0 && sRun > 0 && (sRun % checkInterval) == 0
878 && sRun + 1 < runs) {
879 if (mountImage != NULL) {
880 // Always remount image before checking its contents
881 unmount_image(base.name.c_str());
882 mount_image(mountImage, base.name.c_str());
883 }
884 check_files(files);
885 }
886 }
887
888 if (mountImage != NULL) {
889 unmount_image(base.name.c_str());
890 mount_image(mountImage, base.name.c_str());
891 }
892
893 check_files(files);
894
895 if (!keepDirty) {
896 for (int i = files.size(); i-- > 0;) {
897 remove_file(files);
898 }
899 remove_dirs(base.name);
900 }
901
902 if (mountImage != NULL) {
903 unmount_image(base.name.c_str());
904 if (!keepDirty)
905 remove_dirs(base.name);
906 }
907
908 printf("%s written in %s, %s/s\n", size_to_string(sWriteTotal).c_str(),
909 time_to_string(sWriteTime).c_str(),
910 size_to_string(int64(0.5 + sWriteTotal
911 / (sWriteTime / 1000000.0))).c_str());
912 printf("%s read in %s, %s/s\n", size_to_string(sReadTotal).c_str(),
913 time_to_string(sReadTime).c_str(),
914 size_to_string(int64(0.5 + sReadTotal
915 / (sReadTime / 1000000.0))).c_str());
916
917 return 0;
918 }
919