From 668ae2ddf854e509dca6e76772b64c9876c1d717 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 22 Aug 2014 10:05:09 -0700 Subject: 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. --- src/fileops.c | 8 +++++--- tests/core/mkdir.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index bebbae4f9..34659ad3b 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -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; } diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index a8c5b10ae..a0e8491a2 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -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)); +} + -- cgit v1.2.1