From 840ec7b1c30d1e81ae53905e248da7e6d339b809 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 10 Feb 2016 00:00:52 +0100 Subject: [PATCH] Use precise quoting for the shell commands --- Makefile.am | 4 +- cmdline/snapraid.c | 32 ++++++++++ cmdline/support.c | 155 +++++++++++++++++++++++++++++++++++++++++++-- cmdline/support.h | 12 +++- cmdline/sync.c | 10 +-- 5 files changed, 201 insertions(+), 12 deletions(-) diff --git a/Makefile.am b/Makefile.am index 924247ce..306acd46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -234,12 +234,14 @@ if HAVE_THREAD_CHECKER $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) scrub -p full -test-io-cache 128 else #### COMMAND LINE #### - $(MSG) Help + $(MSG) Pre test $(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -H $(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -V + $(TESTENV) ./snapraid$(EXEEXT) test $(TESTENV) ./mktest$(EXEEXT) if !HAVE_MEMORY_CHECKER if HAVE_EMULATOR + $(MSG) Speed # Run the self test in AVX2 $(TESTENV_AVX2) ./snapraid$(EXEEXT) --test-skip-device -c $(CONF) status # Run the speed test in different architectures diff --git a/cmdline/snapraid.c b/cmdline/snapraid.c index cb76952e..f3a6028d 100644 --- a/cmdline/snapraid.c +++ b/cmdline/snapraid.c @@ -89,6 +89,36 @@ void memory(void) msg_progress("Using %u MiB of memory for the FileSystem.\n", (unsigned)(malloc_counter_get() / MEBI)); } +void test(int argc, char* argv[]) +{ + int i; + char buffer[QUOTE_MAX]; + + /* special testing code for quoting */ + if (argc < 2 || strcmp(argv[1], "test") != 0) + return; + + for (i = 2; i < argc; ++i) { + printf("argv[%d]\n", i); + printf("\t#%s#\n", argv[i]); + printf("\t#%s#\n", quote(0, argv[i], buffer)); + } + +#ifdef _WIN32 + assert(strcmp(quote(0, " ", buffer), "\" \"") == 0); + assert(strcmp(quote(0, " \" ", buffer), "\" \"\\\"\" \"") == 0); + assert(strcmp(quote(0, "&|()<>^", buffer), "^&^|^(^)^<^>^^") == 0); + assert(strcmp(quote(0, "&|()<>^ ", buffer), "\"&|()<>^ \"") == 0); +#else + assert(strcmp(quote(0, ",._+:@%%/-", buffer), ",._+:@%%/-") == 0); + assert(strcmp(quote(0, " ", buffer), "\\ ") == 0); +#endif + + printf("Everything OK\n"); + + exit(EXIT_SUCCESS); +} + /****************************************************************************/ /* log */ @@ -472,6 +502,8 @@ int main(int argc, char* argv[]) struct tm* tm; int i; + test(argc, argv); + lock_init(); /* defaults */ diff --git a/cmdline/support.c b/cmdline/support.c index dc1260ff..fd7388f6 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -379,11 +379,11 @@ void printp(double v, size_t pad) printl(buf, pad); } -#define ESCAPE(from,to) \ +#define ESCAPE(from,escape,to) \ case from : \ if (p == end) \ goto bail; \ - *p++ = '\\'; \ + *p++ = escape; \ if (p == end) \ goto bail; \ *p++ = to; \ @@ -401,10 +401,10 @@ const char* esc(const char* str, char* buffer) switch (c) { - ESCAPE('\n', 'n'); - ESCAPE('\r', 'r'); - ESCAPE(':', 'd'); - ESCAPE('\\', '\\'); + ESCAPE('\n', '\\', 'n'); + ESCAPE('\r', '\\', 'r'); + ESCAPE(':', '\\', 'd'); + ESCAPE('\\', '\\', '\\'); default: if (p == end) @@ -430,6 +430,149 @@ bail: /* LCOV_EXCL_STOP */ } +const char* quote(char prefix, const char* str, char* buffer) +{ + char* begin = buffer; + char* end = begin + QUOTE_MAX; + char* p = begin; + +#ifdef _WIN32 + int has_quote = strchr(str, ' ') != 0; + + if (has_quote) { + if (p == end) + goto bail; + *p++ = '"'; + } +#endif + + /* add the prefix char, if any */ + if (prefix) { + if (p == end) + goto bail; + *p++ = prefix; + } + + /* copy string with escaping */ + while (*str) { + char c = *str; + + switch (c) { +#ifdef _WIN32 + /* + * Windows shell escape + * + * The Windows NT Command Shell + * https://technet.microsoft.com/en-us/library/cc723564.aspx + */ + case '"' : + /* double quote, it needs to be quoted with \ */ + if (has_quote) { + /* " -> "\"" -> (close quote)(quoted with \ ")(reopen quote) */ + if (p == end) + goto bail; + *p++ = '"'; + if (p == end) + goto bail; + *p++ = '\\'; + if (p == end) + goto bail; + *p++ = '"'; + if (p == end) + goto bail; + *p++ = '"'; + } else { + /* " -> \" */ + if (p == end) + goto bail; + *p++ = '\\'; + if (p == end) + goto bail; + *p++ = '"'; + } + break; + case '&' : + case '|' : + case '(' : + case ')' : + case '<' : + case '>' : + case '^' : + /* reserved chars, they need to be quoted with ^ */ + if (has_quote) { + if (p == end) + goto bail; + *p++ = c; + } else { + if (p == end) + goto bail; + *p++ = '^'; + if (p == end) + goto bail; + *p++ = c; + } + break; + default: + /* unquoted */ + if (p == end) + goto bail; + *p++ = c; + break; +#else + case 'a' ... 'z' : + case 'A' ... 'Z' : + case '0' ... '9' : + case ',' : + case '.' : + case '_' : + case '+' : + case ':' : + case '@' : + case '%' : + case '/' : + case '-' : + /* safe chars for all shells, unquoted */ + if (p == end) + goto bail; + *p++ = c; + break; + default: + /* all the others are quoted with \ */ + if (p == end) + goto bail; + *p++ = '\\'; + if (p == end) + goto bail; + *p++ = c; + break; +#endif + } + + ++str; + } + +#ifdef _WIN32 + if (has_quote) { + if (p == end) + goto bail; + *p++ = '"'; + } +#endif + + /* put final 0 */ + if (p == end) + goto bail; + *p = 0; + + return begin; + +bail: + /* LCOV_EXCL_START */ + log_fatal("Quote too long\n"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ +} + char* polish(char* s) { char* i = s; diff --git a/cmdline/support.h b/cmdline/support.h index fc84f5bc..8bcbe0d5 100644 --- a/cmdline/support.h +++ b/cmdline/support.h @@ -216,7 +216,7 @@ void printp(double v, size_t pad); #define ESC_MAX (PATH_MAX*2+1) /** - * Escape a string. + * Escape a string for the log. * * \param buffer Preallocated buffer of ESC_MAX size. * @@ -224,6 +224,16 @@ void printp(double v, size_t pad); */ const char* esc(const char* str, char* buffer); +#define QUOTE_MAX (PATH_MAX*2+1) + +/** + * Quote a string for the shell. + * + * \param prefix Char to prefix at the string. + * \param buffer Preallocated buffer of QUOTE_MAX size. + */ +const char* quote(char prefix, const char* str, char* buffer); + /** * Polish a string. * diff --git a/cmdline/sync.c b/cmdline/sync.c index 5a4c750c..fcc27e38 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -43,6 +43,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta unsigned silent_error; unsigned io_error; char esc_buffer[ESC_MAX]; + char quote_buffer[QUOTE_MAX]; /* maps the disks to handles */ handle = handle_mapping(state, &diskmax); @@ -194,7 +195,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta log_tag("error:%u:%s:%s: Open error. %s\n", i, disk->name, esc(file->sub, esc_buffer), strerror(errno)); log_fatal("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path); - log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); + log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", quote('/', file->sub, quote_buffer)); ++error; goto bail; /* LCOV_EXCL_STOP */ @@ -238,7 +239,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub, esc_buffer), file_pos, strerror(errno)); log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be read.\n", handle[j].path); - log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); + log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", quote('/', file->sub, quote_buffer)); ++error; goto bail; /* LCOV_EXCL_STOP */ @@ -480,6 +481,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas unsigned char* buffer = task->buffer; int ret; char esc_buffer[ESC_MAX]; + char quote_buffer[QUOTE_MAX]; /* if the disk position is not used */ if (!disk) { @@ -570,7 +572,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc(task->file->sub, esc_buffer), strerror(errno)); log_fatal("WARNING! Unexpected open error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle->path); - log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", task->file->sub); + log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", quote('/', task->file->sub, quote_buffer)); task->state = TASK_STATE_ERROR; return; /* LCOV_EXCL_STOP */ @@ -612,7 +614,7 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc(task->file->sub, esc_buffer), task->file_pos, strerror(errno)); log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be read.\n", handle->path); - log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", task->file->sub); + log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", quote('/', task->file->sub, quote_buffer)); task->state = TASK_STATE_ERROR; return; /* LCOV_EXCL_STOP */