85ab28a0f1f0ff6276c587ec6d5be7716d8e0c83
   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
  15static char bad_path[] = "/bad-path/";
  16
  17static char *get_pathname(void)
  18{
  19        static char pathname_array[4][PATH_MAX];
  20        static int index;
  21        return pathname_array[3 & ++index];
  22}
  23
  24static char *cleanup_path(char *path)
  25{
  26        /* Clean it up */
  27        if (!memcmp(path, "./", 2)) {
  28                path += 2;
  29                while (*path == '/')
  30                        path++;
  31        }
  32        return path;
  33}
  34
  35char *mksnpath(char *buf, size_t n, const char *fmt, ...)
  36{
  37        va_list args;
  38        unsigned len;
  39
  40        va_start(args, fmt);
  41        len = vsnprintf(buf, n, fmt, args);
  42        va_end(args);
  43        if (len >= n) {
  44                snprintf(buf, n, bad_path);
  45                return buf;
  46        }
  47        return cleanup_path(buf);
  48}
  49
  50char *git_snpath(char *buf, size_t n, const char *fmt, ...)
  51{
  52        const char *git_dir = get_git_dir();
  53        va_list args;
  54        size_t len;
  55
  56        len = strlen(git_dir);
  57        if (n < len + 1)
  58                goto bad;
  59        memcpy(buf, git_dir, len);
  60        if (len && !is_dir_sep(git_dir[len-1]))
  61                buf[len++] = '/';
  62        va_start(args, fmt);
  63        len += vsnprintf(buf + len, n - len, fmt, args);
  64        va_end(args);
  65        if (len >= n)
  66                goto bad;
  67        return cleanup_path(buf);
  68bad:
  69        snprintf(buf, n, bad_path);
  70        return buf;
  71}
  72
  73char *mkpath(const char *fmt, ...)
  74{
  75        va_list args;
  76        unsigned len;
  77        char *pathname = get_pathname();
  78
  79        va_start(args, fmt);
  80        len = vsnprintf(pathname, PATH_MAX, fmt, args);
  81        va_end(args);
  82        if (len >= PATH_MAX)
  83                return bad_path;
  84        return cleanup_path(pathname);
  85}
  86
  87char *git_path(const char *fmt, ...)
  88{
  89        const char *git_dir = get_git_dir();
  90        char *pathname = get_pathname();
  91        va_list args;
  92        unsigned len;
  93
  94        len = strlen(git_dir);
  95        if (len > PATH_MAX-100)
  96                return bad_path;
  97        memcpy(pathname, git_dir, len);
  98        if (len && git_dir[len-1] != '/')
  99                pathname[len++] = '/';
 100        va_start(args, fmt);
 101        len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
 102        va_end(args);
 103        if (len >= PATH_MAX)
 104                return bad_path;
 105        return cleanup_path(pathname);
 106}
 107
 108
 109/* git_mkstemp() - create tmp file honoring TMPDIR variable */
 110int git_mkstemp(char *path, size_t len, const char *template)
 111{
 112        const char *tmp;
 113        size_t n;
 114
 115        tmp = getenv("TMPDIR");
 116        if (!tmp)
 117                tmp = "/tmp";
 118        n = snprintf(path, len, "%s/%s", tmp, template);
 119        if (len <= n) {
 120                errno = ENAMETOOLONG;
 121                return -1;
 122        }
 123        return mkstemp(path);
 124}
 125
 126
 127int validate_headref(const char *path)
 128{
 129        struct stat st;
 130        char *buf, buffer[256];
 131        unsigned char sha1[20];
 132        int fd;
 133        ssize_t len;
 134
 135        if (lstat(path, &st) < 0)
 136                return -1;
 137
 138        /* Make sure it is a "refs/.." symlink */
 139        if (S_ISLNK(st.st_mode)) {
 140                len = readlink(path, buffer, sizeof(buffer)-1);
 141                if (len >= 5 && !memcmp("refs/", buffer, 5))
 142                        return 0;
 143                return -1;
 144        }
 145
 146        /*
 147         * Anything else, just open it and try to see if it is a symbolic ref.
 148         */
 149        fd = open(path, O_RDONLY);
 150        if (fd < 0)
 151                return -1;
 152        len = read_in_full(fd, buffer, sizeof(buffer)-1);
 153        close(fd);
 154
 155        /*
 156         * Is it a symbolic ref?
 157         */
 158        if (len < 4)
 159                return -1;
 160        if (!memcmp("ref:", buffer, 4)) {
 161                buf = buffer + 4;
 162                len -= 4;
 163                while (len && isspace(*buf))
 164                        buf++, len--;
 165                if (len >= 5 && !memcmp("refs/", buf, 5))
 166                        return 0;
 167        }
 168
 169        /*
 170         * Is this a detached HEAD?
 171         */
 172        if (!get_sha1_hex(buffer, sha1))
 173                return 0;
 174
 175        return -1;
 176}
 177
 178static char *user_path(char *buf, char *path, int sz)
 179{
 180        struct passwd *pw;
 181        char *slash;
 182        int len, baselen;
 183
 184        if (!path || path[0] != '~')
 185                return NULL;
 186        path++;
 187        slash = strchr(path, '/');
 188        if (path[0] == '/' || !path[0]) {
 189                pw = getpwuid(getuid());
 190        }
 191        else {
 192                if (slash) {
 193                        *slash = 0;
 194                        pw = getpwnam(path);
 195                        *slash = '/';
 196                }
 197                else
 198                        pw = getpwnam(path);
 199        }
 200        if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
 201                return NULL;
 202        baselen = strlen(pw->pw_dir);
 203        memcpy(buf, pw->pw_dir, baselen);
 204        while ((1 < baselen) && (buf[baselen-1] == '/')) {
 205                buf[baselen-1] = 0;
 206                baselen--;
 207        }
 208        if (slash && slash[1]) {
 209                len = strlen(slash);
 210                if (sz <= baselen + len)
 211                        return NULL;
 212                memcpy(buf + baselen, slash, len + 1);
 213        }
 214        return buf;
 215}
 216
 217/*
 218 * First, one directory to try is determined by the following algorithm.
 219 *
 220 * (0) If "strict" is given, the path is used as given and no DWIM is
 221 *     done. Otherwise:
 222 * (1) "~/path" to mean path under the running user's home directory;
 223 * (2) "~user/path" to mean path under named user's home directory;
 224 * (3) "relative/path" to mean cwd relative directory; or
 225 * (4) "/absolute/path" to mean absolute directory.
 226 *
 227 * Unless "strict" is given, we try access() for existence of "%s.git/.git",
 228 * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
 229 * what we try.
 230 *
 231 * Second, we try chdir() to that.  Upon failure, we return NULL.
 232 *
 233 * Then, we try if the current directory is a valid git repository.
 234 * Upon failure, we return NULL.
 235 *
 236 * If all goes well, we return the directory we used to chdir() (but
 237 * before ~user is expanded), avoiding getcwd() resolving symbolic
 238 * links.  User relative paths are also returned as they are given,
 239 * except DWIM suffixing.
 240 */
 241char *enter_repo(char *path, int strict)
 242{
 243        static char used_path[PATH_MAX];
 244        static char validated_path[PATH_MAX];
 245
 246        if (!path)
 247                return NULL;
 248
 249        if (!strict) {
 250                static const char *suffix[] = {
 251                        ".git/.git", "/.git", ".git", "", NULL,
 252                };
 253                int len = strlen(path);
 254                int i;
 255                while ((1 < len) && (path[len-1] == '/')) {
 256                        path[len-1] = 0;
 257                        len--;
 258                }
 259                if (PATH_MAX <= len)
 260                        return NULL;
 261                if (path[0] == '~') {
 262                        if (!user_path(used_path, path, PATH_MAX))
 263                                return NULL;
 264                        strcpy(validated_path, path);
 265                        path = used_path;
 266                }
 267                else if (PATH_MAX - 10 < len)
 268                        return NULL;
 269                else {
 270                        path = strcpy(used_path, path);
 271                        strcpy(validated_path, path);
 272                }
 273                len = strlen(path);
 274                for (i = 0; suffix[i]; i++) {
 275                        strcpy(path + len, suffix[i]);
 276                        if (!access(path, F_OK)) {
 277                                strcat(validated_path, suffix[i]);
 278                                break;
 279                        }
 280                }
 281                if (!suffix[i] || chdir(path))
 282                        return NULL;
 283                path = validated_path;
 284        }
 285        else if (chdir(path))
 286                return NULL;
 287
 288        if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
 289            validate_headref("HEAD") == 0) {
 290                setenv(GIT_DIR_ENVIRONMENT, ".", 1);
 291                check_repository_format();
 292                return path;
 293        }
 294
 295        return NULL;
 296}
 297
 298int adjust_shared_perm(const char *path)
 299{
 300        struct stat st;
 301        int mode;
 302
 303        if (!shared_repository)
 304                return 0;
 305        if (lstat(path, &st) < 0)
 306                return -1;
 307        mode = st.st_mode;
 308
 309        if (shared_repository) {
 310                int tweak = shared_repository;
 311                if (!(mode & S_IWUSR))
 312                        tweak &= ~0222;
 313                mode |= tweak;
 314        } else {
 315                /* Preserve old PERM_UMASK behaviour */
 316                if (mode & S_IWUSR)
 317                        mode |= S_IWGRP;
 318        }
 319
 320        if (S_ISDIR(mode)) {
 321                mode |= FORCE_DIR_SET_GID;
 322
 323                /* Copy read bits to execute bits */
 324                mode |= (shared_repository & 0444) >> 2;
 325        }
 326
 327        if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
 328                return -2;
 329        return 0;
 330}
 331
 332const char *make_relative_path(const char *abs, const char *base)
 333{
 334        static char buf[PATH_MAX + 1];
 335        int baselen;
 336        if (!base)
 337                return abs;
 338        baselen = strlen(base);
 339        if (prefixcmp(abs, base))
 340                return abs;
 341        if (abs[baselen] == '/')
 342                baselen++;
 343        else if (base[baselen - 1] != '/')
 344                return abs;
 345        strcpy(buf, abs + baselen);
 346        return buf;
 347}
 348
 349/*
 350 * path = absolute path
 351 * buf = buffer of at least max(2, strlen(path)+1) bytes
 352 * It is okay if buf == path, but they should not overlap otherwise.
 353 *
 354 * Performs the following normalizations on path, storing the result in buf:
 355 * - Removes trailing slashes.
 356 * - Removes empty components.
 357 * - Removes "." components.
 358 * - Removes ".." components, and the components the precede them.
 359 * "" and paths that contain only slashes are normalized to "/".
 360 * Returns the length of the output.
 361 *
 362 * Note that this function is purely textual.  It does not follow symlinks,
 363 * verify the existence of the path, or make any system calls.
 364 */
 365int normalize_absolute_path(char *buf, const char *path)
 366{
 367        const char *comp_start = path, *comp_end = path;
 368        char *dst = buf;
 369        int comp_len;
 370        assert(buf);
 371        assert(path);
 372
 373        while (*comp_start) {
 374                assert(*comp_start == '/');
 375                while (*++comp_end && *comp_end != '/')
 376                        ; /* nothing */
 377                comp_len = comp_end - comp_start;
 378
 379                if (!strncmp("/",  comp_start, comp_len) ||
 380                    !strncmp("/.", comp_start, comp_len))
 381                        goto next;
 382
 383                if (!strncmp("/..", comp_start, comp_len)) {
 384                        while (dst > buf && *--dst != '/')
 385                                ; /* nothing */
 386                        goto next;
 387                }
 388
 389                memcpy(dst, comp_start, comp_len);
 390                dst += comp_len;
 391        next:
 392                comp_start = comp_end;
 393        }
 394
 395        if (dst == buf)
 396                *dst++ = '/';
 397
 398        *dst = '\0';
 399        return dst - buf;
 400}
 401
 402/*
 403 * path = Canonical absolute path
 404 * prefix_list = Colon-separated list of absolute paths
 405 *
 406 * Determines, for each path in prefix_list, whether the "prefix" really
 407 * is an ancestor directory of path.  Returns the length of the longest
 408 * ancestor directory, excluding any trailing slashes, or -1 if no prefix
 409 * is an ancestor.  (Note that this means 0 is returned if prefix_list is
 410 * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
 411 * are not considered to be their own ancestors.  path must be in a
 412 * canonical form: empty components, or "." or ".." components are not
 413 * allowed.  prefix_list may be null, which is like "".
 414 */
 415int longest_ancestor_length(const char *path, const char *prefix_list)
 416{
 417        char buf[PATH_MAX+1];
 418        const char *ceil, *colon;
 419        int len, max_len = -1;
 420
 421        if (prefix_list == NULL || !strcmp(path, "/"))
 422                return -1;
 423
 424        for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
 425                for (colon = ceil; *colon && *colon != ':'; colon++);
 426                len = colon - ceil;
 427                if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
 428                        continue;
 429                strlcpy(buf, ceil, len+1);
 430                len = normalize_absolute_path(buf, buf);
 431                /* Strip "trailing slashes" from "/". */
 432                if (len == 1)
 433                        len = 0;
 434
 435                if (!strncmp(path, buf, len) &&
 436                    path[len] == '/' &&
 437                    len > max_len) {
 438                        max_len = len;
 439                }
 440        }
 441
 442        return max_len;
 443}