Use precise quoting for the shell commands

This commit is contained in:
Andrea Mazzoleni 2016-02-10 00:00:52 +01:00
parent 12b30c198e
commit 840ec7b1c3
5 changed files with 201 additions and 12 deletions

View file

@ -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

View file

@ -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 */

View file

@ -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;

View file

@ -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.
*

View file

@ -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 */