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