diff --git a/TODO b/TODO index a87c5e75..438cc450 100644 --- a/TODO +++ b/TODO @@ -8,9 +8,6 @@ This is the list of TODO items for SnapRAID. * Avoid to do optimized move operation if more files with same timestamp/size are present. -* In the log print file name as escaped to avoid that internal ":" and "\n" -get misinterpreted. - - Minor * Should "diff" warn about not completed sync ? diff --git a/cmdline/check.c b/cmdline/check.c index 4e48543f..c19c5baf 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -338,7 +338,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign disk = handle->disk->name; sub = block_file_get(block)->sub; - ftag("entry:%u:%s:%s:%s:%s:%s:%u:\n", j, state, hash, data, disk, sub, block_file_pos(block)); + ftag("entry:%u:%s:%s:%s:%s:%s:%u:\n", j, state, hash, data, disk, esc(sub), block_file_pos(block)); } else { ftag("entry:%u:%s:%s:%s:\n", j, state, hash, data); } @@ -673,7 +673,7 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s /* LCOV_EXCL_STOP */ } - ftag("status:unrecoverable:%s:%s\n", disk->name, file->sub); + ftag("status:unrecoverable:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("unrecoverable %s\n", path); } @@ -688,7 +688,7 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s goto close_and_continue; } - ftag("status:recovered:%s:%s\n", disk->name, file->sub); + ftag("status:recovered:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("recovered %s\n", path); } @@ -723,30 +723,30 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s /* LCOV_EXCL_STOP */ } } else { - ftag("collision:%s:%s:%s: Not setting modification time to avoid inode collision\n", disk->name, file->sub, collide_file->sub); + ftag("collision:%s:%s:%s: Not setting modification time to avoid inode collision\n", disk->name, esc(file->sub), esc(collide_file->sub)); } } else { /* we are not fixing, but only checking */ /* print just the final status */ if (file_flag_has(file, FILE_IS_DAMAGED)) { if (state->opt.auditonly) { - ftag("status:damaged:%s:%s\n", disk->name, file->sub); + ftag("status:damaged:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("damaged %s\n", path); } } else { - ftag("status:unrecoverable:%s:%s\n", disk->name, file->sub); + ftag("status:unrecoverable:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("unrecoverable %s\n", path); } } } else if (file_flag_has(file, FILE_IS_FIXED)) { - ftag("status:recoverable:%s:%s\n", disk->name, file->sub); + ftag("status:recoverable:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("recoverable %s\n", path); } } else { - ftag("status:correct:%s:%s\n", disk->name, file->sub); + ftag("status:correct:%s:%s\n", disk->name, esc(file->sub)); if (state->opt.verbose) { fout("correct %s\n", path); } @@ -1019,7 +1019,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].handle = &handle[j]; ++failed_count; - ftag("error:%u:%s:%s: Open error at position %u\n", i, disk->name, file->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Open error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); ++error; continue; } @@ -1047,7 +1047,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna && handle[j].st.st_size > file->size ) { flog("File '%s' is larger than expected.\n", handle[j].path); - ftag("error:%u:%s:%s: Size error\n", i, disk->name, file->sub); + ftag("error:%u:%s:%s: Size error\n", i, disk->name, esc(file->sub)); ++error; if (fix) { @@ -1061,7 +1061,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - ftag("fixed:%u:%s:%s: Fixed size\n", i, disk->name, file->sub); + ftag("fixed:%u:%s:%s: Fixed size\n", i, disk->name, esc(file->sub)); ++recovered_error; } } @@ -1083,7 +1083,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].handle = &handle[j]; ++failed_count; - ftag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, file->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); ++error; continue; } @@ -1124,7 +1124,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].handle = &handle[j]; ++failed_count; - ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, file->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); ++error; continue; } @@ -1183,7 +1183,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* print a list of all the errors in files */ for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad) - ftag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block)); + ftag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); } /* keep track of damaged files */ @@ -1201,7 +1201,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad && failed[j].is_outofdate) { ++partial_recover_error; - ftag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block)); + ftag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); } } if (partial_recover_error != 0) { @@ -1266,7 +1266,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* note that it could be also marked as damaged in other iterations */ file_flag_set(block_file_get(failed[j].block), FILE_IS_FIXED); - ftag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block)); + ftag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); ++recovered_error; } @@ -1369,17 +1369,17 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna unsuccesful = 1; flog("Error stating empty file '%s'. %s.\n", path, strerror(errno)); - ftag("error:%s:%s: Empty file stat error\n", disk->name, file->sub); + ftag("error:%s:%s: Empty file stat error\n", disk->name, esc(file->sub)); ++error; } else if (!S_ISREG(st.st_mode)) { unsuccesful = 1; - ftag("error:%s:%s: Empty file error for not regular file\n", disk->name, file->sub); + ftag("error:%s:%s: Empty file error for not regular file\n", disk->name, esc(file->sub)); ++error; } else if (st.st_size != 0) { unsuccesful = 1; - ftag("error:%s:%s: Empty file error for size '%" PRIu64 "'\n", disk->name, file->sub, st.st_size); + ftag("error:%s:%s: Empty file error for size '%" PRIu64 "'\n", disk->name, esc(file->sub), st.st_size); ++error; } @@ -1440,10 +1440,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - ftag("fixed:%s:%s: Fixed empty file\n", disk->name, file->sub); + ftag("fixed:%s:%s: Fixed empty file\n", disk->name, esc(file->sub)); ++recovered_error; - ftag("status:recovered:%s:%s\n", disk->name, file->sub); + ftag("status:recovered:%s:%s\n", disk->name, esc(file->sub)); if (!state->opt.quiet) { fout("recovered %s%s\n", disk->dir, file->sub); } @@ -1479,12 +1479,12 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna unsuccesful = 1; flog("Error stating hardlink '%s'. %s.\n", path, strerror(errno)); - ftag("hardlinkerror:%s:%s:%s: Hardlink stat error\n", disk->name, link->sub, link->linkto); + ftag("hardlinkerror:%s:%s:%s: Hardlink stat error\n", disk->name, esc(link->sub), esc(link->linkto)); ++error; } else if (!S_ISREG(st.st_mode)) { unsuccesful = 1; - ftag("hardlinkerror:%s:%s:%s: Hardlink error for not regular file\n", disk->name, link->sub, link->linkto); + ftag("hardlinkerror:%s:%s:%s: Hardlink error for not regular file\n", disk->name, esc(link->sub), esc(link->linkto)); ++error; } @@ -1509,18 +1509,18 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna } flog("Error stating hardlink-to '%s'. %s.\n", pathto, strerror(errno)); - ftag("hardlinkerror:%s:%s:%s: Hardlink to stat error\n", disk->name, link->sub, link->linkto); + ftag("hardlinkerror:%s:%s:%s: Hardlink to stat error\n", disk->name, esc(link->sub), esc(link->linkto)); ++error; } else if (!S_ISREG(stto.st_mode)) { unsuccesful = 1; - ftag("hardlinkerror:%s:%s:%s: Hardlink-to error for not regular file\n", disk->name, link->sub, link->linkto); + ftag("hardlinkerror:%s:%s:%s: Hardlink-to error for not regular file\n", disk->name, esc(link->sub), esc(link->linkto)); ++error; } else if (!unsuccesful && st.st_ino != stto.st_ino) { unsuccesful = 1; flog("Mismatch hardlink '%s' and '%s'. Different inode.\n", path, pathto); - ftag("hardlinkerror:%s:%s:%s: Hardlink mismatch for different inode\n", disk->name, link->sub, link->linkto); + ftag("hardlinkerror:%s:%s:%s: Hardlink mismatch for different inode\n", disk->name, esc(link->sub), esc(link->linkto)); ++error; } } else { @@ -1531,13 +1531,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna unsuccesful = 1; flog("Error reading symlink '%s'. %s.\n", path, strerror(errno)); - ftag("symlinkerror:%s:%s: Symlink read error\n", disk->name, link->sub); + ftag("symlinkerror:%s:%s: Symlink read error\n", disk->name, esc(link->sub)); ++error; } else if (ret >= PATH_MAX) { unsuccesful = 1; flog("Error reading symlink '%s'. Symlink too long.\n", path); - ftag("symlinkerror:%s:%s: Symlink read error\n", disk->name, link->sub); + ftag("symlinkerror:%s:%s: Symlink read error\n", disk->name, esc(link->sub)); ++error; } else { linkto[ret] = 0; @@ -1545,7 +1545,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna if (strcmp(linkto, link->linkto) != 0) { unsuccesful = 1; - ftag("symlinkerror:%s:%s: Symlink data error '%s' instead of '%s'\n", disk->name, link->sub, linkto, link->linkto); + ftag("symlinkerror:%s:%s: Symlink data error '%s' instead of '%s'\n", disk->name, esc(link->sub), linkto, link->linkto); ++error; } } @@ -1593,7 +1593,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - ftag("hardlinkfixed:%s:%s: Fixed hardlink error\n", disk->name, link->sub); + ftag("hardlinkfixed:%s:%s: Fixed hardlink error\n", disk->name, esc(link->sub)); ++recovered_error; } else { ret = symlink(link->linkto, path); @@ -1612,11 +1612,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - ftag("symlinkfixed:%s:%s: Fixed symlink error\n", disk->name, link->sub); + ftag("symlinkfixed:%s:%s: Fixed symlink error\n", disk->name, esc(link->sub)); ++recovered_error; } - ftag("status:recovered:%s:%s\n", disk->name, link->sub); + ftag("status:recovered:%s:%s\n", disk->name, esc(link->sub)); if (!state->opt.quiet) { fout("recovered %s%s\n", disk->dir, link->sub); } @@ -1647,12 +1647,12 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna unsuccesful = 1; flog("Error stating dir '%s'. %s.\n", path, strerror(errno)); - ftag("dir_error:%s:%s: Dir stat error\n", disk->name, dir->sub); + ftag("dir_error:%s:%s: Dir stat error\n", disk->name, esc(dir->sub)); ++error; } else if (!S_ISDIR(st.st_mode)) { unsuccesful = 1; - ftag("dir_error:%s:%s: Dir error for not directory\n", disk->name, dir->sub); + ftag("dir_error:%s:%s: Dir error for not directory\n", disk->name, esc(dir->sub)); ++error; } @@ -1685,10 +1685,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* LCOV_EXCL_STOP */ } - ftag("dir_fixed:%s:%s: Fixed dir error\n", disk->name, dir->sub); + ftag("dir_fixed:%s:%s: Fixed dir error\n", disk->name, esc(dir->sub)); ++recovered_error; - ftag("status:recovered:%s:%s\n", disk->name, dir->sub); + ftag("status:recovered:%s:%s\n", disk->name, esc(dir->sub)); if (!state->opt.quiet) { fout("recovered %s%s\n", disk->dir, dir->sub); } diff --git a/cmdline/device.c b/cmdline/device.c index 61ab8c12..da4fa55e 100644 --- a/cmdline/device.c +++ b/cmdline/device.c @@ -846,7 +846,7 @@ static void state_smart(int verbose, unsigned n, tommy_list* low) printf("\n"); - ftag("smart:%s:%s:%s:%g\n", devinfo->file, devinfo->name, devinfo->smart_serial, afr); + ftag("smart:%s:%s:%s:%g\n", devinfo->file, devinfo->name, esc(devinfo->smart_serial), afr); for (j = 0; j < 256; ++j) { if (devinfo->smart[j] != SMART_UNASSIGNED) ftag("attr:%s:%u:%" PRIu64 "\n", devinfo->file, j, devinfo->smart[j]); diff --git a/cmdline/dry.c b/cmdline/dry.c index 14743596..0d2058e0 100644 --- a/cmdline/dry.c +++ b/cmdline/dry.c @@ -82,7 +82,7 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected close error in a data disk, it isn't possible to dry.\n"); ferr("Stopping at block %u\n", i); ++error; @@ -105,7 +105,7 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit /* read from the file */ read_size = handle_read(&handle[j], block, buffer_aligned, state->block_size, flog); if (read_size == -1) { - ftag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, block_file_get(block)->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(block_file_get(block)->sub), block_file_pos(block)); ++error; continue; } @@ -156,7 +156,7 @@ bail: ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ diff --git a/cmdline/dup.c b/cmdline/dup.c index 879d7e36..d0402155 100644 --- a/cmdline/dup.c +++ b/cmdline/dup.c @@ -125,7 +125,7 @@ void state_dup(struct snapraid_state* state) if (dup) { ++count; size += dup->file->size; - ftag("dup:%s:%s:%s:%s:%" PRIu64 ": dup\n", disk->name, file->sub, dup->disk->name, dup->file->sub, dup->file->size); + ftag("dup:%s:%s:%s:%s:%" PRIu64 ": dup\n", disk->name, esc(file->sub), dup->disk->name, esc(dup->file->sub), dup->file->size); fout("%12" PRIu64 " %s%s = %s%s\n", file->size, disk->dir, file->sub, dup->disk->dir, dup->file->sub); hash_free(hash); } else { diff --git a/cmdline/list.c b/cmdline/list.c index 37d1d6d4..5800a74a 100644 --- a/cmdline/list.c +++ b/cmdline/list.c @@ -59,7 +59,7 @@ void state_list(struct snapraid_state* state) ++file_count; file_size += file->size; - ftag("file:%s:%s:%" PRIu64 ":%" PRIi64 ":%u:%" PRIi64 "\n", disk->name, file->sub, file->size, file->mtime_sec, file->mtime_nsec, file->inode); + ftag("file:%s:%s:%" PRIu64 ":%" PRIi64 ":%u:%" PRIi64 "\n", disk->name, esc(file->sub), file->size, file->mtime_sec, file->mtime_nsec, file->inode); t = file->mtime_sec; #if HAVE_LOCALTIME_R @@ -99,7 +99,7 @@ void state_list(struct snapraid_state* state) ++link_count; - ftag("link_%s:%s:%s:%s\n", type, disk->name, link->sub, link->linkto); + ftag("link_%s:%s:%s:%s\n", type, disk->name, esc(link->sub), esc(link->linkto)); printf("%12s ", type); printf(" "); diff --git a/cmdline/nano.c b/cmdline/nano.c index fb8c081b..afcad737 100644 --- a/cmdline/nano.c +++ b/cmdline/nano.c @@ -125,7 +125,7 @@ void state_nano(struct snapraid_state* state) /* state changed, we need to update it */ state->need_write = 1; - ftag("nano:%s:%s\n", disk->name, file->sub); + ftag("nano:%s:%s\n", disk->name, esc(file->sub)); fout("nano %s%s\n", disk->dir, file->sub); } } diff --git a/cmdline/parity.c b/cmdline/parity.c index b28c0760..d1028dde 100644 --- a/cmdline/parity.c +++ b/cmdline/parity.c @@ -102,7 +102,7 @@ void parity_overflow(struct snapraid_state* state, data_off_t size) if (file->blockmax > 0) { if (file->blockvec[file->blockmax - 1].parity_pos >= blockalloc) { - ftag("outofparity:%s:%s\n", disk->name, file->sub); + ftag("outofparity:%s:%s\n", disk->name, esc(file->sub)); fflush_log(); if (first) { first = 0; diff --git a/cmdline/scan.c b/cmdline/scan.c index 850603dd..88dcdfd7 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -108,7 +108,7 @@ static void scan_link(struct snapraid_scan* scan, int is_diff, const char* sub, ++scan->count_equal; if (state->opt.gui) { - ftag("scan:equal:%s:%s\n", disk->name, link->sub); + ftag("scan:equal:%s:%s\n", disk->name, esc(link->sub)); } } else { /* it's an update */ @@ -118,7 +118,7 @@ static void scan_link(struct snapraid_scan* scan, int is_diff, const char* sub, ++scan->count_change; - ftag("scan:update:%s:%s\n", disk->name, link->sub); + ftag("scan:update:%s:%s\n", disk->name, esc(link->sub)); if (is_diff) { fout("update %s%s\n", disk->dir, link->sub); } @@ -135,7 +135,7 @@ static void scan_link(struct snapraid_scan* scan, int is_diff, const char* sub, /* create the new link */ ++scan->count_insert; - ftag("scan:add:%s:%s\n", disk->name, sub); + ftag("scan:add:%s:%s\n", disk->name, esc(sub)); if (is_diff) { fout("add %s%s\n", disk->dir, sub); } @@ -638,7 +638,7 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, /* if the path is different, it means a moved file with the same inode */ ++scan->count_move; - ftag("scan:move:%s:%s:%s\n", disk->name, file->sub, sub); + ftag("scan:move:%s:%s:%s\n", disk->name, esc(file->sub), esc(sub)); if (is_diff) { fout("move %s%s -> %s%s\n", disk->dir, file->sub, disk->dir, sub); } @@ -659,7 +659,7 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, ++scan->count_equal; if (state->opt.gui) { - ftag("scan:equal:%s:%s\n", disk->name, file->sub); + ftag("scan:equal:%s:%s\n", disk->name, esc(file->sub)); } } @@ -775,7 +775,7 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, /* like when restoring a backup that restores also the timestamp */ ++scan->count_restore; - ftag("scan:restore:%s:%s\n", disk->name, sub); + ftag("scan:restore:%s:%s\n", disk->name, esc(sub)); if (is_diff) { fout("restore %s%s\n", disk->dir, sub); } @@ -798,7 +798,7 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, ++scan->count_equal; if (state->opt.gui) { - ftag("scan:equal:%s:%s\n", disk->name, file->sub); + ftag("scan:equal:%s:%s\n", disk->name, esc(file->sub)); } } @@ -875,7 +875,7 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, /* revert old counter and use the copy one */ ++scan->count_copy; - ftag("scan:copy:%s:%s:%s:%s\n", other_disk->name, other_file->sub, disk->name, file->sub); + ftag("scan:copy:%s:%s:%s:%s\n", other_disk->name, esc(other_file->sub), disk->name, esc(file->sub)); if (is_diff) { fout("copy %s%s -> %s%s\n", other_disk->dir, other_file->sub, disk->dir, file->sub); } @@ -894,14 +894,14 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub, if (is_file_already_present) { ++scan->count_change; - ftag("scan:update:%s:%s\n", disk->name, sub); + ftag("scan:update:%s:%s\n", disk->name, esc(sub)); if (is_diff) { fout("update %s%s\n", disk->dir, sub); } } else { ++scan->count_insert; - ftag("scan:add:%s:%s\n", disk->name, sub); + ftag("scan:add:%s:%s\n", disk->name, esc(sub)); if (is_diff) { fout("add %s%s\n", disk->dir, sub); } @@ -1455,7 +1455,7 @@ void state_scan(struct snapraid_state* state, int is_diff) if (!file_flag_has(file, FILE_IS_PRESENT)) { ++scan->count_remove; - ftag("scan:remove:%s:%s\n", disk->name, file->sub); + ftag("scan:remove:%s:%s\n", disk->name, esc(file->sub)); if (is_diff) { fout("remove %s%s\n", disk->dir, file->sub); } @@ -1476,7 +1476,7 @@ void state_scan(struct snapraid_state* state, int is_diff) if (!link_flag_has(link, FILE_IS_PRESENT)) { ++scan->count_remove; - ftag("scan:remove:%s:%s\n", disk->name, link->sub); + ftag("scan:remove:%s:%s\n", disk->name, esc(link->sub)); if (is_diff) { fout("remove %s%s\n", disk->dir, link->sub); } diff --git a/cmdline/scrub.c b/cmdline/scrub.c index 33fabd72..4d3d1f23 100644 --- a/cmdline/scrub.c +++ b/cmdline/scrub.c @@ -228,7 +228,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output close error in a data disk, it isn't possible to scrub.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -236,7 +236,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par goto bail; } - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected close error in a data disk, it isn't possible to scrub.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping at block %u\n", i); @@ -252,7 +252,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output open error in a data disk, it isn't possible to scrub.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -261,7 +261,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* LCOV_EXCL_STOP */ } - ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ++error; error_on_this_block = 1; continue; @@ -288,7 +288,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* file we are processing for error reporting */ struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); if (io_error >= state->opt.io_error_limit) { /* LCOV_EXCL_START */ ferr("DANGER! Unexpected input/output read error in a data disk, it isn't possible to scrub.\n"); @@ -305,7 +305,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par continue; } - ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); ++error; error_on_this_block = 1; continue; @@ -330,7 +330,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par if (block_has_updated_hash(block)) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { - ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, handle[j].file->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, esc(handle[j].file->sub), block_file_pos(block)); /* it's a silent error only if we are dealing with synced files */ if (file_is_unsynced) { @@ -511,7 +511,7 @@ bail: ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ diff --git a/cmdline/support.c b/cmdline/support.c index a29fdece..eef723c6 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -93,6 +93,75 @@ void ftag(const char* format, ...) } } +/** + * Number of buffers for esc(). + */ +#define ESC_MAX 4 + +/** + * Buffer table to allow multiple calls to esc(). + */ +static char esc_table[ESC_MAX][PATH_MAX*2+1]; + +/** + * Next buffer to use. + */ +static unsigned esc_index = 0; + +#define ESCAPE(from,to) \ + case from : \ + if (p == end) \ + goto bail; \ + *p++ = '\\'; \ + if (p == end) \ + goto bail; \ + *p++ = to; \ + break + +const char* esc(const char* str) +{ + char* begin = esc_table[esc_index]; + char* end = begin + PATH_MAX; + char* p = begin; + + /* next call uses the next buffer */ + esc_index = (esc_index + 1) % ESC_MAX; + + /* copy string with escaping */ + while (*str) { + char c = *str; + + switch (c) { + + ESCAPE('\n', 'n'); + ESCAPE('\r', 'r'); + ESCAPE(':', 'd'); + ESCAPE('\\', '\\'); + + default: + if (p == end) + goto bail; + *p++ = c; + break; + } + + ++str; + } + + /* put final 0 */ + if (p == end) + goto bail; + *p = 0; + + return begin; + +bail: + /* LCOV_EXCL_START */ + ferr("Escape too long\n"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ +} + /****************************************************************************/ /* path */ diff --git a/cmdline/support.h b/cmdline/support.h index 6deb131d..f3b135a7 100644 --- a/cmdline/support.h +++ b/cmdline/support.h @@ -70,6 +70,13 @@ void ftag(const char* format, ...) __attribute__((format(printf, 1, 2))); */ typedef void fptr(const char* format, ...); +/** + * Escape a string. + * + * Chars ':', '\n', '\r' and '\' are escaped to '\d', '\\n', '\\r' and '\\'. + */ +const char* esc(const char* str); + /****************************************************************************/ /* path */ diff --git a/cmdline/sync.c b/cmdline/sync.c index 57ff3fcf..906767d6 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -132,7 +132,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -140,7 +140,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping at block %u\n", i); @@ -156,7 +156,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -166,7 +166,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta } if (errno == ENOENT) { - ftag("error:%u:%s:%s: Open ENOENT error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open ENOENT error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); flog("Missing file '%s'.\n", handle[j].path); flog("WARNING! You cannot modify data disk during a sync.\n"); flog("Rerun the sync command when finished.\n"); @@ -177,7 +177,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta } if (errno == EACCES) { - ftag("error:%u:%s:%s: Open EACCES error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open EACCES error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); flog("No access at file '%s'.\n", handle[j].path); flog("WARNING! Please fix the access permission in the data disk.\n"); flog("Rerun the sync command when finished.\n"); @@ -187,7 +187,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta } /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -204,7 +204,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta ) { /* file we are processing for error reporting */ struct snapraid_file* file = block_file_get(block); - ftag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, file->sub); + ftag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, esc(file->sub)); if (handle[j].st.st_size != file->size) { flog("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle[j].path, file->size, handle[j].st.st_size); } else if (handle[j].st.st_mtime != file->mtime_sec @@ -227,7 +227,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta /* file we are processing for error reporting */ struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); ferr("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -235,7 +235,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); ferr("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be read.\n", handle[j].path); ferr("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -287,7 +287,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - ftag("error:%u:%s:%s: Close EIO error. %s\n", blockmax, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close EIO error. %s\n", blockmax, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", blockmax); @@ -295,7 +295,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - ftag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping at block %u\n", blockmax); @@ -336,7 +336,7 @@ bail: struct snapraid_disk* disk = handle[j].disk; ret = handle_close(&handle[j]); if (ret == -1) { - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ @@ -602,7 +602,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -610,7 +610,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari goto bail; } - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping at block %u\n", i); @@ -626,7 +626,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); ferr("Stopping at block %u\n", i); @@ -636,7 +636,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } if (errno == ENOENT) { - ftag("error:%u:%s:%s: Open ENOENT error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open ENOENT error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); flog("Missing file '%s'.\n", handle[j].path); flog("WARNING! You cannot modify data disk during a sync.\n"); flog("Rerun the sync command when finished.\n"); @@ -648,7 +648,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } if (errno == EACCES) { - ftag("error:%u:%s:%s: Open EACCES error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open EACCES error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); flog("No access at file '%s'.\n", handle[j].path); flog("WARNING! Please fix the access permission in the data disk.\n"); flog("Rerun the sync command when finished.\n"); @@ -659,7 +659,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be accessed.\n", handle[j].path); ferr("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -676,7 +676,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari ) { /* file we are processing for error reporting */ struct snapraid_file* file = block_file_get(block); - ftag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, file->sub); + ftag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, esc(file->sub)); if (handle[j].st.st_size != file->size) { flog("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle[j].path, file->size, handle[j].st.st_size); } else if (handle[j].st.st_mtime != file->mtime_sec @@ -700,7 +700,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* file we are processing for error reporting */ struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); if (io_error >= state->opt.io_error_limit) { ferr("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, handle[j].path); @@ -715,7 +715,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari continue; } - ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, file->sub, block_file_pos(block), strerror(errno)); + ftag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); ferr("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); ferr("Ensure that file '%s' can be read.\n", handle[j].path); ferr("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -748,7 +748,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* if the file has invalid parity, it's a REP changed during the sync */ if (block_has_invalid_parity(block)) { - ftag("error:%u:%s:%s: Unexpected data change\n", i, disk->name, file->sub); + ftag("error:%u:%s:%s: Unexpected data change\n", i, disk->name, esc(file->sub)); flog("Data change at file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); flog("WARNING! Unexpected data modification of a file without parity!\n"); @@ -768,7 +768,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari error_on_this_block = 1; continue; } else { /* otherwise it's a BLK with silent error */ - ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, file->sub, block_file_pos(block)); + ftag("error:%u:%s:%s: Data error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); flog("Data error in file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); /* save the failed block for the fix */ @@ -1160,7 +1160,7 @@ bail: ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ - ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, file->sub, strerror(errno)); + ftag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); ferr("DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */