const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; char *sanitized = xmalloc(len + strlen(path) + 1); if (is_absolute_path(orig)) strcpy(sanitized, path); else { if (len) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); } if (normalize_path_copy(sanitized, sanitized)) goto error_out; if (is_absolute_path(orig)) { size_t len, total; const char *work_tree = get_git_work_tree(); if (!work_tree) goto error_out; len = strlen(work_tree); total = strlen(sanitized) + 1; if (strncmp(sanitized, work_tree, len) || (sanitized[len] != '\0' && sanitized[len] != '/')) { error_out: die("'%s' is outside repository", orig); } if (sanitized[len] == '/') len++; memmove(sanitized, sanitized + len, total - len); } return sanitized; }
int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { char *buf = xmalloc(PATH_MAX + 1); int rv = normalize_path_copy(buf, argv[2]); if (rv) buf = "++failed++"; puts(buf); return 0; } if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) { while (argc > 2) { puts(make_absolute_path(argv[2])); argc--; argv++; } return 0; } if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { int len = longest_ancestor_length(argv[2], argv[3]); printf("%d\n", len); return 0; } fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; }
static void copy_alternates(struct strbuf *src, struct strbuf *dst, const char *src_repo) { /* * Read from the source objects/info/alternates file * and copy the entries to corresponding file in the * destination repository with add_to_alternates_file(). * Both src and dst have "$path/objects/info/alternates". * * Instead of copying bit-for-bit from the original, * we need to append to existing one so that the already * created entry via "clone -s" is not lost, and also * to turn entries with paths relative to the original * absolute, so that they can be used in the new repository. */ FILE *in = fopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in, '\n') != EOF) { char *abs_path, abs_buf[PATH_MAX]; if (!line.len || line.buf[0] == '#') continue; if (is_absolute_path(line.buf)) { add_to_alternates_file(line.buf); continue; } abs_path = mkpath("%s/objects/%s", src_repo, line.buf); normalize_path_copy(abs_buf, abs_path); add_to_alternates_file(abs_buf); } strbuf_release(&line); fclose(in); }
/* * path = Canonical absolute path * prefix_list = Colon-separated list of absolute paths * * Determines, for each path in prefix_list, whether the "prefix" really * is an ancestor directory of path. Returns the length of the longest * ancestor directory, excluding any trailing slashes, or -1 if no prefix * is an ancestor. (Note that this means 0 is returned if prefix_list is * "/".) "/foo" is not considered an ancestor of "/foobar". Directories * are not considered to be their own ancestors. path must be in a * canonical form: empty components, or "." or ".." components are not * allowed. prefix_list may be null, which is like "". */ int longest_ancestor_length(const char *path, const char *prefix_list) { char buf[PATH_MAX+1]; const char *ceil, *colon; int len, max_len = -1; if (prefix_list == NULL || !strcmp(path, "/")) return -1; for (colon = ceil = prefix_list; *colon; ceil = colon+1) { for (colon = ceil; *colon && *colon != PATH_SEP; colon++); len = colon - ceil; if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) continue; strlcpy(buf, ceil, len+1); if (normalize_path_copy(buf, buf) < 0) continue; len = strlen(buf); if (len > 0 && buf[len-1] == '/') buf[--len] = '\0'; if (!strncmp(path, buf, len) && path[len] == '/' && len > max_len) { max_len = len; } } return max_len; }
int refname_is_safe(const char *refname) { const char *rest; if (skip_prefix(refname, "refs/", &rest)) { char *buf; int result; size_t restlen = strlen(rest); /* rest must not be empty, or start or end with "/" */ if (!restlen || *rest == '/' || rest[restlen - 1] == '/') return 0; /* * Does the refname try to escape refs/? * For example: refs/foo/../bar is safe but refs/foo/../../bar * is not. */ buf = xmallocz(restlen); result = !normalize_path_copy(buf, rest) && !strcmp(buf, rest); free(buf); return result; } do { if (!isupper(*refname) && *refname != '_') return 0; refname++; } while (*refname); return 1; }
/* * A "string_list_each_func_t" function that normalizes an entry from * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason, * die with an explanation. */ static int normalize_ceiling_entry(struct string_list_item *item, void *unused) { char *ceil = item->string; if (!*ceil) die("Empty path is not supported"); if (!is_absolute_path(ceil)) die("Path \"%s\" is not absolute", ceil); if (normalize_path_copy(ceil, ceil) < 0) die("Path \"%s\" could not be normalized", ceil); return 1; }
/* * A "string_list_each_func_t" function that normalizes an entry from * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason, * die with an explanation. */ static int normalize_ceiling_entry(struct string_list_item *item, void *unused) { const char *ceil = item->string; int len = strlen(ceil); char buf[PATH_MAX+1]; if (len == 0) die("Empty path is not supported"); if (len > PATH_MAX) die("Path \"%s\" is too long", ceil); if (!is_absolute_path(ceil)) die("Path \"%s\" is not absolute", ceil); if (normalize_path_copy(buf, ceil) < 0) die("Path \"%s\" could not be normalized", ceil); len = strlen(buf); free(item->string); item->string = xstrdup(buf); return 1; }
int refname_is_safe(const char *refname) { if (starts_with(refname, "refs/")) { char *buf; int result; buf = xmallocz(strlen(refname)); /* * Does the refname try to escape refs/? * For example: refs/foo/../bar is safe but refs/foo/../../bar * is not. */ result = !normalize_path_copy(buf, refname + strlen("refs/")); free(buf); return result; } while (*refname) { if (!isupper(*refname) && *refname != '_') return 0; refname++; } return 1; }
static char *prefix_path_gently(const char *prefix, int len, const char *path) { const char *orig = path; char *sanitized; if (is_absolute_path(orig)) { const char *temp = real_path(path); sanitized = xmalloc(len + strlen(temp) + 1); strcpy(sanitized, temp); } else { sanitized = xmalloc(len + strlen(path) + 1); if (len) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); } if (normalize_path_copy(sanitized, sanitized)) goto error_out; if (is_absolute_path(orig)) { size_t root_len, len, total; const char *work_tree = get_git_work_tree(); if (!work_tree) goto error_out; len = strlen(work_tree); root_len = offset_1st_component(work_tree); total = strlen(sanitized) + 1; if (strncmp(sanitized, work_tree, len) || (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) { error_out: free(sanitized); return NULL; } if (sanitized[len] == '/') len++; memmove(sanitized, sanitized + len, total - len); } return sanitized; }
int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { char *buf = xmalloc(PATH_MAX + 1); int rv = normalize_path_copy(buf, argv[2]); if (rv) buf = "++failed++"; puts(buf); return 0; } if (argc >= 2 && !strcmp(argv[1], "real_path")) { while (argc > 2) { puts(real_path(argv[2])); argc--; argv++; } return 0; } if (argc >= 2 && !strcmp(argv[1], "absolute_path")) { while (argc > 2) { puts(absolute_path(argv[2])); argc--; argv++; } return 0; } if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { int len; struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; char *path = xstrdup(argv[2]); /* * We have to normalize the arguments because under * Windows, bash mangles arguments that look like * absolute POSIX paths or colon-separate lists of * absolute POSIX paths into DOS paths (e.g., * "/foo:/foo/bar" might be converted to * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"), * whereas longest_ancestor_length() requires paths * that use forward slashes. */ if (normalize_path_copy(path, path)) die("Path \"%s\" could not be normalized", argv[2]); string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1); filter_string_list(&ceiling_dirs, 0, normalize_ceiling_entry, NULL); len = longest_ancestor_length(path, &ceiling_dirs); string_list_clear(&ceiling_dirs, 0); free(path); printf("%d\n", len); return 0; } if (argc >= 4 && !strcmp(argv[1], "prefix_path")) { char *prefix = argv[2]; int prefix_len = strlen(prefix); int nongit_ok; setup_git_directory_gently(&nongit_ok); while (argc > 3) { puts(prefix_path(prefix, prefix_len, argv[3])); argc--; argv++; } return 0; } if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) { char *prefix = strip_path_suffix(argv[2], argv[3]); printf("%s\n", prefix ? prefix : "(null)"); return 0; } if (argc == 3 && !strcmp(argv[1], "mingw_path")) { puts(argv[2]); return 0; } if (argc == 4 && !strcmp(argv[1], "relative_path")) { struct strbuf sb = STRBUF_INIT; const char *in, *prefix, *rel; normalize_argv_string(&in, argv[2]); normalize_argv_string(&prefix, argv[3]); rel = relative_path(in, prefix, &sb); if (!rel) puts("(null)"); else puts(strlen(rel) > 0 ? rel : "(empty)"); strbuf_release(&sb); return 0; } fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; }