setup.con commit send-email: respect in-reply-to regardless of threading (3e0c4ff)
   1#include "cache.h"
   2#include "dir.h"
   3
   4static int inside_git_dir = -1;
   5static int inside_work_tree = -1;
   6
   7static int sanitary_path_copy(char *dst, const char *src)
   8{
   9        char *dst0;
  10
  11        if (has_dos_drive_prefix(src)) {
  12                *dst++ = *src++;
  13                *dst++ = *src++;
  14        }
  15        dst0 = dst;
  16
  17        if (is_dir_sep(*src)) {
  18                *dst++ = '/';
  19                while (is_dir_sep(*src))
  20                        src++;
  21        }
  22
  23        for (;;) {
  24                char c = *src;
  25
  26                /*
  27                 * A path component that begins with . could be
  28                 * special:
  29                 * (1) "." and ends   -- ignore and terminate.
  30                 * (2) "./"           -- ignore them, eat slash and continue.
  31                 * (3) ".." and ends  -- strip one and terminate.
  32                 * (4) "../"          -- strip one, eat slash and continue.
  33                 */
  34                if (c == '.') {
  35                        if (!src[1]) {
  36                                /* (1) */
  37                                src++;
  38                        } else if (is_dir_sep(src[1])) {
  39                                /* (2) */
  40                                src += 2;
  41                                while (is_dir_sep(*src))
  42                                        src++;
  43                                continue;
  44                        } else if (src[1] == '.') {
  45                                if (!src[2]) {
  46                                        /* (3) */
  47                                        src += 2;
  48                                        goto up_one;
  49                                } else if (is_dir_sep(src[2])) {
  50                                        /* (4) */
  51                                        src += 3;
  52                                        while (is_dir_sep(*src))
  53                                                src++;
  54                                        goto up_one;
  55                                }
  56                        }
  57                }
  58
  59                /* copy up to the next '/', and eat all '/' */
  60                while ((c = *src++) != '\0' && !is_dir_sep(c))
  61                        *dst++ = c;
  62                if (is_dir_sep(c)) {
  63                        *dst++ = '/';
  64                        while (is_dir_sep(c))
  65                                c = *src++;
  66                        src--;
  67                } else if (!c)
  68                        break;
  69                continue;
  70
  71        up_one:
  72                /*
  73                 * dst0..dst is prefix portion, and dst[-1] is '/';
  74                 * go up one level.
  75                 */
  76                dst -= 2; /* go past trailing '/' if any */
  77                if (dst < dst0)
  78                        return -1;
  79                while (1) {
  80                        if (dst <= dst0)
  81                                break;
  82                        c = *dst--;
  83                        if (c == '/') { /* MinGW: cannot be '\\' anymore */
  84                                dst += 2;
  85                                break;
  86                        }
  87                }
  88        }
  89        *dst = '\0';
  90        return 0;
  91}
  92
  93const char *prefix_path(const char *prefix, int len, const char *path)
  94{
  95        const char *orig = path;
  96        char *sanitized = xmalloc(len + strlen(path) + 1);
  97        if (is_absolute_path(orig))
  98                strcpy(sanitized, path);
  99        else {
 100                if (len)
 101                        memcpy(sanitized, prefix, len);
 102                strcpy(sanitized + len, path);
 103        }
 104        if (sanitary_path_copy(sanitized, sanitized))
 105                goto error_out;
 106        if (is_absolute_path(orig)) {
 107                const char *work_tree = get_git_work_tree();
 108                size_t len = strlen(work_tree);
 109                size_t total = strlen(sanitized) + 1;
 110                if (strncmp(sanitized, work_tree, len) ||
 111                    (sanitized[len] != '\0' && sanitized[len] != '/')) {
 112                error_out:
 113                        die("'%s' is outside repository", orig);
 114                }
 115                if (sanitized[len] == '/')
 116                        len++;
 117                memmove(sanitized, sanitized + len, total - len);
 118        }
 119        return sanitized;
 120}
 121
 122/*
 123 * Unlike prefix_path, this should be used if the named file does
 124 * not have to interact with index entry; i.e. name of a random file
 125 * on the filesystem.
 126 */
 127const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 128{
 129        static char path[PATH_MAX];
 130#ifndef __MINGW32__
 131        if (!pfx || !*pfx || is_absolute_path(arg))
 132                return arg;
 133        memcpy(path, pfx, pfx_len);
 134        strcpy(path + pfx_len, arg);
 135#else
 136        char *p;
 137        /* don't add prefix to absolute paths, but still replace '\' by '/' */
 138        if (is_absolute_path(arg))
 139                pfx_len = 0;
 140        else
 141                memcpy(path, pfx, pfx_len);
 142        strcpy(path + pfx_len, arg);
 143        for (p = path + pfx_len; *p; p++)
 144                if (*p == '\\')
 145                        *p = '/';
 146#endif
 147        return path;
 148}
 149
 150/*
 151 * Verify a filename that we got as an argument for a pathspec
 152 * entry. Note that a filename that begins with "-" never verifies
 153 * as true, because even if such a filename were to exist, we want
 154 * it to be preceded by the "--" marker (or we want the user to
 155 * use a format like "./-filename")
 156 */
 157void verify_filename(const char *prefix, const char *arg)
 158{
 159        const char *name;
 160        struct stat st;
 161
 162        if (*arg == '-')
 163                die("bad flag '%s' used after filename", arg);
 164        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
 165        if (!lstat(name, &st))
 166                return;
 167        if (errno == ENOENT)
 168                die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
 169                    "Use '--' to separate paths from revisions", arg);
 170        die("'%s': %s", arg, strerror(errno));
 171}
 172
 173/*
 174 * Opposite of the above: the command line did not have -- marker
 175 * and we parsed the arg as a refname.  It should not be interpretable
 176 * as a filename.
 177 */
 178void verify_non_filename(const char *prefix, const char *arg)
 179{
 180        const char *name;
 181        struct stat st;
 182
 183        if (!is_inside_work_tree() || is_inside_git_dir())
 184                return;
 185        if (*arg == '-')
 186                return; /* flag */
 187        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
 188        if (!lstat(name, &st))
 189                die("ambiguous argument '%s': both revision and filename\n"
 190                    "Use '--' to separate filenames from revisions", arg);
 191        if (errno != ENOENT && errno != ENOTDIR)
 192                die("'%s': %s", arg, strerror(errno));
 193}
 194
 195const char **get_pathspec(const char *prefix, const char **pathspec)
 196{
 197        const char *entry = *pathspec;
 198        const char **src, **dst;
 199        int prefixlen;
 200
 201        if (!prefix && !entry)
 202                return NULL;
 203
 204        if (!entry) {
 205                static const char *spec[2];
 206                spec[0] = prefix;
 207                spec[1] = NULL;
 208                return spec;
 209        }
 210
 211        /* Otherwise we have to re-write the entries.. */
 212        src = pathspec;
 213        dst = pathspec;
 214        prefixlen = prefix ? strlen(prefix) : 0;
 215        while (*src) {
 216                const char *p = prefix_path(prefix, prefixlen, *src);
 217                *(dst++) = p;
 218                src++;
 219        }
 220        *dst = NULL;
 221        if (!*pathspec)
 222                return NULL;
 223        return pathspec;
 224}
 225
 226/*
 227 * Test if it looks like we're at a git directory.
 228 * We want to see:
 229 *
 230 *  - either an objects/ directory _or_ the proper
 231 *    GIT_OBJECT_DIRECTORY environment variable
 232 *  - a refs/ directory
 233 *  - either a HEAD symlink or a HEAD file that is formatted as
 234 *    a proper "ref:", or a regular file HEAD that has a properly
 235 *    formatted sha1 object name.
 236 */
 237static int is_git_directory(const char *suspect)
 238{
 239        char path[PATH_MAX];
 240        size_t len = strlen(suspect);
 241
 242        strcpy(path, suspect);
 243        if (getenv(DB_ENVIRONMENT)) {
 244                if (access(getenv(DB_ENVIRONMENT), X_OK))
 245                        return 0;
 246        }
 247        else {
 248                strcpy(path + len, "/objects");
 249                if (access(path, X_OK))
 250                        return 0;
 251        }
 252
 253        strcpy(path + len, "/refs");
 254        if (access(path, X_OK))
 255                return 0;
 256
 257        strcpy(path + len, "/HEAD");
 258        if (validate_headref(path))
 259                return 0;
 260
 261        return 1;
 262}
 263
 264int is_inside_git_dir(void)
 265{
 266        if (inside_git_dir < 0)
 267                inside_git_dir = is_inside_dir(get_git_dir());
 268        return inside_git_dir;
 269}
 270
 271int is_inside_work_tree(void)
 272{
 273        if (inside_work_tree < 0)
 274                inside_work_tree = is_inside_dir(get_git_work_tree());
 275        return inside_work_tree;
 276}
 277
 278/*
 279 * set_work_tree() is only ever called if you set GIT_DIR explicitely.
 280 * The old behaviour (which we retain here) is to set the work tree root
 281 * to the cwd, unless overridden by the config, the command line, or
 282 * GIT_WORK_TREE.
 283 */
 284static const char *set_work_tree(const char *dir)
 285{
 286        char buffer[PATH_MAX + 1];
 287
 288        if (!getcwd(buffer, sizeof(buffer)))
 289                die ("Could not get the current working directory");
 290        git_work_tree_cfg = xstrdup(buffer);
 291        inside_work_tree = 1;
 292
 293        return NULL;
 294}
 295
 296void setup_work_tree(void)
 297{
 298        const char *work_tree, *git_dir;
 299        static int initialized = 0;
 300
 301        if (initialized)
 302                return;
 303        work_tree = get_git_work_tree();
 304        git_dir = get_git_dir();
 305        if (!is_absolute_path(git_dir))
 306                git_dir = make_absolute_path(git_dir);
 307        if (!work_tree || chdir(work_tree))
 308                die("This operation must be run in a work tree");
 309        set_git_dir(make_relative_path(git_dir, work_tree));
 310        initialized = 1;
 311}
 312
 313static int check_repository_format_gently(int *nongit_ok)
 314{
 315        git_config(check_repository_format_version, NULL);
 316        if (GIT_REPO_VERSION < repository_format_version) {
 317                if (!nongit_ok)
 318                        die ("Expected git repo version <= %d, found %d",
 319                             GIT_REPO_VERSION, repository_format_version);
 320                warning("Expected git repo version <= %d, found %d",
 321                        GIT_REPO_VERSION, repository_format_version);
 322                warning("Please upgrade Git");
 323                *nongit_ok = -1;
 324                return -1;
 325        }
 326        return 0;
 327}
 328
 329/*
 330 * Try to read the location of the git directory from the .git file,
 331 * return path to git directory if found.
 332 */
 333const char *read_gitfile_gently(const char *path)
 334{
 335        char *buf;
 336        struct stat st;
 337        int fd;
 338        size_t len;
 339
 340        if (stat(path, &st))
 341                return NULL;
 342        if (!S_ISREG(st.st_mode))
 343                return NULL;
 344        fd = open(path, O_RDONLY);
 345        if (fd < 0)
 346                die("Error opening %s: %s", path, strerror(errno));
 347        buf = xmalloc(st.st_size + 1);
 348        len = read_in_full(fd, buf, st.st_size);
 349        close(fd);
 350        if (len != st.st_size)
 351                die("Error reading %s", path);
 352        buf[len] = '\0';
 353        if (prefixcmp(buf, "gitdir: "))
 354                die("Invalid gitfile format: %s", path);
 355        while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
 356                len--;
 357        if (len < 9)
 358                die("No path in gitfile: %s", path);
 359        buf[len] = '\0';
 360        if (!is_git_directory(buf + 8))
 361                die("Not a git repository: %s", buf + 8);
 362        path = make_absolute_path(buf + 8);
 363        free(buf);
 364        return path;
 365}
 366
 367/*
 368 * We cannot decide in this function whether we are in the work tree or
 369 * not, since the config can only be read _after_ this function was called.
 370 */
 371const char *setup_git_directory_gently(int *nongit_ok)
 372{
 373        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
 374        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
 375        static char cwd[PATH_MAX+1];
 376        const char *gitdirenv;
 377        const char *gitfile_dir;
 378        int len, offset, ceil_offset;
 379
 380        /*
 381         * Let's assume that we are in a git repository.
 382         * If it turns out later that we are somewhere else, the value will be
 383         * updated accordingly.
 384         */
 385        if (nongit_ok)
 386                *nongit_ok = 0;
 387
 388        /*
 389         * If GIT_DIR is set explicitly, we're not going
 390         * to do any discovery, but we still do repository
 391         * validation.
 392         */
 393        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 394        if (gitdirenv) {
 395                if (PATH_MAX - 40 < strlen(gitdirenv))
 396                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
 397                if (is_git_directory(gitdirenv)) {
 398                        static char buffer[1024 + 1];
 399                        const char *retval;
 400
 401                        if (!work_tree_env) {
 402                                retval = set_work_tree(gitdirenv);
 403                                /* config may override worktree */
 404                                if (check_repository_format_gently(nongit_ok))
 405                                        return NULL;
 406                                return retval;
 407                        }
 408                        if (check_repository_format_gently(nongit_ok))
 409                                return NULL;
 410                        retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
 411                                        get_git_work_tree());
 412                        if (!retval || !*retval)
 413                                return NULL;
 414                        set_git_dir(make_absolute_path(gitdirenv));
 415                        if (chdir(work_tree_env) < 0)
 416                                die ("Could not chdir to %s", work_tree_env);
 417                        strcat(buffer, "/");
 418                        return retval;
 419                }
 420                if (nongit_ok) {
 421                        *nongit_ok = 1;
 422                        return NULL;
 423                }
 424                die("Not a git repository: '%s'", gitdirenv);
 425        }
 426
 427        if (!getcwd(cwd, sizeof(cwd)-1))
 428                die("Unable to read current working directory");
 429
 430        ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
 431        if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
 432                ceil_offset = 1;
 433
 434        /*
 435         * Test in the following order (relative to the cwd):
 436         * - .git (file containing "gitdir: <path>")
 437         * - .git/
 438         * - ./ (bare)
 439         * - ../.git
 440         * - ../.git/
 441         * - ../ (bare)
 442         * - ../../.git/
 443         *   etc.
 444         */
 445        offset = len = strlen(cwd);
 446        for (;;) {
 447                gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
 448                if (gitfile_dir) {
 449                        if (set_git_dir(gitfile_dir))
 450                                die("Repository setup failed");
 451                        break;
 452                }
 453                if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
 454                        break;
 455                if (is_git_directory(".")) {
 456                        inside_git_dir = 1;
 457                        if (!work_tree_env)
 458                                inside_work_tree = 0;
 459                        setenv(GIT_DIR_ENVIRONMENT, ".", 1);
 460                        check_repository_format_gently(nongit_ok);
 461                        return NULL;
 462                }
 463                while (--offset > ceil_offset && cwd[offset] != '/');
 464                if (offset <= ceil_offset) {
 465                        if (nongit_ok) {
 466                                if (chdir(cwd))
 467                                        die("Cannot come back to cwd");
 468                                *nongit_ok = 1;
 469                                return NULL;
 470                        }
 471                        die("Not a git repository");
 472                }
 473                chdir("..");
 474        }
 475
 476        inside_git_dir = 0;
 477        if (!work_tree_env)
 478                inside_work_tree = 1;
 479        git_work_tree_cfg = xstrndup(cwd, offset);
 480        if (check_repository_format_gently(nongit_ok))
 481                return NULL;
 482        if (offset == len)
 483                return NULL;
 484
 485        /* Make "offset" point to past the '/', and add a '/' at the end */
 486        offset++;
 487        cwd[len++] = '/';
 488        cwd[len] = 0;
 489        return cwd + offset;
 490}
 491
 492int git_config_perm(const char *var, const char *value)
 493{
 494        int i;
 495        char *endptr;
 496
 497        if (value == NULL)
 498                return PERM_GROUP;
 499
 500        if (!strcmp(value, "umask"))
 501                return PERM_UMASK;
 502        if (!strcmp(value, "group"))
 503                return PERM_GROUP;
 504        if (!strcmp(value, "all") ||
 505            !strcmp(value, "world") ||
 506            !strcmp(value, "everybody"))
 507                return PERM_EVERYBODY;
 508
 509        /* Parse octal numbers */
 510        i = strtol(value, &endptr, 8);
 511
 512        /* If not an octal number, maybe true/false? */
 513        if (*endptr != 0)
 514                return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
 515
 516        /*
 517         * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
 518         * a chmod value.
 519         */
 520        switch (i) {
 521        case PERM_UMASK:               /* 0 */
 522                return PERM_UMASK;
 523        case OLD_PERM_GROUP:           /* 1 */
 524                return PERM_GROUP;
 525        case OLD_PERM_EVERYBODY:       /* 2 */
 526                return PERM_EVERYBODY;
 527        }
 528
 529        /* A filemode value was given: 0xxx */
 530
 531        if ((i & 0600) != 0600)
 532                die("Problem with core.sharedRepository filemode value "
 533                    "(0%.3o).\nThe owner of files must always have "
 534                    "read and write permissions.", i);
 535
 536        /*
 537         * Mask filemode value. Others can not get write permission.
 538         * x flags for directories are handled separately.
 539         */
 540        return i & 0666;
 541}
 542
 543int check_repository_format_version(const char *var, const char *value, void *cb)
 544{
 545        if (strcmp(var, "core.repositoryformatversion") == 0)
 546                repository_format_version = git_config_int(var, value);
 547        else if (strcmp(var, "core.sharedrepository") == 0)
 548                shared_repository = git_config_perm(var, value);
 549        else if (strcmp(var, "core.bare") == 0) {
 550                is_bare_repository_cfg = git_config_bool(var, value);
 551                if (is_bare_repository_cfg == 1)
 552                        inside_work_tree = -1;
 553        } else if (strcmp(var, "core.worktree") == 0) {
 554                if (!value)
 555                        return config_error_nonbool(var);
 556                free(git_work_tree_cfg);
 557                git_work_tree_cfg = xstrdup(value);
 558                inside_work_tree = -1;
 559        }
 560        return 0;
 561}
 562
 563int check_repository_format(void)
 564{
 565        return check_repository_format_gently(NULL);
 566}
 567
 568const char *setup_git_directory(void)
 569{
 570        const char *retval = setup_git_directory_gently(NULL);
 571
 572        /* If the work tree is not the default one, recompute prefix */
 573        if (inside_work_tree < 0) {
 574                static char buffer[PATH_MAX + 1];
 575                char *rel;
 576                if (retval && chdir(retval))
 577                        die ("Could not jump back into original cwd");
 578                rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
 579                if (rel && *rel && chdir(get_git_work_tree()))
 580                        die ("Could not jump to working directory");
 581                return rel && *rel ? strcat(rel, "/") : NULL;
 582        }
 583
 584        return retval;
 585}