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:
Johannes Schindelin
2019-12-04 21:26:31 +01:00
31 changed files with 812 additions and 67 deletions

View File

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

View File

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