get_repo_path: refactor path-allocation

The get_repo_path function calls mkpath() and then does some non-trivial operations on it, like calling is_git_directory() and read_gitfile(). These are actually OK (they do not use more pathname static buffers themselves), but it takes a fair bit of work to verify. Let's use our own strbuf to store the path, and we can simply reuse it for each iteration of the loop (we can even avoid rewriting the beginning part, since we are trying a series of suffixes). To make the strbuf cleanup easier, we split out a thin wrapper. As a bonus, this wrapper can factor out the canonicalization that happens in all of the early-return code paths. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 10, 2015 at 05:37 UTC 0ea68e429647cc5400fe8fa257056083b0a6459d
1 file changed +29 -14
builtin/clone.c
+29 -14
@@ -99,51 +99,66 @@ static const char *argv_submodule[] = {
99 "submodule", "update", "--init", "--recursive", NULL
100 };
101
102 -static char *get_repo_path(const char *repo, int *is_bundle)
102 +static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
103 {
104 static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
105 static char *bundle_suffix[] = { ".bundle", "" };
106 + size_t baselen = path->len;
107 struct stat st;
108 int i;
109
110 for (i = 0; i < ARRAY_SIZE(suffix); i++) {
110 - const char *path;
111 - path = mkpath("%s%s", repo, suffix[i]);
112 - if (stat(path, &st))
111 + strbuf_setlen(path, baselen);
112 + strbuf_addstr(path, suffix[i]);
113 + if (stat(path->buf, &st))
114 continue;
114 - if (S_ISDIR(st.st_mode) && is_git_directory(path)) {
115 + if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) {
116 *is_bundle = 0;
116 - return xstrdup(absolute_path(path));
117 + return path->buf;
118 } else if (S_ISREG(st.st_mode) && st.st_size > 8) {
119 /* Is it a "gitfile"? */
120 char signature[8];
120 - int len, fd = open(path, O_RDONLY);
121 + const char *dst;
122 + int len, fd = open(path->buf, O_RDONLY);
123 if (fd < 0)
124 continue;
125 len = read_in_full(fd, signature, 8);
126 close(fd);
127 if (len != 8 || strncmp(signature, "gitdir: ", 8))
128 continue;
127 - path = read_gitfile(path);
128 - if (path) {
129 + dst = read_gitfile(path->buf);
130 + if (dst) {
131 *is_bundle = 0;
130 - return xstrdup(absolute_path(path));
132 + return dst;
133 }
134 }
135 }
136
137 for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
136 - const char *path;
137 - path = mkpath("%s%s", repo, bundle_suffix[i]);
138 - if (!stat(path, &st) && S_ISREG(st.st_mode)) {
138 + strbuf_setlen(path, baselen);
139 + strbuf_addstr(path, bundle_suffix[i]);
140 + if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) {
141 *is_bundle = 1;
140 - return xstrdup(absolute_path(path));
142 + return path->buf;
143 }
144 }
145
146 return NULL;
147 }
148
149 +static char *get_repo_path(const char *repo, int *is_bundle)
150 +{
151 + struct strbuf path = STRBUF_INIT;
152 + const char *raw;
153 + char *canon;
154 +
155 + strbuf_addstr(&path, repo);
156 + raw = get_repo_path_1(&path, is_bundle);
157 + canon = raw ? xstrdup(absolute_path(raw)) : NULL;
158 + strbuf_release(&path);
159 + return canon;
160 +}
161 +
162 static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
163 {
164 const char *end = repo + strlen(repo), *start;