format-patch: create leading components of output directory

'git format-patch -o <outdir>' did an equivalent of 'mkdir <outdir>' not 'mkdir -p <outdir>', which is being corrected. Avoid the usage of 'adjust_shared_perm' on the leading directories which may have security implications. Achieved by temporarily disabling of 'config.sharedRepository' like 'git init' does. Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Bert Wesarg committed Oct 11, 2019 at 10:36 UTC edefc318731f69c3e5354ead9f7505e789562375
4 files changed +42 -2
Documentation/config/format.txt
+1 -1
@@ -81,7 +81,7 @@ format.coverLetter::
81
82 format.outputDirectory::
83 Set a custom directory to store the resulting files instead of the
84 - current working directory.
84 + current working directory. All directory components will be created.
85
86 format.useAutoBase::
87 A boolean value which lets you enable the `--base=auto` option of
Documentation/git-format-patch.txt
+2 -1
@@ -66,7 +66,8 @@ they are created in the current working directory. The default path
66 can be set with the `format.outputDirectory` configuration option.
67 The `-o` option takes precedence over `format.outputDirectory`.
68 To store patches in the current working directory even when
69 -`format.outputDirectory` points elsewhere, use `-o .`.
69 +`format.outputDirectory` points elsewhere, use `-o .`. All directory
70 +components will be created.
71
72 By default, the subject of a single patch is "[PATCH] " followed by
73 the concatenation of lines from the commit message up to the first blank
builtin/log.c
+16
@@ -1765,10 +1765,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
1765 setup_pager();
1766
1767 if (output_directory) {
1768 + int saved;
1769 if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
1770 rev.diffopt.use_color = GIT_COLOR_NEVER;
1771 if (use_stdout)
1772 die(_("standard output, or directory, which one?"));
1773 + /*
1774 + * We consider <outdir> as 'outside of gitdir', therefore avoid
1775 + * applying adjust_shared_perm in s-c-l-d.
1776 + */
1777 + saved = get_shared_repository();
1778 + set_shared_repository(0);
1779 + switch (safe_create_leading_directories_const(output_directory)) {
1780 + case SCLD_OK:
1781 + case SCLD_EXISTS:
1782 + break;
1783 + default:
1784 + die(_("could not create leading directories "
1785 + "of '%s'"), output_directory);
1786 + }
1787 + set_shared_repository(saved);
1788 if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
1789 die_errno(_("could not create directory '%s'"),
1790 output_directory);
t/t4014-format-patch.sh
+23
@@ -1606,6 +1606,29 @@ test_expect_success 'From line has expected format' '
1606 test_cmp from filtered
1607 '
1608
1609 +test_expect_success 'format-patch -o with no leading directories' '
1610 + rm -fr patches &&
1611 + git format-patch -o patches master..side &&
1612 + count=$(git rev-list --count master..side) &&
1613 + ls patches >list &&
1614 + test_line_count = $count list
1615 +'
1616 +
1617 +test_expect_success 'format-patch -o with leading existing directories' '
1618 + git format-patch -o patches/side master..side &&
1619 + count=$(git rev-list --count master..side) &&
1620 + ls patches/side >list &&
1621 + test_line_count = $count list
1622 +'
1623 +
1624 +test_expect_success 'format-patch -o with leading non-existing directories' '
1625 + rm -fr patches &&
1626 + git format-patch -o patches/side master..side &&
1627 + count=$(git rev-list --count master..side) &&
1628 + ls patches/side >list &&
1629 + test_line_count = $count list
1630 +'
1631 +
1632 test_expect_success 'format-patch format.outputDirectory option' '
1633 test_config format.outputDirectory patches &&
1634 rm -fr patches &&