mkdir.c 6.83 KB
Newer Older
1 2 3 4 5 6 7 8
#include "clar_libgit2.h"
#include "fileops.h"
#include "path.h"
#include "posix.h"

static void cleanup_basic_dirs(void *ref)
{
	GIT_UNUSED(ref);
9 10 11 12 13
	git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
	git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
	git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
	git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
	git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
}

void test_core_mkdir__basic(void)
{
	cl_set_cleanup(cleanup_basic_dirs, NULL);

	/* make a directory */
	cl_assert(!git_path_isdir("d0"));
	cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
	cl_assert(git_path_isdir("d0"));

	/* make a path */
	cl_assert(!git_path_isdir("d1"));
	cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
	cl_assert(git_path_isdir("d1"));
	cl_assert(git_path_isdir("d1/d1.1"));
	cl_assert(git_path_isdir("d1/d1.1/d1.2"));

	/* make a dir exclusively */
	cl_assert(!git_path_isdir("d2"));
	cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
	cl_assert(git_path_isdir("d2"));

	/* make exclusive failure */
	cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));

	/* make a path exclusively */
	cl_assert(!git_path_isdir("d3"));
	cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
	cl_assert(git_path_isdir("d3"));
	cl_assert(git_path_isdir("d3/d3.1/d3.2"));

	/* make exclusive path failure */
	cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
	/* ??? Should EXCL only apply to the last item in the path? */

	/* path with trailing slash? */
	cl_assert(!git_path_isdir("d4"));
	cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
	cl_assert(git_path_isdir("d4/d4.1"));
}

static void cleanup_basedir(void *ref)
{
	GIT_UNUSED(ref);
59
	git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
}

void test_core_mkdir__with_base(void)
{
#define BASEDIR "base/dir/here"

	cl_set_cleanup(cleanup_basedir, NULL);

	cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));

	cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
	cl_assert(git_path_isdir(BASEDIR "/a"));

	cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
	cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));

	/* exclusive with existing base */
	cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));

	/* fail: exclusive with duplicated suffix */
	cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));

	/* fail: exclusive with any duplicated component */
	cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));

	/* success: exclusive without path */
	cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));

	/* path with shorter base and existing dirs */
	cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
	cl_assert(git_path_isdir("base/dir/here/d"));

	/* fail: path with shorter base and existing dirs */
	cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));

	/* fail: base with missing components */
	cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));

	/* success: shift missing component to path */
	cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
}

static void cleanup_chmod_root(void *ref)
{
	mode_t *mode = ref;
105 106

	if (*mode != 0) {
107
		(void)p_umask(*mode);
108 109
		git__free(mode);
	}
110

111
	git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
112 113
}

114 115 116 117
#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)

static void check_mode_at_line(
	mode_t expected, mode_t actual, const char *file, int line)
118
{
119 120 121 122 123 124 125 126 127
	/* FAT filesystems don't support exec bit, nor group/world bits */
	if (!cl_is_chmod_supported()) {
		expected &= 0600;
		actual &= 0600;
	}

	clar__assert_equal(
		file, line, "expected_mode != actual_mode", 1,
		"%07o", (int)expected, (int)(actual & 0777));
128 129
}

130 131 132
void test_core_mkdir__chmods(void)
{
	struct stat st;
133 134
	mode_t *old = git__malloc(sizeof(mode_t));
	*old = p_umask(022);
135

136
	cl_set_cleanup(cleanup_chmod_root, old);
137 138 139 140 141 142

	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));
143
	check_mode(0755, st.st_mode);
144
	cl_git_pass(git_path_lstat("r/mode/is", &st));
145
	check_mode(0755, st.st_mode);
146
	cl_git_pass(git_path_lstat("r/mode/is/important", &st));
147
	check_mode(0755, st.st_mode);
148 149 150 151

	cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));

	cl_git_pass(git_path_lstat("r/mode2", &st));
152
	check_mode(0755, st.st_mode);
153
	cl_git_pass(git_path_lstat("r/mode2/is2", &st));
154
	check_mode(0755, st.st_mode);
155
	cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
156
	check_mode(0777, st.st_mode);
157 158 159 160

	cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));

	cl_git_pass(git_path_lstat("r/mode3", &st));
161
	check_mode(0777, st.st_mode);
162
	cl_git_pass(git_path_lstat("r/mode3/is3", &st));
163
	check_mode(0777, st.st_mode);
164
	cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
165
	check_mode(0777, st.st_mode);
166 167 168 169 170 171

	/* test that we chmod existing dir */

	cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));

	cl_git_pass(git_path_lstat("r/mode", &st));
172
	check_mode(0755, st.st_mode);
173
	cl_git_pass(git_path_lstat("r/mode/is", &st));
174
	check_mode(0755, st.st_mode);
175
	cl_git_pass(git_path_lstat("r/mode/is/important", &st));
176
	check_mode(0777, st.st_mode);
177 178 179 180 181 182

	/* test that we chmod even existing dirs if CHMOD_PATH is set */

	cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));

	cl_git_pass(git_path_lstat("r/mode2", &st));
183
	check_mode(0777, st.st_mode);
184
	cl_git_pass(git_path_lstat("r/mode2/is2", &st));
185
	check_mode(0777, st.st_mode);
186
	cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
187
	check_mode(0777, st.st_mode);
188
}
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

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));
}