maintenance: fix SEGFAULT when no repository

The "git maintenance run" and "git maintenance start/stop" commands holds a file-based lock at the .git/maintenance.lock and .git/schedule.lock respectively. These locks are used to ensure only one maintenance process is executed at the time as both operations involves writing data into the git repository. The path to the lock file is built using "the_repository->objects->odb->path" that results in SEGFAULT when we have no repository available as "the_repository->objects->odb" is set to NULL. Let's teach maintenance command to use RUN_SETUP option that will provide the validation and fail when running outside of a repository. Hence fixing the SEGFAULT for all three operations and making the behaviour consistent across all subcommands. Setting the RUN_SETUP also provides the same protection for all subcommands given that the "register" and "unregister" also requires to be executed inside a repository. Furthermore let's remove the local validation implemented by the "register" and "unregister" as this will not be required anymore with the new option. Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Rafael Silva committed Nov 26, 2020 at 20:41 UTC e72f7defc4f454cc7ad512d9a16a78b83f2606d8
3 files changed +9 -8
builtin/gc.c
-7
@@ -1446,10 +1446,6 @@ static int maintenance_register(void)
1446 struct child_process config_set = CHILD_PROCESS_INIT;
1447 struct child_process config_get = CHILD_PROCESS_INIT;
1448
1449 - /* There is no current repository, so skip registering it */
1450 - if (!the_repository || !the_repository->gitdir)
1451 - return 0;
1452 -
1449 /* Disable foreground maintenance */
1450 git_config_set("maintenance.auto", "false");
1451
@@ -1486,9 +1482,6 @@ static int maintenance_unregister(void)
1482 {
1483 struct child_process config_unset = CHILD_PROCESS_INIT;
1484
1489 - if (!the_repository || !the_repository->gitdir)
1490 - return error(_("no current repository to unregister"));
1491 -
1485 config_unset.git_cmd = 1;
1486 strvec_pushl(&config_unset.args, "config", "--global", "--unset",
1487 "maintenance.repo",
git.c
+1 -1
@@ -535,7 +535,7 @@ static struct cmd_struct commands[] = {
535 { "ls-tree", cmd_ls_tree, RUN_SETUP },
536 { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
537 { "mailsplit", cmd_mailsplit, NO_PARSEOPT },
538 - { "maintenance", cmd_maintenance, RUN_SETUP_GENTLY | NO_PARSEOPT },
538 + { "maintenance", cmd_maintenance, RUN_SETUP | NO_PARSEOPT },
539 { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
540 { "merge-base", cmd_merge_base, RUN_SETUP },
541 { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
t/t7900-maintenance.sh
+8
@@ -441,4 +441,12 @@ test_expect_success 'register preserves existing strategy' '
441 test_config maintenance.strategy incremental
442 '
443
444 +test_execpt_success 'fails when running outside of a repository' '
445 + nongit test_must_fail git maintenance run &&
446 + nongit test_must_fail git maintenance stop &&
447 + nongit test_must_fail git maintenance start &&
448 + nongit test_must_fail git maintenance register &&
449 + nongit test_must_fail git maintenance unregister
450 +'
451 +
452 test_done