Git-Raw

 view release on metacpan or  search on metacpan

deps/libgit2/src/libgit2/repository.c  view on Meta::CPAN

	size_t validation_len = 0, i;
	bool is_safe = false;
	int error = 0;

	/*
	 * If there's a worktree, validate the permissions to it *and*
	 * the git directory, and use the worktree as the configuration
	 * key for allowlisting the directory. In a bare setup, only
	 * look at the gitdir and use that as the allowlist. So we
	 * examine all `validation_paths` but use only the first as
	 * the configuration lookup.
	 */

	if (repo->workdir)
		validation_paths[validation_len++] = repo->workdir;

	if (repo->gitlink)
		validation_paths[validation_len++] = repo->gitlink;

	validation_paths[validation_len++] = repo->gitdir;

	for (i = 0; i < validation_len; i++) {
		path = validation_paths[i];

		if ((error = validate_ownership_path(&is_safe, path)) < 0)
			goto done;

		if (!is_safe)
			break;
	}

	if (is_safe ||
	    (error = validate_ownership_config(&is_safe, validation_paths[0])) < 0)
		goto done;

	if (!is_safe) {
		git_error_set(GIT_ERROR_CONFIG,
			"repository path '%s' is not owned by current user",
			path);
		error = GIT_EOWNER;
	}

done:
	return error;
}

static int find_repo(
	git_str *gitdir_path,
	git_str *workdir_path,
	git_str *gitlink_path,
	git_str *commondir_path,
	const char *start_path,
	uint32_t flags,
	const char *ceiling_dirs)
{
	git_str path = GIT_STR_INIT;
	git_str repo_link = GIT_STR_INIT;
	git_str common_link = GIT_STR_INIT;
	struct stat st;
	dev_t initial_device = 0;
	int min_iterations;
	bool in_dot_git, is_valid;
	size_t ceiling_offset = 0;
	int error;

	git_str_clear(gitdir_path);

	error = git_fs_path_prettify(&path, start_path, NULL);
	if (error < 0)
		return error;

	/* in_dot_git toggles each loop:
	 * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
	 * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we
	 * assume we started with /a/b/c.git and don't append .git the first
	 * time through.
	 * min_iterations indicates the number of iterations left before going
	 * further counts as a search. */
	if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
		in_dot_git = true;
		min_iterations = 1;
	} else {
		in_dot_git = false;
		min_iterations = 2;
	}

	for (;;) {
		if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
			if (!in_dot_git) {
				if ((error = git_str_joinpath(&path, path.ptr, DOT_GIT)) < 0)
					goto out;
			}
			in_dot_git = !in_dot_git;
		}

		if (p_stat(path.ptr, &st) == 0) {
			/* check that we have not crossed device boundaries */
			if (initial_device == 0)
				initial_device = st.st_dev;
			else if (st.st_dev != initial_device &&
				 !(flags & GIT_REPOSITORY_OPEN_CROSS_FS))
				break;

			if (S_ISDIR(st.st_mode)) {
				if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0)
					goto out;

				if (is_valid) {
					if ((error = git_fs_path_to_dir(&path)) < 0 ||
					    (error = git_str_set(gitdir_path, path.ptr, path.size)) < 0)
						goto out;

					if (gitlink_path)
						if ((error = git_str_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
							goto out;
					if (commondir_path)
						git_str_swap(&common_link, commondir_path);

					break;
				}
			} else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
				if ((error = read_gitfile(&repo_link, path.ptr)) < 0 ||
				    (error = is_valid_repository_path(&is_valid, &repo_link, &common_link)) < 0)
					goto out;

				if (is_valid) {
					git_str_swap(gitdir_path, &repo_link);

					if (gitlink_path)
						if ((error = git_str_put(gitlink_path, path.ptr, path.size)) < 0)
							goto out;
					if (commondir_path)
						git_str_swap(&common_link, commondir_path);
				}
				break;
			}
		}

		/* Move up one directory. If we're in_dot_git, we'll search the
		 * parent itself next. If we're !in_dot_git, we'll search .git
		 * in the parent directory next (added at the top of the loop). */
		if ((error = git_fs_path_dirname_r(&path, path.ptr)) < 0)
			goto out;

		/* Once we've checked the directory (and .git if applicable),
		 * find the ceiling for a search. */
		if (min_iterations && (--min_iterations == 0))
			ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);

		/* Check if we should stop searching here. */
		if (min_iterations == 0 &&
		    (path.ptr[ceiling_offset] == 0 || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))
			break;
	}

	if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
		if (!git_str_len(gitdir_path))
			git_str_clear(workdir_path);
		else if ((error = git_fs_path_dirname_r(workdir_path, path.ptr)) < 0 ||
			 (error = git_fs_path_to_dir(workdir_path)) < 0)
			goto out;
	}

	/* If we didn't find the repository, and we don't have any other error
	 * to report, report that. */
	if (!git_str_len(gitdir_path)) {
		git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path);
		error = GIT_ENOTFOUND;
		goto out;
	}

out:
	git_str_dispose(&path);
	git_str_dispose(&repo_link);
	git_str_dispose(&common_link);
	return error;
}

int git_repository_open_bare(
	git_repository **repo_ptr,
	const char *bare_path)
{
	git_str path = GIT_STR_INIT, common_path = GIT_STR_INIT;
	git_repository *repo = NULL;
	bool is_valid;
	int error;

	if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
	    (error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0)
		return error;

	if (!is_valid) {
		git_str_dispose(&path);
		git_str_dispose(&common_path);
		git_error_set(GIT_ERROR_REPOSITORY, "path is not a repository: %s", bare_path);
		return GIT_ENOTFOUND;
	}

	repo = repository_alloc();
	GIT_ERROR_CHECK_ALLOC(repo);

	repo->gitdir = git_str_detach(&path);
	GIT_ERROR_CHECK_ALLOC(repo->gitdir);
	repo->commondir = git_str_detach(&common_path);
	GIT_ERROR_CHECK_ALLOC(repo->commondir);

	/* of course we're bare! */
	repo->is_bare = 1;
	repo->is_worktree = 0;
	repo->workdir = NULL;



( run in 0.503 second using v1.01-cache-2.11-cpan-71847e10f99 )