Sync with 2.14.6
* maint-2.14: (28 commits) Git 2.14.6 mingw: handle `subst`-ed "DOS drives" mingw: refuse to access paths with trailing spaces or periods mingw: refuse to access paths with illegal characters unpack-trees: let merged_entry() pass through do_add_entry()'s errors quote-stress-test: offer to test quoting arguments for MSYS2 sh t6130/t9350: prepare for stringent Win32 path validation quote-stress-test: allow skipping some trials quote-stress-test: accept arguments to test via the command-line tests: add a helper to stress test argument quoting mingw: fix quoting of arguments Disallow dubiously-nested submodule git directories protect_ntfs: turn on NTFS protection by default path: also guard `.gitmodules` against NTFS Alternate Data Streams is_ntfs_dotgit(): speed it up mingw: disallow backslash characters in tree objects' file names path: safeguard `.git` against NTFS Alternate Streams Accesses clone --recurse-submodules: prevent name squatting on Windows is_ntfs_dotgit(): only verify the leading segment test-path-utils: offer to run a protectNTFS/protectHFS benchmark ...
This commit is contained in:
@ -176,6 +176,99 @@ static int is_dotgitmodules(const char *path)
|
||||
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* A very simple, reproducible pseudo-random generator. Copied from
|
||||
* `test-genrandom.c`.
|
||||
*/
|
||||
static uint64_t my_random_value = 1234;
|
||||
|
||||
static uint64_t my_random(void)
|
||||
{
|
||||
my_random_value = my_random_value * 1103515245 + 12345;
|
||||
return my_random_value;
|
||||
}
|
||||
|
||||
/*
|
||||
* A fast approximation of the square root, without requiring math.h.
|
||||
*
|
||||
* It uses Newton's method to approximate the solution of 0 = x^2 - value.
|
||||
*/
|
||||
static double my_sqrt(double value)
|
||||
{
|
||||
const double epsilon = 1e-6;
|
||||
double x = value;
|
||||
|
||||
if (value == 0)
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
double delta = (value / x - x) / 2;
|
||||
if (delta < epsilon && delta > -epsilon)
|
||||
return x + delta;
|
||||
x += delta;
|
||||
}
|
||||
}
|
||||
|
||||
static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
|
||||
{
|
||||
size_t i, j, nr, min_len = 3, max_len = 20;
|
||||
char **names;
|
||||
int repetitions = 15, file_mode = 0100644;
|
||||
uint64_t begin, end;
|
||||
double m[3][2], v[3][2];
|
||||
uint64_t cumul;
|
||||
double cumul2;
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) {
|
||||
file_mode = 0120000;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
nr = argc > 1 ? strtoul(argv[1], NULL, 0) : 1000000;
|
||||
ALLOC_ARRAY(names, nr);
|
||||
|
||||
if (argc > 2) {
|
||||
min_len = strtoul(argv[2], NULL, 0);
|
||||
if (argc > 3)
|
||||
max_len = strtoul(argv[3], NULL, 0);
|
||||
if (min_len > max_len)
|
||||
die("min_len > max_len");
|
||||
}
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
size_t len = min_len + (my_random() % (max_len + 1 - min_len));
|
||||
|
||||
names[i] = xmallocz(len);
|
||||
while (len > 0)
|
||||
names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' ')));
|
||||
}
|
||||
|
||||
for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
|
||||
for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) {
|
||||
cumul = 0;
|
||||
cumul2 = 0;
|
||||
for (i = 0; i < repetitions; i++) {
|
||||
begin = getnanotime();
|
||||
for (j = 0; j < nr; j++)
|
||||
verify_path(names[j], file_mode);
|
||||
end = getnanotime();
|
||||
printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6);
|
||||
cumul += end - begin;
|
||||
cumul2 += (end - begin) * (end - begin);
|
||||
}
|
||||
m[protect_ntfs][protect_hfs] = cumul / (double)repetitions;
|
||||
v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]);
|
||||
printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6);
|
||||
}
|
||||
|
||||
for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
|
||||
for (protect_hfs = 0; protect_hfs < 2; protect_hfs++)
|
||||
printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_main(int argc, const char **argv)
|
||||
{
|
||||
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
|
||||
@ -290,6 +383,26 @@ int cmd_main(int argc, const char **argv)
|
||||
return !!res;
|
||||
}
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "protect_ntfs_hfs"))
|
||||
return !!protect_ntfs_hfs_benchmark(argc - 1, argv + 1);
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "is_valid_path")) {
|
||||
int res = 0, expect = 1, i;
|
||||
|
||||
for (i = 2; i < argc; i++)
|
||||
if (!strcmp("--not", argv[i]))
|
||||
expect = 0;
|
||||
else if (expect != is_valid_path(argv[i]))
|
||||
res = error("'%s' is%s a valid path",
|
||||
argv[i], expect ? " not" : "");
|
||||
else
|
||||
fprintf(stderr,
|
||||
"'%s' is%s a valid path\n",
|
||||
argv[i], expect ? "" : " not");
|
||||
|
||||
return !!res;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
|
||||
argv[1] ? argv[1] : "(there was none)");
|
||||
return 1;
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "run-command.h"
|
||||
#include "argv-array.h"
|
||||
#include "strbuf.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "gettext.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static int number_callbacks;
|
||||
static int parallel_next(struct child_process *cp,
|
||||
@ -49,11 +49,145 @@ static int task_finished(int result,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint64_t my_random_next = 1234;
|
||||
|
||||
static uint64_t my_random(void)
|
||||
{
|
||||
uint64_t res = my_random_next;
|
||||
my_random_next = my_random_next * 1103515245 + 12345;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int quote_stress_test(int argc, const char **argv)
|
||||
{
|
||||
/*
|
||||
* We are running a quote-stress test.
|
||||
* spawn a subprocess that runs quote-stress with a
|
||||
* special option that echoes back the arguments that
|
||||
* were passed in.
|
||||
*/
|
||||
char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
|
||||
int i, j, k, trials = 100, skip = 0, msys2 = 0;
|
||||
struct strbuf out = STRBUF_INIT;
|
||||
struct argv_array args = ARGV_ARRAY_INIT;
|
||||
struct option options[] = {
|
||||
OPT_INTEGER('n', "trials", &trials, "Number of trials"),
|
||||
OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
|
||||
OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const usage[] = {
|
||||
"test-run-command quote-stress-test <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, NULL, options, usage, 0);
|
||||
|
||||
setenv("MSYS_NO_PATHCONV", "1", 0);
|
||||
|
||||
for (i = 0; i < trials; i++) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
size_t arg_count, arg_offset;
|
||||
int ret = 0;
|
||||
|
||||
argv_array_clear(&args);
|
||||
if (msys2)
|
||||
argv_array_pushl(&args, "sh", "-c",
|
||||
"printf %s\\\\0 \"$@\"", "skip", NULL);
|
||||
else
|
||||
argv_array_pushl(&args, "test-run-command",
|
||||
"quote-echo", NULL);
|
||||
arg_offset = args.argc;
|
||||
|
||||
if (argc > 0) {
|
||||
trials = 1;
|
||||
arg_count = argc;
|
||||
for (j = 0; j < arg_count; j++)
|
||||
argv_array_push(&args, argv[j]);
|
||||
} else {
|
||||
arg_count = 1 + (my_random() % 5);
|
||||
for (j = 0; j < arg_count; j++) {
|
||||
char buf[20];
|
||||
size_t min_len = 1;
|
||||
size_t arg_len = min_len +
|
||||
(my_random() % (ARRAY_SIZE(buf) - min_len));
|
||||
|
||||
for (k = 0; k < arg_len; k++)
|
||||
buf[k] = special[my_random() %
|
||||
ARRAY_SIZE(special)];
|
||||
buf[arg_len] = '\0';
|
||||
|
||||
argv_array_push(&args, buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (i < skip)
|
||||
continue;
|
||||
|
||||
cp.argv = args.argv;
|
||||
strbuf_reset(&out);
|
||||
if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
|
||||
return error("Failed to spawn child process");
|
||||
|
||||
for (j = 0, k = 0; j < arg_count; j++) {
|
||||
const char *arg = args.argv[j + arg_offset];
|
||||
|
||||
if (strcmp(arg, out.buf + k))
|
||||
ret = error("incorrectly quoted arg: '%s', "
|
||||
"echoed back as '%s'",
|
||||
arg, out.buf + k);
|
||||
k += strlen(out.buf + k) + 1;
|
||||
}
|
||||
|
||||
if (k != out.len)
|
||||
ret = error("got %d bytes, but consumed only %d",
|
||||
(int)out.len, (int)k);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
|
||||
for (j = 0; j < arg_count; j++)
|
||||
fprintf(stderr, "arg #%d: '%s'\n",
|
||||
(int)j, args.argv[j + arg_offset]);
|
||||
|
||||
strbuf_release(&out);
|
||||
argv_array_clear(&args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (i && (i % 100) == 0)
|
||||
fprintf(stderr, "Trials completed: %d\n", (int)i);
|
||||
}
|
||||
|
||||
strbuf_release(&out);
|
||||
argv_array_clear(&args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quote_echo(int argc, const char **argv)
|
||||
{
|
||||
while (argc > 1) {
|
||||
fwrite(argv[1], strlen(argv[1]), 1, stdout);
|
||||
fputc('\0', stdout);
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_main(int argc, const char **argv)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
int jobs;
|
||||
|
||||
if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
|
||||
return !!quote_stress_test(argc - 1, argv + 1);
|
||||
|
||||
if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
|
||||
return !!quote_echo(argc - 1, argv + 1);
|
||||
|
||||
if (argc < 3)
|
||||
return 1;
|
||||
proc.argv = (const char **)argv + 2;
|
||||
|
Reference in New Issue
Block a user