setup.c 11.1 KB
Newer Older
1
#include "cache.h"
2 3 4 5
#include "dir.h"

static int inside_git_dir = -1;
static int inside_work_tree = -1;
6

7
static int sanitary_path_copy(char *dst, const char *src)
8
{
9 10 11 12 13 14 15 16
	char *dst0 = dst;

	if (*src == '/') {
		*dst++ = '/';
		while (*src == '/')
			src++;
	}

17
	for (;;) {
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
		char c = *src;

		/*
		 * A path component that begins with . could be
		 * special:
		 * (1) "." and ends   -- ignore and terminate.
		 * (2) "./"           -- ignore them, eat slash and continue.
		 * (3) ".." and ends  -- strip one and terminate.
		 * (4) "../"          -- strip one, eat slash and continue.
		 */
		if (c == '.') {
			switch (src[1]) {
			case '\0':
				/* (1) */
				src++;
				break;
			case '/':
				/* (2) */
				src += 2;
				while (*src == '/')
					src++;
				continue;
			case '.':
				switch (src[2]) {
				case '\0':
					/* (3) */
					src += 2;
					goto up_one;
				case '/':
					/* (4) */
					src += 3;
					while (*src == '/')
						src++;
					goto up_one;
				}
			}
54
		}
55 56 57 58

		/* copy up to the next '/', and eat all '/' */
		while ((c = *src++) != '\0' && c != '/')
			*dst++ = c;
59
		if (c == '/') {
60 61 62 63 64
			*dst++ = c;
			while (c == '/')
				c = *src++;
			src--;
		} else if (!c)
65 66
			break;
		continue;
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

	up_one:
		/*
		 * dst0..dst is prefix portion, and dst[-1] is '/';
		 * go up one level.
		 */
		dst -= 2; /* go past trailing '/' if any */
		if (dst < dst0)
			return -1;
		while (1) {
			if (dst <= dst0)
				break;
			c = *dst--;
			if (c == '/') {
				dst += 2;
				break;
			}
		}
85
	}
86 87 88
	*dst = '\0';
	return 0;
}
J
Junio C Hamano 已提交
89

90 91 92 93
const char *prefix_path(const char *prefix, int len, const char *path)
{
	const char *orig = path;
	char *sanitized = xmalloc(len + strlen(path) + 1);
94
	if (is_absolute_path(orig))
95 96 97 98 99
		strcpy(sanitized, path);
	else {
		if (len)
			memcpy(sanitized, prefix, len);
		strcpy(sanitized + len, path);
100
	}
101 102
	if (sanitary_path_copy(sanitized, sanitized))
		goto error_out;
103
	if (is_absolute_path(orig)) {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
		const char *work_tree = get_git_work_tree();
		size_t len = strlen(work_tree);
		size_t total = strlen(sanitized) + 1;
		if (strncmp(sanitized, work_tree, len) ||
		    (sanitized[len] != '\0' && sanitized[len] != '/')) {
		error_out:
			error("'%s' is outside repository", orig);
			free(sanitized);
			return NULL;
		}
		if (sanitized[len] == '/')
			len++;
		memmove(sanitized, sanitized + len, total - len);
	}
	return sanitized;
119 120
}

J
Junio C Hamano 已提交
121
/*
122 123 124 125 126 127 128
 * Unlike prefix_path, this should be used if the named file does
 * not have to interact with index entry; i.e. name of a random file
 * on the filesystem.
 */
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
	static char path[PATH_MAX];
129
	if (!pfx || !*pfx || is_absolute_path(arg))
130 131 132 133 134 135
		return arg;
	memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
	return path;
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/*
 * Verify a filename that we got as an argument for a pathspec
 * entry. Note that a filename that begins with "-" never verifies
 * as true, because even if such a filename were to exist, we want
 * it to be preceded by the "--" marker (or we want the user to
 * use a format like "./-filename")
 */
void verify_filename(const char *prefix, const char *arg)
{
	const char *name;
	struct stat st;

	if (*arg == '-')
		die("bad flag '%s' used after filename", arg);
	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
	if (!lstat(name, &st))
		return;
	if (errno == ENOENT)
154 155
		die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
		    "Use '--' to separate paths from revisions", arg);
156 157 158
	die("'%s': %s", arg, strerror(errno));
}

159 160 161 162 163 164 165 166 167 168
/*
 * Opposite of the above: the command line did not have -- marker
 * and we parsed the arg as a refname.  It should not be interpretable
 * as a filename.
 */
void verify_non_filename(const char *prefix, const char *arg)
{
	const char *name;
	struct stat st;

169
	if (!is_inside_work_tree() || is_inside_git_dir())
170
		return;
171 172 173 174 175 176
	if (*arg == '-')
		return; /* flag */
	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
	if (!lstat(name, &st))
		die("ambiguous argument '%s': both revision and filename\n"
		    "Use '--' to separate filenames from revisions", arg);
177
	if (errno != ENOENT && errno != ENOTDIR)
178 179 180
		die("'%s': %s", arg, strerror(errno));
}

J
Junio C Hamano 已提交
181
const char **get_pathspec(const char *prefix, const char **pathspec)
182
{
J
Junio C Hamano 已提交
183
	const char *entry = *pathspec;
184
	const char **src, **dst;
185 186
	int prefixlen;

187 188
	if (!prefix && !entry)
		return NULL;
189 190 191 192 193 194 195 196 197

	if (!entry) {
		static const char *spec[2];
		spec[0] = prefix;
		spec[1] = NULL;
		return spec;
	}

	/* Otherwise we have to re-write the entries.. */
198 199
	src = pathspec;
	dst = pathspec;
200
	prefixlen = prefix ? strlen(prefix) : 0;
201 202 203 204 205 206 207 208 209 210
	while (*src) {
		const char *p = prefix_path(prefix, prefixlen, *src);
		if (p)
			*(dst++) = p;
		src++;
	}
	*dst = NULL;
	if (!*pathspec)
		return NULL;
	return pathspec;
211 212
}

213
/*
214
 * Test if it looks like we're at a git directory.
215
 * We want to see:
216
 *
217
 *  - either an objects/ directory _or_ the proper
218
 *    GIT_OBJECT_DIRECTORY environment variable
219
 *  - a refs/ directory
J
Junio C Hamano 已提交
220
 *  - either a HEAD symlink or a HEAD file that is formatted as
J
Junio C Hamano 已提交
221 222
 *    a proper "ref:", or a regular file HEAD that has a properly
 *    formatted sha1 object name.
223
 */
224
static int is_git_directory(const char *suspect)
225
{
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	char path[PATH_MAX];
	size_t len = strlen(suspect);

	strcpy(path, suspect);
	if (getenv(DB_ENVIRONMENT)) {
		if (access(getenv(DB_ENVIRONMENT), X_OK))
			return 0;
	}
	else {
		strcpy(path + len, "/objects");
		if (access(path, X_OK))
			return 0;
	}

	strcpy(path + len, "/refs");
	if (access(path, X_OK))
J
Junio C Hamano 已提交
242
		return 0;
243 244

	strcpy(path + len, "/HEAD");
J
Junio C Hamano 已提交
245
	if (validate_headref(path))
246 247
		return 0;

J
Junio C Hamano 已提交
248
	return 1;
249 250
}

251 252
int is_inside_git_dir(void)
{
253 254 255
	if (inside_git_dir < 0)
		inside_git_dir = is_inside_dir(get_git_dir());
	return inside_git_dir;
256 257 258 259
}

int is_inside_work_tree(void)
{
260 261 262
	if (inside_work_tree < 0)
		inside_work_tree = is_inside_dir(get_git_work_tree());
	return inside_work_tree;
263 264
}

265
/*
266 267 268 269
 * set_work_tree() is only ever called if you set GIT_DIR explicitely.
 * The old behaviour (which we retain here) is to set the work tree root
 * to the cwd, unless overridden by the config, the command line, or
 * GIT_WORK_TREE.
270
 */
271
static const char *set_work_tree(const char *dir)
272
{
273
	char buffer[PATH_MAX + 1];
274

275 276 277
	if (!getcwd(buffer, sizeof(buffer)))
		die ("Could not get the current working directory");
	git_work_tree_cfg = xstrdup(buffer);
278 279
	inside_work_tree = 1;

280
	return NULL;
281 282
}

283 284
void setup_work_tree(void)
{
285 286 287 288 289 290 291
	const char *work_tree, *git_dir;
	static int initialized = 0;

	if (initialized)
		return;
	work_tree = get_git_work_tree();
	git_dir = get_git_dir();
M
Mike Hommey 已提交
292 293 294 295
	if (!is_absolute_path(git_dir))
		set_git_dir(make_absolute_path(git_dir));
	if (!work_tree || chdir(work_tree))
		die("This operation must be run in a work tree");
296
	initialized = 1;
M
Mike Hommey 已提交
297 298
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
static int check_repository_format_gently(int *nongit_ok)
{
	git_config(check_repository_format_version);
	if (GIT_REPO_VERSION < repository_format_version) {
		if (!nongit_ok)
			die ("Expected git repo version <= %d, found %d",
			     GIT_REPO_VERSION, repository_format_version);
		warning("Expected git repo version <= %d, found %d",
			GIT_REPO_VERSION, repository_format_version);
		warning("Please upgrade Git");
		*nongit_ok = -1;
		return -1;
	}
	return 0;
}

315 316 317 318
/*
 * We cannot decide in this function whether we are in the work tree or
 * not, since the config can only be read _after_ this function was called.
 */
319
const char *setup_git_directory_gently(int *nongit_ok)
320
{
321
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
322
	static char cwd[PATH_MAX+1];
323 324
	const char *gitdirenv;
	int len, offset;
325

326 327 328 329 330
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
331
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
332 333 334 335
	if (gitdirenv) {
		if (PATH_MAX - 40 < strlen(gitdirenv))
			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
		if (is_git_directory(gitdirenv)) {
336 337 338
			static char buffer[1024 + 1];
			const char *retval;

339 340
			if (!work_tree_env) {
				retval = set_work_tree(gitdirenv);
341 342 343
				/* config may override worktree */
				if (check_repository_format_gently(nongit_ok))
					return NULL;
344 345
				return retval;
			}
346 347
			if (check_repository_format_gently(nongit_ok))
				return NULL;
348 349 350 351 352 353 354 355 356
			retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
					get_git_work_tree());
			if (!retval || !*retval)
				return NULL;
			set_git_dir(make_absolute_path(gitdirenv));
			if (chdir(work_tree_env) < 0)
				die ("Could not chdir to %s", work_tree_env);
			strcat(buffer, "/");
			return retval;
357
		}
358
		if (nongit_ok) {
359 360 361
			*nongit_ok = 1;
			return NULL;
		}
362
		die("Not a git repository: '%s'", gitdirenv);
363
	}
364

365
	if (!getcwd(cwd, sizeof(cwd)-1))
366 367
		die("Unable to read current working directory");

368
	/*
369 370 371 372 373 374 375
	 * Test in the following order (relative to the cwd):
	 * - .git/
	 * - ./ (bare)
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
376
	 */
377 378 379 380 381 382 383 384 385
	offset = len = strlen(cwd);
	for (;;) {
		if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
			break;
		if (is_git_directory(".")) {
			inside_git_dir = 1;
			if (!work_tree_env)
				inside_work_tree = 0;
			setenv(GIT_DIR_ENVIRONMENT, ".", 1);
386
			check_repository_format_gently(nongit_ok);
387 388
			return NULL;
		}
389 390 391 392 393 394 395 396 397 398 399 400
		chdir("..");
		do {
			if (!offset) {
				if (nongit_ok) {
					if (chdir(cwd))
						die("Cannot come back to cwd");
					*nongit_ok = 1;
					return NULL;
				}
				die("Not a git repository");
			}
		} while (cwd[--offset] != '/');
401 402
	}

403 404 405 406
	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
	git_work_tree_cfg = xstrndup(cwd, offset);
407 408
	if (check_repository_format_gently(nongit_ok))
		return NULL;
409
	if (offset == len)
410 411
		return NULL;

412 413 414 415 416
	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
417
}
418

419 420 421
int git_config_perm(const char *var, const char *value)
{
	if (value) {
422
		int i;
423 424 425 426 427 428 429 430
		if (!strcmp(value, "umask"))
			return PERM_UMASK;
		if (!strcmp(value, "group"))
			return PERM_GROUP;
		if (!strcmp(value, "all") ||
		    !strcmp(value, "world") ||
		    !strcmp(value, "everybody"))
			return PERM_EVERYBODY;
431 432 433
		i = atoi(value);
		if (i > 1)
			return i;
434 435 436 437
	}
	return git_config_bool(var, value);
}

J
Junio C Hamano 已提交
438 439
int check_repository_format_version(const char *var, const char *value)
{
440 441
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
442
	else if (strcmp(var, "core.sharedrepository") == 0)
443
		shared_repository = git_config_perm(var, value);
444 445 446 447 448
	else if (strcmp(var, "core.bare") == 0) {
		is_bare_repository_cfg = git_config_bool(var, value);
		if (is_bare_repository_cfg == 1)
			inside_work_tree = -1;
	} else if (strcmp(var, "core.worktree") == 0) {
449 450
		if (!value)
			return config_error_nonbool(var);
451 452 453 454 455
		if (git_work_tree_cfg)
			free(git_work_tree_cfg);
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
456
	return 0;
J
Junio C Hamano 已提交
457 458 459 460
}

int check_repository_format(void)
{
461
	return check_repository_format_gently(NULL);
J
Junio C Hamano 已提交
462 463
}

464 465
const char *setup_git_directory(void)
{
466
	const char *retval = setup_git_directory_gently(NULL);
467 468 469 470 471 472 473 474 475 476 477

	/* If the work tree is not the default one, recompute prefix */
	if (inside_work_tree < 0) {
		static char buffer[PATH_MAX + 1];
		char *rel;
		if (retval && chdir(retval))
			die ("Could not jump back into original cwd");
		rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
		return rel && *rel ? strcat(rel, "/") : NULL;
	}

478 479
	return retval;
}