path.con commit submodule: print graph output next to submodule log (0f33a06)
   1/*
   2 * I'm tired of doing "vsnprintf()" etc just to open a
   3 * file, so here's a "return static buffer with printf"
   4 * interface for paths.
   5 *
   6 * It's obviously not thread-safe. Sue me. But it's quite
   7 * useful for doing things like
   8 *
   9 *   f = open(mkpath("%s/%s.git", base, name), O_RDONLY);
  10 *
  11 * which is what it's designed for.
  12 */
  13#include "cache.h"
  14#include "strbuf.h"
  15#include "string-list.h"
  16
  17#ifndef get_st_mode_bits
  18/*
  19 * The replacement lstat(2) we use on Cygwin is incomplete and
  20 * may return wrong permission bits. Most of the time we do not care,
  21 * but the callsites of this wrapper do care.
  22 */
  23int get_st_mode_bits(const char *path, int *mode)
  24{
  25        struct stat st;
  26        if (lstat(path, &st) < 0)
  27                return -1;
  28        *mode = st.st_mode;
  29        return 0;
  30}
  31#endif
  32
  33static char bad_path[] = "/bad-path/";
  34
  35static char *get_pathname(void)
  36{
  37        static char pathname_array[4][PATH_MAX];
  38        static int index;
  39        return pathname_array[3 & ++index];
  40}
  41
  42static char *cleanup_path(char *path)
  43{
  44        /* Clean it up */
  45        if (!memcmp(path, "./", 2)) {
  46                path += 2;
  47                while (*path == '/')
  48                        path++;
  49        }
  50        return path;
  51}
  52
  53char *mksnpath(char *buf, size_t n, const char *fmt, ...)
  54{
  55        va_list args;
  56        unsigned len;
  57
  58        va_start(args, fmt);
  59        len = vsnprintf(buf, n, fmt, args);
  60        va_end(args);
  61        if (len >= n) {
  62                strlcpy(buf, bad_path, n);
  63                return buf;
  64        }
  65        return cleanup_path(buf);
  66}
  67
  68static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
  69{
  70        const char *git_dir = get_git_dir();
  71        size_t len;
  72
  73        len = strlen(git_dir);
  74        if (n < len + 1)
  75                goto bad;
  76        memcpy(buf, git_dir, len);
  77        if (len && !is_dir_sep(git_dir[len-1]))
  78                buf[len++] = '/';
  79        len += vsnprintf(buf + len, n - len, fmt, args);
  80        if (len >= n)
  81                goto bad;
  82        return cleanup_path(buf);
  83bad:
  84        strlcpy(buf, bad_path, n);
  85        return buf;
  86}
  87
  88char *git_snpath(char *buf, size_t n, const char *fmt, ...)
  89{
  90        char *ret;
  91        va_list args;
  92        va_start(args, fmt);
  93        ret = vsnpath(buf, n, fmt, args);
  94        va_end(args);
  95        return ret;
  96}
  97
  98char *git_pathdup(const char *fmt, ...)
  99{
 100        char path[PATH_MAX], *ret;
 101        va_list args;
 102        va_start(args, fmt);
 103        ret = vsnpath(path, sizeof(path), fmt, args);
 104        va_end(args);
 105        return xstrdup(ret);
 106}
 107
 108char *mkpathdup(const char *fmt, ...)
 109{
 110        char *path;
 111        struct strbuf sb = STRBUF_INIT;
 112        va_list args;
 113
 114        va_start(args, fmt);
 115        strbuf_vaddf(&sb, fmt, args);
 116        va_end(args);
 117        path = xstrdup(cleanup_path(sb.buf));
 118
 119        strbuf_release(&sb);
 120        return path;
 121}
 122
 123char *mkpath(const char *fmt, ...)
 124{
 125        va_list args;
 126        unsigned len;
 127        char *pathname = get_pathname();
 128
 129        va_start(args, fmt);
 130        len = vsnprintf(pathname, PATH_MAX, fmt, args);
 131        va_end(args);
 132        if (len >= PATH_MAX)
 133                return bad_path;
 134        return cleanup_path(pathname);
 135}
 136
 137char *git_path(const char *fmt, ...)
 138{
 139        char *pathname = get_pathname();
 140        va_list args;
 141        char *ret;
 142
 143        va_start(args, fmt);
 144        ret = vsnpath(pathname, PATH_MAX, fmt, args);
 145        va_end(args);
 146        return ret;
 147}
 148
 149void home_config_paths(char **global, char **xdg, char *file)
 150{
 151        char *xdg_home = getenv("XDG_CONFIG_HOME");
 152        char *home = getenv("HOME");
 153        char *to_free = NULL;
 154
 155        if (!home) {
 156                if (global)
 157                        *global = NULL;
 158        } else {
 159                if (!xdg_home) {
 160                        to_free = mkpathdup("%s/.config", home);
 161                        xdg_home = to_free;
 162                }
 163                if (global)
 164                        *global = mkpathdup("%s/.gitconfig", home);
 165        }
 166
 167        if (!xdg_home)
 168                *xdg = NULL;
 169        else
 170                *xdg = mkpathdup("%s/git/%s", xdg_home, file);
 171
 172        free(to_free);
 173}
 174
 175char *git_path_submodule(const char *path, const char *fmt, ...)
 176{
 177        char *pathname = get_pathname();
 178        struct strbuf buf = STRBUF_INIT;
 179        const char *git_dir;
 180        va_list args;
 181        unsigned len;
 182
 183        len = strlen(path);
 184        if (len > PATH_MAX-100)
 185                return bad_path;
 186
 187        strbuf_addstr(&buf, path);
 188        if (len && path[len-1] != '/')
 189                strbuf_addch(&buf, '/');
 190        strbuf_addstr(&buf, ".git");
 191
 192        git_dir = read_gitfile(buf.buf);
 193        if (git_dir) {
 194                strbuf_reset(&buf);
 195                strbuf_addstr(&buf, git_dir);
 196        }
 197        strbuf_addch(&buf, '/');
 198
 199        if (buf.len >= PATH_MAX)
 200                return bad_path;
 201        memcpy(pathname, buf.buf, buf.len + 1);
 202
 203        strbuf_release(&buf);
 204        len = strlen(pathname);
 205
 206        va_start(args, fmt);
 207        len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
 208        va_end(args);
 209        if (len >= PATH_MAX)
 210                return bad_path;
 211        return cleanup_path(pathname);
 212}
 213
 214int validate_headref(const char *path)
 215{
 216        struct stat st;
 217        char *buf, buffer[256];
 218        unsigned char sha1[20];
 219        int fd;
 220        ssize_t len;
 221
 222        if (lstat(path, &st) < 0)
 223                return -1;
 224
 225        /* Make sure it is a "refs/.." symlink */
 226        if (S_ISLNK(st.st_mode)) {
 227                len = readlink(path, buffer, sizeof(buffer)-1);
 228                if (len >= 5 && !memcmp("refs/", buffer, 5))
 229                        return 0;
 230                return -1;
 231        }
 232
 233        /*
 234         * Anything else, just open it and try to see if it is a symbolic ref.
 235         */
 236        fd = open(path, O_RDONLY);
 237        if (fd < 0)
 238                return -1;
 239        len = read_in_full(fd, buffer, sizeof(buffer)-1);
 240        close(fd);
 241
 242        /*
 243         * Is it a symbolic ref?
 244         */
 245        if (len < 4)
 246                return -1;
 247        if (!memcmp("ref:", buffer, 4)) {
 248                buf = buffer + 4;
 249                len -= 4;
 250                while (len && isspace(*buf))
 251                        buf++, len--;
 252                if (len >= 5 && !memcmp("refs/", buf, 5))
 253                        return 0;
 254        }
 255
 256        /*
 257         * Is this a detached HEAD?
 258         */
 259        if (!get_sha1_hex(buffer, sha1))
 260                return 0;
 261
 262        return -1;
 263}
 264
 265static struct passwd *getpw_str(const char *username, size_t len)
 266{
 267        struct passwd *pw;
 268        char *username_z = xmalloc(len + 1);
 269        memcpy(username_z, username, len);
 270        username_z[len] = '\0';
 271        pw = getpwnam(username_z);
 272        free(username_z);
 273        return pw;
 274}
 275
 276/*
 277 * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
 278 * then it is a newly allocated string. Returns NULL on getpw failure or
 279 * if path is NULL.
 280 */
 281char *expand_user_path(const char *path)
 282{
 283        struct strbuf user_path = STRBUF_INIT;
 284        const char *first_slash = strchrnul(path, '/');
 285        const char *to_copy = path;
 286
 287        if (path == NULL)
 288                goto return_null;
 289        if (path[0] == '~') {
 290                const char *username = path + 1;
 291                size_t username_len = first_slash - username;
 292                if (username_len == 0) {
 293                        const char *home = getenv("HOME");
 294                        if (!home)
 295                                goto return_null;
 296                        strbuf_add(&user_path, home, strlen(home));
 297                } else {
 298                        struct passwd *pw = getpw_str(username, username_len);
 299                        if (!pw)
 300                                goto return_null;
 301                        strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
 302                }
 303                to_copy = first_slash;
 304        }
 305        strbuf_add(&user_path, to_copy, strlen(to_copy));
 306        return strbuf_detach(&user_path, NULL);
 307return_null:
 308        strbuf_release(&user_path);
 309        return NULL;
 310}
 311
 312/*
 313 * First, one directory to try is determined by the following algorithm.
 314 *
 315 * (0) If "strict" is given, the path is used as given and no DWIM is
 316 *     done. Otherwise:
 317 * (1) "~/path" to mean path under the running user's home directory;
 318 * (2) "~user/path" to mean path under named user's home directory;
 319 * (3) "relative/path" to mean cwd relative directory; or
 320 * (4) "/absolute/path" to mean absolute directory.
 321 *
 322 * Unless "strict" is given, we try access() for existence of "%s.git/.git",
 323 * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
 324 * what we try.
 325 *
 326 * Second, we try chdir() to that.  Upon failure, we return NULL.
 327 *
 328 * Then, we try if the current directory is a valid git repository.
 329 * Upon failure, we return NULL.
 330 *
 331 * If all goes well, we return the directory we used to chdir() (but
 332 * before ~user is expanded), avoiding getcwd() resolving symbolic
 333 * links.  User relative paths are also returned as they are given,
 334 * except DWIM suffixing.
 335 */
 336const char *enter_repo(const char *path, int strict)
 337{
 338        static char used_path[PATH_MAX];
 339        static char validated_path[PATH_MAX];
 340
 341        if (!path)
 342                return NULL;
 343
 344        if (!strict) {
 345                static const char *suffix[] = {
 346                        "/.git", "", ".git/.git", ".git", NULL,
 347                };
 348                const char *gitfile;
 349                int len = strlen(path);
 350                int i;
 351                while ((1 < len) && (path[len-1] == '/'))
 352                        len--;
 353
 354                if (PATH_MAX <= len)
 355                        return NULL;
 356                strncpy(used_path, path, len); used_path[len] = 0 ;
 357                strcpy(validated_path, used_path);
 358
 359                if (used_path[0] == '~') {
 360                        char *newpath = expand_user_path(used_path);
 361                        if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
 362                                free(newpath);
 363                                return NULL;
 364                        }
 365                        /*
 366                         * Copy back into the static buffer. A pity
 367                         * since newpath was not bounded, but other
 368                         * branches of the if are limited by PATH_MAX
 369                         * anyway.
 370                         */
 371                        strcpy(used_path, newpath); free(newpath);
 372                }
 373                else if (PATH_MAX - 10 < len)
 374                        return NULL;
 375                len = strlen(used_path);
 376                for (i = 0; suffix[i]; i++) {
 377                        struct stat st;
 378                        strcpy(used_path + len, suffix[i]);
 379                        if (!stat(used_path, &st) &&
 380                            (S_ISREG(st.st_mode) ||
 381                            (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {
 382                                strcat(validated_path, suffix[i]);
 383                                break;
 384                        }
 385                }
 386                if (!suffix[i])
 387                        return NULL;
 388                gitfile = read_gitfile(used_path) ;
 389                if (gitfile)
 390                        strcpy(used_path, gitfile);
 391                if (chdir(used_path))
 392                        return NULL;
 393                path = validated_path;
 394        }
 395        else if (chdir(path))
 396                return NULL;
 397
 398        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
 399            validate_headref("HEAD") == 0) {
 400                set_git_dir(".");
 401                check_repository_format();
 402                return path;
 403        }
 404
 405        return NULL;
 406}
 407
 408int set_shared_perm(const char *path, int mode)
 409{
 410        int tweak, shared, orig_mode;
 411
 412        if (!shared_repository) {
 413                if (mode)
 414                        return chmod(path, mode & ~S_IFMT);
 415                return 0;
 416        }
 417        if (!mode) {
 418                if (get_st_mode_bits(path, &mode) < 0)
 419                        return -1;
 420                orig_mode = mode;
 421        } else
 422                orig_mode = 0;
 423        if (shared_repository < 0)
 424                shared = -shared_repository;
 425        else
 426                shared = shared_repository;
 427        tweak = shared;
 428
 429        if (!(mode & S_IWUSR))
 430                tweak &= ~0222;
 431        if (mode & S_IXUSR)
 432                /* Copy read bits to execute bits */
 433                tweak |= (tweak & 0444) >> 2;
 434        if (shared_repository < 0)
 435                mode = (mode & ~0777) | tweak;
 436        else
 437                mode |= tweak;
 438
 439        if (S_ISDIR(mode)) {
 440                /* Copy read bits to execute bits */
 441                mode |= (shared & 0444) >> 2;
 442                mode |= FORCE_DIR_SET_GID;
 443        }
 444
 445        if (((shared_repository < 0
 446              ? (orig_mode & (FORCE_DIR_SET_GID | 0777))
 447              : (orig_mode & mode)) != mode) &&
 448            chmod(path, (mode & ~S_IFMT)) < 0)
 449                return -2;
 450        return 0;
 451}
 452
 453const char *relative_path(const char *abs, const char *base)
 454{
 455        static char buf[PATH_MAX + 1];
 456        int i = 0, j = 0;
 457
 458        if (!base || !base[0])
 459                return abs;
 460        while (base[i]) {
 461                if (is_dir_sep(base[i])) {
 462                        if (!is_dir_sep(abs[j]))
 463                                return abs;
 464                        while (is_dir_sep(base[i]))
 465                                i++;
 466                        while (is_dir_sep(abs[j]))
 467                                j++;
 468                        continue;
 469                } else if (abs[j] != base[i]) {
 470                        return abs;
 471                }
 472                i++;
 473                j++;
 474        }
 475        if (
 476            /* "/foo" is a prefix of "/foo" */
 477            abs[j] &&
 478            /* "/foo" is not a prefix of "/foobar" */
 479            !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
 480           )
 481                return abs;
 482        while (is_dir_sep(abs[j]))
 483                j++;
 484        if (!abs[j])
 485                strcpy(buf, ".");
 486        else
 487                strcpy(buf, abs + j);
 488        return buf;
 489}
 490
 491/*
 492 * It is okay if dst == src, but they should not overlap otherwise.
 493 *
 494 * Performs the following normalizations on src, storing the result in dst:
 495 * - Ensures that components are separated by '/' (Windows only)
 496 * - Squashes sequences of '/'.
 497 * - Removes "." components.
 498 * - Removes ".." components, and the components the precede them.
 499 * Returns failure (non-zero) if a ".." component appears as first path
 500 * component anytime during the normalization. Otherwise, returns success (0).
 501 *
 502 * Note that this function is purely textual.  It does not follow symlinks,
 503 * verify the existence of the path, or make any system calls.
 504 */
 505int normalize_path_copy(char *dst, const char *src)
 506{
 507        char *dst0;
 508
 509        if (has_dos_drive_prefix(src)) {
 510                *dst++ = *src++;
 511                *dst++ = *src++;
 512        }
 513        dst0 = dst;
 514
 515        if (is_dir_sep(*src)) {
 516                *dst++ = '/';
 517                while (is_dir_sep(*src))
 518                        src++;
 519        }
 520
 521        for (;;) {
 522                char c = *src;
 523
 524                /*
 525                 * A path component that begins with . could be
 526                 * special:
 527                 * (1) "." and ends   -- ignore and terminate.
 528                 * (2) "./"           -- ignore them, eat slash and continue.
 529                 * (3) ".." and ends  -- strip one and terminate.
 530                 * (4) "../"          -- strip one, eat slash and continue.
 531                 */
 532                if (c == '.') {
 533                        if (!src[1]) {
 534                                /* (1) */
 535                                src++;
 536                        } else if (is_dir_sep(src[1])) {
 537                                /* (2) */
 538                                src += 2;
 539                                while (is_dir_sep(*src))
 540                                        src++;
 541                                continue;
 542                        } else if (src[1] == '.') {
 543                                if (!src[2]) {
 544                                        /* (3) */
 545                                        src += 2;
 546                                        goto up_one;
 547                                } else if (is_dir_sep(src[2])) {
 548                                        /* (4) */
 549                                        src += 3;
 550                                        while (is_dir_sep(*src))
 551                                                src++;
 552                                        goto up_one;
 553                                }
 554                        }
 555                }
 556
 557                /* copy up to the next '/', and eat all '/' */
 558                while ((c = *src++) != '\0' && !is_dir_sep(c))
 559                        *dst++ = c;
 560                if (is_dir_sep(c)) {
 561                        *dst++ = '/';
 562                        while (is_dir_sep(c))
 563                                c = *src++;
 564                        src--;
 565                } else if (!c)
 566                        break;
 567                continue;
 568
 569        up_one:
 570                /*
 571                 * dst0..dst is prefix portion, and dst[-1] is '/';
 572                 * go up one level.
 573                 */
 574                dst--;  /* go to trailing '/' */
 575                if (dst <= dst0)
 576                        return -1;
 577                /* Windows: dst[-1] cannot be backslash anymore */
 578                while (dst0 < dst && dst[-1] != '/')
 579                        dst--;
 580        }
 581        *dst = '\0';
 582        return 0;
 583}
 584
 585/*
 586 * path = Canonical absolute path
 587 * prefixes = string_list containing normalized, absolute paths without
 588 * trailing slashes (except for the root directory, which is denoted by "/").
 589 *
 590 * Determines, for each path in prefixes, whether the "prefix"
 591 * is an ancestor directory of path.  Returns the length of the longest
 592 * ancestor directory, excluding any trailing slashes, or -1 if no prefix
 593 * is an ancestor.  (Note that this means 0 is returned if prefixes is
 594 * ["/"].) "/foo" is not considered an ancestor of "/foobar".  Directories
 595 * are not considered to be their own ancestors.  path must be in a
 596 * canonical form: empty components, or "." or ".." components are not
 597 * allowed.
 598 */
 599int longest_ancestor_length(const char *path, struct string_list *prefixes)
 600{
 601        int i, max_len = -1;
 602
 603        if (!strcmp(path, "/"))
 604                return -1;
 605
 606        for (i = 0; i < prefixes->nr; i++) {
 607                const char *ceil = prefixes->items[i].string;
 608                int len = strlen(ceil);
 609
 610                if (len == 1 && ceil[0] == '/')
 611                        len = 0; /* root matches anything, with length 0 */
 612                else if (!strncmp(path, ceil, len) && path[len] == '/')
 613                        ; /* match of length len */
 614                else
 615                        continue; /* no match */
 616
 617                if (len > max_len)
 618                        max_len = len;
 619        }
 620
 621        return max_len;
 622}
 623
 624/* strip arbitrary amount of directory separators at end of path */
 625static inline int chomp_trailing_dir_sep(const char *path, int len)
 626{
 627        while (len && is_dir_sep(path[len - 1]))
 628                len--;
 629        return len;
 630}
 631
 632/*
 633 * If path ends with suffix (complete path components), returns the
 634 * part before suffix (sans trailing directory separators).
 635 * Otherwise returns NULL.
 636 */
 637char *strip_path_suffix(const char *path, const char *suffix)
 638{
 639        int path_len = strlen(path), suffix_len = strlen(suffix);
 640
 641        while (suffix_len) {
 642                if (!path_len)
 643                        return NULL;
 644
 645                if (is_dir_sep(path[path_len - 1])) {
 646                        if (!is_dir_sep(suffix[suffix_len - 1]))
 647                                return NULL;
 648                        path_len = chomp_trailing_dir_sep(path, path_len);
 649                        suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
 650                }
 651                else if (path[--path_len] != suffix[--suffix_len])
 652                        return NULL;
 653        }
 654
 655        if (path_len && !is_dir_sep(path[path_len - 1]))
 656                return NULL;
 657        return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
 658}
 659
 660int daemon_avoid_alias(const char *p)
 661{
 662        int sl, ndot;
 663
 664        /*
 665         * This resurrects the belts and suspenders paranoia check by HPA
 666         * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
 667         * does not do getcwd() based path canonicalization.
 668         *
 669         * sl becomes true immediately after seeing '/' and continues to
 670         * be true as long as dots continue after that without intervening
 671         * non-dot character.
 672         */
 673        if (!p || (*p != '/' && *p != '~'))
 674                return -1;
 675        sl = 1; ndot = 0;
 676        p++;
 677
 678        while (1) {
 679                char ch = *p++;
 680                if (sl) {
 681                        if (ch == '.')
 682                                ndot++;
 683                        else if (ch == '/') {
 684                                if (ndot < 3)
 685                                        /* reject //, /./ and /../ */
 686                                        return -1;
 687                                ndot = 0;
 688                        }
 689                        else if (ch == 0) {
 690                                if (0 < ndot && ndot < 3)
 691                                        /* reject /.$ and /..$ */
 692                                        return -1;
 693                                return 0;
 694                        }
 695                        else
 696                                sl = ndot = 0;
 697                }
 698                else if (ch == 0)
 699                        return 0;
 700                else if (ch == '/') {
 701                        sl = 1;
 702                        ndot = 0;
 703                }
 704        }
 705}
 706
 707int offset_1st_component(const char *path)
 708{
 709        if (has_dos_drive_prefix(path))
 710                return 2 + is_dir_sep(path[2]);
 711        return is_dir_sep(path[0]);
 712}