Implement idea of total (all) and new coverage directories, as well as concept of crash directory
diff --git a/cmdline.c b/cmdline.c
index e107631..731cc66 100644
--- a/cmdline.c
+++ b/cmdline.c
@@ -135,8 +135,10 @@
                 .fileCnt = 0,
                 .fileCntDone = false,
                 .fileExtn = "fuzz",
-                .workDir = ".",
-                .covDir = NULL,
+                .workDir = NULL,
+                .crashDir = NULL,
+                .covDirAll = NULL,
+                .covDirNew = NULL,
                 .saveUnique = true,
             },
         .nullifyStdio = true,
@@ -282,7 +284,9 @@
         { { "debug_level", required_argument, NULL, 'd' }, "Debug level (0 - FATAL ... 4 - DEBUG), (default: '3' [INFO])" },
         { { "extension", required_argument, NULL, 'e' }, "Input file extension (e.g. 'swf'), (default: 'fuzz')" },
         { { "workspace", required_argument, NULL, 'W' }, "Workspace directory to save crashes & runtime files (default: '.')" },
-        { { "covdir", required_argument, NULL, 0x103 }, "New coverage is written to a separate directory (default: use the input directory)" },
+        { { "crashdir", required_argument, NULL, 0x10b }, "Directory where crashes are saved to (default: workspace directory)" },
+        { { "covdir_all", required_argument, NULL, 0x103 }, "Coverage is written to a separate directory (default: input directory)" },
+        { { "covdir_new", required_argument, NULL, 0x10a }, "New coverage (beyond the dry-run fuzzing phase) is written to this separate directory" },
         { { "dict", required_argument, NULL, 'w' }, "Dictionary file. Format:http://llvm.org/docs/LibFuzzer.html#dictionaries" },
         { { "stackhash_bl", required_argument, NULL, 'B' }, "Stackhashes blacklist file (one entry per line)" },
         { { "mutate_cmd", required_argument, NULL, 'c' }, "External command producing fuzz files (instead of internal mutators)" },
@@ -344,6 +348,9 @@
                 break;
             case 'f':
                 hfuzz->io.inputDir = optarg;
+                if (hfuzz->io.covDirAll == NULL) {
+                    hfuzz->io.covDirAll = optarg;
+                }
                 break;
             case 'x':
                 hfuzz->dynFileMethod = _HF_DYNFILE_NONE;
@@ -375,6 +382,9 @@
             case 'W':
                 hfuzz->io.workDir = optarg;
                 break;
+            case 0x10b:
+                hfuzz->io.crashDir = optarg;
+                break;
             case 'r':
                 hfuzz->mutationsPerRun = strtoul(optarg, NULL, 10);
                 break;
@@ -421,7 +431,10 @@
                 hfuzz->dataLimit = strtoull(optarg, NULL, 0);
                 break;
             case 0x103:
-                hfuzz->io.covDir = optarg;
+                hfuzz->io.covDirAll = optarg;
+                break;
+            case 0x10a:
+                hfuzz->io.covDirNew = optarg;
                 break;
             case 0x104:
                 hfuzz->postExternalCommand = optarg;
@@ -558,11 +571,19 @@
         return false;
     }
 
-    if (hfuzz->io.workDir[0] != '.' || strlen(hfuzz->io.workDir) > 2) {
-        if (!files_exists(hfuzz->io.workDir)) {
-            LOG_E("Provided workspace directory '%s' doesn't exist", hfuzz->io.workDir);
-            return false;
-        }
+    if (hfuzz->io.workDir == NULL) {
+        hfuzz->io.workDir = ".";
+    }
+    if (!files_exists(hfuzz->io.workDir)) {
+        LOG_E("Provided workspace directory '%s' doesn't exist", hfuzz->io.workDir);
+        return false;
+    }
+    if (hfuzz->io.crashDir == NULL) {
+        hfuzz->io.crashDir = hfuzz->io.workDir;
+    }
+    if (!files_exists(hfuzz->io.crashDir)) {
+        LOG_E("Provided crash directory '%s' doesn't exist", hfuzz->io.crashDir);
+        return false;
     }
 
     if (hfuzz->linux.pid > 0 || hfuzz->linux.pidFile) {
diff --git a/display.c b/display.c
index 1bd6dc4..c013197 100644
--- a/display.c
+++ b/display.c
@@ -187,7 +187,7 @@
         }
     }
     display_put("\n   Input Dir : [% " _HF_MONETARY_MOD "zu] '" ESC_BOLD "%s" ESC_RESET "'\n",
-        ATOMIC_GET(hfuzz->io.fileCnt), hfuzz->io.inputDir != NULL ? hfuzz->io.inputDir : "[NONE]");
+        ATOMIC_GET(hfuzz->io.fileCnt), hfuzz->io.inputDir);
 
     if (hfuzz->linux.pid > 0) {
         display_put("  Remote cmd : [" ESC_BOLD "%d" ESC_RESET "] '" ESC_BOLD "%s" ESC_RESET "'\n",
diff --git a/fuzz.c b/fuzz.c
index f27baff..d6f4f20 100644
--- a/fuzz.c
+++ b/fuzz.c
@@ -371,6 +371,30 @@
     return true;
 }
 
+static bool fuzz_writeCovFile(const char* dir, const uint8_t* data, size_t len) {
+    char fname[PATH_MAX];
+
+    uint64_t crc64f = util_CRC64(data, len);
+    uint64_t crc64r = util_CRC64Rev(data, len);
+    snprintf(fname, sizeof(fname), "%s/%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
+        dir, crc64f, crc64r, (uint32_t)len);
+
+    if (access(fname, R_OK) == 0) {
+        LOG_D("File '%s' already exists in the output corpus directory '%s'", fname, dir);
+        return true;
+    }
+
+    LOG_D("Adding file '%s' to the corpus directory '%s'", fname, dir);
+
+    if (files_writeBufToFile(fname, data, len, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC) ==
+        false) {
+        LOG_W("Couldn't write buffer to file '%s'", fname);
+        return false;
+    }
+
+    return true;
+}
+
 static void fuzz_addFileToFileQ(run_t* run) {
     struct dynfile_t* dynfile = (struct dynfile_t*)util_Malloc(sizeof(struct dynfile_t));
     dynfile->size = run->dynamicFileSz;
@@ -381,29 +405,17 @@
     TAILQ_INSERT_TAIL(&run->global->dynfileq, dynfile, pointers);
     run->global->dynfileqCnt++;
 
-    /* No need to add new coverage if we are supposed to append new coverage-inducing inputs only */
-    if (run->state == _HF_STATE_DYNAMIC_PRE && run->global->io.covDir == NULL) {
-        LOG_D("New coverage found, but we're in the initial coverage assessment state. Skipping");
+    if (!fuzz_writeCovFile(run->global->io.covDirAll, run->dynamicFile, run->dynamicFileSz)) {
+        LOG_E("Couldn't save the coverage data to '%s'", run->global->io.covDirAll);
+    }
+
+    /* No need to add files to the new coverage dir, if this is just the dry-run phase */
+    if (run->state == _HF_STATE_DYNAMIC_PRE || run->global->io.covDirNew == NULL) {
         return;
     }
 
-    char fname[PATH_MAX];
-    uint64_t crc64f = util_CRC64(run->dynamicFile, run->dynamicFileSz);
-    uint64_t crc64r = util_CRC64Rev(run->dynamicFile, run->dynamicFileSz);
-    snprintf(fname, sizeof(fname), "%s/%016" PRIx64 "%016" PRIx64 ".%08" PRIx32 ".honggfuzz.cov",
-        run->global->io.covDir ? run->global->io.covDir : run->global->io.inputDir, crc64f, crc64r,
-        (uint32_t)run->dynamicFileSz);
-
-    if (access(fname, R_OK) == 0) {
-        LOG_D("File '%s' already exists in the corpus directory", fname);
-        return;
-    }
-
-    LOG_D("Adding file '%s' to the corpus directory", fname);
-
-    if (files_writeBufToFile(fname, run->dynamicFile, run->dynamicFileSz,
-            O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC) == false) {
-        LOG_W("Couldn't write buffer to file '%s'", fname);
+    if (!fuzz_writeCovFile(run->global->io.covDirNew, run->dynamicFile, run->dynamicFileSz)) {
+        LOG_E("Couldn't save the new coverage data to '%s'", run->global->io.covDirNew);
     }
 }
 
diff --git a/honggfuzz.c b/honggfuzz.c
index ab870ba..2212ca0 100644
--- a/honggfuzz.c
+++ b/honggfuzz.c
@@ -150,15 +150,8 @@
     }
 
     if (!input_init(&hfuzz)) {
-        if (hfuzz.externalCommand) {
-            LOG_I(
-                "No input file corpus loaded, the external command '%s' is responsible for "
-                "creating the fuzz files",
-                hfuzz.externalCommand);
-        } else {
-            LOG_F("Couldn't load input files");
-            exit(EXIT_FAILURE);
-        }
+        LOG_F("Couldn't load input corpus");
+        exit(EXIT_FAILURE);
     }
 
     if (hfuzz.dictionaryFile && (input_parseDictionary(&hfuzz) == false)) {
diff --git a/honggfuzz.h b/honggfuzz.h
index 180a390..bff83e4 100644
--- a/honggfuzz.h
+++ b/honggfuzz.h
@@ -178,13 +178,15 @@
     bool useVerifier;
     time_t timeStart;
     struct {
-        char* inputDir;
+        const char* inputDir;
         DIR* inputDirP;
         size_t fileCnt;
-        char* fileExtn;
+        const char* fileExtn;
         bool fileCntDone;
-        char* workDir;
-        char* covDir;
+        const char* workDir;
+        const char* crashDir;
+        const char* covDirAll;
+        const char* covDirNew;
         bool saveUnique;
     } io;
     unsigned mutationsPerRun;
diff --git a/libcommon/files.c b/libcommon/files.c
index beaa829..8c02b24 100644
--- a/libcommon/files.c
+++ b/libcommon/files.c
@@ -112,7 +112,7 @@
     return (ssize_t)readSz;
 }
 
-bool files_exists(char* fileName) { return (access(fileName, F_OK) != -1); }
+bool files_exists(const char* fileName) { return (access(fileName, F_OK) != -1); }
 
 bool files_writePatternToFd(int fd, off_t size, unsigned char p) {
     void* buf = malloc(size);
@@ -161,7 +161,7 @@
     return true;
 }
 
-const char* files_basename(char* path) {
+const char* files_basename(const char* path) {
     const char* base = strrchr(path, '/');
     return base ? base + 1 : path;
 }
diff --git a/libcommon/files.h b/libcommon/files.h
index 3be6984..0d7d9bd 100644
--- a/libcommon/files.h
+++ b/libcommon/files.h
@@ -46,9 +46,9 @@
 
 bool files_sendToSocket(int fd, const uint8_t* buf, size_t fileSz);
 
-extern bool files_exists(char* fileName);
+extern bool files_exists(const char* fileName);
 
-extern const char* files_basename(char* fileName);
+extern const char* files_basename(const char* fileName);
 
 extern bool files_copyFile(
     const char* source, const char* destination, bool* dstExists, bool try_link);
diff --git a/libcommon/util.c b/libcommon/util.c
index b45760c..b3ceb6e 100644
--- a/libcommon/util.c
+++ b/libcommon/util.c
@@ -626,7 +626,7 @@
     0x9090000000000000ULL,
 };
 
-uint64_t util_CRC64(uint8_t* buf, size_t len) {
+uint64_t util_CRC64(const uint8_t* buf, size_t len) {
     uint64_t res = 0ULL;
 
     for (size_t i = 0; i < len; i++) {
@@ -636,7 +636,7 @@
     return res;
 }
 
-uint64_t util_CRC64Rev(uint8_t* buf, size_t len) {
+uint64_t util_CRC64Rev(const uint8_t* buf, size_t len) {
     uint64_t res = 0ULL;
 
     for (ssize_t i = (ssize_t)len - 1; i >= 0; i--) {
diff --git a/libcommon/util.h b/libcommon/util.h
index ff152af..76cacdb 100644
--- a/libcommon/util.h
+++ b/libcommon/util.h
@@ -119,7 +119,7 @@
 
 extern size_t util_decodeCString(char* s);
 
-extern uint64_t util_CRC64(uint8_t* buf, size_t len);
-extern uint64_t util_CRC64Rev(uint8_t* buf, size_t len);
+extern uint64_t util_CRC64(const uint8_t* buf, size_t len);
+extern uint64_t util_CRC64Rev(const uint8_t* buf, size_t len);
 
 #endif
diff --git a/linux/trace.c b/linux/trace.c
index e09209f..18471c2 100644
--- a/linux/trace.c
+++ b/linux/trace.c
@@ -797,19 +797,19 @@
 
     /* If dry run mode, copy file with same name into workspace */
     if (run->global->mutationsPerRun == 0U && run->global->useVerifier) {
-        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.workDir,
+        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.crashDir,
             run->origFileName);
     } else if (saveUnique) {
         snprintf(run->crashFileName, sizeof(run->crashFileName),
             "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s",
-            run->global->io.workDir, arch_sigName(si.si_signo), pc, run->backtrace, si.si_code,
+            run->global->io.crashDir, arch_sigName(si.si_signo), pc, run->backtrace, si.si_code,
             sig_addr, instr, run->global->io.fileExtn);
     } else {
         char localtmstr[PATH_MAX];
         util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr), time(NULL));
         snprintf(run->crashFileName, sizeof(run->crashFileName),
             "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s",
-            run->global->io.workDir, arch_sigName(si.si_signo), pc, run->backtrace, si.si_code,
+            run->global->io.crashDir, arch_sigName(si.si_signo), pc, run->backtrace, si.si_code,
             sig_addr, instr, localtmstr, pid, run->global->io.fileExtn);
     }
 
@@ -1026,14 +1026,14 @@
 
     /* If dry run mode, copy file with same name into workspace */
     if (run->global->mutationsPerRun == 0U && run->global->useVerifier) {
-        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.workDir,
+        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.crashDir,
             run->origFileName);
     } else {
         /* Keep the crashes file name format identical */
         if (run->backtrace != 0ULL && run->global->io.saveUnique) {
             snprintf(run->crashFileName, sizeof(run->crashFileName),
                 "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%s.ADDR.%p.INSTR.%s.%s",
-                run->global->io.workDir, "SAN", pc, run->backtrace, op, crashAddr, "[UNKNOWN]",
+                run->global->io.crashDir, "SAN", pc, run->backtrace, op, crashAddr, "[UNKNOWN]",
                 run->global->io.fileExtn);
         } else {
             /* If no stack hash available, all crashes treated as unique */
@@ -1041,7 +1041,7 @@
             util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr), time(NULL));
             snprintf(run->crashFileName, sizeof(run->crashFileName),
                 "%s/%s.PC.%" REG_PM ".STACK.%" PRIx64 ".CODE.%s.ADDR.%p.INSTR.%s.%s.%s",
-                run->global->io.workDir, "SAN", pc, run->backtrace, op, crashAddr, "[UNKNOWN]",
+                run->global->io.crashDir, "SAN", pc, run->backtrace, op, crashAddr, "[UNKNOWN]",
                 localtmstr, run->global->io.fileExtn);
         }
     }
diff --git a/mac/arch.c b/mac/arch.c
index 543b5fe..b9a629a 100644
--- a/mac/arch.c
+++ b/mac/arch.c
@@ -252,11 +252,11 @@
 
     /* If dry run mode, copy file with same name into workspace */
     if (run->global->mutationsPerRun == 0U && run->global->useVerifier) {
-        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.workDir,
+        snprintf(run->crashFileName, sizeof(run->crashFileName), "%s/%s", run->global->io.crashDir,
             run->origFileName);
     } else if (run->global->io.saveUnique) {
         snprintf(run->crashFileName, sizeof(run->crashFileName),
-            "%s/%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s", run->global->io.workDir,
+            "%s/%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.%s", run->global->io.crashDir,
             arch_sigs[termsig].descr, exception_to_string(run->exception), run->pc, run->backtrace,
             run->access, run->global->io.fileExtn);
     } else {
@@ -265,7 +265,7 @@
 
         snprintf(run->crashFileName, sizeof(run->crashFileName),
             "%s/%s.%s.PC.%.16llx.STACK.%.16llx.ADDR.%.16llx.TIME.%s.PID.%.5d.%s",
-            run->global->io.workDir, arch_sigs[termsig].descr, exception_to_string(run->exception),
+            run->global->io.crashDir, arch_sigs[termsig].descr, exception_to_string(run->exception),
             run->pc, run->backtrace, run->access, localtmstr, run->pid, run->global->io.fileExtn);
     }
 
diff --git a/posix/arch.c b/posix/arch.c
index 12225b3..82cf929 100644
--- a/posix/arch.c
+++ b/posix/arch.c
@@ -125,7 +125,7 @@
     if (run->global->mutationsPerRun == 0U && run->global->useVerifier) {
         snprintf(newname, sizeof(newname), "%s", run->origFileName);
     } else {
-        snprintf(newname, sizeof(newname), "%s/%s.PID.%d.TIME.%s.%s", run->global->io.workDir,
+        snprintf(newname, sizeof(newname), "%s/%s.PID.%d.TIME.%s.%s", run->global->io.crashDir,
             arch_sigs[termsig].descr, run->pid, localtmstr, run->global->io.fileExtn);
     }