Merge branch 'ee/clean-remove-dirs'
Replace "is this subdirectory a separate repository that should not be touched?" check "git clean" does by checking if it has .git/HEAD using the submodule-related code with a more optimized check. * ee/clean-remove-dirs: read_gitfile_gently: fix use-after-free clean: improve performance when removing lots of directories p7300: add performance tests for clean t7300: add tests to document behavior of clean and nested git setup: sanity check file size in read_gitfile_gently setup: add gentle version of read_gitfile
This commit is contained in:
89
setup.c
89
setup.c
@ -411,35 +411,58 @@ static void update_linked_gitdir(const char *gitfile, const char *gitdir)
|
||||
/*
|
||||
* Try to read the location of the git directory from the .git file,
|
||||
* return path to git directory if found.
|
||||
*
|
||||
* On failure, if return_error_code is not NULL, return_error_code
|
||||
* will be set to an error code and NULL will be returned. If
|
||||
* return_error_code is NULL the function will die instead (for most
|
||||
* cases).
|
||||
*/
|
||||
const char *read_gitfile(const char *path)
|
||||
const char *read_gitfile_gently(const char *path, int *return_error_code)
|
||||
{
|
||||
char *buf;
|
||||
char *dir;
|
||||
const int max_file_size = 1 << 20; /* 1MB */
|
||||
int error_code = 0;
|
||||
char *buf = NULL;
|
||||
char *dir = NULL;
|
||||
const char *slash;
|
||||
struct stat st;
|
||||
int fd;
|
||||
ssize_t len;
|
||||
|
||||
if (stat(path, &st))
|
||||
return NULL;
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return NULL;
|
||||
if (stat(path, &st)) {
|
||||
error_code = READ_GITFILE_ERR_STAT_FAILED;
|
||||
goto cleanup_return;
|
||||
}
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
error_code = READ_GITFILE_ERR_NOT_A_FILE;
|
||||
goto cleanup_return;
|
||||
}
|
||||
if (st.st_size > max_file_size) {
|
||||
error_code = READ_GITFILE_ERR_TOO_LARGE;
|
||||
goto cleanup_return;
|
||||
}
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
die_errno("Error opening '%s'", path);
|
||||
if (fd < 0) {
|
||||
error_code = READ_GITFILE_ERR_OPEN_FAILED;
|
||||
goto cleanup_return;
|
||||
}
|
||||
buf = xmalloc(st.st_size + 1);
|
||||
len = read_in_full(fd, buf, st.st_size);
|
||||
close(fd);
|
||||
if (len != st.st_size)
|
||||
die("Error reading %s", path);
|
||||
if (len != st.st_size) {
|
||||
error_code = READ_GITFILE_ERR_READ_FAILED;
|
||||
goto cleanup_return;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
if (!starts_with(buf, "gitdir: "))
|
||||
die("Invalid gitfile format: %s", path);
|
||||
if (!starts_with(buf, "gitdir: ")) {
|
||||
error_code = READ_GITFILE_ERR_INVALID_FORMAT;
|
||||
goto cleanup_return;
|
||||
}
|
||||
while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
|
||||
len--;
|
||||
if (len < 9)
|
||||
die("No path in gitfile: %s", path);
|
||||
if (len < 9) {
|
||||
error_code = READ_GITFILE_ERR_NO_PATH;
|
||||
goto cleanup_return;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
dir = buf + 8;
|
||||
|
||||
@ -453,15 +476,41 @@ const char *read_gitfile(const char *path)
|
||||
free(buf);
|
||||
buf = dir;
|
||||
}
|
||||
|
||||
if (!is_git_directory(dir))
|
||||
die("Not a git repository: %s", dir);
|
||||
|
||||
if (!is_git_directory(dir)) {
|
||||
error_code = READ_GITFILE_ERR_NOT_A_REPO;
|
||||
goto cleanup_return;
|
||||
}
|
||||
update_linked_gitdir(path, dir);
|
||||
path = real_path(dir);
|
||||
|
||||
cleanup_return:
|
||||
if (return_error_code)
|
||||
*return_error_code = error_code;
|
||||
else if (error_code) {
|
||||
switch (error_code) {
|
||||
case READ_GITFILE_ERR_STAT_FAILED:
|
||||
case READ_GITFILE_ERR_NOT_A_FILE:
|
||||
/* non-fatal; follow return path */
|
||||
break;
|
||||
case READ_GITFILE_ERR_OPEN_FAILED:
|
||||
die_errno("Error opening '%s'", path);
|
||||
case READ_GITFILE_ERR_TOO_LARGE:
|
||||
die("Too large to be a .git file: '%s'", path);
|
||||
case READ_GITFILE_ERR_READ_FAILED:
|
||||
die("Error reading %s", path);
|
||||
case READ_GITFILE_ERR_INVALID_FORMAT:
|
||||
die("Invalid gitfile format: %s", path);
|
||||
case READ_GITFILE_ERR_NO_PATH:
|
||||
die("No path in gitfile: %s", path);
|
||||
case READ_GITFILE_ERR_NOT_A_REPO:
|
||||
die("Not a git repository: %s", dir);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return path;
|
||||
return error_code ? NULL : path;
|
||||
}
|
||||
|
||||
static const char *setup_explicit_git_dir(const char *gitdirenv,
|
||||
|
||||
Reference in New Issue
Block a user