Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 541 lines (483 sloc) 11.869 kb
26c8a53 Add "mkpath()" helper function
Linus Torvalds authored
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
15 static char bad_path[] = "/bad-path/";
16
e7676d2 Allow multiple "git_path()" uses
Linus Torvalds authored
17 static 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
26c8a53 Add "mkpath()" helper function
Linus Torvalds authored
24 static 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
108bebe Alex Riesen Add mksnpath which allows you to specify the output buffer
raalkml authored
35 char *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) {
9db56f7 Daniel Lowe Fix non-literal format in printf-style calls
dlowe-net authored
44 strlcpy(buf, bad_path, n);
108bebe Alex Riesen Add mksnpath which allows you to specify the output buffer
raalkml authored
45 return buf;
46 }
47 return cleanup_path(buf);
48 }
49
aba13e7 Alex Riesen git_pathdup: returns xstrdup-ed copy of the formatted path
raalkml authored
50 static char *git_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
fe2d777 Alex Riesen Add git_snpath: a .git path formatting routine with output buffer
raalkml authored
51 {
52 const char *git_dir = get_git_dir();
53 size_t len;
54
55 len = strlen(git_dir);
56 if (n < len + 1)
57 goto bad;
58 memcpy(buf, git_dir, len);
59 if (len && !is_dir_sep(git_dir[len-1]))
60 buf[len++] = '/';
61 len += vsnprintf(buf + len, n - len, fmt, args);
62 if (len >= n)
63 goto bad;
64 return cleanup_path(buf);
65 bad:
9db56f7 Daniel Lowe Fix non-literal format in printf-style calls
dlowe-net authored
66 strlcpy(buf, bad_path, n);
fe2d777 Alex Riesen Add git_snpath: a .git path formatting routine with output buffer
raalkml authored
67 return buf;
68 }
69
aba13e7 Alex Riesen git_pathdup: returns xstrdup-ed copy of the formatted path
raalkml authored
70 char *git_snpath(char *buf, size_t n, const char *fmt, ...)
71 {
72 va_list args;
73 va_start(args, fmt);
74 (void)git_vsnpath(buf, n, fmt, args);
75 va_end(args);
76 return buf;
77 }
78
79 char *git_pathdup(const char *fmt, ...)
80 {
81 char path[PATH_MAX];
82 va_list args;
83 va_start(args, fmt);
84 (void)git_vsnpath(path, sizeof(path), fmt, args);
85 va_end(args);
86 return xstrdup(path);
87 }
88
26c8a53 Add "mkpath()" helper function
Linus Torvalds authored
89 char *mkpath(const char *fmt, ...)
90 {
91 va_list args;
92 unsigned len;
e7676d2 Allow multiple "git_path()" uses
Linus Torvalds authored
93 char *pathname = get_pathname();
26c8a53 Add "mkpath()" helper function
Linus Torvalds authored
94
95 va_start(args, fmt);
96 len = vsnprintf(pathname, PATH_MAX, fmt, args);
97 va_end(args);
98 if (len >= PATH_MAX)
99 return bad_path;
100 return cleanup_path(pathname);
101 }
102
103 char *git_path(const char *fmt, ...)
104 {
5da1606 [PATCH] Provide access to git_dir through get_git_dir().
Sven Verdoolaege authored
105 const char *git_dir = get_git_dir();
e7676d2 Allow multiple "git_path()" uses
Linus Torvalds authored
106 char *pathname = get_pathname();
26c8a53 Add "mkpath()" helper function
Linus Torvalds authored
107 va_list args;
108 unsigned len;
109
110 len = strlen(git_dir);
111 if (len > PATH_MAX-100)
112 return bad_path;
113 memcpy(pathname, git_dir, len);
114 if (len && git_dir[len-1] != '/')
115 pathname[len++] = '/';
116 va_start(args, fmt);
117 len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
118 va_end(args);
119 if (len >= PATH_MAX)
120 return bad_path;
121 return cleanup_path(pathname);
122 }
f2db68e [PATCH] git: add git_mkstemp()
Holger Eitzenberger authored
123
124
125 /* git_mkstemp() - create tmp file honoring TMPDIR variable */
126 int git_mkstemp(char *path, size_t len, const char *template)
127 {
e7a7be8 Junio C Hamano git_mkstemp(): be careful not to overflow the path buffer.
gitster authored
128 const char *tmp;
129 size_t n;
130
131 tmp = getenv("TMPDIR");
132 if (!tmp)
133 tmp = "/tmp";
134 n = snprintf(path, len, "%s/%s", tmp, template);
135 if (len <= n) {
136 errno = ENAMETOOLONG;
137 return -1;
35c3c62 [PATCH] git_mkstemp() fix
Holger Eitzenberger authored
138 }
f2db68e [PATCH] git: add git_mkstemp()
Holger Eitzenberger authored
139 return mkstemp(path);
140 }
141
142
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
143 int validate_headref(const char *path)
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
144 {
145 struct stat st;
146 char *buf, buffer[256];
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
147 unsigned char sha1[20];
0104ca0 Heikki Orsila Make read_in_full() and write_in_full() consistent with xread() and xwri...
heikkiorsila authored
148 int fd;
149 ssize_t len;
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
150
151 if (lstat(path, &st) < 0)
152 return -1;
153
154 /* Make sure it is a "refs/.." symlink */
155 if (S_ISLNK(st.st_mode)) {
156 len = readlink(path, buffer, sizeof(buffer)-1);
157 if (len >= 5 && !memcmp("refs/", buffer, 5))
158 return 0;
159 return -1;
160 }
161
162 /*
163 * Anything else, just open it and try to see if it is a symbolic ref.
164 */
165 fd = open(path, O_RDONLY);
166 if (fd < 0)
167 return -1;
93d26e4 Andy Whitcroft short i/o: fix calls to read to use xread or read_in_full
awhitcroft authored
168 len = read_in_full(fd, buffer, sizeof(buffer)-1);
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
169 close(fd);
170
171 /*
172 * Is it a symbolic ref?
173 */
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
174 if (len < 4)
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
175 return -1;
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
176 if (!memcmp("ref:", buffer, 4)) {
177 buf = buffer + 4;
178 len -= 4;
179 while (len && isspace(*buf))
180 buf++, len--;
181 if (len >= 5 && !memcmp("refs/", buf, 5))
182 return 0;
183 }
184
185 /*
186 * Is this a detached HEAD?
187 */
188 if (!get_sha1_hex(buffer, sha1))
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
189 return 0;
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
190
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
191 return -1;
192 }
193
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
194 static char *user_path(char *buf, char *path, int sz)
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
195 {
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
196 struct passwd *pw;
197 char *slash;
198 int len, baselen;
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
199
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
200 if (!path || path[0] != '~')
201 return NULL;
202 path++;
203 slash = strchr(path, '/');
204 if (path[0] == '/' || !path[0]) {
205 pw = getpwuid(getuid());
206 }
207 else {
208 if (slash) {
209 *slash = 0;
210 pw = getpwnam(path);
211 *slash = '/';
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
212 }
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
213 else
214 pw = getpwnam(path);
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
215 }
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
216 if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
217 return NULL;
218 baselen = strlen(pw->pw_dir);
219 memcpy(buf, pw->pw_dir, baselen);
220 while ((1 < baselen) && (buf[baselen-1] == '/')) {
221 buf[baselen-1] = 0;
222 baselen--;
223 }
224 if (slash && slash[1]) {
225 len = strlen(slash);
226 if (sz <= baselen + len)
227 return NULL;
228 memcpy(buf + baselen, slash, len + 1);
229 }
230 return buf;
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
231 }
232
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
233 /*
234 * First, one directory to try is determined by the following algorithm.
235 *
236 * (0) If "strict" is given, the path is used as given and no DWIM is
237 * done. Otherwise:
238 * (1) "~/path" to mean path under the running user's home directory;
239 * (2) "~user/path" to mean path under named user's home directory;
240 * (3) "relative/path" to mean cwd relative directory; or
241 * (4) "/absolute/path" to mean absolute directory.
242 *
243 * Unless "strict" is given, we try access() for existence of "%s.git/.git",
244 * "%s/.git", "%s.git", "%s" in this order. The first one that exists is
245 * what we try.
246 *
247 * Second, we try chdir() to that. Upon failure, we return NULL.
248 *
249 * Then, we try if the current directory is a valid git repository.
250 * Upon failure, we return NULL.
251 *
252 * If all goes well, we return the directory we used to chdir() (but
253 * before ~user is expanded), avoiding getcwd() resolving symbolic
254 * links. User relative paths are also returned as they are given,
255 * except DWIM suffixing.
256 */
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
257 char *enter_repo(char *path, int strict)
258 {
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
259 static char used_path[PATH_MAX];
260 static char validated_path[PATH_MAX];
261
262 if (!path)
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
263 return NULL;
264
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
265 if (!strict) {
266 static const char *suffix[] = {
267 ".git/.git", "/.git", ".git", "", NULL,
268 };
269 int len = strlen(path);
270 int i;
271 while ((1 < len) && (path[len-1] == '/')) {
272 path[len-1] = 0;
273 len--;
274 }
275 if (PATH_MAX <= len)
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
276 return NULL;
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
277 if (path[0] == '~') {
278 if (!user_path(used_path, path, PATH_MAX))
279 return NULL;
280 strcpy(validated_path, path);
281 path = used_path;
282 }
283 else if (PATH_MAX - 10 < len)
284 return NULL;
285 else {
286 path = strcpy(used_path, path);
287 strcpy(validated_path, path);
288 }
289 len = strlen(path);
290 for (i = 0; suffix[i]; i++) {
291 strcpy(path + len, suffix[i]);
292 if (!access(path, F_OK)) {
293 strcat(validated_path, suffix[i]);
294 break;
295 }
296 }
297 if (!suffix[i] || chdir(path))
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
298 return NULL;
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
299 path = validated_path;
0870ca7 Do not DWIM in userpath library under strict mode.
Junio C Hamano authored
300 }
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
301 else if (chdir(path))
302 return NULL;
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
303
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
304 if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
c847f53 Detached HEAD (experimental)
Junio C Hamano authored
305 validate_headref("HEAD") == 0) {
7627943 Matthias Lederhofer getenv/setenv: use constants if available
matled authored
306 setenv(GIT_DIR_ENVIRONMENT, ".", 1);
1644162 Check repository format version in enter_repo().
Junio C Hamano authored
307 check_repository_format();
d79374c [PATCH] daemon.c and path.enter_repo(): revamp path validation.
Junio C Hamano authored
308 return path;
54f4b87 Library code for user-relative paths, take three.
Andreas Ericsson authored
309 }
310
311 return NULL;
312 }
138086a shared repository - add a few missing calls to adjust_shared_perm().
Junio C Hamano authored
313
314 int adjust_shared_perm(const char *path)
315 {
316 struct stat st;
317 int mode;
318
319 if (!shared_repository)
320 return 0;
321 if (lstat(path, &st) < 0)
322 return -1;
323 mode = st.st_mode;
06cbe85 Heikki Orsila Make core.sharedRepository more generic
heikkiorsila authored
324
325 if (shared_repository) {
326 int tweak = shared_repository;
327 if (!(mode & S_IWUSR))
328 tweak &= ~0222;
8c6202d Fix backwards-incompatible handling of core.sharedRepository
Petr Baudis authored
329 mode |= tweak;
06cbe85 Heikki Orsila Make core.sharedRepository more generic
heikkiorsila authored
330 } else {
331 /* Preserve old PERM_UMASK behaviour */
332 if (mode & S_IWUSR)
333 mode |= S_IWGRP;
334 }
335
336 if (S_ISDIR(mode)) {
81a24b5 Alex Riesen Do not use GUID on dir in git init --shared=all on FreeBSD
raalkml authored
337 mode |= FORCE_DIR_SET_GID;
06cbe85 Heikki Orsila Make core.sharedRepository more generic
heikkiorsila authored
338
339 /* Copy read bits to execute bits */
340 mode |= (shared_repository & 0444) >> 2;
341 }
342
fe732ed adjust_shared_perm: chmod() only when needed.
Junio C Hamano authored
343 if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
138086a shared repository - add a few missing calls to adjust_shared_perm().
Junio C Hamano authored
344 return -2;
345 return 0;
346 }
e5392c5 dscho Add is_absolute_path() and make_absolute_path()
dscho authored
347
044bbbc Linus Torvalds Make git_dir a path relative to work_tree in setup_work_tree()
torvalds authored
348 const char *make_relative_path(const char *abs, const char *base)
349 {
350 static char buf[PATH_MAX + 1];
351 int baselen;
352 if (!base)
353 return abs;
354 baselen = strlen(base);
355 if (prefixcmp(abs, base))
356 return abs;
357 if (abs[baselen] == '/')
358 baselen++;
359 else if (base[baselen - 1] != '/')
360 return abs;
361 strcpy(buf, abs + baselen);
362 return buf;
363 }
ae299be Implement normalize_absolute_path
David Reiss authored
364
365 /*
366 * path = absolute path
367 * buf = buffer of at least max(2, strlen(path)+1) bytes
368 * It is okay if buf == path, but they should not overlap otherwise.
369 *
370 * Performs the following normalizations on path, storing the result in buf:
371 * - Removes trailing slashes.
372 * - Removes empty components.
373 * - Removes "." components.
374 * - Removes ".." components, and the components the precede them.
375 * "" and paths that contain only slashes are normalized to "/".
376 * Returns the length of the output.
377 *
378 * Note that this function is purely textual. It does not follow symlinks,
379 * verify the existence of the path, or make any system calls.
380 */
381 int normalize_absolute_path(char *buf, const char *path)
382 {
383 const char *comp_start = path, *comp_end = path;
384 char *dst = buf;
385 int comp_len;
386 assert(buf);
387 assert(path);
388
389 while (*comp_start) {
390 assert(*comp_start == '/');
391 while (*++comp_end && *comp_end != '/')
392 ; /* nothing */
393 comp_len = comp_end - comp_start;
394
395 if (!strncmp("/", comp_start, comp_len) ||
396 !strncmp("/.", comp_start, comp_len))
397 goto next;
398
399 if (!strncmp("/..", comp_start, comp_len)) {
400 while (dst > buf && *--dst != '/')
401 ; /* nothing */
402 goto next;
403 }
404
1442171 Jeff King fix overlapping memcpy in normalize_absolute_path
authored
405 memmove(dst, comp_start, comp_len);
ae299be Implement normalize_absolute_path
David Reiss authored
406 dst += comp_len;
407 next:
408 comp_start = comp_end;
409 }
410
411 if (dst == buf)
412 *dst++ = '/';
413
414 *dst = '\0';
415 return dst - buf;
416 }
0454dd9 Add support for GIT_CEILING_DIRECTORIES
David Reiss authored
417
f3cad0a Move sanitary_path_copy() to path.c and rename it to normalize_path_copy...
Johannes Sixt authored
418 int normalize_path_copy(char *dst, const char *src)
419 {
420 char *dst0;
421
422 if (has_dos_drive_prefix(src)) {
423 *dst++ = *src++;
424 *dst++ = *src++;
425 }
426 dst0 = dst;
427
428 if (is_dir_sep(*src)) {
429 *dst++ = '/';
430 while (is_dir_sep(*src))
431 src++;
432 }
433
434 for (;;) {
435 char c = *src;
436
437 /*
438 * A path component that begins with . could be
439 * special:
440 * (1) "." and ends -- ignore and terminate.
441 * (2) "./" -- ignore them, eat slash and continue.
442 * (3) ".." and ends -- strip one and terminate.
443 * (4) "../" -- strip one, eat slash and continue.
444 */
445 if (c == '.') {
446 if (!src[1]) {
447 /* (1) */
448 src++;
449 } else if (is_dir_sep(src[1])) {
450 /* (2) */
451 src += 2;
452 while (is_dir_sep(*src))
453 src++;
454 continue;
455 } else if (src[1] == '.') {
456 if (!src[2]) {
457 /* (3) */
458 src += 2;
459 goto up_one;
460 } else if (is_dir_sep(src[2])) {
461 /* (4) */
462 src += 3;
463 while (is_dir_sep(*src))
464 src++;
465 goto up_one;
466 }
467 }
468 }
469
470 /* copy up to the next '/', and eat all '/' */
471 while ((c = *src++) != '\0' && !is_dir_sep(c))
472 *dst++ = c;
473 if (is_dir_sep(c)) {
474 *dst++ = '/';
475 while (is_dir_sep(c))
476 c = *src++;
477 src--;
478 } else if (!c)
479 break;
480 continue;
481
482 up_one:
483 /*
484 * dst0..dst is prefix portion, and dst[-1] is '/';
485 * go up one level.
486 */
f42302b Test and fix normalize_path_copy()
Johannes Sixt authored
487 dst--; /* go to trailing '/' */
488 if (dst <= dst0)
f3cad0a Move sanitary_path_copy() to path.c and rename it to normalize_path_copy...
Johannes Sixt authored
489 return -1;
f42302b Test and fix normalize_path_copy()
Johannes Sixt authored
490 /* Windows: dst[-1] cannot be backslash anymore */
491 while (dst0 < dst && dst[-1] != '/')
492 dst--;
f3cad0a Move sanitary_path_copy() to path.c and rename it to normalize_path_copy...
Johannes Sixt authored
493 }
494 *dst = '\0';
495 return 0;
496 }
497
0454dd9 Add support for GIT_CEILING_DIRECTORIES
David Reiss authored
498 /*
499 * path = Canonical absolute path
500 * prefix_list = Colon-separated list of absolute paths
501 *
2860b57 Nguyễn Thái Ngọc Duy Fix typo in comments of longest_ancestor_length()
pclouds authored
502 * Determines, for each path in prefix_list, whether the "prefix" really
0454dd9 Add support for GIT_CEILING_DIRECTORIES
David Reiss authored
503 * is an ancestor directory of path. Returns the length of the longest
504 * ancestor directory, excluding any trailing slashes, or -1 if no prefix
505 * is an ancestor. (Note that this means 0 is returned if prefix_list is
506 * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
507 * are not considered to be their own ancestors. path must be in a
508 * canonical form: empty components, or "." or ".." components are not
509 * allowed. prefix_list may be null, which is like "".
510 */
511 int longest_ancestor_length(const char *path, const char *prefix_list)
512 {
513 char buf[PATH_MAX+1];
514 const char *ceil, *colon;
515 int len, max_len = -1;
516
517 if (prefix_list == NULL || !strcmp(path, "/"))
518 return -1;
519
520 for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
43a7ddb Fix GIT_CEILING_DIRECTORIES on Windows
René Scharfe authored
521 for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
0454dd9 Add support for GIT_CEILING_DIRECTORIES
David Reiss authored
522 len = colon - ceil;
523 if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
524 continue;
525 strlcpy(buf, ceil, len+1);
43a7ddb Fix GIT_CEILING_DIRECTORIES on Windows
René Scharfe authored
526 if (normalize_path_copy(buf, buf) < 0)
527 continue;
528 len = strlen(buf);
529 if (len > 0 && buf[len-1] == '/')
530 buf[--len] = '\0';
0454dd9 Add support for GIT_CEILING_DIRECTORIES
David Reiss authored
531
532 if (!strncmp(path, buf, len) &&
533 path[len] == '/' &&
534 len > max_len) {
535 max_len = len;
536 }
537 }
538
539 return max_len;
540 }
Something went wrong with that request. Please try again.