diff options
Diffstat (limited to 'setup.c')
-rw-r--r-- | setup.c | 224 |
1 files changed, 156 insertions, 68 deletions
@@ -6,6 +6,70 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; /* + * The input parameter must contain an absolute path, and it must already be + * normalized. + * + * Find the part of an absolute path that lies inside the work tree by + * dereferencing symlinks outside the work tree, for example: + * /dir1/repo/dir2/file (work tree is /dir1/repo) -> dir2/file + * /dir/file (work tree is /) -> dir/file + * /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2 + * /dir/repolink/file (repolink points to /dir/repo) -> file + * /dir/repo (exactly equal to work tree) -> (empty string) + */ +static int abspath_part_inside_repo(char *path) +{ + size_t len; + size_t wtlen; + char *path0; + int off; + const char *work_tree = get_git_work_tree(); + + if (!work_tree) + return -1; + wtlen = strlen(work_tree); + len = strlen(path); + off = offset_1st_component(path); + + /* check if work tree is already the prefix */ + if (wtlen <= len && !strncmp(path, work_tree, wtlen)) { + if (path[wtlen] == '/') { + memmove(path, path + wtlen + 1, len - wtlen); + return 0; + } else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') { + /* work tree is the root, or the whole path */ + memmove(path, path + wtlen, len - wtlen + 1); + return 0; + } + /* work tree might match beginning of a symlink to work tree */ + off = wtlen; + } + path0 = path; + path += off; + + /* check each '/'-terminated level */ + while (*path) { + path++; + if (*path == '/') { + *path = '\0'; + if (strcmp(real_path(path0), work_tree) == 0) { + memmove(path0, path + 1, len - (path - path0)); + return 0; + } + *path = '/'; + } + } + + /* check whole path */ + if (strcmp(real_path(path0), work_tree) == 0) { + *path0 = '\0'; + return 0; + } + + return -1; +} + +/* * Normalize "path", prepending the "prefix" for relative paths. If * remaining_prefix is not NULL, return the actual prefix still * remains in the path. For example, prefix = sub1/sub2/ and path is @@ -22,11 +86,17 @@ char *prefix_path_gently(const char *prefix, int len, const char *orig = path; char *sanitized; if (is_absolute_path(orig)) { - const char *temp = real_path(path); - sanitized = xmalloc(len + strlen(temp) + 1); - strcpy(sanitized, temp); + sanitized = xmalloc(strlen(path) + 1); if (remaining_prefix) *remaining_prefix = 0; + if (normalize_path_copy_len(sanitized, path, remaining_prefix)) { + free(sanitized); + return NULL; + } + if (abspath_part_inside_repo(sanitized)) { + free(sanitized); + return NULL; + } } else { sanitized = xmalloc(len + strlen(path) + 1); if (len) @@ -34,26 +104,10 @@ char *prefix_path_gently(const char *prefix, int len, strcpy(sanitized + len, path); if (remaining_prefix) *remaining_prefix = len; - } - if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) - goto error_out; - if (is_absolute_path(orig)) { - size_t root_len, len, total; - const char *work_tree = get_git_work_tree(); - if (!work_tree) - goto error_out; - len = strlen(work_tree); - root_len = offset_1st_component(work_tree); - total = strlen(sanitized) + 1; - if (strncmp(sanitized, work_tree, len) || - (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) { - error_out: + if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) { free(sanitized); return NULL; } - if (sanitized[len] == '/') - len++; - memmove(sanitized, sanitized + len, total - len); } return sanitized; } @@ -82,7 +136,7 @@ int check_filename(const char *prefix, const char *arg) const char *name; struct stat st; - if (!prefixcmp(arg, ":/")) { + if (starts_with(arg, ":/")) { if (arg[2] == '\0') /* ":/" is root dir, always exists */ return 1; name = arg + 2; @@ -304,7 +358,7 @@ const char *read_gitfile(const char *path) if (len != st.st_size) die("Error reading %s", path); buf[len] = '\0'; - if (prefixcmp(buf, "gitdir: ")) + if (!starts_with(buf, "gitdir: ")) die("Invalid gitfile format: %s", path); while (buf[len - 1] == '\n' || buf[len - 1] == '\r') len--; @@ -333,7 +387,7 @@ const char *read_gitfile(const char *path) } static const char *setup_explicit_git_dir(const char *gitdirenv, - char *cwd, int len, + struct strbuf *cwd, int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); @@ -380,16 +434,16 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, if (is_absolute_path(git_work_tree_cfg)) set_git_work_tree(git_work_tree_cfg); else { - char core_worktree[PATH_MAX]; + char *core_worktree; if (chdir(gitdirenv)) die_errno("Could not chdir to '%s'", gitdirenv); if (chdir(git_work_tree_cfg)) die_errno("Could not chdir to '%s'", git_work_tree_cfg); - if (!getcwd(core_worktree, PATH_MAX)) - die_errno("Could not get directory '%s'", git_work_tree_cfg); - if (chdir(cwd)) + core_worktree = xgetcwd(); + if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); set_git_work_tree(core_worktree); + free(core_worktree); } } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { @@ -405,21 +459,20 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, worktree = get_git_work_tree(); /* both get_git_work_tree() and cwd are already normalized */ - if (!strcmp(cwd, worktree)) { /* cwd == worktree */ + if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */ set_git_dir(gitdirenv); free(gitfile); return NULL; } - offset = dir_inside_of(cwd, worktree); + offset = dir_inside_of(cwd->buf, worktree); if (offset >= 0) { /* cwd inside worktree? */ set_git_dir(real_path(gitdirenv)); if (chdir(worktree)) die_errno("Could not chdir to '%s'", worktree); - cwd[len++] = '/'; - cwd[len] = '\0'; + strbuf_addch(cwd, '/'); free(gitfile); - return cwd + offset; + return cwd->buf + offset; } /* cwd outside worktree */ @@ -429,7 +482,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, } static const char *setup_discovered_git_dir(const char *gitdir, - char *cwd, int offset, int len, + struct strbuf *cwd, int offset, int *nongit_ok) { if (check_repository_format_gently(gitdir, nongit_ok)) @@ -437,17 +490,17 @@ static const char *setup_discovered_git_dir(const char *gitdir, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { - if (offset != len && !is_absolute_path(gitdir)) + if (offset != cwd->len && !is_absolute_path(gitdir)) gitdir = xstrdup(real_path(gitdir)); - if (chdir(cwd)) + if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); - return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok); + return setup_explicit_git_dir(gitdir, cwd, nongit_ok); } /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */ if (is_bare_repository_cfg > 0) { - set_git_dir(offset == len ? gitdir : real_path(gitdir)); - if (chdir(cwd)) + set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir)); + if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); return NULL; } @@ -458,18 +511,18 @@ static const char *setup_discovered_git_dir(const char *gitdir, set_git_dir(gitdir); inside_git_dir = 0; inside_work_tree = 1; - if (offset == len) + if (offset == cwd->len) return NULL; /* Make "offset" point to past the '/', and add a '/' at the end */ offset++; - cwd[len++] = '/'; - cwd[len] = 0; - return cwd + offset; + strbuf_addch(cwd, '/'); + return cwd->buf + offset; } /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */ -static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok) +static const char *setup_bare_git_dir(struct strbuf *cwd, int offset, + int *nongit_ok) { int root_len; @@ -482,20 +535,20 @@ static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongi if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { const char *gitdir; - gitdir = offset == len ? "." : xmemdupz(cwd, offset); - if (chdir(cwd)) + gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset); + if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); - return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok); + return setup_explicit_git_dir(gitdir, cwd, nongit_ok); } inside_git_dir = 1; inside_work_tree = 0; - if (offset != len) { - if (chdir(cwd)) + if (offset != cwd->len) { + if (chdir(cwd->buf)) die_errno("Cannot come back to cwd"); - root_len = offset_1st_component(cwd); - cwd[offset > root_len ? offset : root_len] = '\0'; - set_git_dir(cwd); + root_len = offset_1st_component(cwd->buf); + strbuf_setlen(cwd, offset > root_len ? offset : root_len); + set_git_dir(cwd->buf); } else set_git_dir("."); @@ -563,14 +616,23 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; - static char cwd[PATH_MAX + 1]; + static struct strbuf cwd = STRBUF_INIT; const char *gitdirenv, *ret; char *gitfile; - int len, offset, offset_parent, ceil_offset = -1; + int offset, offset_parent, ceil_offset = -1; dev_t current_device = 0; int one_filesystem = 1; /* + * We may have read an incomplete configuration before + * setting-up the git directory. If so, clear the cache so + * that the next queries to the configuration reload complete + * configuration (including the per-repo config file that we + * ignored previously). + */ + git_config_clear(); + + /* * 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. @@ -578,9 +640,9 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) if (nongit_ok) *nongit_ok = 0; - if (!getcwd(cwd, sizeof(cwd) - 1)) + if (strbuf_getcwd(&cwd)) die_errno("Unable to read current working directory"); - offset = len = strlen(cwd); + offset = cwd.len; /* * If GIT_DIR is set explicitly, we're not going @@ -589,7 +651,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); if (gitdirenv) - return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); + return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok); if (env_ceiling_dirs) { int empty_entry_found = 0; @@ -597,11 +659,11 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); filter_string_list(&ceiling_dirs, 0, canonicalize_ceiling_entry, &empty_entry_found); - ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs); + ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs); string_list_clear(&ceiling_dirs, 0); } - if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) + if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf)) ceil_offset = 1; /* @@ -629,7 +691,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) if (gitdirenv) { ret = setup_discovered_git_dir(gitdirenv, - cwd, offset, len, + &cwd, offset, nongit_ok); free(gitfile); return ret; @@ -637,29 +699,31 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) free(gitfile); if (is_git_directory(".")) - return setup_bare_git_dir(cwd, offset, len, nongit_ok); + return setup_bare_git_dir(&cwd, offset, nongit_ok); offset_parent = offset; - while (--offset_parent > ceil_offset && cwd[offset_parent] != '/'); + while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/'); if (offset_parent <= ceil_offset) - return setup_nongit(cwd, nongit_ok); + return setup_nongit(cwd.buf, nongit_ok); if (one_filesystem) { - dev_t parent_device = get_device_or_die("..", cwd, offset); + dev_t parent_device = get_device_or_die("..", cwd.buf, + offset); if (parent_device != current_device) { if (nongit_ok) { - if (chdir(cwd)) + if (chdir(cwd.buf)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } - cwd[offset] = '\0'; + strbuf_setlen(&cwd, offset); die("Not a git repository (or any parent up to mount point %s)\n" - "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd); + "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", + cwd.buf); } } if (chdir("..")) { - cwd[offset] = '\0'; - die_errno("Cannot change to '%s/..'", cwd); + strbuf_setlen(&cwd, offset); + die_errno("Cannot change to '%s/..'", cwd.buf); } offset = offset_parent; } @@ -787,3 +851,27 @@ void sanitize_stdfds(void) if (fd > 2) close(fd); } + +int daemonize(void) +{ +#ifdef NO_POSIX_GOODIES + errno = ENOSYS; + return -1; +#else + switch (fork()) { + case 0: + break; + case -1: + die_errno("fork failed"); + default: + exit(0); + } + if (setsid() == -1) + die_errno("setsid failed"); + close(0); + close(1); + close(2); + sanitize_stdfds(); + return 0; +#endif +} |