setup.c 12.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
	while (*src) {
		const char *p = prefix_path(prefix, prefixlen, *src);
		if (p)
			*(dst++) = p;
205 206
		else
			exit(128); /* error message already given */
207 208 209 210 211 212
		src++;
	}
	*dst = NULL;
	if (!*pathspec)
		return NULL;
	return pathspec;
213 214
}

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

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

J
Junio C Hamano 已提交
250
	return 1;
251 252
}

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

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

267
/*
268 269 270 271
 * 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.
272
 */
273
static const char *set_work_tree(const char *dir)
274
{
275
	char buffer[PATH_MAX + 1];
276

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

282
	return NULL;
283 284
}

285 286
void setup_work_tree(void)
{
287 288 289 290 291 292 293
	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 已提交
294 295 296 297
	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");
298
	initialized = 1;
M
Mike Hommey 已提交
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
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;
}

317 318 319 320
/*
 * 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.
 */
321
const char *setup_git_directory_gently(int *nongit_ok)
322
{
323
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
324
	static char cwd[PATH_MAX+1];
325 326
	const char *gitdirenv;
	int len, offset;
327

328 329 330 331 332 333 334 335
	/*
	 * 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;

336 337 338 339 340
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
341
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
342 343 344 345
	if (gitdirenv) {
		if (PATH_MAX - 40 < strlen(gitdirenv))
			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
		if (is_git_directory(gitdirenv)) {
346 347 348
			static char buffer[1024 + 1];
			const char *retval;

349 350
			if (!work_tree_env) {
				retval = set_work_tree(gitdirenv);
351 352 353
				/* config may override worktree */
				if (check_repository_format_gently(nongit_ok))
					return NULL;
354 355
				return retval;
			}
356 357
			if (check_repository_format_gently(nongit_ok))
				return NULL;
358 359 360 361 362 363 364 365 366
			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;
367
		}
368
		if (nongit_ok) {
369 370 371
			*nongit_ok = 1;
			return NULL;
		}
372
		die("Not a git repository: '%s'", gitdirenv);
373
	}
374

375
	if (!getcwd(cwd, sizeof(cwd)-1))
376 377
		die("Unable to read current working directory");

378
	/*
379 380 381 382 383 384 385
	 * Test in the following order (relative to the cwd):
	 * - .git/
	 * - ./ (bare)
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
386
	 */
387 388 389 390 391 392 393 394 395
	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);
396
			check_repository_format_gently(nongit_ok);
397 398
			return NULL;
		}
399 400 401 402 403 404 405 406 407 408 409 410
		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] != '/');
411 412
	}

413 414 415 416
	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
	git_work_tree_cfg = xstrndup(cwd, offset);
417 418
	if (check_repository_format_gently(nongit_ok))
		return NULL;
419
	if (offset == len)
420 421
		return NULL;

422 423 424 425 426
	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
427
}
428

429 430
int git_config_perm(const char *var, const char *value)
{
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
	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;
464
	}
465 466 467 468 469 470 471 472 473 474 475 476 477

	/* 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;
478 479
}

J
Junio C Hamano 已提交
480 481
int check_repository_format_version(const char *var, const char *value)
{
482 483
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
484
	else if (strcmp(var, "core.sharedrepository") == 0)
485
		shared_repository = git_config_perm(var, value);
486 487 488 489 490
	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) {
491 492
		if (!value)
			return config_error_nonbool(var);
493
		free(git_work_tree_cfg);
494 495 496
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
497
	return 0;
J
Junio C Hamano 已提交
498 499 500 501
}

int check_repository_format(void)
{
502
	return check_repository_format_gently(NULL);
J
Junio C Hamano 已提交
503 504
}

505 506
const char *setup_git_directory(void)
{
507
	const char *retval = setup_git_directory_gently(NULL);
508 509 510 511 512 513 514 515 516 517 518

	/* 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;
	}

519 520
	return retval;
}