setup.con commit Remove empty ref directories that prevent creating a ref. (9c7b0b3)
   1#include "cache.h"
   2
   3const char *prefix_path(const char *prefix, int len, const char *path)
   4{
   5        const char *orig = path;
   6        for (;;) {
   7                char c;
   8                if (*path != '.')
   9                        break;
  10                c = path[1];
  11                /* "." */
  12                if (!c) {
  13                        path++;
  14                        break;
  15                }
  16                /* "./" */
  17                if (c == '/') {
  18                        path += 2;
  19                        continue;
  20                }
  21                if (c != '.')
  22                        break;
  23                c = path[2];
  24                if (!c)
  25                        path += 2;
  26                else if (c == '/')
  27                        path += 3;
  28                else
  29                        break;
  30                /* ".." and "../" */
  31                /* Remove last component of the prefix */
  32                do {
  33                        if (!len)
  34                                die("'%s' is outside repository", orig);
  35                        len--;
  36                } while (len && prefix[len-1] != '/');
  37                continue;
  38        }
  39        if (len) {
  40                int speclen = strlen(path);
  41                char *n = xmalloc(speclen + len + 1);
  42        
  43                memcpy(n, prefix, len);
  44                memcpy(n + len, path, speclen+1);
  45                path = n;
  46        }
  47        return path;
  48}
  49
  50/* 
  51 * Unlike prefix_path, this should be used if the named file does
  52 * not have to interact with index entry; i.e. name of a random file
  53 * on the filesystem.
  54 */
  55const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
  56{
  57        static char path[PATH_MAX];
  58        if (!pfx || !*pfx || arg[0] == '/')
  59                return arg;
  60        memcpy(path, pfx, pfx_len);
  61        strcpy(path + pfx_len, arg);
  62        return path;
  63}
  64
  65/*
  66 * Verify a filename that we got as an argument for a pathspec
  67 * entry. Note that a filename that begins with "-" never verifies
  68 * as true, because even if such a filename were to exist, we want
  69 * it to be preceded by the "--" marker (or we want the user to
  70 * use a format like "./-filename")
  71 */
  72void verify_filename(const char *prefix, const char *arg)
  73{
  74        const char *name;
  75        struct stat st;
  76
  77        if (*arg == '-')
  78                die("bad flag '%s' used after filename", arg);
  79        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
  80        if (!lstat(name, &st))
  81                return;
  82        if (errno == ENOENT)
  83                die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
  84                    "Use '--' to separate paths from revisions", arg);
  85        die("'%s': %s", arg, strerror(errno));
  86}
  87
  88/*
  89 * Opposite of the above: the command line did not have -- marker
  90 * and we parsed the arg as a refname.  It should not be interpretable
  91 * as a filename.
  92 */
  93void verify_non_filename(const char *prefix, const char *arg)
  94{
  95        const char *name;
  96        struct stat st;
  97
  98        if (*arg == '-')
  99                return; /* flag */
 100        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
 101        if (!lstat(name, &st))
 102                die("ambiguous argument '%s': both revision and filename\n"
 103                    "Use '--' to separate filenames from revisions", arg);
 104        if (errno != ENOENT)
 105                die("'%s': %s", arg, strerror(errno));
 106}
 107
 108const char **get_pathspec(const char *prefix, const char **pathspec)
 109{
 110        const char *entry = *pathspec;
 111        const char **p;
 112        int prefixlen;
 113
 114        if (!prefix && !entry)
 115                return NULL;
 116
 117        if (!entry) {
 118                static const char *spec[2];
 119                spec[0] = prefix;
 120                spec[1] = NULL;
 121                return spec;
 122        }
 123
 124        /* Otherwise we have to re-write the entries.. */
 125        p = pathspec;
 126        prefixlen = prefix ? strlen(prefix) : 0;
 127        do {
 128                *p = prefix_path(prefix, prefixlen, entry);
 129        } while ((entry = *++p) != NULL);
 130        return (const char **) pathspec;
 131}
 132
 133/*
 134 * Test if it looks like we're at the top level git directory.
 135 * We want to see:
 136 *
 137 *  - either a .git/objects/ directory _or_ the proper
 138 *    GIT_OBJECT_DIRECTORY environment variable
 139 *  - a refs/ directory under ".git"
 140 *  - either a HEAD symlink or a HEAD file that is formatted as
 141 *    a proper "ref:".
 142 */
 143static int is_toplevel_directory(void)
 144{
 145        if (access(".git/refs/", X_OK) ||
 146            access(getenv(DB_ENVIRONMENT) ?
 147                   getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
 148            validate_symref(".git/HEAD"))
 149                return 0;
 150        return 1;
 151}
 152
 153const char *setup_git_directory_gently(int *nongit_ok)
 154{
 155        static char cwd[PATH_MAX+1];
 156        int len, offset;
 157
 158        /*
 159         * If GIT_DIR is set explicitly, we're not going
 160         * to do any discovery, but we still do repository
 161         * validation.
 162         */
 163        if (getenv(GIT_DIR_ENVIRONMENT)) {
 164                char path[PATH_MAX];
 165                int len = strlen(getenv(GIT_DIR_ENVIRONMENT));
 166                if (sizeof(path) - 40 < len)
 167                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
 168                memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len);
 169                
 170                strcpy(path + len, "/refs");
 171                if (access(path, X_OK))
 172                        goto bad_dir_environ;
 173                strcpy(path + len, "/HEAD");
 174                if (validate_symref(path))
 175                        goto bad_dir_environ;
 176                if (getenv(DB_ENVIRONMENT)) {
 177                        if (access(getenv(DB_ENVIRONMENT), X_OK))
 178                                goto bad_dir_environ;
 179                }
 180                else {
 181                        strcpy(path + len, "/objects");
 182                        if (access(path, X_OK))
 183                                goto bad_dir_environ;
 184                }
 185                return NULL;
 186        bad_dir_environ:
 187                if (nongit_ok) {
 188                        *nongit_ok = 1;
 189                        return NULL;
 190                }
 191                path[len] = 0;
 192                die("Not a git repository: '%s'", path);
 193        }
 194
 195        if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
 196                die("Unable to read current working directory");
 197
 198        offset = len = strlen(cwd);
 199        for (;;) {
 200                if (is_toplevel_directory())
 201                        break;
 202                chdir("..");
 203                do {
 204                        if (!offset) {
 205                                if (nongit_ok) {
 206                                        if (chdir(cwd))
 207                                                die("Cannot come back to cwd");
 208                                        *nongit_ok = 1;
 209                                        return NULL;
 210                                }
 211                                die("Not a git repository");
 212                        }
 213                } while (cwd[--offset] != '/');
 214        }
 215
 216        if (offset == len)
 217                return NULL;
 218
 219        /* Make "offset" point to past the '/', and add a '/' at the end */
 220        offset++;
 221        cwd[len++] = '/';
 222        cwd[len] = 0;
 223        return cwd + offset;
 224}
 225
 226int git_config_perm(const char *var, const char *value)
 227{
 228        if (value) {
 229                if (!strcmp(value, "umask"))
 230                        return PERM_UMASK;
 231                if (!strcmp(value, "group"))
 232                        return PERM_GROUP;
 233                if (!strcmp(value, "all") ||
 234                    !strcmp(value, "world") ||
 235                    !strcmp(value, "everybody"))
 236                        return PERM_EVERYBODY;
 237        }
 238        return git_config_bool(var, value);
 239}
 240
 241int check_repository_format_version(const char *var, const char *value)
 242{
 243       if (strcmp(var, "core.repositoryformatversion") == 0)
 244               repository_format_version = git_config_int(var, value);
 245        else if (strcmp(var, "core.sharedrepository") == 0)
 246                shared_repository = git_config_perm(var, value);
 247        else if (strcmp(var, "receive.denynonfastforwards") == 0)
 248                deny_non_fast_forwards = git_config_bool(var, value);
 249       return 0;
 250}
 251
 252int check_repository_format(void)
 253{
 254        git_config(check_repository_format_version);
 255        if (GIT_REPO_VERSION < repository_format_version)
 256                die ("Expected git repo version <= %d, found %d",
 257                     GIT_REPO_VERSION, repository_format_version);
 258        return 0;
 259}
 260
 261const char *setup_git_directory(void)
 262{
 263        const char *retval = setup_git_directory_gently(NULL);
 264        check_repository_format();
 265        return retval;
 266}