setup.c 14.0 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
	char *dst0;
10

11 12 13 14 15 16 17
	if (has_dos_drive_prefix(src)) {
		*dst++ = *src++;
		*dst++ = *src++;
	}
	dst0 = dst;

	if (is_dir_sep(*src)) {
18
		*dst++ = '/';
19
		while (is_dir_sep(*src))
20 21 22
			src++;
	}

23
	for (;;) {
24 25 26 27 28 29 30 31 32 33 34
		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 == '.') {
35
			if (!src[1]) {
36 37
				/* (1) */
				src++;
38
			} else if (is_dir_sep(src[1])) {
39 40
				/* (2) */
				src += 2;
41
				while (is_dir_sep(*src))
42 43
					src++;
				continue;
44 45
			} else if (src[1] == '.') {
				if (!src[2]) {
46 47 48
					/* (3) */
					src += 2;
					goto up_one;
49
				} else if (is_dir_sep(src[2])) {
50 51
					/* (4) */
					src += 3;
52
					while (is_dir_sep(*src))
53 54 55 56
						src++;
					goto up_one;
				}
			}
57
		}
58 59

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

	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--;
83
			if (c == '/') {	/* MinGW: cannot be '\\' anymore */
84 85 86 87
				dst += 2;
				break;
			}
		}
88
	}
89 90 91
	*dst = '\0';
	return 0;
}
J
Junio C Hamano 已提交
92

93 94 95 96
const char *prefix_path(const char *prefix, int len, const char *path)
{
	const char *orig = path;
	char *sanitized = xmalloc(len + strlen(path) + 1);
97
	if (is_absolute_path(orig))
98 99 100 101 102
		strcpy(sanitized, path);
	else {
		if (len)
			memcpy(sanitized, prefix, len);
		strcpy(sanitized + len, path);
103
	}
104 105
	if (sanitary_path_copy(sanitized, sanitized))
		goto error_out;
106
	if (is_absolute_path(orig)) {
107 108 109 110 111 112
		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:
113
			die("'%s' is outside repository", orig);
114 115 116 117 118 119
		}
		if (sanitized[len] == '/')
			len++;
		memmove(sanitized, sanitized + len, total - len);
	}
	return sanitized;
120 121
}

J
Junio C Hamano 已提交
122
/*
123 124 125 126 127 128 129
 * 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];
130
#ifndef __MINGW32__
131
	if (!pfx || !*pfx || is_absolute_path(arg))
132 133 134
		return arg;
	memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
135 136 137 138 139 140 141 142 143 144 145 146
#else
	char *p;
	/* don't add prefix to absolute paths, but still replace '\' by '/' */
	if (is_absolute_path(arg))
		pfx_len = 0;
	else
		memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
	for (p = path + pfx_len; *p; p++)
		if (*p == '\\')
			*p = '/';
#endif
147 148 149
	return path;
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/*
 * 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)
168 169
		die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
		    "Use '--' to separate paths from revisions", arg);
170 171 172
	die("'%s': %s", arg, strerror(errno));
}

173 174 175 176 177 178 179 180 181 182
/*
 * 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;

183
	if (!is_inside_work_tree() || is_inside_git_dir())
184
		return;
185 186 187 188 189 190
	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);
191
	if (errno != ENOENT && errno != ENOTDIR)
192 193 194
		die("'%s': %s", arg, strerror(errno));
}

J
Junio C Hamano 已提交
195
const char **get_pathspec(const char *prefix, const char **pathspec)
196
{
J
Junio C Hamano 已提交
197
	const char *entry = *pathspec;
198
	const char **src, **dst;
199 200
	int prefixlen;

201 202
	if (!prefix && !entry)
		return NULL;
203 204 205 206 207 208 209 210 211

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

	/* Otherwise we have to re-write the entries.. */
212 213
	src = pathspec;
	dst = pathspec;
214
	prefixlen = prefix ? strlen(prefix) : 0;
215 216
	while (*src) {
		const char *p = prefix_path(prefix, prefixlen, *src);
217
		*(dst++) = p;
218 219 220 221 222 223
		src++;
	}
	*dst = NULL;
	if (!*pathspec)
		return NULL;
	return pathspec;
224 225
}

226
/*
227
 * Test if it looks like we're at a git directory.
228
 * We want to see:
229
 *
230
 *  - either an objects/ directory _or_ the proper
231
 *    GIT_OBJECT_DIRECTORY environment variable
232
 *  - a refs/ directory
J
Junio C Hamano 已提交
233
 *  - either a HEAD symlink or a HEAD file that is formatted as
J
Junio C Hamano 已提交
234 235
 *    a proper "ref:", or a regular file HEAD that has a properly
 *    formatted sha1 object name.
236
 */
237
static int is_git_directory(const char *suspect)
238
{
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	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 已提交
255
		return 0;
256 257

	strcpy(path + len, "/HEAD");
J
Junio C Hamano 已提交
258
	if (validate_headref(path))
259 260
		return 0;

J
Junio C Hamano 已提交
261
	return 1;
262 263
}

264 265
int is_inside_git_dir(void)
{
266 267 268
	if (inside_git_dir < 0)
		inside_git_dir = is_inside_dir(get_git_dir());
	return inside_git_dir;
269 270 271 272
}

int is_inside_work_tree(void)
{
273 274 275
	if (inside_work_tree < 0)
		inside_work_tree = is_inside_dir(get_git_work_tree());
	return inside_work_tree;
276 277
}

278
/*
279 280 281 282
 * 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.
283
 */
284
static const char *set_work_tree(const char *dir)
285
{
286
	char buffer[PATH_MAX + 1];
287

288 289 290
	if (!getcwd(buffer, sizeof(buffer)))
		die ("Could not get the current working directory");
	git_work_tree_cfg = xstrdup(buffer);
291 292
	inside_work_tree = 1;

293
	return NULL;
294 295
}

296 297
void setup_work_tree(void)
{
298 299 300 301 302 303 304
	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 已提交
305
	if (!is_absolute_path(git_dir))
306
		git_dir = make_absolute_path(git_dir);
M
Mike Hommey 已提交
307 308
	if (!work_tree || chdir(work_tree))
		die("This operation must be run in a work tree");
309
	set_git_dir(make_relative_path(git_dir, work_tree));
310
	initialized = 1;
M
Mike Hommey 已提交
311 312
}

313 314
static int check_repository_format_gently(int *nongit_ok)
{
315
	git_config(check_repository_format_version, NULL);
316 317 318 319 320 321 322 323 324 325 326 327 328
	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;
}

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
/*
 * Try to read the location of the git directory from the .git file,
 * return path to git directory if found.
 */
const char *read_gitfile_gently(const char *path)
{
	char *buf;
	struct stat st;
	int fd;
	size_t len;

	if (stat(path, &st))
		return NULL;
	if (!S_ISREG(st.st_mode))
		return NULL;
	fd = open(path, O_RDONLY);
	if (fd < 0)
		die("Error opening %s: %s", path, strerror(errno));
	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);
	buf[len] = '\0';
	if (prefixcmp(buf, "gitdir: "))
		die("Invalid gitfile format: %s", path);
	while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
		len--;
	if (len < 9)
		die("No path in gitfile: %s", path);
	buf[len] = '\0';
	if (!is_git_directory(buf + 8))
		die("Not a git repository: %s", buf + 8);
	path = make_absolute_path(buf + 8);
	free(buf);
	return path;
}

367 368 369 370
/*
 * 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.
 */
371
const char *setup_git_directory_gently(int *nongit_ok)
372
{
373
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
374
	const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
375
	static char cwd[PATH_MAX+1];
376
	const char *gitdirenv;
377
	const char *gitfile_dir;
378
	int len, offset, ceil_offset;
379

380 381 382 383 384 385 386 387
	/*
	 * Let's assume that we are in a git repository.
	 * If it turns out later that we are somewhere else, the value will be
	 * updated accordingly.
	 */
	if (nongit_ok)
		*nongit_ok = 0;

388 389 390 391 392
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
393
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
394 395 396 397
	if (gitdirenv) {
		if (PATH_MAX - 40 < strlen(gitdirenv))
			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
		if (is_git_directory(gitdirenv)) {
398 399 400
			static char buffer[1024 + 1];
			const char *retval;

401 402
			if (!work_tree_env) {
				retval = set_work_tree(gitdirenv);
403 404 405
				/* config may override worktree */
				if (check_repository_format_gently(nongit_ok))
					return NULL;
406 407
				return retval;
			}
408 409
			if (check_repository_format_gently(nongit_ok))
				return NULL;
410 411 412 413 414 415 416 417 418
			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;
419
		}
420
		if (nongit_ok) {
421 422 423
			*nongit_ok = 1;
			return NULL;
		}
424
		die("Not a git repository: '%s'", gitdirenv);
425
	}
426

427
	if (!getcwd(cwd, sizeof(cwd)-1))
428 429
		die("Unable to read current working directory");

430
	ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
J
Junio C Hamano 已提交
431 432
	if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
		ceil_offset = 1;
433

434
	/*
435
	 * Test in the following order (relative to the cwd):
436
	 * - .git (file containing "gitdir: <path>")
437 438
	 * - .git/
	 * - ./ (bare)
439
	 * - ../.git
440 441 442 443
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
444
	 */
445 446
	offset = len = strlen(cwd);
	for (;;) {
447 448 449 450 451 452
		gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
		if (gitfile_dir) {
			if (set_git_dir(gitfile_dir))
				die("Repository setup failed");
			break;
		}
453 454 455 456 457 458 459
		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);
460
			check_repository_format_gently(nongit_ok);
461 462
			return NULL;
		}
463 464 465 466 467 468 469
		while (--offset > ceil_offset && cwd[offset] != '/');
		if (offset <= ceil_offset) {
			if (nongit_ok) {
				if (chdir(cwd))
					die("Cannot come back to cwd");
				*nongit_ok = 1;
				return NULL;
470
			}
471 472
			die("Not a git repository");
		}
473
		chdir("..");
474 475
	}

476 477 478 479
	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
	git_work_tree_cfg = xstrndup(cwd, offset);
480 481
	if (check_repository_format_gently(nongit_ok))
		return NULL;
482
	if (offset == len)
483 484
		return NULL;

485 486 487 488 489
	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
490
}
491

492 493
int git_config_perm(const char *var, const char *value)
{
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
	int i;
	char *endptr;

	if (value == NULL)
		return PERM_GROUP;

	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;

	/* Parse octal numbers */
	i = strtol(value, &endptr, 8);

	/* If not an octal number, maybe true/false? */
	if (*endptr != 0)
		return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;

	/*
	 * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
	 * a chmod value.
	 */
	switch (i) {
	case PERM_UMASK:               /* 0 */
		return PERM_UMASK;
	case OLD_PERM_GROUP:           /* 1 */
		return PERM_GROUP;
	case OLD_PERM_EVERYBODY:       /* 2 */
		return PERM_EVERYBODY;
527
	}
528 529 530 531 532 533 534 535 536 537 538 539 540

	/* A filemode value was given: 0xxx */

	if ((i & 0600) != 0600)
		die("Problem with core.sharedRepository filemode value "
		    "(0%.3o).\nThe owner of files must always have "
		    "read and write permissions.", i);

	/*
	 * Mask filemode value. Others can not get write permission.
	 * x flags for directories are handled separately.
	 */
	return i & 0666;
541 542
}

543
int check_repository_format_version(const char *var, const char *value, void *cb)
J
Junio C Hamano 已提交
544
{
545 546
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
547
	else if (strcmp(var, "core.sharedrepository") == 0)
548
		shared_repository = git_config_perm(var, value);
549 550 551 552 553
	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) {
554 555
		if (!value)
			return config_error_nonbool(var);
556
		free(git_work_tree_cfg);
557 558 559
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
560
	return 0;
J
Junio C Hamano 已提交
561 562 563 564
}

int check_repository_format(void)
{
565
	return check_repository_format_gently(NULL);
J
Junio C Hamano 已提交
566 567
}

568 569
const char *setup_git_directory(void)
{
570
	const char *retval = setup_git_directory_gently(NULL);
571 572 573 574 575 576 577 578

	/* 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());
579 580
		if (rel && *rel && chdir(get_git_work_tree()))
			die ("Could not jump to working directory");
581 582 583
		return rel && *rel ? strcat(rel, "/") : NULL;
	}

584 585
	return retval;
}