source src/path.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
1 | - | /* | ||
2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
3 | - | * | ||
4 | - | * This file is part of libgit2, distributed under the GNU GPL v2 with | ||
5 | - | * a Linking Exception. For full terms see the included COPYING file. | ||
6 | - | */ | ||
7 | - | |||
8 | - | #include "path.h" | ||
9 | - | |||
10 | - | #include "posix.h" | ||
11 | - | #include "repository.h" | ||
12 | - | #ifdef GIT_WIN32 | ||
13 | - | #include "win32/posix.h" | ||
14 | - | #include "win32/w32_buffer.h" | ||
15 | - | #include "win32/w32_util.h" | ||
16 | - | #include "win32/version.h" | ||
17 | - | #include <aclapi.h> | ||
18 | - | #else | ||
19 | - | #include <dirent.h> | ||
20 | - | #endif | ||
21 | - | #include <stdio.h> | ||
22 | - | #include <ctype.h> | ||
23 | - | |||
24 | 657289 | 2 | static int dos_drive_prefix_length(const char *path) | |
25 | - | { | ||
26 | - | int i; | ||
27 | - | |||
28 | - | /* | ||
29 | - | * Does it start with an ASCII letter (i.e. highest bit not set), | ||
30 | - | * followed by a colon? | ||
31 | - | */ | ||
32 | 657289 | 2 | if (!(0x80 & (unsigned char)*path)) | |
33 | 656962 | 3-7 | return *path && path[1] == ':' ? 2 : 0; | |
34 | - | |||
35 | - | /* | ||
36 | - | * While drive letters must be letters of the English alphabet, it is | ||
37 | - | * possible to assign virtually _any_ Unicode character via `subst` as | ||
38 | - | * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff | ||
39 | - | * like this: | ||
40 | - | * | ||
41 | - | * subst ֍: %USERPROFILE%\Desktop | ||
42 | - | */ | ||
43 | 993 | 8-11 | for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++) | |
44 | - | ; /* skip first UTF-8 character */ | ||
45 | 327 | 12 | return path[i] == ':' ? i + 1 : 0; | |
46 | - | } | ||
47 | - | |||
48 | - | #ifdef GIT_WIN32 | ||
49 | - | static bool looks_like_network_computer_name(const char *path, int pos) | ||
50 | - | { | ||
51 | - | if (pos < 3) | ||
52 | - | return false; | ||
53 | - | |||
54 | - | if (path[0] != '/' || path[1] != '/') | ||
55 | - | return false; | ||
56 | - | |||
57 | - | while (pos-- > 2) { | ||
58 | - | if (path[pos] == '/') | ||
59 | - | return false; | ||
60 | - | } | ||
61 | - | |||
62 | - | return true; | ||
63 | - | } | ||
64 | - | #endif | ||
65 | - | |||
66 | - | /* | ||
67 | - | * Based on the Android implementation, BSD licensed. | ||
68 | - | * http://android.git.kernel.org/ | ||
69 | - | * | ||
70 | - | * Copyright (C) 2008 The Android Open Source Project | ||
71 | - | * All rights reserved. | ||
72 | - | * | ||
73 | - | * Redistribution and use in source and binary forms, with or without | ||
74 | - | * modification, are permitted provided that the following conditions | ||
75 | - | * are met: | ||
76 | - | * * Redistributions of source code must retain the above copyright | ||
77 | - | * notice, this list of conditions and the following disclaimer. | ||
78 | - | * * Redistributions in binary form must reproduce the above copyright | ||
79 | - | * notice, this list of conditions and the following disclaimer in | ||
80 | - | * the documentation and/or other materials provided with the | ||
81 | - | * distribution. | ||
82 | - | * | ||
83 | - | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
84 | - | * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
85 | - | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
86 | - | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
87 | - | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
88 | - | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
89 | - | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
90 | - | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | ||
91 | - | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
92 | - | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | ||
93 | - | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
94 | - | * SUCH DAMAGE. | ||
95 | - | */ | ||
96 | 8007 | 2 | int git_path_basename_r(git_buf *buffer, const char *path) | |
97 | - | { | ||
98 | - | const char *endp, *startp; | ||
99 | - | int len, result; | ||
100 | - | |||
101 | - | /* Empty or NULL string gets treated as "." */ | ||
102 | 8007 | 2,3 | if (path == NULL || *path == '\0') { | |
103 | 4 | 4 | startp = "."; | |
104 | 4 | 4 | len = 1; | |
105 | 4 | 4 | goto Exit; | |
106 | - | } | ||
107 | - | |||
108 | - | /* Strip trailing slashes */ | ||
109 | 8003 | 5 | endp = path + strlen(path) - 1; | |
110 | 8012 | 5,7,8 | while (endp > path && *endp == '/') | |
111 | 9 | 6 | endp--; | |
112 | - | |||
113 | - | /* All slashes becomes "/" */ | ||
114 | 8003 | 9,10 | if (endp == path && *endp == '/') { | |
115 | 2 | 11 | startp = "/"; | |
116 | 2 | 11 | len = 1; | |
117 | 2 | 11 | goto Exit; | |
118 | - | } | ||
119 | - | |||
120 | - | /* Find the start of the base */ | ||
121 | 8001 | 12 | startp = endp; | |
122 | 109027 | 12,14,15 | while (startp > path && *(startp - 1) != '/') | |
123 | 101026 | 13 | startp--; | |
124 | - | |||
125 | - | /* Cast is safe because max path < max int */ | ||
126 | 8001 | 16 | len = (int)(endp - startp + 1); | |
127 | - | |||
128 | - | Exit: | ||
129 | 8007 | 17 | result = len; | |
130 | - | |||
131 | 8007 | 17-19 | if (buffer != NULL && git_buf_set(buffer, startp, len) < 0) | |
132 | ##### | 20 | return -1; | |
133 | - | |||
134 | 8007 | 21 | return result; | |
135 | - | } | ||
136 | - | |||
137 | - | /* | ||
138 | - | * Determine if the path is a Windows prefix and, if so, returns | ||
139 | - | * its actual lentgh. If it is not a prefix, returns -1. | ||
140 | - | */ | ||
141 | 164851 | 2 | static int win32_prefix_length(const char *path, int len) | |
142 | - | { | ||
143 | - | #ifndef GIT_WIN32 | ||
144 | - | GIT_UNUSED(path); | ||
145 | - | GIT_UNUSED(len); | ||
146 | - | #else | ||
147 | - | /* | ||
148 | - | * Mimic unix behavior where '/.git' returns '/': 'C:/.git' | ||
149 | - | * will return 'C:/' here | ||
150 | - | */ | ||
151 | - | if (dos_drive_prefix_length(path) == len) | ||
152 | - | return len; | ||
153 | - | |||
154 | - | /* | ||
155 | - | * Similarly checks if we're dealing with a network computer name | ||
156 | - | * '//computername/.git' will return '//computername/' | ||
157 | - | */ | ||
158 | - | if (looks_like_network_computer_name(path, len)) | ||
159 | - | return len; | ||
160 | - | #endif | ||
161 | - | |||
162 | 164851 | 2 | return -1; | |
163 | - | } | ||
164 | - | |||
165 | - | /* | ||
166 | - | * Based on the Android implementation, BSD licensed. | ||
167 | - | * Check http://android.git.kernel.org/ | ||
168 | - | */ | ||
169 | - | 2 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files)int git_path_dirname_r(git_buf *buffer, const char *path) | |
170 | - | { | ||
171 | - | const char *endp; | ||
172 | - | 2 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) int is_prefix = 0, len; | |
173 | - | |||
174 | - | /* Empty or NULL string gets treated as "." */ | ||
175 | - | 2,3 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (path == NULL || *path == '\0') { | |
176 | - | 4 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) path = "."; | |
177 | - | 4 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = 1; | |
178 | - | 4 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
179 | - | } | ||
180 | - | |||
181 | - | /* Strip trailing slashes */ | ||
182 | - | 5 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp = path + strlen(path) - 1; | |
183 | - | 5,7,8 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) while (endp > path && *endp == '/') | |
184 | - | 6 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--; | |
185 | - | |||
186 | - | 9 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp - path + 1 > INT_MAX) { | |
187 | - | 10 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_INVALID, "path too long"); | |
188 | - | 11 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = -1; | |
189 | - | 11 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
190 | - | } | ||
191 | - | |||
192 | - | 12,13 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { | |
193 | - | 14 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) is_prefix = 1; | |
194 | - | 14 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
195 | - | } | ||
196 | - | |||
197 | - | /* Find the start of the dir */ | ||
198 | - | 15,17,18 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) while (endp > path && *endp != '/') | |
199 | - | 16 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--; | |
200 | - | |||
201 | - | /* Either the dir is "/" or there are no slashes */ | ||
202 | - | 19 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp == path) { | |
203 | - | 20-22 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) path = (*endp == '/') ? "/" : "."; | |
204 | - | 23 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = 1; | |
205 | - | 23 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
206 | - | } | ||
207 | - | |||
208 | - | do { | ||
209 | - | 24 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--; | |
210 | - | 24,25 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) } while (endp > path && *endp == '/'); | |
211 | - | |||
212 | - | 26 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp - path + 1 > INT_MAX) { | |
213 | - | 27 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_INVALID, "path too long"); | |
214 | - | 28 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = -1; | |
215 | - | 28 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
216 | - | } | ||
217 | - | |||
218 | - | 29,30 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { | |
219 | - | 31 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) is_prefix = 1; | |
220 | - | 31 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit; | |
221 | - | } | ||
222 | - | |||
223 | - | /* Cast is safe because max path < max int */ | ||
224 | - | 32 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = (int)(endp - path + 1); | |
225 | - | |||
226 | - | Exit: | ||
227 | - | 33 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (buffer) { | |
228 | - | 34,35 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (git_buf_set(buffer, path, len) < 0) | |
229 | - | 36 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return -1; | |
230 | - | 37-39 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (is_prefix && git_buf_putc(buffer, '/') < 0) | |
231 | - | 40 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return -1; | |
232 | - | } | ||
233 | - | |||
234 | - | 41 | suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return len; | |
235 | - | } | ||
236 | - | |||
237 | - | |||
238 | 101 | 2 | char *git_path_dirname(const char *path) | |
239 | - | { | ||
240 | 101 | 2 | git_buf buf = GIT_BUF_INIT; | |
241 | - | char *dirname; | ||
242 | - | |||
243 | 101 | 2 | git_path_dirname_r(&buf, path); | |
244 | 101 | 3 | dirname = git_buf_detach(&buf); | |
245 | 101 | 4 | git_buf_dispose(&buf); /* avoid memleak if error occurs */ | |
246 | - | |||
247 | 101 | 5 | return dirname; | |
248 | - | } | ||
249 | - | |||
250 | 7996 | 2 | char *git_path_basename(const char *path) | |
251 | - | { | ||
252 | 7996 | 2 | git_buf buf = GIT_BUF_INIT; | |
253 | - | char *basename; | ||
254 | - | |||
255 | 7996 | 2 | git_path_basename_r(&buf, path); | |
256 | 7996 | 3 | basename = git_buf_detach(&buf); | |
257 | 7996 | 4 | git_buf_dispose(&buf); /* avoid memleak if error occurs */ | |
258 | - | |||
259 | 7996 | 5 | return basename; | |
260 | - | } | ||
261 | - | |||
262 | 2138 | 2 | size_t git_path_basename_offset(git_buf *buffer) | |
263 | - | { | ||
264 | - | ssize_t slash; | ||
265 | - | |||
266 | 2138 | 2,3 | if (!buffer || buffer->size <= 0) | |
267 | ##### | 4 | return 0; | |
268 | - | |||
269 | 2138 | 5 | slash = git_buf_rfind_next(buffer, '/'); | |
270 | - | |||
271 | 2138 | 6,7 | if (slash >= 0 && buffer->ptr[slash] == '/') | |
272 | 2136 | 8 | return (size_t)(slash + 1); | |
273 | - | |||
274 | 2 | 9 | return 0; | |
275 | - | } | ||
276 | - | |||
277 | 11 | 2 | const char *git_path_topdir(const char *path) | |
278 | - | { | ||
279 | - | size_t len; | ||
280 | - | ssize_t i; | ||
281 | - | |||
282 | 11 | 2,3 | assert(path); | |
283 | 11 | 4 | len = strlen(path); | |
284 | - | |||
285 | 11 | 4,5 | if (!len || path[len - 1] != '/') | |
286 | 4 | 6 | return NULL; | |
287 | - | |||
288 | 28 | 7,10,11 | for (i = (ssize_t)len - 2; i >= 0; --i) | |
289 | 25 | 8 | if (path[i] == '/') | |
290 | 4 | 9 | break; | |
291 | - | |||
292 | 7 | 12 | return &path[i + 1]; | |
293 | - | } | ||
294 | - | |||
295 | 657166 | 2 | int git_path_root(const char *path) | |
296 | - | { | ||
297 | 657166 | 2 | int offset = 0, prefix_len; | |
298 | - | |||
299 | - | /* Does the root of the path look like a windows drive ? */ | ||
300 | 657166 | 2,3 | if ((prefix_len = dos_drive_prefix_length(path))) | |
301 | 12 | 4 | offset += prefix_len; | |
302 | - | |||
303 | - | #ifdef GIT_WIN32 | ||
304 | - | /* Are we dealing with a windows network path? */ | ||
305 | - | else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') || | ||
306 | - | (path[0] == '\\' && path[1] == '\\' && path[2] != '\\')) | ||
307 | - | { | ||
308 | - | offset += 2; | ||
309 | - | |||
310 | - | /* Skip the computer name segment */ | ||
311 | - | while (path[offset] && path[offset] != '/' && path[offset] != '\\') | ||
312 | - | offset++; | ||
313 | - | } | ||
314 | - | |||
315 | - | if (path[offset] == '\\') | ||
316 | - | return offset; | ||
317 | - | #endif | ||
318 | - | |||
319 | 657154 | 5 | if (path[offset] == '/') | |
320 | 60610 | 6 | return offset; | |
321 | - | |||
322 | 596544 | 7 | return -1; /* Not a real error - signals that path is not rooted */ | |
323 | - | } | ||
324 | - | |||
325 | 6748 | 2 | static void path_trim_slashes(git_buf *path) | |
326 | - | { | ||
327 | 6748 | 2 | int ceiling = git_path_root(path->ptr) + 1; | |
328 | 6748 | 3,4 | assert(ceiling >= 0); | |
329 | - | |||
330 | 13261 | 5,9 | while (path->size > (size_t)ceiling) { | |
331 | 13260 | 6 | if (path->ptr[path->size-1] != '/') | |
332 | 6747 | 7 | break; | |
333 | - | |||
334 | 6513 | 8 | path->ptr[path->size-1] = '\0'; | |
335 | 6513 | 8 | path->size--; | |
336 | - | } | ||
337 | 6748 | 10 | } | |
338 | - | |||
339 | - | 2 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files)int git_path_join_unrooted( | |
340 | - | git_buf *path_out, const char *path, const char *base, ssize_t *root_at) | ||
341 | - | { | ||
342 | - | ssize_t root; | ||
343 | - | |||
344 | - | 2-4 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) assert(path && path_out); | |
345 | - | |||
346 | - | 5 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = (ssize_t)git_path_root(path); | |
347 | - | |||
348 | - | 6,7 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (base != NULL && root < 0) { | |
349 | - | 8,9 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (git_buf_joinpath(path_out, base, path) < 0) | |
350 | - | 10 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return -1; | |
351 | - | |||
352 | - | 11 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = (ssize_t)strlen(base); | |
353 | - | } else { | ||
354 | - | 12,13 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (git_buf_sets(path_out, path) < 0) | |
355 | - | 14 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return -1; | |
356 | - | |||
357 | - | 15 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (root < 0) | |
358 | - | 16 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = 0; | |
359 | - | 17 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) else if (base) | |
360 | - | 18 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) git_path_equal_or_prefixed(base, path, &root); | |
361 | - | } | ||
362 | - | |||
363 | - | 19 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (root_at) | |
364 | - | 20 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) *root_at = root; | |
365 | - | |||
366 | - | 21 | suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return 0; | |
367 | - | } | ||
368 | - | |||
369 | 1139 | 2 | void git_path_squash_slashes(git_buf *path) | |
370 | - | { | ||
371 | - | char *p, *q; | ||
372 | - | |||
373 | 1139 | 2 | if (path->size == 0) | |
374 | 1139 | 3,12 | return; | |
375 | - | |||
376 | 15617 | 4,9,10 | for (p = path->ptr, q = path->ptr; *q; p++, q++) { | |
377 | 14483 | 5 | *p = *q; | |
378 | - | |||
379 | 14483 | 5,7,8 | while (*q == '/' && *(q+1) == '/') { | |
380 | ##### | 6 | path->size--; | |
381 | ##### | 6 | q++; | |
382 | - | } | ||
383 | - | } | ||
384 | - | |||
385 | 1134 | 11 | *p = '\0'; | |
386 | - | } | ||
387 | - | |||
388 | - | 2 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files)int git_path_prettify(git_buf *path_out, const char *path, const char *base) | |
389 | - | { | ||
390 | - | char buf[GIT_PATH_MAX]; | ||
391 | - | |||
392 | - | 2-4 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) assert(path && path_out); | |
393 | - | |||
394 | - | /* construct path if needed */ | ||
395 | - | 5-7 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (base != NULL && git_path_root(path) < 0) { | |
396 | - | 8,9 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (git_buf_joinpath(path_out, base, path) < 0) | |
397 | - | 10 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return -1; | |
398 | - | 11 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) path = path_out->ptr; | |
399 | - | } | ||
400 | - | |||
401 | - | 12,13 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (p_realpath(path, buf) == NULL) { | |
402 | - | /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */ | ||
403 | - | 14-19 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; | |
404 | - | 20 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path); | |
405 | - | |||
406 | - | 21 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) git_buf_clear(path_out); | |
407 | - | |||
408 | - | 22 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return error; | |
409 | - | } | ||
410 | - | |||
411 | - | 23 | suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return git_buf_sets(path_out, buf); | |
412 | - | } | ||
413 | - | |||
414 | 2190 | 2 | int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base) | |
415 | - | { | ||
416 | 2190 | 2 | int error = git_path_prettify(path_out, path, base); | |
417 | 2190 | 3 | return (error < 0) ? error : git_path_to_dir(path_out); | |
418 | - | } | ||
419 | - | |||
420 | - | 2 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files)int git_path_to_dir(git_buf *path) | |
421 | - | { | ||
422 | - | 2,4 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) if (path->asize > 0 && | |
423 | - | 3,6 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) git_buf_len(path) > 0 && | |
424 | - | 5 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) path->ptr[git_buf_len(path) - 1] != '/') | |
425 | - | 7 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) git_buf_putc(path, '/'); | |
426 | - | |||
427 | - | 8 | suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) return git_buf_oom(path) ? -1 : 0; | |
428 | - | } | ||
429 | - | |||
430 | 8 | 2 | void git_path_string_to_dir(char* path, size_t size) | |
431 | - | { | ||
432 | 8 | 2 | size_t end = strlen(path); | |
433 | - | |||
434 | 8 | 2-4 | if (end && path[end - 1] != '/' && end < size) { | |
435 | 4 | 5 | path[end] = '/'; | |
436 | 4 | 5 | path[end + 1] = '\0'; | |
437 | - | } | ||
438 | 8 | 6 | } | |
439 | - | |||
440 | 97 | 2 | int git__percent_decode(git_buf *decoded_out, const char *input) | |
441 | - | { | ||
442 | - | int len, hi, lo, i; | ||
443 | 97 | 2-4 | assert(decoded_out && input); | |
444 | - | |||
445 | 97 | 5 | len = (int)strlen(input); | |
446 | 97 | 5 | git_buf_clear(decoded_out); | |
447 | - | |||
448 | 4758 | 6,19,20 | for(i = 0; i < len; i++) | |
449 | - | { | ||
450 | 4661 | 7 | char c = input[i]; | |
451 | - | |||
452 | 4661 | 7 | if (c != '%') | |
453 | 4633 | 8 | goto append; | |
454 | - | |||
455 | 28 | 9 | if (i >= len - 2) | |
456 | 3 | 10 | goto append; | |
457 | - | |||
458 | 25 | 11 | hi = git__fromhex(input[i + 1]); | |
459 | 25 | 12 | lo = git__fromhex(input[i + 2]); | |
460 | - | |||
461 | 25 | 13,14 | if (hi < 0 || lo < 0) | |
462 | - | goto append; | ||
463 | - | |||
464 | 22 | 15 | c = (char)(hi << 4 | lo); | |
465 | 22 | 15 | i += 2; | |
466 | - | |||
467 | - | append: | ||
468 | 4661 | 16,17 | if (git_buf_putc(decoded_out, c) < 0) | |
469 | ##### | 18 | return -1; | |
470 | - | } | ||
471 | - | |||
472 | 97 | 21 | return 0; | |
473 | - | } | ||
474 | - | |||
475 | 7 | 2 | static int error_invalid_local_file_uri(const char *uri) | |
476 | - | { | ||
477 | 7 | 2 | git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri); | |
478 | 7 | 3 | return -1; | |
479 | - | } | ||
480 | - | |||
481 | 333 | 2 | static int local_file_url_prefixlen(const char *file_url) | |
482 | - | { | ||
483 | 333 | 2 | int len = -1; | |
484 | - | |||
485 | 333 | 2,3 | if (git__prefixcmp(file_url, "file://") == 0) { | |
486 | 178 | 4 | if (file_url[7] == '/') | |
487 | 166 | 5 | len = 8; | |
488 | 12 | 6,7 | else if (git__prefixcmp(file_url + 7, "localhost/") == 0) | |
489 | 7 | 8 | len = 17; | |
490 | - | } | ||
491 | - | |||
492 | 333 | 9 | return len; | |
493 | - | } | ||
494 | - | |||
495 | 239 | 2 | bool git_path_is_local_file_url(const char *file_url) | |
496 | - | { | ||
497 | 239 | 2 | return (local_file_url_prefixlen(file_url) > 0); | |
498 | - | } | ||
499 | - | |||
500 | 94 | 2 | int git_path_fromurl(git_buf *local_path_out, const char *file_url) | |
501 | - | { | ||
502 | - | int offset; | ||
503 | - | |||
504 | 94 | 2-4 | assert(local_path_out && file_url); | |
505 | - | |||
506 | 94 | 5-7 | if ((offset = local_file_url_prefixlen(file_url)) < 0 || | |
507 | 90 | 7,8 | file_url[offset] == '\0' || file_url[offset] == '/') | |
508 | 7 | 9 | return error_invalid_local_file_uri(file_url); | |
509 | - | |||
510 | - | #ifndef GIT_WIN32 | ||
511 | 87 | 10 | offset--; /* A *nix absolute path starts with a forward slash */ | |
512 | - | #endif | ||
513 | - | |||
514 | 87 | 10 | git_buf_clear(local_path_out); | |
515 | 87 | 11 | return git__percent_decode(local_path_out, file_url + offset); | |
516 | - | } | ||
517 | - | |||
518 | - | 2 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files)int git_path_walk_up( | |
519 | - | git_buf *path, | ||
520 | - | const char *ceiling, | ||
521 | - | int (*cb)(void *data, const char *), | ||
522 | - | void *data) | ||
523 | - | { | ||
524 | - | 2 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) int error = 0; | |
525 | - | git_buf iter; | ||
526 | - | 2 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) ssize_t stop = 0, scan; | |
527 | - | 2 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) char oldc = '\0'; | |
528 | - | |||
529 | - | 2-4 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) assert(path && cb); | |
530 | - | |||
531 | - | 5 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (ceiling != NULL) { | |
532 | - | 6,7 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (git__prefixcmp(path->ptr, ceiling) == 0) | |
533 | - | 8 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) stop = (ssize_t)strlen(ceiling); | |
534 | - | else | ||
535 | - | 9,10 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) stop = git_buf_len(path); | |
536 | - | } | ||
537 | - | 11 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan = git_buf_len(path); | |
538 | - | |||
539 | - | /* empty path: yield only once */ | ||
540 | - | 12 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (!scan) { | |
541 | - | 13 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, ""); | |
542 | - | 14 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error) | |
543 | - | 15 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error); | |
544 | - | 16 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) return error; | |
545 | - | } | ||
546 | - | |||
547 | - | 17 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr = path->ptr; | |
548 | - | 17 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.size = git_buf_len(path); | |
549 | - | 18 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.asize = path->asize; | |
550 | - | |||
551 | - | 18,26 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) while (scan >= stop) { | |
552 | - | 19 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, iter.ptr); | |
553 | - | 20 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = oldc; | |
554 | - | |||
555 | - | 20 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error) { | |
556 | - | 21 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error); | |
557 | - | 22 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) break; | |
558 | - | } | ||
559 | - | |||
560 | - | 23 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan = git_buf_rfind_next(&iter, '/'); | |
561 | - | 24 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (scan >= 0) { | |
562 | - | 25 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan++; | |
563 | - | 25 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) oldc = iter.ptr[scan]; | |
564 | - | 25 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.size = scan; | |
565 | - | 25 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = '\0'; | |
566 | - | } | ||
567 | - | } | ||
568 | - | |||
569 | - | 27 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (scan >= 0) | |
570 | - | 28 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = oldc; | |
571 | - | |||
572 | - | /* relative path: yield for the last component */ | ||
573 | - | 29-31 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (!error && stop == 0 && iter.ptr[0] != '/') { | |
574 | - | 32 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, ""); | |
575 | - | 33 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error) | |
576 | - | 34 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error); | |
577 | - | } | ||
578 | - | |||
579 | - | 35 | suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) return error; | |
580 | - | } | ||
581 | - | |||
582 | 219430 | 2 | bool git_path_exists(const char *path) | |
583 | - | { | ||
584 | 219430 | 2,3 | assert(path); | |
585 | 219430 | 4 | return p_access(path, F_OK) == 0; | |
586 | - | } | ||
587 | - | |||
588 | 94699 | 2 | bool git_path_isdir(const char *path) | |
589 | - | { | ||
590 | - | struct stat st; | ||
591 | 94719 | 2,3 | if (p_stat(path, &st) < 0) | |
592 | 10005 | 4 | return false; | |
593 | - | |||
594 | 84714 | 5 | return S_ISDIR(st.st_mode) != 0; | |
595 | - | } | ||
596 | - | |||
597 | 20881 | 2 | bool git_path_isfile(const char *path) | |
598 | - | { | ||
599 | - | struct stat st; | ||
600 | - | |||
601 | 20881 | 2,3 | assert(path); | |
602 | 20883 | 4,5 | if (p_stat(path, &st) < 0) | |
603 | 10040 | 6 | return false; | |
604 | - | |||
605 | 10843 | 7 | return S_ISREG(st.st_mode) != 0; | |
606 | - | } | ||
607 | - | |||
608 | 4 | 2 | bool git_path_islink(const char *path) | |
609 | - | { | ||
610 | - | struct stat st; | ||
611 | - | |||
612 | 4 | 2,3 | assert(path); | |
613 | 4 | 4,5 | if (p_lstat(path, &st) < 0) | |
614 | ##### | 6 | return false; | |
615 | - | |||
616 | 4 | 7 | return S_ISLNK(st.st_mode) != 0; | |
617 | - | } | ||
618 | - | |||
619 | - | #ifdef GIT_WIN32 | ||
620 | - | |||
621 | - | bool git_path_is_empty_dir(const char *path) | ||
622 | - | { | ||
623 | - | git_win32_path filter_w; | ||
624 | - | bool empty = false; | ||
625 | - | |||
626 | - | if (git_win32__findfirstfile_filter(filter_w, path)) { | ||
627 | - | WIN32_FIND_DATAW findData; | ||
628 | - | HANDLE hFind = FindFirstFileW(filter_w, &findData); | ||
629 | - | |||
630 | - | /* FindFirstFile will fail if there are no children to the given | ||
631 | - | * path, which can happen if the given path is a file (and obviously | ||
632 | - | * has no children) or if the given path is an empty mount point. | ||
633 | - | * (Most directories have at least directory entries '.' and '..', | ||
634 | - | * but ridiculously another volume mounted in another drive letter's | ||
635 | - | * path space do not, and thus have nothing to enumerate.) If | ||
636 | - | * FindFirstFile fails, check if this is a directory-like thing | ||
637 | - | * (a mount point). | ||
638 | - | */ | ||
639 | - | if (hFind == INVALID_HANDLE_VALUE) | ||
640 | - | return git_path_isdir(path); | ||
641 | - | |||
642 | - | /* If the find handle was created successfully, then it's a directory */ | ||
643 | - | empty = true; | ||
644 | - | |||
645 | - | do { | ||
646 | - | /* Allow the enumeration to return . and .. and still be considered | ||
647 | - | * empty. In the special case of drive roots (i.e. C:\) where . and | ||
648 | - | * .. do not occur, we can still consider the path to be an empty | ||
649 | - | * directory if there's nothing there. */ | ||
650 | - | if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { | ||
651 | - | empty = false; | ||
652 | - | break; | ||
653 | - | } | ||
654 | - | } while (FindNextFileW(hFind, &findData)); | ||
655 | - | |||
656 | - | FindClose(hFind); | ||
657 | - | } | ||
658 | - | |||
659 | - | return empty; | ||
660 | - | } | ||
661 | - | |||
662 | - | #else | ||
663 | - | |||
664 | 59 | 2 | static int path_found_entry(void *payload, git_buf *path) | |
665 | - | { | ||
666 | - | GIT_UNUSED(payload); | ||
667 | 59 | 2 | return !git_path_is_dot_or_dotdot(path->ptr); | |
668 | - | } | ||
669 | - | |||
670 | 75 | 2 | bool git_path_is_empty_dir(const char *path) | |
671 | - | { | ||
672 | - | int error; | ||
673 | 75 | 2 | git_buf dir = GIT_BUF_INIT; | |
674 | - | |||
675 | 75 | 2,3 | if (!git_path_isdir(path)) | |
676 | 2 | 4 | return false; | |
677 | - | |||
678 | 73 | 5,6 | if ((error = git_buf_sets(&dir, path)) != 0) | |
679 | ##### | 7 | git_error_clear(); | |
680 | - | else | ||
681 | 73 | 8 | error = git_path_direach(&dir, 0, path_found_entry, NULL); | |
682 | - | |||
683 | 73 | 9 | git_buf_dispose(&dir); | |
684 | - | |||
685 | 73 | 10 | return !error; | |
686 | - | } | ||
687 | - | |||
688 | - | #endif | ||
689 | - | |||
690 | 45859 | 2 | int git_path_set_error(int errno_value, const char *path, const char *action) | |
691 | - | { | ||
692 | 45859 | 2 | switch (errno_value) { | |
693 | - | case ENOENT: | ||
694 | - | case ENOTDIR: | ||
695 | 45856 | 3 | git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action); | |
696 | 45853 | 4 | return GIT_ENOTFOUND; | |
697 | - | |||
698 | - | case EINVAL: | ||
699 | - | case ENAMETOOLONG: | ||
700 | ##### | 5 | git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path); | |
701 | ##### | 6 | return GIT_EINVALIDSPEC; | |
702 | - | |||
703 | - | case EEXIST: | ||
704 | ##### | 7 | git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path); | |
705 | ##### | 8 | return GIT_EEXISTS; | |
706 | - | |||
707 | - | case EACCES: | ||
708 | 3 | 9 | git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path); | |
709 | 3 | 10 | return GIT_ELOCKED; | |
710 | - | |||
711 | - | default: | ||
712 | ##### | 11 | git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path); | |
713 | ##### | 12 | return -1; | |
714 | - | } | ||
715 | - | } | ||
716 | - | |||
717 | 33592 | 2 | int git_path_lstat(const char *path, struct stat *st) | |
718 | - | { | ||
719 | 33592 | 2,3 | if (p_lstat(path, st) == 0) | |
720 | 31434 | 4 | return 0; | |
721 | - | |||
722 | 2155 | 5 | return git_path_set_error(errno, path, "stat"); | |
723 | - | } | ||
724 | - | |||
725 | 19388 | 2 | static bool _check_dir_contents( | |
726 | - | git_buf *dir, | ||
727 | - | const char *sub, | ||
728 | - | bool (*predicate)(const char *)) | ||
729 | - | { | ||
730 | - | bool result; | ||
731 | 19388 | 2 | size_t dir_size = git_buf_len(dir); | |
732 | 19387 | 3 | size_t sub_size = strlen(sub); | |
733 | - | size_t alloc_size; | ||
734 | - | |||
735 | - | /* leave base valid even if we could not make space for subdir */ | ||
736 | 19387 | 3-5,7 | if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) || | |
737 | 19387 | 6,8,10 | GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) || | |
738 | 19385 | 9 | git_buf_try_grow(dir, alloc_size, false) < 0) | |
739 | ##### | 11 | return false; | |
740 | - | |||
741 | - | /* save excursion */ | ||
742 | 19387 | 12,13 | if (git_buf_joinpath(dir, dir->ptr, sub) < 0) | |
743 | ##### | 14 | return false; | |
744 | - | |||
745 | 19385 | 15 | result = predicate(dir->ptr); | |
746 | - | |||
747 | - | /* restore path */ | ||
748 | 19389 | 16 | git_buf_truncate(dir, dir_size); | |
749 | 19389 | 17 | return result; | |
750 | - | } | ||
751 | - | |||
752 | 1201 | 2 | bool git_path_contains(git_buf *dir, const char *item) | |
753 | - | { | ||
754 | 1201 | 2 | return _check_dir_contents(dir, item, &git_path_exists); | |
755 | - | } | ||
756 | - | |||
757 | 8369 | 2 | bool git_path_contains_dir(git_buf *base, const char *subdir) | |
758 | - | { | ||
759 | 8369 | 2 | return _check_dir_contents(base, subdir, &git_path_isdir); | |
760 | - | } | ||
761 | - | |||
762 | 9820 | 2 | bool git_path_contains_file(git_buf *base, const char *file) | |
763 | - | { | ||
764 | 9820 | 2 | return _check_dir_contents(base, file, &git_path_isfile); | |
765 | - | } | ||
766 | - | |||
767 | - | 2 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files)int git_path_find_dir(git_buf *dir, const char *path, const char *base) | |
768 | - | { | ||
769 | - | 2 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) int error = git_path_join_unrooted(dir, path, base, NULL); | |
770 | - | |||
771 | - | 3 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error) { | |
772 | - | char buf[GIT_PATH_MAX]; | ||
773 | - | 4,5 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (p_realpath(dir->ptr, buf) != NULL) | |
774 | - | 6,7 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = git_buf_sets(dir, buf); | |
775 | - | } | ||
776 | - | |||
777 | - | /* call dirname if this is not a directory */ | ||
778 | - | 8 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error) /* && git_path_isdir(dir->ptr) == false) */ | |
779 | - | 9-13 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; | |
780 | - | |||
781 | - | 14 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error) | |
782 | - | 15 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = git_path_to_dir(dir); | |
783 | - | |||
784 | - | 16 | suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) return error; | |
785 | - | } | ||
786 | - | |||
787 | 3152 | 2 | int git_path_resolve_relative(git_buf *path, size_t ceiling) | |
788 | - | { | ||
789 | - | char *base, *to, *from, *next; | ||
790 | - | size_t len; | ||
791 | - | |||
792 | 3152 | 2-5 | GIT_ERROR_CHECK_ALLOC_BUF(path); | |
793 | - | |||
794 | 3152 | 6 | if (ceiling > path->size) | |
795 | ##### | 7 | ceiling = path->size; | |
796 | - | |||
797 | - | /* recognize drive prefixes, etc. that should not be backed over */ | ||
798 | 3152 | 8 | if (ceiling == 0) | |
799 | 3152 | 9,10 | ceiling = git_path_root(path->ptr) + 1; | |
800 | - | |||
801 | - | /* recognize URL prefixes that should not be backed over */ | ||
802 | 3152 | 11 | if (ceiling == 0) { | |
803 | 3869 | 12-16 | for (next = path->ptr; *next && git__isalpha(*next); ++next); | |
804 | 2973 | 17-19 | if (next[0] == ':' && next[1] == '/' && next[2] == '/') | |
805 | 8 | 20 | ceiling = (next + 3) - path->ptr; | |
806 | - | } | ||
807 | - | |||
808 | 3152 | 21 | base = to = from = path->ptr + ceiling; | |
809 | - | |||
810 | 8135 | 21,60 | while (*from) { | |
811 | 22863 | 22-25 | for (next = from; *next && *next != '/'; ++next); | |
812 | - | |||
813 | 4990 | 26 | len = next - from; | |
814 | - | |||
815 | 4990 | 26-28 | if (len == 1 && from[0] == '.') | |
816 | - | /* do nothing with singleton dot */; | ||
817 | - | |||
818 | 2233 | 29-31 | else if (len == 2 && from[0] == '.' && from[1] == '.') { | |
819 | - | /* error out if trying to up one from a hard base */ | ||
820 | 590 | 32,33 | if (to == base && ceiling != 0) { | |
821 | 7 | 34 | git_error_set(GIT_ERROR_INVALID, | |
822 | - | "cannot strip root component off url"); | ||
823 | 7 | 35 | return -1; | |
824 | - | } | ||
825 | - | |||
826 | - | /* no more path segments to strip, | ||
827 | - | * use '../' as a new base path */ | ||
828 | 583 | 36,50 | if (to == base) { | |
829 | 19 | 37 | if (*next == '/') | |
830 | 16 | 38 | len++; | |
831 | - | |||
832 | 19 | 39 | if (to != from) | |
833 | 3 | 40 | memmove(to, from, len); | |
834 | - | |||
835 | 19 | 41 | to += len; | |
836 | - | /* this is now the base, can't back up from a | ||
837 | - | * relative prefix */ | ||
838 | 19 | 41 | base = to; | |
839 | - | } else { | ||
840 | - | /* back up a path segment */ | ||
841 | 1128 | 42-45 | while (to > base && to[-1] == '/') to--; | |
842 | 5966 | 46-49 | while (to > base && to[-1] != '/') to--; | |
843 | - | } | ||
844 | - | } else { | ||
845 | 1643 | 51,52 | if (*next == '/' && *from != '/') | |
846 | 1283 | 53 | len++; | |
847 | - | |||
848 | 1643 | 54 | if (to != from) | |
849 | 369 | 55 | memmove(to, from, len); | |
850 | - | |||
851 | 1643 | 56 | to += len; | |
852 | - | } | ||
853 | - | |||
854 | 4983 | 57 | from += len; | |
855 | - | |||
856 | 5594 | 57-59 | while (*from == '/') from++; | |
857 | - | } | ||
858 | - | |||
859 | 3145 | 61 | *to = '\0'; | |
860 | - | |||
861 | 3145 | 61 | path->size = to - path->ptr; | |
862 | - | |||
863 | 3145 | 61 | return 0; | |
864 | - | } | ||
865 | - | |||
866 | 175 | 2 | int git_path_apply_relative(git_buf *target, const char *relpath) | |
867 | - | { | ||
868 | 175 | 2-4,6 | return git_buf_joinpath(target, git_buf_cstr(target), relpath) || | |
869 | 175 | 5 | git_path_resolve_relative(target, 0); | |
870 | - | } | ||
871 | - | |||
872 | 28279 | 2 | int git_path_cmp( | |
873 | - | const char *name1, size_t len1, int isdir1, | ||
874 | - | const char *name2, size_t len2, int isdir2, | ||
875 | - | int (*compare)(const char *, const char *, size_t)) | ||
876 | - | { | ||
877 | - | unsigned char c1, c2; | ||
878 | 28279 | 2 | size_t len = len1 < len2 ? len1 : len2; | |
879 | - | int cmp; | ||
880 | - | |||
881 | 28279 | 2 | cmp = compare(name1, name2, len); | |
882 | 28279 | 3 | if (cmp) | |
883 | 27992 | 4 | return cmp; | |
884 | - | |||
885 | 287 | 5 | c1 = name1[len]; | |
886 | 287 | 5 | c2 = name2[len]; | |
887 | - | |||
888 | 287 | 5,6 | if (c1 == '\0' && isdir1) | |
889 | 113 | 7 | c1 = '/'; | |
890 | - | |||
891 | 287 | 8,9 | if (c2 == '\0' && isdir2) | |
892 | 129 | 10 | c2 = '/'; | |
893 | - | |||
894 | 287 | 11 | return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; | |
895 | - | } | ||
896 | - | |||
897 | 705 | 2 | size_t git_path_common_dirlen(const char *one, const char *two) | |
898 | - | { | ||
899 | 705 | 2 | const char *p, *q, *dirsep = NULL; | |
900 | - | |||
901 | 4505 | 2,8-10 | for (p = one, q = two; *p && *q; p++, q++) { | |
902 | 4220 | 3,4 | if (*p == '/' && *q == '/') | |
903 | 719 | 5 | dirsep = p; | |
904 | 3501 | 6 | else if (*p != *q) | |
905 | 420 | 7 | break; | |
906 | - | } | ||
907 | - | |||
908 | 705 | 11 | return dirsep ? (dirsep - one) + 1 : 0; | |
909 | - | } | ||
910 | - | |||
911 | 75 | 2 | int git_path_make_relative(git_buf *path, const char *parent) | |
912 | - | { | ||
913 | - | const char *p, *q, *p_dirsep, *q_dirsep; | ||
914 | 75 | 2 | size_t plen = path->size, newlen, alloclen, depth = 1, i, offset; | |
915 | - | |||
916 | 2174 | 2,8-10 | for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { | |
917 | 2164 | 3,4 | if (*p == '/' && *q == '/') { | |
918 | 254 | 5 | p_dirsep = p; | |
919 | 254 | 5 | q_dirsep = q; | |
920 | - | } | ||
921 | 1910 | 6 | else if (*p != *q) | |
922 | 65 | 7 | break; | |
923 | - | } | ||
924 | - | |||
925 | - | /* need at least 1 common path segment */ | ||
926 | 75 | 11-13 | if ((p_dirsep == path->ptr || q_dirsep == parent) && | |
927 | 9 | 13,14 | (*p_dirsep != '/' || *q_dirsep != '/')) { | |
928 | 6 | 15 | git_error_set(GIT_ERROR_INVALID, | |
929 | - | "%s is not a parent of %s", parent, path->ptr); | ||
930 | 6 | 16 | return GIT_ENOTFOUND; | |
931 | - | } | ||
932 | - | |||
933 | 69 | 17,18 | if (*p == '/' && !*q) | |
934 | 3 | 19 | p++; | |
935 | 66 | 20,21 | else if (!*p && *q == '/') | |
936 | 2 | 22 | q++; | |
937 | 64 | 23,24 | else if (!*p && !*q) | |
938 | 1 | 25,26 | return git_buf_clear(path), 0; | |
939 | - | else { | ||
940 | 63 | 27 | p = p_dirsep + 1; | |
941 | 63 | 27 | q = q_dirsep + 1; | |
942 | - | } | ||
943 | - | |||
944 | 68 | 28 | plen -= (p - path->ptr); | |
945 | - | |||
946 | 68 | 28 | if (!*q) | |
947 | 7 | 29 | return git_buf_set(path, p, plen); | |
948 | - | |||
949 | 114 | 30-33 | for (; (q = strchr(q, '/')) && *(q + 1); q++) | |
950 | 53 | 31 | depth++; | |
951 | - | |||
952 | 61 | 34-40 | GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3); | |
953 | 61 | 41-47 | GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen); | |
954 | - | |||
955 | 61 | 48-54 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1); | |
956 | - | |||
957 | - | /* save the offset as we might realllocate the pointer */ | ||
958 | 61 | 55 | offset = p - path->ptr; | |
959 | 61 | 55,56 | if (git_buf_try_grow(path, alloclen, 1) < 0) | |
960 | ##### | 57 | return -1; | |
961 | 61 | 58 | p = path->ptr + offset; | |
962 | - | |||
963 | 61 | 58 | memmove(path->ptr + (depth * 3), p, plen + 1); | |
964 | - | |||
965 | 175 | 58-60 | for (i = 0; i < depth; i++) | |
966 | 114 | 59 | memcpy(path->ptr + (i * 3), "../", 3); | |
967 | - | |||
968 | 61 | 61 | path->size = newlen; | |
969 | 61 | 61 | return 0; | |
970 | - | } | ||
971 | - | |||
972 | ##### | 2 | bool git_path_has_non_ascii(const char *path, size_t pathlen) | |
973 | - | { | ||
974 | ##### | 2 | const uint8_t *scan = (const uint8_t *)path, *end; | |
975 | - | |||
976 | ##### | 2,5,6 | for (end = scan + pathlen; scan < end; ++scan) | |
977 | ##### | 3 | if (*scan & 0x80) | |
978 | ##### | 4 | return true; | |
979 | - | |||
980 | ##### | 7 | return false; | |
981 | - | } | ||
982 | - | |||
983 | - | #ifdef GIT_USE_ICONV | ||
984 | - | |||
985 | - | int git_path_iconv_init_precompose(git_path_iconv_t *ic) | ||
986 | - | { | ||
987 | - | git_buf_init(&ic->buf, 0); | ||
988 | - | ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING); | ||
989 | - | return 0; | ||
990 | - | } | ||
991 | - | |||
992 | - | void git_path_iconv_clear(git_path_iconv_t *ic) | ||
993 | - | { | ||
994 | - | if (ic) { | ||
995 | - | if (ic->map != (iconv_t)-1) | ||
996 | - | iconv_close(ic->map); | ||
997 | - | git_buf_dispose(&ic->buf); | ||
998 | - | } | ||
999 | - | } | ||
1000 | - | |||
1001 | - | int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) | ||
1002 | - | { | ||
1003 | - | char *nfd = (char*)*in, *nfc; | ||
1004 | - | size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; | ||
1005 | - | int retry = 1; | ||
1006 | - | |||
1007 | - | if (!ic || ic->map == (iconv_t)-1 || | ||
1008 | - | !git_path_has_non_ascii(*in, *inlen)) | ||
1009 | - | return 0; | ||
1010 | - | |||
1011 | - | git_buf_clear(&ic->buf); | ||
1012 | - | |||
1013 | - | while (1) { | ||
1014 | - | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1); | ||
1015 | - | if (git_buf_grow(&ic->buf, alloclen) < 0) | ||
1016 | - | return -1; | ||
1017 | - | |||
1018 | - | nfc = ic->buf.ptr + ic->buf.size; | ||
1019 | - | nfclen = ic->buf.asize - ic->buf.size; | ||
1020 | - | |||
1021 | - | rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen); | ||
1022 | - | |||
1023 | - | ic->buf.size = (nfc - ic->buf.ptr); | ||
1024 | - | |||
1025 | - | if (rv != (size_t)-1) | ||
1026 | - | break; | ||
1027 | - | |||
1028 | - | /* if we cannot convert the data (probably because iconv thinks | ||
1029 | - | * it is not valid UTF-8 source data), then use original data | ||
1030 | - | */ | ||
1031 | - | if (errno != E2BIG) | ||
1032 | - | return 0; | ||
1033 | - | |||
1034 | - | /* make space for 2x the remaining data to be converted | ||
1035 | - | * (with per retry overhead to avoid infinite loops) | ||
1036 | - | */ | ||
1037 | - | wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4); | ||
1038 | - | |||
1039 | - | if (retry++ > 4) | ||
1040 | - | goto fail; | ||
1041 | - | } | ||
1042 | - | |||
1043 | - | ic->buf.ptr[ic->buf.size] = '\0'; | ||
1044 | - | |||
1045 | - | *in = ic->buf.ptr; | ||
1046 | - | *inlen = ic->buf.size; | ||
1047 | - | |||
1048 | - | return 0; | ||
1049 | - | |||
1050 | - | fail: | ||
1051 | - | git_error_set(GIT_ERROR_OS, "unable to convert unicode path data"); | ||
1052 | - | return -1; | ||
1053 | - | } | ||
1054 | - | |||
1055 | - | static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; | ||
1056 | - | static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; | ||
1057 | - | |||
1058 | - | /* Check if the platform is decomposing unicode data for us. We will | ||
1059 | - | * emulate core Git and prefer to use precomposed unicode data internally | ||
1060 | - | * on these platforms, composing the decomposed unicode on the fly. | ||
1061 | - | * | ||
1062 | - | * This mainly happens on the Mac where HDFS stores filenames as | ||
1063 | - | * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will | ||
1064 | - | * return decomposed unicode from readdir() even when the actual | ||
1065 | - | * filesystem is storing precomposed unicode. | ||
1066 | - | */ | ||
1067 | - | bool git_path_does_fs_decompose_unicode(const char *root) | ||
1068 | - | { | ||
1069 | - | git_buf path = GIT_BUF_INIT; | ||
1070 | - | int fd; | ||
1071 | - | bool found_decomposed = false; | ||
1072 | - | char tmp[6]; | ||
1073 | - | |||
1074 | - | /* Create a file using a precomposed path and then try to find it | ||
1075 | - | * using the decomposed name. If the lookup fails, then we will mark | ||
1076 | - | * that we should precompose unicode for this repository. | ||
1077 | - | */ | ||
1078 | - | if (git_buf_joinpath(&path, root, nfc_file) < 0 || | ||
1079 | - | (fd = p_mkstemp(path.ptr)) < 0) | ||
1080 | - | goto done; | ||
1081 | - | p_close(fd); | ||
1082 | - | |||
1083 | - | /* record trailing digits generated by mkstemp */ | ||
1084 | - | memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); | ||
1085 | - | |||
1086 | - | /* try to look up as NFD path */ | ||
1087 | - | if (git_buf_joinpath(&path, root, nfd_file) < 0) | ||
1088 | - | goto done; | ||
1089 | - | memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); | ||
1090 | - | |||
1091 | - | found_decomposed = git_path_exists(path.ptr); | ||
1092 | - | |||
1093 | - | /* remove temporary file (using original precomposed path) */ | ||
1094 | - | if (git_buf_joinpath(&path, root, nfc_file) < 0) | ||
1095 | - | goto done; | ||
1096 | - | memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); | ||
1097 | - | |||
1098 | - | (void)p_unlink(path.ptr); | ||
1099 | - | |||
1100 | - | done: | ||
1101 | - | git_buf_dispose(&path); | ||
1102 | - | return found_decomposed; | ||
1103 | - | } | ||
1104 | - | |||
1105 | - | #else | ||
1106 | - | |||
1107 | 1 | 2 | bool git_path_does_fs_decompose_unicode(const char *root) | |
1108 | - | { | ||
1109 | - | GIT_UNUSED(root); | ||
1110 | 1 | 2 | return false; | |
1111 | - | } | ||
1112 | - | |||
1113 | - | #endif | ||
1114 | - | |||
1115 | - | #if defined(__sun) || defined(__GNU__) | ||
1116 | - | typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1]; | ||
1117 | - | #else | ||
1118 | - | typedef struct dirent path_dirent_data; | ||
1119 | - | #endif | ||
1120 | - | |||
1121 | 8796 | 2 | int git_path_direach( | |
1122 | - | git_buf *path, | ||
1123 | - | uint32_t flags, | ||
1124 | - | int (*fn)(void *, git_buf *), | ||
1125 | - | void *arg) | ||
1126 | - | { | ||
1127 | 8796 | 2 | int error = 0; | |
1128 | - | ssize_t wd_len; | ||
1129 | - | DIR *dir; | ||
1130 | - | struct dirent *de; | ||
1131 | - | |||
1132 | - | #ifdef GIT_USE_ICONV | ||
1133 | - | git_path_iconv_t ic = GIT_PATH_ICONV_INIT; | ||
1134 | - | #endif | ||
1135 | - | |||
1136 | - | GIT_UNUSED(flags); | ||
1137 | - | |||
1138 | 8796 | 2,3 | if (git_path_to_dir(path) < 0) | |
1139 | ##### | 4 | return -1; | |
1140 | - | |||
1141 | 8790 | 5 | wd_len = git_buf_len(path); | |
1142 | - | |||
1143 | 8797 | 6,7 | if ((dir = opendir(path->ptr)) == NULL) { | |
1144 | ##### | 8 | git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr); | |
1145 | ##### | 9,10 | if (errno == ENOENT) | |
1146 | ##### | 11 | return GIT_ENOTFOUND; | |
1147 | - | |||
1148 | ##### | 12 | return -1; | |
1149 | - | } | ||
1150 | - | |||
1151 | - | #ifdef GIT_USE_ICONV | ||
1152 | - | if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) | ||
1153 | - | (void)git_path_iconv_init_precompose(&ic); | ||
1154 | - | #endif | ||
1155 | - | |||
1156 | 52950 | 13,28,29 | while ((de = readdir(dir)) != NULL) { | |
1157 | 44288 | 14 | const char *de_path = de->d_name; | |
1158 | 44288 | 14 | size_t de_len = strlen(de_path); | |
1159 | - | |||
1160 | 44308 | 14,15 | if (git_path_is_dot_or_dotdot(de_path)) | |
1161 | 17571 | 16 | continue; | |
1162 | - | |||
1163 | - | #ifdef GIT_USE_ICONV | ||
1164 | - | if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) | ||
1165 | - | break; | ||
1166 | - | #endif | ||
1167 | - | |||
1168 | 26737 | 17,18 | if ((error = git_buf_put(path, de_path, de_len)) < 0) | |
1169 | ##### | 19 | break; | |
1170 | - | |||
1171 | 26625 | 20 | git_error_clear(); | |
1172 | 26557 | 21 | error = fn(arg, path); | |
1173 | - | |||
1174 | 26598 | 22 | git_buf_truncate(path, wd_len); /* restore path */ | |
1175 | - | |||
1176 | - | /* Only set our own error if the callback did not set one already */ | ||
1177 | 26564 | 23 | if (error != 0) { | |
1178 | 64 | 24,25 | if (!git_error_last()) | |
1179 | 60 | 26 | git_error_set_after_callback(error); | |
1180 | - | |||
1181 | 64 | 27 | break; | |
1182 | - | } | ||
1183 | - | } | ||
1184 | - | |||
1185 | 8726 | 30 | closedir(dir); | |
1186 | - | |||
1187 | - | #ifdef GIT_USE_ICONV | ||
1188 | - | git_path_iconv_clear(&ic); | ||
1189 | - | #endif | ||
1190 | - | |||
1191 | 8795 | 31 | return error; | |
1192 | - | } | ||
1193 | - | |||
1194 | - | #if defined(GIT_WIN32) && !defined(__MINGW32__) | ||
1195 | - | |||
1196 | - | /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7 | ||
1197 | - | * and better. | ||
1198 | - | */ | ||
1199 | - | #ifndef FIND_FIRST_EX_LARGE_FETCH | ||
1200 | - | # define FIND_FIRST_EX_LARGE_FETCH 2 | ||
1201 | - | #endif | ||
1202 | - | |||
1203 | - | int git_path_diriter_init( | ||
1204 | - | git_path_diriter *diriter, | ||
1205 | - | const char *path, | ||
1206 | - | unsigned int flags) | ||
1207 | - | { | ||
1208 | - | git_win32_path path_filter; | ||
1209 | - | |||
1210 | - | static int is_win7_or_later = -1; | ||
1211 | - | if (is_win7_or_later < 0) | ||
1212 | - | is_win7_or_later = git_has_win32_version(6, 1, 0); | ||
1213 | - | |||
1214 | - | assert(diriter && path); | ||
1215 | - | |||
1216 | - | memset(diriter, 0, sizeof(git_path_diriter)); | ||
1217 | - | diriter->handle = INVALID_HANDLE_VALUE; | ||
1218 | - | |||
1219 | - | if (git_buf_puts(&diriter->path_utf8, path) < 0) | ||
1220 | - | return -1; | ||
1221 | - | |||
1222 | - | path_trim_slashes(&diriter->path_utf8); | ||
1223 | - | |||
1224 | - | if (diriter->path_utf8.size == 0) { | ||
1225 | - | git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); | ||
1226 | - | return -1; | ||
1227 | - | } | ||
1228 | - | |||
1229 | - | if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || | ||
1230 | - | !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { | ||
1231 | - | git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path); | ||
1232 | - | return -1; | ||
1233 | - | } | ||
1234 | - | |||
1235 | - | diriter->handle = FindFirstFileExW( | ||
1236 | - | path_filter, | ||
1237 | - | is_win7_or_later ? FindExInfoBasic : FindExInfoStandard, | ||
1238 | - | &diriter->current, | ||
1239 | - | FindExSearchNameMatch, | ||
1240 | - | NULL, | ||
1241 | - | is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); | ||
1242 | - | |||
1243 | - | if (diriter->handle == INVALID_HANDLE_VALUE) { | ||
1244 | - | git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path); | ||
1245 | - | return -1; | ||
1246 | - | } | ||
1247 | - | |||
1248 | - | diriter->parent_utf8_len = diriter->path_utf8.size; | ||
1249 | - | diriter->flags = flags; | ||
1250 | - | return 0; | ||
1251 | - | } | ||
1252 | - | |||
1253 | - | static int diriter_update_paths(git_path_diriter *diriter) | ||
1254 | - | { | ||
1255 | - | size_t filename_len, path_len; | ||
1256 | - | |||
1257 | - | filename_len = wcslen(diriter->current.cFileName); | ||
1258 | - | |||
1259 | - | if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) || | ||
1260 | - | GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2)) | ||
1261 | - | return -1; | ||
1262 | - | |||
1263 | - | if (path_len > GIT_WIN_PATH_UTF16) { | ||
1264 | - | git_error_set(GIT_ERROR_FILESYSTEM, | ||
1265 | - | "invalid path '%.*ls\\%ls' (path too long)", | ||
1266 | - | diriter->parent_len, diriter->path, diriter->current.cFileName); | ||
1267 | - | return -1; | ||
1268 | - | } | ||
1269 | - | |||
1270 | - | diriter->path[diriter->parent_len] = L'\\'; | ||
1271 | - | memcpy(&diriter->path[diriter->parent_len+1], | ||
1272 | - | diriter->current.cFileName, filename_len * sizeof(wchar_t)); | ||
1273 | - | diriter->path[path_len-1] = L'\0'; | ||
1274 | - | |||
1275 | - | git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len); | ||
1276 | - | |||
1277 | - | if (diriter->parent_utf8_len > 0 && | ||
1278 | - | diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/') | ||
1279 | - | git_buf_putc(&diriter->path_utf8, '/'); | ||
1280 | - | |||
1281 | - | git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); | ||
1282 | - | |||
1283 | - | if (git_buf_oom(&diriter->path_utf8)) | ||
1284 | - | return -1; | ||
1285 | - | |||
1286 | - | return 0; | ||
1287 | - | } | ||
1288 | - | |||
1289 | - | int git_path_diriter_next(git_path_diriter *diriter) | ||
1290 | - | { | ||
1291 | - | bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); | ||
1292 | - | |||
1293 | - | do { | ||
1294 | - | /* Our first time through, we already have the data from | ||
1295 | - | * FindFirstFileW. Use it, otherwise get the next file. | ||
1296 | - | */ | ||
1297 | - | if (!diriter->needs_next) | ||
1298 | - | diriter->needs_next = 1; | ||
1299 | - | else if (!FindNextFileW(diriter->handle, &diriter->current)) | ||
1300 | - | return GIT_ITEROVER; | ||
1301 | - | } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName)); | ||
1302 | - | |||
1303 | - | if (diriter_update_paths(diriter) < 0) | ||
1304 | - | return -1; | ||
1305 | - | |||
1306 | - | return 0; | ||
1307 | - | } | ||
1308 | - | |||
1309 | - | int git_path_diriter_filename( | ||
1310 | - | const char **out, | ||
1311 | - | size_t *out_len, | ||
1312 | - | git_path_diriter *diriter) | ||
1313 | - | { | ||
1314 | - | assert(out && out_len && diriter); | ||
1315 | - | |||
1316 | - | assert(diriter->path_utf8.size > diriter->parent_utf8_len); | ||
1317 | - | |||
1318 | - | *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1]; | ||
1319 | - | *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1; | ||
1320 | - | return 0; | ||
1321 | - | } | ||
1322 | - | |||
1323 | - | int git_path_diriter_fullpath( | ||
1324 | - | const char **out, | ||
1325 | - | size_t *out_len, | ||
1326 | - | git_path_diriter *diriter) | ||
1327 | - | { | ||
1328 | - | assert(out && out_len && diriter); | ||
1329 | - | |||
1330 | - | *out = diriter->path_utf8.ptr; | ||
1331 | - | *out_len = diriter->path_utf8.size; | ||
1332 | - | return 0; | ||
1333 | - | } | ||
1334 | - | |||
1335 | - | int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) | ||
1336 | - | { | ||
1337 | - | assert(out && diriter); | ||
1338 | - | |||
1339 | - | return git_win32__file_attribute_to_stat(out, | ||
1340 | - | (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current, | ||
1341 | - | diriter->path); | ||
1342 | - | } | ||
1343 | - | |||
1344 | - | void git_path_diriter_free(git_path_diriter *diriter) | ||
1345 | - | { | ||
1346 | - | if (diriter == NULL) | ||
1347 | - | return; | ||
1348 | - | |||
1349 | - | git_buf_dispose(&diriter->path_utf8); | ||
1350 | - | |||
1351 | - | if (diriter->handle != INVALID_HANDLE_VALUE) { | ||
1352 | - | FindClose(diriter->handle); | ||
1353 | - | diriter->handle = INVALID_HANDLE_VALUE; | ||
1354 | - | } | ||
1355 | - | } | ||
1356 | - | |||
1357 | - | #else | ||
1358 | - | |||
1359 | 6747 | 2 | int git_path_diriter_init( | |
1360 | - | git_path_diriter *diriter, | ||
1361 | - | const char *path, | ||
1362 | - | unsigned int flags) | ||
1363 | - | { | ||
1364 | 6747 | 2-4 | assert(diriter && path); | |
1365 | - | |||
1366 | 6747 | 5 | memset(diriter, 0, sizeof(git_path_diriter)); | |
1367 | - | |||
1368 | 6748 | 5,6 | if (git_buf_puts(&diriter->path, path) < 0) | |
1369 | ##### | 7 | return -1; | |
1370 | - | |||
1371 | 6748 | 8 | path_trim_slashes(&diriter->path); | |
1372 | - | |||
1373 | 6748 | 9 | if (diriter->path.size == 0) { | |
1374 | ##### | 10 | git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path); | |
1375 | ##### | 11 | return -1; | |
1376 | - | } | ||
1377 | - | |||
1378 | 6748 | 12,13 | if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { | |
1379 | 4 | 14 | git_buf_dispose(&diriter->path); | |
1380 | - | |||
1381 | 4 | 15 | git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path); | |
1382 | 4 | 16 | return -1; | |
1383 | - | } | ||
1384 | - | |||
1385 | - | #ifdef GIT_USE_ICONV | ||
1386 | - | if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0) | ||
1387 | - | (void)git_path_iconv_init_precompose(&diriter->ic); | ||
1388 | - | #endif | ||
1389 | - | |||
1390 | 6743 | 17 | diriter->parent_len = diriter->path.size; | |
1391 | 6743 | 17 | diriter->flags = flags; | |
1392 | - | |||
1393 | 6743 | 17 | return 0; | |
1394 | - | } | ||
1395 | - | |||
1396 | 39262 | 2 | int git_path_diriter_next(git_path_diriter *diriter) | |
1397 | - | { | ||
1398 | - | struct dirent *de; | ||
1399 | - | const char *filename; | ||
1400 | - | size_t filename_len; | ||
1401 | 39262 | 2 | bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT); | |
1402 | 39262 | 2 | int error = 0; | |
1403 | - | |||
1404 | 39262 | 2,3 | assert(diriter); | |
1405 | - | |||
1406 | 39262 | 4,5 | errno = 0; | |
1407 | - | |||
1408 | - | do { | ||
1409 | 52767 | 6,7 | if ((de = readdir(diriter->dir)) == NULL) { | |
1410 | 6744 | 8,9 | if (!errno) | |
1411 | 6743 | 10 | return GIT_ITEROVER; | |
1412 | - | |||
1413 | ##### | 11 | git_error_set(GIT_ERROR_OS, | |
1414 | - | "could not read directory '%s'", diriter->path.ptr); | ||
1415 | ##### | 12 | return -1; | |
1416 | - | } | ||
1417 | 46023 | 13-15 | } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); | |
1418 | - | |||
1419 | 32538 | 16 | filename = de->d_name; | |
1420 | 32538 | 16 | filename_len = strlen(filename); | |
1421 | - | |||
1422 | - | #ifdef GIT_USE_ICONV | ||
1423 | - | if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && | ||
1424 | - | (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) | ||
1425 | - | return error; | ||
1426 | - | #endif | ||
1427 | - | |||
1428 | 32538 | 16 | git_buf_truncate(&diriter->path, diriter->parent_len); | |
1429 | - | |||
1430 | 32534 | 17,18 | if (diriter->parent_len > 0 && | |
1431 | 32532 | 18 | diriter->path.ptr[diriter->parent_len-1] != '/') | |
1432 | 32506 | 19 | git_buf_putc(&diriter->path, '/'); | |
1433 | - | |||
1434 | 32527 | 20 | git_buf_put(&diriter->path, filename, filename_len); | |
1435 | - | |||
1436 | 32531 | 21,22 | if (git_buf_oom(&diriter->path)) | |
1437 | ##### | 23 | return -1; | |
1438 | - | |||
1439 | 32531 | 24 | return error; | |
1440 | - | } | ||
1441 | - | |||
1442 | 3 | 2 | int git_path_diriter_filename( | |
1443 | - | const char **out, | ||
1444 | - | size_t *out_len, | ||
1445 | - | git_path_diriter *diriter) | ||
1446 | - | { | ||
1447 | 3 | 2-5 | assert(out && out_len && diriter); | |
1448 | - | |||
1449 | 3 | 6,7 | assert(diriter->path.size > diriter->parent_len); | |
1450 | - | |||
1451 | 3 | 8 | *out = &diriter->path.ptr[diriter->parent_len+1]; | |
1452 | 3 | 8 | *out_len = diriter->path.size - diriter->parent_len - 1; | |
1453 | 3 | 8 | return 0; | |
1454 | - | } | ||
1455 | - | |||
1456 | - | 2 | suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files)int git_path_diriter_fullpath( | |
1457 | - | const char **out, | ||
1458 | - | size_t *out_len, | ||
1459 | - | git_path_diriter *diriter) | ||
1460 | - | { | ||
1461 | - | 2-5 | suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) assert(out && out_len && diriter); | |
1462 | - | |||
1463 | - | 6 | suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) *out = diriter->path.ptr; | |
1464 | - | 6 | suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) *out_len = diriter->path.size; | |
1465 | - | 6 | suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) return 0; | |
1466 | - | } | ||
1467 | - | |||
1468 | - | 2 | suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files)int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter) | |
1469 | - | { | ||
1470 | - | 2-4 | suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files) assert(out && diriter); | |
1471 | - | |||
1472 | - | 5 | suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files) return git_path_lstat(diriter->path.ptr, out); | |
1473 | - | } | ||
1474 | - | |||
1475 | 6747 | 2 | void git_path_diriter_free(git_path_diriter *diriter) | |
1476 | - | { | ||
1477 | 6747 | 2 | if (diriter == NULL) | |
1478 | 6748 | 3,8 | return; | |
1479 | - | |||
1480 | 6747 | 4 | if (diriter->dir) { | |
1481 | 6743 | 5 | closedir(diriter->dir); | |
1482 | 6744 | 6 | diriter->dir = NULL; | |
1483 | - | } | ||
1484 | - | |||
1485 | - | #ifdef GIT_USE_ICONV | ||
1486 | - | git_path_iconv_clear(&diriter->ic); | ||
1487 | - | #endif | ||
1488 | - | |||
1489 | 6748 | 7 | git_buf_dispose(&diriter->path); | |
1490 | - | } | ||
1491 | - | |||
1492 | - | #endif | ||
1493 | - | |||
1494 | 272 | 2 | int git_path_dirload( | |
1495 | - | git_vector *contents, | ||
1496 | - | const char *path, | ||
1497 | - | size_t prefix_len, | ||
1498 | - | uint32_t flags) | ||
1499 | - | { | ||
1500 | 272 | 2 | git_path_diriter iter = GIT_PATH_DIRITER_INIT; | |
1501 | - | const char *name; | ||
1502 | - | size_t name_len; | ||
1503 | - | char *dup; | ||
1504 | - | int error; | ||
1505 | - | |||
1506 | 272 | 2-4 | assert(contents && path); | |
1507 | - | |||
1508 | 272 | 5,6 | if ((error = git_path_diriter_init(&iter, path, flags)) < 0) | |
1509 | ##### | 7 | return error; | |
1510 | - | |||
1511 | 5119 | 8,20,21 | while ((error = git_path_diriter_next(&iter)) == 0) { | |
1512 | 4847 | 9,10 | if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0) | |
1513 | ##### | 11 | break; | |
1514 | - | |||
1515 | 4847 | 12,13 | assert(name_len > prefix_len); | |
1516 | - | |||
1517 | 4847 | 14 | dup = git__strndup(name + prefix_len, name_len - prefix_len); | |
1518 | 4847 | 15,16 | GIT_ERROR_CHECK_ALLOC(dup); | |
1519 | - | |||
1520 | 4847 | 17,18 | if ((error = git_vector_insert(contents, dup)) < 0) | |
1521 | ##### | 19 | break; | |
1522 | - | } | ||
1523 | - | |||
1524 | 272 | 22 | if (error == GIT_ITEROVER) | |
1525 | 272 | 23 | error = 0; | |
1526 | - | |||
1527 | 272 | 24 | git_path_diriter_free(&iter); | |
1528 | 272 | 25 | return error; | |
1529 | - | } | ||
1530 | - | |||
1531 | 134 | 2 | int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path) | |
1532 | - | { | ||
1533 | 134 | 2,3 | if (git_path_is_local_file_url(url_or_path)) | |
1534 | 60 | 4 | return git_path_fromurl(local_path_out, url_or_path); | |
1535 | - | else | ||
1536 | 74 | 5 | return git_buf_sets(local_path_out, url_or_path); | |
1537 | - | } | ||
1538 | - | |||
1539 | - | /* Reject paths like AUX or COM1, or those versions that end in a dot or | ||
1540 | - | * colon. ("AUX." or "AUX:") | ||
1541 | - | */ | ||
1542 | 126 | 2 | GIT_INLINE(bool) verify_dospath( | |
1543 | - | const char *component, | ||
1544 | - | size_t len, | ||
1545 | - | const char dospath[3], | ||
1546 | - | bool trailing_num) | ||
1547 | - | { | ||
1548 | 126 | 2-4 | size_t last = trailing_num ? 4 : 3; | |
1549 | - | |||
1550 | 126 | 5-7 | if (len < last || git__strncasecmp(component, dospath, 3) != 0) | |
1551 | 99 | 8 | return true; | |
1552 | - | |||
1553 | 27 | 9-11 | if (trailing_num && (component[3] < '1' || component[3] > '9')) | |
1554 | 4 | 12 | return true; | |
1555 | - | |||
1556 | 16 | 14 | return (len > last && | |
1557 | 23 | 13,15-17 | component[last] != '.' && | |
1558 | 10 | 15 | component[last] != ':'); | |
1559 | - | } | ||
1560 | - | |||
1561 | 409 | 2 | static int32_t next_hfs_char(const char **in, size_t *len) | |
1562 | - | { | ||
1563 | 453 | 2,10 | while (*len) { | |
1564 | - | int32_t codepoint; | ||
1565 | 413 | 3 | int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint); | |
1566 | 413 | 4 | if (cp_len < 0) | |
1567 | 369 | 5,9 | return -1; | |
1568 | - | |||
1569 | 408 | 6 | (*in) += cp_len; | |
1570 | 408 | 6 | (*len) -= cp_len; | |
1571 | - | |||
1572 | - | /* these code points are ignored completely */ | ||
1573 | 408 | 6 | switch (codepoint) { | |
1574 | - | case 0x200c: /* ZERO WIDTH NON-JOINER */ | ||
1575 | - | case 0x200d: /* ZERO WIDTH JOINER */ | ||
1576 | - | case 0x200e: /* LEFT-TO-RIGHT MARK */ | ||
1577 | - | case 0x200f: /* RIGHT-TO-LEFT MARK */ | ||
1578 | - | case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */ | ||
1579 | - | case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */ | ||
1580 | - | case 0x202c: /* POP DIRECTIONAL FORMATTING */ | ||
1581 | - | case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */ | ||
1582 | - | case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */ | ||
1583 | - | case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */ | ||
1584 | - | case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */ | ||
1585 | - | case 0x206c: /* INHIBIT ARABIC FORM SHAPING */ | ||
1586 | - | case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */ | ||
1587 | - | case 0x206e: /* NATIONAL DIGIT SHAPES */ | ||
1588 | - | case 0x206f: /* NOMINAL DIGIT SHAPES */ | ||
1589 | - | case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */ | ||
1590 | 44 | 7 | continue; | |
1591 | - | } | ||
1592 | - | |||
1593 | - | /* fold into lowercase -- this will only fold characters in | ||
1594 | - | * the ASCII range, which is perfectly fine, because the | ||
1595 | - | * git folder name can only be composed of ascii characters | ||
1596 | - | */ | ||
1597 | 364 | 8 | return git__tolower(codepoint); | |
1598 | - | } | ||
1599 | 40 | 11 | return 0; /* NULL byte -- end of string */ | |
1600 | - | } | ||
1601 | - | |||
1602 | 105 | 2 | static bool verify_dotgit_hfs_generic(const char *path, size_t len, const char *needle, size_t needle_len) | |
1603 | - | { | ||
1604 | - | size_t i; | ||
1605 | - | char c; | ||
1606 | - | |||
1607 | 105 | 2,3 | if (next_hfs_char(&path, &len) != '.') | |
1608 | 39 | 4 | return true; | |
1609 | - | |||
1610 | 304 | 5,9,10 | for (i = 0; i < needle_len; i++) { | |
1611 | 250 | 6 | c = next_hfs_char(&path, &len); | |
1612 | 250 | 7 | if (c != needle[i]) | |
1613 | 12 | 8 | return true; | |
1614 | - | } | ||
1615 | - | |||
1616 | 54 | 11,12 | if (next_hfs_char(&path, &len) != '\0') | |
1617 | 19 | 13 | return true; | |
1618 | - | |||
1619 | 35 | 14 | return false; | |
1620 | - | } | ||
1621 | - | |||
1622 | 81 | 2 | static bool verify_dotgit_hfs(const char *path, size_t len) | |
1623 | - | { | ||
1624 | 81 | 2 | return verify_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git")); | |
1625 | - | } | ||
1626 | - | |||
1627 | 94215 | 2 | GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len) | |
1628 | - | { | ||
1629 | 94215 | 2 | git_buf *reserved = git_repository__reserved_names_win32; | |
1630 | 94215 | 2 | size_t reserved_len = git_repository__reserved_names_win32_len; | |
1631 | 94215 | 2 | size_t start = 0, i; | |
1632 | - | |||
1633 | 94215 | 2 | if (repo) | |
1634 | 19387 | 3 | git_repository__reserved_names(&reserved, &reserved_len, repo, true); | |
1635 | - | |||
1636 | 281292 | 4,8,9 | for (i = 0; i < reserved_len; i++) { | |
1637 | 187751 | 5 | git_buf *r = &reserved[i]; | |
1638 | - | |||
1639 | 187751 | 5,6 | if (len >= r->size && | |
1640 | 143834 | 6 | strncasecmp(path, r->ptr, r->size) == 0) { | |
1641 | 674 | 7 | start = r->size; | |
1642 | 674 | 7 | break; | |
1643 | - | } | ||
1644 | - | } | ||
1645 | - | |||
1646 | 94228 | 10 | if (!start) | |
1647 | 93538 | 11 | return true; | |
1648 | - | |||
1649 | - | /* | ||
1650 | - | * Reject paths that start with Windows-style directory separators | ||
1651 | - | * (".git\") or NTFS alternate streams (".git:") and could be used | ||
1652 | - | * to write to the ".git" directory on Windows platforms. | ||
1653 | - | */ | ||
1654 | 690 | 12,13 | if (path[start] == '\\' || path[start] == ':') | |
1655 | 6 | 14 | return false; | |
1656 | - | |||
1657 | - | /* Reject paths like '.git ' or '.git.' */ | ||
1658 | 704 | 15,19,20 | for (i = start; i < len; i++) { | |
1659 | 669 | 16,17 | if (path[i] != ' ' && path[i] != '.') | |
1660 | 649 | 18 | return true; | |
1661 | - | } | ||
1662 | - | |||
1663 | 35 | 21 | return false; | |
1664 | - | } | ||
1665 | - | |||
1666 | - | /* | ||
1667 | - | * Windows paths that end with spaces and/or dots are elided to the | ||
1668 | - | * path without them for backward compatibility. That is to say | ||
1669 | - | * that opening file "foo ", "foo." or even "foo . . ." will all | ||
1670 | - | * map to a filename of "foo". This function identifies spaces and | ||
1671 | - | * dots at the end of a filename, whether the proper end of the | ||
1672 | - | * filename (end of string) or a colon (which would indicate a | ||
1673 | - | * Windows alternate data stream.) | ||
1674 | - | */ | ||
1675 | 55 | 2 | GIT_INLINE(bool) ntfs_end_of_filename(const char *path) | |
1676 | - | { | ||
1677 | 55 | 2 | const char *c = path; | |
1678 | - | |||
1679 | 71 | 9 | for (;; c++) { | |
1680 | 126 | 3,4 | if (*c == '\0' || *c == ':') | |
1681 | 51 | 5 | return true; | |
1682 | 75 | 6,7 | if (*c != ' ' && *c != '.') | |
1683 | 4 | 8 | return false; | |
1684 | 71 | 9 | } | |
1685 | - | |||
1686 | - | return true; | ||
1687 | - | } | ||
1688 | - | |||
1689 | 184 | 2 | GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const char *dotgit_name, size_t dotgit_len, const char *shortname_pfix) | |
1690 | - | { | ||
1691 | - | int i, saw_tilde; | ||
1692 | - | |||
1693 | 184 | 2-4 | if (name[0] == '.' && len >= dotgit_len && | |
1694 | 35 | 4 | !strncasecmp(name + 1, dotgit_name, dotgit_len)) { | |
1695 | 31 | 5,6 | return !ntfs_end_of_filename(name + dotgit_len + 1); | |
1696 | - | } | ||
1697 | - | |||
1698 | - | /* Detect the basic NTFS shortname with the first six chars */ | ||
1699 | 153 | 7-9 | if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' && | |
1700 | 13 | 9,10 | name[7] >= '1' && name[7] <= '4') | |
1701 | 13 | 11,12 | return !ntfs_end_of_filename(name + 8); | |
1702 | - | |||
1703 | - | /* Catch fallback names */ | ||
1704 | 279 | 13,31,32 | for (i = 0, saw_tilde = 0; i < 8; i++) { | |
1705 | 268 | 14 | if (name[i] == '\0') { | |
1706 | 2 | 15 | return true; | |
1707 | 266 | 16 | } else if (saw_tilde) { | |
1708 | 35 | 17,18 | if (name[i] < '0' || name[i] > '9') | |
1709 | ##### | 19 | return true; | |
1710 | 231 | 20 | } else if (name[i] == '~') { | |
1711 | 18 | 21,22 | if (name[i+1] < '1' || name[i+1] > '9') | |
1712 | 5 | 23 | return true; | |
1713 | 13 | 24 | saw_tilde = 1; | |
1714 | 213 | 25 | } else if (i >= 6) { | |
1715 | ##### | 26 | return true; | |
1716 | 213 | 27 | } else if ((unsigned char)name[i] > 127) { | |
1717 | ##### | 28 | return true; | |
1718 | 213 | 29 | } else if (git__tolower(name[i]) != shortname_pfix[i]) { | |
1719 | 122 | 30 | return true; | |
1720 | - | } | ||
1721 | - | } | ||
1722 | - | |||
1723 | 11 | 33 | return !ntfs_end_of_filename(name + i); | |
1724 | - | } | ||
1725 | - | |||
1726 | 1236515 | 2 | GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags) | |
1727 | - | { | ||
1728 | 1236515 | 2,3 | if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\') | |
1729 | 3 | 4 | return false; | |
1730 | - | |||
1731 | 1236512 | 5,6 | if ((flags & GIT_PATH_REJECT_SLASH) && c == '/') | |
1732 | 3 | 7 | return false; | |
1733 | - | |||
1734 | 1236509 | 8 | if (flags & GIT_PATH_REJECT_NT_CHARS) { | |
1735 | 45 | 9 | if (c < 32) | |
1736 | 2 | 10 | return false; | |
1737 | - | |||
1738 | 43 | 11 | switch (c) { | |
1739 | - | case '<': | ||
1740 | - | case '>': | ||
1741 | - | case ':': | ||
1742 | - | case '"': | ||
1743 | - | case '|': | ||
1744 | - | case '?': | ||
1745 | - | case '*': | ||
1746 | 7 | 12 | return false; | |
1747 | - | } | ||
1748 | - | } | ||
1749 | - | |||
1750 | 1236500 | 13 | return true; | |
1751 | - | } | ||
1752 | - | |||
1753 | - | /* | ||
1754 | - | * Return the length of the common prefix between str and prefix, comparing them | ||
1755 | - | * case-insensitively (must be ASCII to match). | ||
1756 | - | */ | ||
1757 | 1 | 2 | GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *prefix) | |
1758 | - | { | ||
1759 | 1 | 2 | size_t count = 0; | |
1760 | - | |||
1761 | 12 | 2,4,5 | while (len >0 && tolower(*str) == tolower(*prefix)) { | |
1762 | 11 | 3 | count++; | |
1763 | 11 | 3 | str++; | |
1764 | 11 | 3 | prefix++; | |
1765 | 11 | 3 | len--; | |
1766 | - | } | ||
1767 | - | |||
1768 | 1 | 6 | return count; | |
1769 | - | } | ||
1770 | - | |||
1771 | - | /* | ||
1772 | - | * We fundamentally don't like some paths when dealing with user-inputted | ||
1773 | - | * strings (in checkout or ref names): we don't want dot or dot-dot | ||
1774 | - | * anywhere, we want to avoid writing weird paths on Windows that can't | ||
1775 | - | * be handled by tools that use the non-\\?\ APIs, we don't want slashes | ||
1776 | - | * or double slashes at the end of paths that can make them ambiguous. | ||
1777 | - | * | ||
1778 | - | * For checkout, we don't want to recurse into ".git" either. | ||
1779 | - | */ | ||
1780 | 105774 | 2 | static bool verify_component( | |
1781 | - | git_repository *repo, | ||
1782 | - | const char *component, | ||
1783 | - | size_t len, | ||
1784 | - | uint16_t mode, | ||
1785 | - | unsigned int flags) | ||
1786 | - | { | ||
1787 | 105774 | 2 | if (len == 0) | |
1788 | 11 | 3 | return false; | |
1789 | - | |||
1790 | 105763 | 4,5 | if ((flags & GIT_PATH_REJECT_TRAVERSAL) && | |
1791 | 2812 | 6 | len == 1 && component[0] == '.') | |
1792 | 16 | 7 | return false; | |
1793 | - | |||
1794 | 105747 | 8,9 | if ((flags & GIT_PATH_REJECT_TRAVERSAL) && | |
1795 | 4120 | 10,11 | len == 2 && component[0] == '.' && component[1] == '.') | |
1796 | 22 | 12 | return false; | |
1797 | - | |||
1798 | 105725 | 13,14 | if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.') | |
1799 | 4 | 15 | return false; | |
1800 | - | |||
1801 | 105721 | 16,17 | if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ') | |
1802 | 5 | 18 | return false; | |
1803 | - | |||
1804 | 105716 | 19,20 | if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':') | |
1805 | 4 | 21 | return false; | |
1806 | - | |||
1807 | 105712 | 22 | if (flags & GIT_PATH_REJECT_DOS_PATHS) { | |
1808 | 27 | 23,24,26 | if (!verify_dospath(component, len, "CON", false) || | |
1809 | 26 | 25,28 | !verify_dospath(component, len, "PRN", false) || | |
1810 | 25 | 27,30 | !verify_dospath(component, len, "AUX", false) || | |
1811 | 19 | 29,32 | !verify_dospath(component, len, "NUL", false) || | |
1812 | 18 | 31,34 | !verify_dospath(component, len, "COM", true) || | |
1813 | 11 | 33 | !verify_dospath(component, len, "LPT", true)) | |
1814 | 17 | 35 | return false; | |
1815 | - | } | ||
1816 | - | |||
1817 | 105695 | 36 | if (flags & GIT_PATH_REJECT_DOT_GIT_HFS) { | |
1818 | 81 | 37,38 | if (!verify_dotgit_hfs(component, len)) | |
1819 | 31 | 39 | return false; | |
1820 | 50 | 40-42 | if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS)) | |
1821 | 2 | 43 | return false; | |
1822 | - | } | ||
1823 | - | |||
1824 | 105662 | 44 | if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) { | |
1825 | 94226 | 45,46 | if (!verify_dotgit_ntfs(repo, component, len)) | |
1826 | 41 | 47 | return false; | |
1827 | 94185 | 48-50 | if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS)) | |
1828 | 3 | 51 | return false; | |
1829 | - | } | ||
1830 | - | |||
1831 | - | /* don't bother rerunning the `.git` test if we ran the HFS or NTFS | ||
1832 | - | * specific tests, they would have already rejected `.git`. | ||
1833 | - | */ | ||
1834 | 105632 | 52,53 | if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && | |
1835 | 105593 | 53,54 | (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && | |
1836 | 11437 | 54 | (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) { | |
1837 | 13 | 55,56 | if (len >= 4 && | |
1838 | 8 | 56,57 | component[0] == '.' && | |
1839 | 7 | 57-59 | (component[1] == 'g' || component[1] == 'G') && | |
1840 | 7 | 59-61 | (component[2] == 'i' || component[2] == 'I') && | |
1841 | 7 | 61,62 | (component[3] == 't' || component[3] == 'T')) { | |
1842 | 7 | 63 | if (len == 4) | |
1843 | 6 | 64 | return false; | |
1844 | - | |||
1845 | 1 | 65-67 | if (S_ISLNK(mode) && common_prefix_icase(component, len, ".gitmodules") == len) | |
1846 | 1 | 68 | return false; | |
1847 | - | } | ||
1848 | - | } | ||
1849 | - | |||
1850 | 105625 | 69 | return true; | |
1851 | - | } | ||
1852 | - | |||
1853 | 52826 | 2 | GIT_INLINE(unsigned int) dotgit_flags( | |
1854 | - | git_repository *repo, | ||
1855 | - | unsigned int flags) | ||
1856 | - | { | ||
1857 | 52826 | 2 | int protectHFS = 0, protectNTFS = 1; | |
1858 | 52826 | 2 | int error = 0; | |
1859 | - | |||
1860 | 52826 | 2 | flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; | |
1861 | - | |||
1862 | - | #ifdef __APPLE__ | ||
1863 | - | protectHFS = 1; | ||
1864 | - | #endif | ||
1865 | - | |||
1866 | 52826 | 2,3 | if (repo && !protectHFS) | |
1867 | 16190 | 4 | error = git_repository__configmap_lookup(&protectHFS, repo, GIT_CONFIGMAP_PROTECTHFS); | |
1868 | 52829 | 5,6 | if (!error && protectHFS) | |
1869 | 57 | 7 | flags |= GIT_PATH_REJECT_DOT_GIT_HFS; | |
1870 | - | |||
1871 | 52829 | 8 | if (repo) | |
1872 | 16190 | 9 | error = git_repository__configmap_lookup(&protectNTFS, repo, GIT_CONFIGMAP_PROTECTNTFS); | |
1873 | 52829 | 10,11 | if (!error && protectNTFS) | |
1874 | 52827 | 12 | flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; | |
1875 | - | |||
1876 | 52829 | 13 | return flags; | |
1877 | - | } | ||
1878 | - | |||
1879 | 58564 | 2 | bool git_path_isvalid( | |
1880 | - | git_repository *repo, | ||
1881 | - | const char *path, | ||
1882 | - | uint16_t mode, | ||
1883 | - | unsigned int flags) | ||
1884 | - | { | ||
1885 | - | const char *start, *c; | ||
1886 | - | |||
1887 | - | /* Upgrade the ".git" checks based on platform */ | ||
1888 | 58564 | 2 | if ((flags & GIT_PATH_REJECT_DOT_GIT)) | |
1889 | 52829 | 3 | flags = dotgit_flags(repo, flags); | |
1890 | - | |||
1891 | 1295336 | 4,13,14 | for (start = c = path; *c; c++) { | |
1892 | 1236867 | 5,6 | if (!verify_char(*c, flags)) | |
1893 | 15 | 7 | return false; | |
1894 | - | |||
1895 | 1236852 | 8 | if (*c == '/') { | |
1896 | 47302 | 9,10 | if (!verify_component(repo, start, (c - start), mode, flags)) | |
1897 | 73 | 11 | return false; | |
1898 | - | |||
1899 | 47229 | 12 | start = c+1; | |
1900 | - | } | ||
1901 | - | } | ||
1902 | - | |||
1903 | 58474 | 15 | return verify_component(repo, start, (c - start), mode, flags); | |
1904 | - | } | ||
1905 | - | |||
1906 | 6 | 2 | int git_path_normalize_slashes(git_buf *out, const char *path) | |
1907 | - | { | ||
1908 | - | int error; | ||
1909 | - | char *p; | ||
1910 | - | |||
1911 | 6 | 2,3 | if ((error = git_buf_puts(out, path)) < 0) | |
1912 | ##### | 4 | return error; | |
1913 | - | |||
1914 | 113 | 5,8,9 | for (p = out->ptr; *p; p++) { | |
1915 | 107 | 6 | if (*p == '\\') | |
1916 | 16 | 7 | *p = '/'; | |
1917 | - | } | ||
1918 | - | |||
1919 | 6 | 10 | return 0; | |
1920 | - | } | ||
1921 | - | |||
1922 | - | static const struct { | ||
1923 | - | const char *file; | ||
1924 | - | const char *hash; | ||
1925 | - | size_t filelen; | ||
1926 | - | } gitfiles[] = { | ||
1927 | - | { "gitignore", "gi250a", CONST_STRLEN("gitignore") }, | ||
1928 | - | { "gitmodules", "gi7eba", CONST_STRLEN("gitmodules") }, | ||
1929 | - | { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") } | ||
1930 | - | }; | ||
1931 | - | |||
1932 | 191 | 2 | extern int git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs) | |
1933 | - | { | ||
1934 | - | const char *file, *hash; | ||
1935 | - | size_t filelen; | ||
1936 | - | |||
1937 | 191 | 2 | if (!(gitfile >= GIT_PATH_GITFILE_GITIGNORE && gitfile < ARRAY_SIZE(gitfiles))) { | |
1938 | 2 | 3 | git_error_set(GIT_ERROR_OS, "invalid gitfile for path validation"); | |
1939 | 2 | 4 | return -1; | |
1940 | - | } | ||
1941 | - | |||
1942 | 189 | 5 | file = gitfiles[gitfile].file; | |
1943 | 189 | 5 | filelen = gitfiles[gitfile].filelen; | |
1944 | 189 | 5 | hash = gitfiles[gitfile].hash; | |
1945 | - | |||
1946 | 189 | 5 | switch (fs) { | |
1947 | - | case GIT_PATH_FS_GENERIC: | ||
1948 | 67 | 6,7,9-12 | return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) || | |
1949 | 19 | 8 | !verify_dotgit_hfs_generic(path, pathlen, file, filelen); | |
1950 | - | case GIT_PATH_FS_NTFS: | ||
1951 | 117 | 13,14 | return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash); | |
1952 | - | case GIT_PATH_FS_HFS: | ||
1953 | 5 | 15,16 | return !verify_dotgit_hfs_generic(path, pathlen, file, filelen); | |
1954 | - | default: | ||
1955 | ##### | 17 | git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation"); | |
1956 | ##### | 18 | return -1; | |
1957 | - | } | ||
1958 | - | } | ||
1959 | - | |||
1960 | 2813 | 2 | bool git_path_supports_symlinks(const char *dir) | |
1961 | - | { | ||
1962 | 2813 | 2 | git_buf path = GIT_BUF_INIT; | |
1963 | 2813 | 2 | bool supported = false; | |
1964 | - | struct stat st; | ||
1965 | - | int fd; | ||
1966 | - | |||
1967 | 2813 | 2,3,5 | if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 || | |
1968 | 2813 | 4,7 | p_close(fd) < 0 || | |
1969 | 2813 | 6,9 | p_unlink(path.ptr) < 0 || | |
1970 | 2813 | 8,11 | p_symlink("testing", path.ptr) < 0 || | |
1971 | 2813 | 10 | p_lstat(path.ptr, &st) < 0) | |
1972 | - | goto done; | ||
1973 | - | |||
1974 | 2813 | 12 | supported = (S_ISLNK(st.st_mode) != 0); | |
1975 | - | done: | ||
1976 | 2813 | 13 | if (path.size) | |
1977 | 2813 | 14 | (void)p_unlink(path.ptr); | |
1978 | 2813 | 15 | git_buf_dispose(&path); | |
1979 | 2813 | 16 | return supported; | |
1980 | - | } | ||
1981 | - | |||
1982 | 3366 | 2 | int git_path_validate_system_file_ownership(const char *path) | |
1983 | - | { | ||
1984 | - | #ifndef GIT_WIN32 | ||
1985 | - | GIT_UNUSED(path); | ||
1986 | 3366 | 2 | return GIT_OK; | |
1987 | - | #else | ||
1988 | - | git_win32_path buf; | ||
1989 | - | PSID owner_sid; | ||
1990 | - | PSECURITY_DESCRIPTOR descriptor = NULL; | ||
1991 | - | HANDLE token; | ||
1992 | - | TOKEN_USER *info = NULL; | ||
1993 | - | DWORD err, len; | ||
1994 | - | int ret; | ||
1995 | - | |||
1996 | - | if (git_win32_path_from_utf8(buf, path) < 0) | ||
1997 | - | return -1; | ||
1998 | - | |||
1999 | - | err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, | ||
2000 | - | OWNER_SECURITY_INFORMATION | | ||
2001 | - | DACL_SECURITY_INFORMATION, | ||
2002 | - | &owner_sid, NULL, NULL, NULL, &descriptor); | ||
2003 | - | |||
2004 | - | if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { | ||
2005 | - | ret = GIT_ENOTFOUND; | ||
2006 | - | goto cleanup; | ||
2007 | - | } | ||
2008 | - | |||
2009 | - | if (err != ERROR_SUCCESS) { | ||
2010 | - | git_error_set(GIT_ERROR_OS, "failed to get security information"); | ||
2011 | - | ret = GIT_ERROR; | ||
2012 | - | goto cleanup; | ||
2013 | - | } | ||
2014 | - | |||
2015 | - | if (!IsValidSid(owner_sid)) { | ||
2016 | - | git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); | ||
2017 | - | ret = GIT_ERROR; | ||
2018 | - | goto cleanup; | ||
2019 | - | } | ||
2020 | - | |||
2021 | - | if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || | ||
2022 | - | IsWellKnownSid(owner_sid, WinLocalSystemSid)) { | ||
2023 | - | ret = GIT_OK; | ||
2024 | - | goto cleanup; | ||
2025 | - | } | ||
2026 | - | |||
2027 | - | /* Obtain current user's SID */ | ||
2028 | - | if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && | ||
2029 | - | !GetTokenInformation(token, TokenUser, NULL, 0, &len)) { | ||
2030 | - | info = git__malloc(len); | ||
2031 | - | GIT_ERROR_CHECK_ALLOC(info); | ||
2032 | - | if (!GetTokenInformation(token, TokenUser, info, len, &len)) { | ||
2033 | - | git__free(info); | ||
2034 | - | info = NULL; | ||
2035 | - | } | ||
2036 | - | } | ||
2037 | - | |||
2038 | - | /* | ||
2039 | - | * If the file is owned by the same account that is running the current | ||
2040 | - | * process, it's okay to read from that file. | ||
2041 | - | */ | ||
2042 | - | if (info && EqualSid(owner_sid, info->User.Sid)) | ||
2043 | - | ret = GIT_OK; | ||
2044 | - | else { | ||
2045 | - | git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid"); | ||
2046 | - | ret = GIT_ERROR; | ||
2047 | - | } | ||
2048 | - | free(info); | ||
2049 | - | |||
2050 | - | cleanup: | ||
2051 | - | if (descriptor) | ||
2052 | - | LocalFree(descriptor); | ||
2053 | - | |||
2054 | - | return ret; | ||
2055 | - | #endif | ||
2056 | - | } |