diff/log -G<pattern>: tests
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index fac34f77a7800193e636f49c799908f629540de1..276916052795c5ffa872b57a7e6723edc71dfdc4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -18,14 +18,15 @@ const char *prefix_path(const char *prefix, int len, const char *path)
        if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
-               size_t len, total;
+               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) ||
-                   (sanitized[len] != '\0' && sanitized[len] != '/')) {
+                   (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
                        die("'%s' is outside repository", orig);
                }
@@ -169,6 +170,8 @@ static int is_git_directory(const char *suspect)
        char path[PATH_MAX];
        size_t len = strlen(suspect);
 
+       if (PATH_MAX <= len + strlen("/objects"))
+               die("Too long path: %.*s", 60, suspect);
        strcpy(path, suspect);
        if (getenv(DB_ENVIRONMENT)) {
                if (access(getenv(DB_ENVIRONMENT), X_OK))
@@ -321,7 +324,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset, ceil_offset;
+       int len, offset, ceil_offset, root_len;
+       dev_t current_device = 0;
+       int one_filesystem = 1;
+       struct stat buf;
 
        /*
         * Let's assume that we are in a git repository.
@@ -389,6 +395,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
         *   etc.
         */
        offset = len = strlen(cwd);
+       one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
+       if (one_filesystem) {
+               if (stat(".", &buf))
+                       die_errno("failed to stat '.'");
+               current_device = buf.st_dev;
+       }
        for (;;) {
                gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
                if (gitfile_dir) {
@@ -403,10 +415,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        if (!work_tree_env)
                                inside_work_tree = 0;
                        if (offset != len) {
-                               cwd[offset] = '\0';
-                               setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+                               root_len = offset_1st_component(cwd);
+                               cwd[offset > root_len ? offset : root_len] = '\0';
+                               set_git_dir(cwd);
                        } else
-                               setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+                               set_git_dir(".");
                        check_repository_format_gently(nongit_ok);
                        return NULL;
                }
@@ -420,14 +433,34 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        }
                        die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
                }
-               if (chdir(".."))
+               if (one_filesystem) {
+                       if (stat("..", &buf)) {
+                               cwd[offset] = '\0';
+                               die_errno("failed to stat '%s/..'", cwd);
+                       }
+                       if (buf.st_dev != current_device) {
+                               if (nongit_ok) {
+                                       if (chdir(cwd))
+                                               die_errno("Cannot come back to cwd");
+                                       *nongit_ok = 1;
+                                       return NULL;
+                               }
+                               cwd[offset] = '\0';
+                               die("Not a git repository (or any parent up to mount parent %s)\n"
+                               "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
+                       }
+               }
+               if (chdir("..")) {
+                       cwd[offset] = '\0';
                        die_errno("Cannot change to '%s/..'", cwd);
+               }
        }
 
        inside_git_dir = 0;
        if (!work_tree_env)
                inside_work_tree = 1;
-       git_work_tree_cfg = xstrndup(cwd, offset);
+       root_len = offset_1st_component(cwd);
+       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
        if (check_repository_format_gently(nongit_ok))
                return NULL;
        if (offset == len)
@@ -516,6 +549,12 @@ int check_repository_format(void)
        return check_repository_format_gently(NULL);
 }
 
+/*
+ * Returns the "prefix", a path to the current working directory
+ * relative to the work tree root, or NULL, if the current working
+ * directory is not a strict subdirectory of the work tree root. The
+ * prefix always ends with a '/' character.
+ */
 const char *setup_git_directory(void)
 {
        const char *retval = setup_git_directory_gently(NULL);