1
0
mirror of https://github.com/git/git synced 2024-06-30 22:54:27 +00:00

builtin/mv: fix leaks for submodule gitfile paths

Similar to the preceding commit, we have effectively given tracking
memory ownership of submodule gitfile paths. Refactor the code to start
tracking allocated strings in a separate `struct strvec` such that we
can easily plug those leaks. Mark now-passing tests as leak free.

Note that ideally, we wouldn't require two separate data structures to
track those paths. But we do need to store `NULL` pointers for the
gitfile paths such that we can indicate that its corresponding entries
in the other arrays do not have such a path at all. And given that
`struct strvec`s cannot store `NULL` pointers we cannot use them to
store this information.

There is another small gotcha that is easy to miss: you may be wondering
why we don't want to store `SUBMODULE_WITH_GITDIR` in the strvec. This
is because this is a mere sentinel value and not actually a string at
all.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt 2024-05-27 13:47:23 +02:00 committed by Junio C Hamano
parent 52a7dab439
commit ebdbefa4fe
5 changed files with 30 additions and 19 deletions

View File

@ -82,21 +82,23 @@ static char *add_slash(const char *path)
#define SUBMODULE_WITH_GITDIR ((const char *)1)
static void prepare_move_submodule(const char *src, int first,
const char **submodule_gitfile)
static const char *submodule_gitfile_path(const char *src, int first)
{
struct strbuf submodule_dotgit = STRBUF_INIT;
const char *path;
if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
die(_("Directory %s is in index and no submodule?"), src);
if (!is_staging_gitmodules_ok(the_repository->index))
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
strbuf_addf(&submodule_dotgit, "%s/.git", src);
*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
if (*submodule_gitfile)
*submodule_gitfile = xstrdup(*submodule_gitfile);
else
*submodule_gitfile = SUBMODULE_WITH_GITDIR;
path = read_gitfile(submodule_dotgit.buf);
strbuf_release(&submodule_dotgit);
if (path)
return path;
return SUBMODULE_WITH_GITDIR;
}
static int index_range_of_same_dir(const char *src, int length,
@ -170,7 +172,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct strvec sources = STRVEC_INIT;
struct strvec dest_paths = STRVEC_INIT;
struct strvec destinations = STRVEC_INIT;
const char **submodule_gitfile;
struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
const char **submodule_gitfiles;
char *dst_w_slash = NULL;
const char **src_dir = NULL;
int src_dir_nr = 0, src_dir_alloc = 0;
@ -208,7 +211,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
flags = 0;
internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags);
dst_w_slash = add_slash(dest_paths.v[0]);
submodule_gitfile = xcalloc(argc, sizeof(char *));
submodule_gitfiles = xcalloc(argc, sizeof(char *));
if (dest_paths.v[0][0] == '\0')
/* special case: "." was normalized to "" */
@ -306,8 +309,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int first = index_name_pos(the_repository->index, src, length), last;
if (first >= 0) {
prepare_move_submodule(src, first,
submodule_gitfile + i);
const char *path = submodule_gitfile_path(src, first);
if (path != SUBMODULE_WITH_GITDIR)
path = strvec_push(&submodule_gitfiles_to_free, path);
submodule_gitfiles[i] = path;
goto act_on_entry;
} else if (index_range_of_same_dir(src, length,
&first, &last) < 1) {
@ -323,7 +328,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
n = argc + last - first;
REALLOC_ARRAY(modes, n);
REALLOC_ARRAY(submodule_gitfile, n);
REALLOC_ARRAY(submodule_gitfiles, n);
dst_with_slash = add_slash(dst);
dst_with_slash_len = strlen(dst_with_slash);
@ -338,7 +343,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
memset(modes + argc + j, 0, sizeof(enum update_mode));
modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
submodule_gitfile[argc + j] = NULL;
submodule_gitfiles[argc + j] = NULL;
free(prefixed_path);
}
@ -427,8 +432,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
strvec_remove(&sources, i);
strvec_remove(&destinations, i);
MOVE_ARRAY(modes + i, modes + i + 1, n);
MOVE_ARRAY(submodule_gitfile + i,
submodule_gitfile + i + 1, n);
MOVE_ARRAY(submodule_gitfiles + i,
submodule_gitfiles + i + 1, n);
i--;
}
}
@ -462,12 +467,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
continue;
die_errno(_("renaming '%s' failed"), src);
}
if (submodule_gitfile[i]) {
if (submodule_gitfiles[i]) {
if (!update_path_in_gitmodules(src, dst))
gitmodules_modified = 1;
if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR)
connect_work_tree_and_git_dir(dst,
submodule_gitfile[i],
submodule_gitfiles[i],
1);
}
@ -573,7 +578,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
strvec_clear(&sources);
strvec_clear(&dest_paths);
strvec_clear(&destinations);
free(submodule_gitfile);
strvec_clear(&submodule_gitfiles_to_free);
free(submodule_gitfiles);
free(modes);
return ret;
}

View File

@ -9,6 +9,7 @@ This test tries to verify that add_submodule_odb works when the submodule was
initialized previously but the checkout has since been removed.
'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Tested non-UTF-8 encoding

View File

@ -1,6 +1,8 @@
#!/bin/sh
test_description='git mv in subdirs'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-data.sh

View File

@ -4,6 +4,7 @@ test_description='check handling of .gitmodule path with dash'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '

View File

@ -10,6 +10,7 @@ while making sure to add submodules using `git submodule add` instead of
`git add` as done in t7401.
'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '