Allow mkdir helper to skip parent errors

Our mkdir helper was failing is a parent directory was not
accessible even if the child directory could be created.
This changes the helper to keep trying child directories
even when the parent is unwritable.
This commit is contained in:
Russell Belfer 2014-08-22 10:05:09 -07:00
parent d28b2b7a5f
commit 668ae2ddf8
2 changed files with 36 additions and 3 deletions

View File

@ -355,8 +355,9 @@ int git_futils_mkdir(
if (p_mkdir(make_path.ptr, mode) < 0) {
int tmp_errno = giterr_system_last();
/* ignore error if directory already exists */
if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
/* ignore error if not at end or if directory already exists */
if (lastch == '\0' &&
(p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
giterr_system_set(tmp_errno);
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
goto done;
@ -374,7 +375,8 @@ int git_futils_mkdir(
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
st.st_mode != mode &&
(error = p_chmod(make_path.ptr, mode)) < 0) {
(error = p_chmod(make_path.ptr, mode)) < 0 &&
lastch == '\0') {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
goto done;
}

View File

@ -186,3 +186,34 @@ void test_core_mkdir__chmods(void)
cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
check_mode(0777, st.st_mode);
}
void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
{
struct stat st;
mode_t *old;
/* FAT filesystems don't support exec bit, nor group/world bits */
if (!cl_is_chmod_supported())
return;
cl_assert((old = git__malloc(sizeof(mode_t))) != NULL);
*old = p_umask(022);
cl_set_cleanup(cleanup_chmod_root, old);
cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
cl_git_pass(git_path_lstat("r/mode", &st));
check_mode(0755, st.st_mode);
cl_must_pass(p_chmod("r/mode", 0111));
cl_git_pass(git_path_lstat("r/mode", &st));
check_mode(0111, st.st_mode);
cl_git_pass(
git_futils_mkdir("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH));
cl_git_pass(git_path_lstat("r/mode/is/okay/inside", &st));
check_mode(0755, st.st_mode);
cl_must_pass(p_chmod("r/mode", 0777));
}