Merge branch 'rp/link-curl-before-ssl' into maint
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index 465b42a1d7aecc3991c9dd2c592d8fa164ba545e..1a374f2bf4f986b00727ee04da6a65f8f914e35f 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,6 +5,7 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 static int work_tree_config_is_bogus;
+static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -228,15 +229,22 @@ void verify_non_filename(const char *prefix, const char *arg)
 }
 
 int get_common_dir(struct strbuf *sb, const char *gitdir)
+{
+       const char *git_env_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+       if (git_env_common_dir) {
+               strbuf_addstr(sb, git_env_common_dir);
+               return 1;
+       } else {
+               return get_common_dir_noenv(sb, gitdir);
+       }
+}
+
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
 {
        struct strbuf data = STRBUF_INIT;
        struct strbuf path = STRBUF_INIT;
-       const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
        int ret = 0;
-       if (git_common_dir) {
-               strbuf_addstr(sb, git_common_dir);
-               return 1;
-       }
+
        strbuf_addf(&path, "%s/commondir", gitdir);
        if (file_exists(path.buf)) {
                if (strbuf_read_file(&data, path.buf, 0) <= 0)
@@ -352,10 +360,25 @@ void setup_work_tree(void)
 
 static int check_repo_format(const char *var, const char *value, void *cb)
 {
+       const char *ext;
+
        if (strcmp(var, "core.repositoryformatversion") == 0)
                repository_format_version = git_config_int(var, value);
        else if (strcmp(var, "core.sharedrepository") == 0)
                shared_repository = git_config_perm(var, value);
+       else if (skip_prefix(var, "extensions.", &ext)) {
+               /*
+                * record any known extensions here; otherwise,
+                * we fall through to recording it as unknown, and
+                * check_repository_format will complain
+                */
+               if (!strcmp(ext, "noop"))
+                       ;
+               else if (!strcmp(ext, "preciousobjects"))
+                       repository_format_precious_objects = git_config_bool(var, value);
+               else
+                       string_list_append(&unknown_extensions, ext);
+       }
        return 0;
 }
 
@@ -366,6 +389,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
        config_fn_t fn;
        int ret = 0;
 
+       string_list_clear(&unknown_extensions, 0);
+
        if (get_common_dir(&sb, gitdir))
                fn = check_repo_format;
        else
@@ -383,16 +408,31 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
         * is a good one.
         */
        git_config_early(fn, NULL, repo_config);
-       if (GIT_REPO_VERSION < repository_format_version) {
+       if (GIT_REPO_VERSION_READ < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
-                            GIT_REPO_VERSION, repository_format_version);
+                            GIT_REPO_VERSION_READ, repository_format_version);
                warning("Expected git repo version <= %d, found %d",
-                       GIT_REPO_VERSION, repository_format_version);
+                       GIT_REPO_VERSION_READ, repository_format_version);
                warning("Please upgrade Git");
                *nongit_ok = -1;
                ret = -1;
        }
+
+       if (repository_format_version >= 1 && unknown_extensions.nr) {
+               int i;
+
+               if (!nongit_ok)
+                       die("unknown repository extension: %s",
+                           unknown_extensions.items[0].string);
+
+               for (i = 0; i < unknown_extensions.nr; i++)
+                       warning("unknown repository extension: %s",
+                               unknown_extensions.items[i].string);
+               *nongit_ok = -1;
+               ret = -1;
+       }
+
        strbuf_release(&sb);
        return ret;
 }
@@ -404,42 +444,65 @@ static void update_linked_gitdir(const char *gitfile, const char *gitdir)
 
        strbuf_addf(&path, "%s/gitdir", gitdir);
        if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
-               write_file(path.buf, 1, "%s\n", gitfile);
+               write_file(path.buf, "%s", gitfile);
        strbuf_release(&path);
 }
 
 /*
  * Try to read the location of the git directory from the .git file,
  * return path to git directory if found.
+ *
+ * On failure, if return_error_code is not NULL, return_error_code
+ * will be set to an error code and NULL will be returned. If
+ * return_error_code is NULL the function will die instead (for most
+ * cases).
  */
-const char *read_gitfile(const char *path)
+const char *read_gitfile_gently(const char *path, int *return_error_code)
 {
-       char *buf;
-       char *dir;
+       const int max_file_size = 1 << 20;  /* 1MB */
+       int error_code = 0;
+       char *buf = NULL;
+       char *dir = NULL;
        const char *slash;
        struct stat st;
        int fd;
        ssize_t len;
 
-       if (stat(path, &st))
-               return NULL;
-       if (!S_ISREG(st.st_mode))
-               return NULL;
+       if (stat(path, &st)) {
+               error_code = READ_GITFILE_ERR_STAT_FAILED;
+               goto cleanup_return;
+       }
+       if (!S_ISREG(st.st_mode)) {
+               error_code = READ_GITFILE_ERR_NOT_A_FILE;
+               goto cleanup_return;
+       }
+       if (st.st_size > max_file_size) {
+               error_code = READ_GITFILE_ERR_TOO_LARGE;
+               goto cleanup_return;
+       }
        fd = open(path, O_RDONLY);
-       if (fd < 0)
-               die_errno("Error opening '%s'", path);
+       if (fd < 0) {
+               error_code = READ_GITFILE_ERR_OPEN_FAILED;
+               goto cleanup_return;
+       }
        buf = xmalloc(st.st_size + 1);
        len = read_in_full(fd, buf, st.st_size);
        close(fd);
-       if (len != st.st_size)
-               die("Error reading %s", path);
+       if (len != st.st_size) {
+               error_code = READ_GITFILE_ERR_READ_FAILED;
+               goto cleanup_return;
+       }
        buf[len] = '\0';
-       if (!starts_with(buf, "gitdir: "))
-               die("Invalid gitfile format: %s", path);
+       if (!starts_with(buf, "gitdir: ")) {
+               error_code = READ_GITFILE_ERR_INVALID_FORMAT;
+               goto cleanup_return;
+       }
        while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
                len--;
-       if (len < 9)
-               die("No path in gitfile: %s", path);
+       if (len < 9) {
+               error_code = READ_GITFILE_ERR_NO_PATH;
+               goto cleanup_return;
+       }
        buf[len] = '\0';
        dir = buf + 8;
 
@@ -453,15 +516,41 @@ const char *read_gitfile(const char *path)
                free(buf);
                buf = dir;
        }
-
-       if (!is_git_directory(dir))
-               die("Not a git repository: %s", dir);
-
+       if (!is_git_directory(dir)) {
+               error_code = READ_GITFILE_ERR_NOT_A_REPO;
+               goto cleanup_return;
+       }
        update_linked_gitdir(path, dir);
        path = real_path(dir);
 
+cleanup_return:
+       if (return_error_code)
+               *return_error_code = error_code;
+       else if (error_code) {
+               switch (error_code) {
+               case READ_GITFILE_ERR_STAT_FAILED:
+               case READ_GITFILE_ERR_NOT_A_FILE:
+                       /* non-fatal; follow return path */
+                       break;
+               case READ_GITFILE_ERR_OPEN_FAILED:
+                       die_errno("Error opening '%s'", path);
+               case READ_GITFILE_ERR_TOO_LARGE:
+                       die("Too large to be a .git file: '%s'", path);
+               case READ_GITFILE_ERR_READ_FAILED:
+                       die("Error reading %s", path);
+               case READ_GITFILE_ERR_INVALID_FORMAT:
+                       die("Invalid gitfile format: %s", path);
+               case READ_GITFILE_ERR_NO_PATH:
+                       die("No path in gitfile: %s", path);
+               case READ_GITFILE_ERR_NOT_A_REPO:
+                       die("Not a git repository: %s", dir);
+               default:
+                       assert(0);
+               }
+       }
+
        free(buf);
-       return path;
+       return error_code ? NULL : path;
 }
 
 static const char *setup_explicit_git_dir(const char *gitdirenv,