summaryrefslogtreecommitdiff
path: root/setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c311
1 files changed, 134 insertions, 177 deletions
diff --git a/setup.c b/setup.c
index 94c1e61bda..613e3b3c13 100644
--- a/setup.c
+++ b/setup.c
@@ -5,46 +5,116 @@
static int inside_git_dir = -1;
static int inside_work_tree = -1;
-static char *prefix_path_gently(const char *prefix, int len, const char *path)
+/*
+ * 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 = 0;
+
+ /* 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 += offset_1st_component(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
+ *
+ * foo -> sub1/sub2/foo (full prefix)
+ * ../foo -> sub1/foo (remaining prefix is sub1/)
+ * ../../bar -> bar (no remaining prefix)
+ * ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
+ * `pwd`/../bar -> sub1/bar (no remaining prefix)
+ */
+char *prefix_path_gently(const char *prefix, int len,
+ int *remaining_prefix, const char *path)
{
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)
memcpy(sanitized, prefix, len);
strcpy(sanitized + len, path);
- }
- if (normalize_path_copy(sanitized, sanitized))
- 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 (remaining_prefix)
+ *remaining_prefix = len;
+ 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;
}
char *prefix_path(const char *prefix, int len, const char *path)
{
- char *r = prefix_path_gently(prefix, len, path);
+ char *r = prefix_path_gently(prefix, len, NULL, path);
if (!r)
die("'%s' is outside repository", path);
return r;
@@ -53,7 +123,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
int path_inside_repo(const char *prefix, const char *path)
{
int len = prefix ? strlen(prefix) : 0;
- char *r = prefix_path_gently(prefix, len, path);
+ char *r = prefix_path_gently(prefix, len, NULL, path);
if (r) {
free(r);
return 1;
@@ -66,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;
@@ -154,155 +224,6 @@ void verify_non_filename(const char *prefix, const char *arg)
"'git <command> [<revision>...] -- [<file>...]'", arg);
}
-/*
- * Magic pathspec
- *
- * NEEDSWORK: These need to be moved to dir.h or even to a new
- * pathspec.h when we restructure get_pathspec() users to use the
- * "struct pathspec" interface.
- *
- * Possible future magic semantics include stuff like:
- *
- * { PATHSPEC_NOGLOB, '!', "noglob" },
- * { PATHSPEC_ICASE, '\0', "icase" },
- * { PATHSPEC_RECURSIVE, '*', "recursive" },
- * { PATHSPEC_REGEXP, '\0', "regexp" },
- *
- */
-#define PATHSPEC_FROMTOP (1<<0)
-
-static struct pathspec_magic {
- unsigned bit;
- char mnemonic; /* this cannot be ':'! */
- const char *name;
-} pathspec_magic[] = {
- { PATHSPEC_FROMTOP, '/', "top" },
-};
-
-/*
- * Take an element of a pathspec and check for magic signatures.
- * Append the result to the prefix.
- *
- * For now, we only parse the syntax and throw out anything other than
- * "top" magic.
- *
- * NEEDSWORK: This needs to be rewritten when we start migrating
- * get_pathspec() users to use the "struct pathspec" interface. For
- * example, a pathspec element may be marked as case-insensitive, but
- * the prefix part must always match literally, and a single stupid
- * string cannot express such a case.
- */
-static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
-{
- unsigned magic = 0;
- const char *copyfrom = elt;
- int i;
-
- if (elt[0] != ':') {
- ; /* nothing to do */
- } else if (elt[1] == '(') {
- /* longhand */
- const char *nextat;
- for (copyfrom = elt + 2;
- *copyfrom && *copyfrom != ')';
- copyfrom = nextat) {
- size_t len = strcspn(copyfrom, ",)");
- if (copyfrom[len] == ',')
- nextat = copyfrom + len + 1;
- else
- /* handle ')' and '\0' */
- nextat = copyfrom + len;
- if (!len)
- continue;
- for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
- if (strlen(pathspec_magic[i].name) == len &&
- !strncmp(pathspec_magic[i].name, copyfrom, len)) {
- magic |= pathspec_magic[i].bit;
- break;
- }
- if (ARRAY_SIZE(pathspec_magic) <= i)
- die("Invalid pathspec magic '%.*s' in '%s'",
- (int) len, copyfrom, elt);
- }
- if (*copyfrom != ')')
- die("Missing ')' at the end of pathspec magic in '%s'", elt);
- copyfrom++;
- } else {
- /* shorthand */
- for (copyfrom = elt + 1;
- *copyfrom && *copyfrom != ':';
- copyfrom++) {
- char ch = *copyfrom;
-
- if (!is_pathspec_magic(ch))
- break;
- for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
- if (pathspec_magic[i].mnemonic == ch) {
- magic |= pathspec_magic[i].bit;
- break;
- }
- if (ARRAY_SIZE(pathspec_magic) <= i)
- die("Unimplemented pathspec magic '%c' in '%s'",
- ch, elt);
- }
- if (*copyfrom == ':')
- copyfrom++;
- }
-
- if (magic & PATHSPEC_FROMTOP)
- return xstrdup(copyfrom);
- else
- return prefix_path(prefix, prefixlen, copyfrom);
-}
-
-/*
- * N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
- * based interface - see pathspec_magic above.
- *
- * Arguments:
- * - prefix - a path relative to the root of the working tree
- * - pathspec - a list of paths underneath the prefix path
- *
- * Iterates over pathspec, prepending each path with prefix,
- * and return the resulting list.
- *
- * If pathspec is empty, return a singleton list containing prefix.
- *
- * If pathspec and prefix are both empty, return an empty list.
- *
- * This is typically used by built-in commands such as add.c, in order
- * to normalize argv arguments provided to the built-in into a list of
- * paths to process, all relative to the root of the working tree.
- */
-const char **get_pathspec(const char *prefix, const char **pathspec)
-{
- const char *entry = *pathspec;
- const char **src, **dst;
- int prefixlen;
-
- if (!prefix && !entry)
- return NULL;
-
- if (!entry) {
- static const char *spec[2];
- spec[0] = prefix;
- spec[1] = NULL;
- return spec;
- }
-
- /* Otherwise we have to re-write the entries.. */
- src = pathspec;
- dst = pathspec;
- prefixlen = prefix ? strlen(prefix) : 0;
- while (*src) {
- *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
- src++;
- }
- *dst = NULL;
- if (!*pathspec)
- return NULL;
- return pathspec;
-}
/*
* Test if it looks like we're at a git directory.
@@ -379,7 +300,7 @@ void setup_work_tree(void)
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
- set_git_dir(relative_path(git_dir, work_tree));
+ set_git_dir(remove_leading_path(git_dir, work_tree));
initialized = 1;
}
@@ -437,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--;
@@ -696,7 +617,7 @@ 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 char cwd[PATH_MAX + 1];
const char *gitdirenv, *ret;
char *gitfile;
int len, offset, offset_parent, ceil_offset = -1;
@@ -711,7 +632,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
if (nongit_ok)
*nongit_ok = 0;
- if (!getcwd(cwd, sizeof(cwd)-1))
+ if (!getcwd(cwd, sizeof(cwd) - 1))
die_errno("Unable to read current working directory");
offset = len = strlen(cwd);
@@ -908,3 +829,39 @@ const char *resolve_gitdir(const char *suspect)
return suspect;
return read_gitfile(suspect);
}
+
+/* if any standard file descriptor is missing open it to /dev/null */
+void sanitize_stdfds(void)
+{
+ int fd = open("/dev/null", O_RDWR, 0);
+ while (fd != -1 && fd < 2)
+ fd = dup(fd);
+ if (fd == -1)
+ die_errno("open /dev/null or dup failed");
+ 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
+}