5 int gitfo_mkdir_2file(const char *file_path
)
7 const int mode
= 0755; /* or 0777 ? */
8 int error
= GIT_SUCCESS
;
9 char target_folder_path
[GIT_PATH_MAX
];
11 error
= git__dirname_r(target_folder_path
, sizeof(target_folder_path
), file_path
);
12 if (error
< GIT_SUCCESS
)
13 return git__throw(GIT_EINVALIDPATH
, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path
);
15 /* Does the containing folder exist? */
16 if (gitfo_isdir(target_folder_path
)) {
17 git__joinpath(target_folder_path
, target_folder_path
, ""); /* Ensure there's a trailing slash */
19 /* Let's create the tree structure */
20 error
= gitfo_mkdir_recurs(target_folder_path
, mode
);
21 if (error
< GIT_SUCCESS
)
22 return error
; /* The callee already takes care of setting the correct error message. */
28 int gitfo_mktemp(char *path_out
, const char *filename
)
32 strcpy(path_out
, filename
);
33 strcat(path_out
, "_git2_XXXXXX");
36 /* FIXME: there may be race conditions when multi-threading
38 if (_mktemp_s(path_out
, GIT_PATH_MAX
) != 0)
39 return git__throw(GIT_EOSERR
, "Failed to make temporary file %s", path_out
);
41 fd
= gitfo_creat(path_out
, 0744);
43 fd
= mkstemp(path_out
);
46 return fd
>= 0 ? fd
: git__throw(GIT_EOSERR
, "Failed to create temporary file %s", path_out
);
49 int gitfo_open(const char *path
, int flags
)
51 int fd
= open(path
, flags
| O_BINARY
);
52 return fd
>= 0 ? fd
: git__throw(GIT_EOSERR
, "Failed to open %s", path
);
55 int gitfo_creat(const char *path
, int mode
)
57 int fd
= open(path
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, mode
);
58 return fd
>= 0 ? fd
: git__throw(GIT_EOSERR
, "Failed to create file. Could not open %s", path
);
61 int gitfo_creat_force(const char *path
, int mode
)
63 if (gitfo_mkdir_2file(path
) < GIT_SUCCESS
)
64 return git__throw(GIT_EOSERR
, "Failed to create file %s", path
);
66 return gitfo_creat(path
, mode
);
69 int gitfo_read(git_file fd
, void *buf
, size_t cnt
)
73 ssize_t r
= read(fd
, b
, cnt
);
75 if (errno
== EINTR
|| errno
== EAGAIN
)
77 return git__throw(GIT_EOSERR
, "Failed to read from file");
81 return git__throw(GIT_EOSERR
, "Failed to read from file");
89 int gitfo_write(git_file fd
, void *buf
, size_t cnt
)
93 ssize_t r
= write(fd
, b
, cnt
);
95 if (errno
== EINTR
|| errno
== EAGAIN
)
97 return git__throw(GIT_EOSERR
, "Failed to write to file");
101 return git__throw(GIT_EOSERR
, "Failed to write to file");
109 int gitfo_isdir(const char *path
)
115 return git__throw(GIT_ENOTFOUND
, "No path given to gitfo_isdir");
119 /* win32: stat path for folders cannot end in a slash */
120 if (path
[len
- 1] == '/') {
121 char *path_fixed
= NULL
;
122 path_fixed
= git__strdup(path
);
123 path_fixed
[len
- 1] = 0;
124 stat_error
= gitfo_stat(path_fixed
, &st
);
127 stat_error
= gitfo_stat(path
, &st
);
130 if (stat_error
< GIT_SUCCESS
)
131 return git__throw(GIT_ENOTFOUND
, "%s does not exist", path
);
133 if (!S_ISDIR(st
.st_mode
))
134 return git__throw(GIT_ENOTFOUND
, "%s is not a directory", path
);
139 int gitfo_isfile(const char *path
)
145 return git__throw(GIT_ENOTFOUND
, "No path given to gitfo_isfile");
147 stat_error
= gitfo_stat(path
, &st
);
149 if (stat_error
< GIT_SUCCESS
)
150 return git__throw(GIT_ENOTFOUND
, "%s does not exist", path
);
152 if (!S_ISREG(st
.st_mode
))
153 return git__throw(GIT_ENOTFOUND
, "%s is not a file", path
);
158 int gitfo_exists(const char *path
)
161 return access(path
, F_OK
);
164 git_off_t
gitfo_size(git_file fd
)
167 if (gitfo_fstat(fd
, &sb
))
168 return git__throw(GIT_EOSERR
, "Failed to get size of file. File missing or corrupted");
172 int gitfo_read_file(gitfo_buf
*obj
, const char *path
)
179 assert(obj
&& path
&& *path
);
181 if ((fd
= gitfo_open(path
, O_RDONLY
)) < 0)
182 return git__throw(GIT_ERROR
, "Failed to open %s for reading", path
);
184 if (((size
= gitfo_size(fd
)) < 0) || !git__is_sizet(size
+1)) {
186 return git__throw(GIT_ERROR
, "Failed to read file `%s`. An error occured while calculating its size", path
);
190 if ((buff
= git__malloc(len
+ 1)) == NULL
) {
195 if (gitfo_read(fd
, buff
, len
) < 0) {
198 return git__throw(GIT_ERROR
, "Failed to read file `%s`", path
);
210 void gitfo_free_buf(gitfo_buf
*obj
)
217 int gitfo_mv(const char *from
, const char *to
)
223 * Win32 POSIX compilance my ass. If the destination
224 * file exists, the `rename` call fails. This is as
225 * close as it gets with the Win32 API.
227 error
= MoveFileEx(from
, to
, MOVEFILE_REPLACE_EXISTING
| MOVEFILE_COPY_ALLOWED
) ? GIT_SUCCESS
: GIT_EOSERR
;
229 /* Don't even try this on Win32 */
230 if (!link(from
, to
)) {
235 if (!rename(from
, to
))
241 if (error
< GIT_SUCCESS
)
242 return git__throw(error
, "Failed to move file from `%s` to `%s`", from
, to
);
247 int gitfo_mv_force(const char *from
, const char *to
)
249 if (gitfo_mkdir_2file(to
) < GIT_SUCCESS
)
250 return GIT_EOSERR
; /* The callee already takes care of setting the correct error message. */
252 return gitfo_mv(from
, to
); /* The callee already takes care of setting the correct error message. */
255 int gitfo_map_ro(git_map
*out
, git_file fd
, git_off_t begin
, size_t len
)
257 if (git__mmap(out
, len
, GIT_PROT_READ
, GIT_MAP_SHARED
, fd
, begin
) < GIT_SUCCESS
)
262 void gitfo_free_map(git_map
*out
)
270 int (*fn
)(void *, char *),
273 size_t wd_len
= strlen(path
);
277 if (!wd_len
|| path_sz
< wd_len
+ 2)
278 return git__throw(GIT_EINVALIDARGS
, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path
);
280 while (path
[wd_len
- 1] == '/')
282 path
[wd_len
++] = '/';
287 return git__throw(GIT_EOSERR
, "Failed to process `%s` tree structure. An error occured while opening the directory", path
);
289 while ((de
= readdir(dir
)) != NULL
) {
293 /* always skip '.' and '..' */
294 if (de
->d_name
[0] == '.') {
295 if (de
->d_name
[1] == '\0')
297 if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
301 de_len
= strlen(de
->d_name
);
302 if (path_sz
< wd_len
+ de_len
+ 1) {
304 return git__throw(GIT_ERROR
, "Failed to process `%s` tree structure. Buffer size is too short", path
);
307 strcpy(path
+ wd_len
, de
->d_name
);
308 result
= fn(arg
, path
);
309 if (result
< GIT_SUCCESS
) {
311 return result
; /* The callee is reponsible for setting the correct error message */
323 static void posixify_path(char *path
)
325 #if GIT_PLATFORM_PATH_SEP != '/'
327 if (*path
== GIT_PLATFORM_PATH_SEP
)
335 int gitfo_retrieve_path_root_offset(const char *path
)
341 /* Does the root of the path look like a windows drive ? */
342 if (isalpha(path
[0]) && (path
[1] == ':'))
347 if (*(path
+ offset
) == '/')
350 return -1; /* Not a real error. Rather a signal than the path is not rooted */
353 int gitfo_mkdir_recurs(const char *path
, int mode
)
355 int error
, root_path_offset
;
357 char *path_copy
= git__strdup(path
);
359 if (path_copy
== NULL
)
365 root_path_offset
= gitfo_retrieve_path_root_offset(pp
);
366 if (root_path_offset
> 0)
367 pp
+= root_path_offset
; /* On Windows, will skip the drive name (eg. C: or D:) */
369 while (error
== GIT_SUCCESS
&& (sp
= strchr(pp
, '/')) != 0) {
370 if (sp
!= pp
&& gitfo_isdir(path_copy
) < GIT_SUCCESS
) {
372 error
= gitfo_mkdir(path_copy
, mode
);
374 /* Do not choke while trying to recreate an existing directory */
384 if (*(pp
- 1) != '/' && error
== GIT_SUCCESS
)
385 error
= gitfo_mkdir(path
, mode
);
389 if (error
< GIT_SUCCESS
)
390 return git__throw(error
, "Failed to recursively create `%s` tree structure", path
);
395 static int retrieve_previous_path_component_start(const char *path
)
397 int offset
, len
, root_offset
, start
= 0;
399 root_offset
= gitfo_retrieve_path_root_offset(path
);
400 if (root_offset
> -1)
401 start
+= root_offset
;
406 /* Skip leading slash */
407 if (path
[start
] == '/')
410 /* Skip trailing slash */
411 if (path
[offset
] == '/')
414 if (offset
< root_offset
)
415 return git__throw(GIT_ERROR
, "Failed to retrieve path component. Wrong offset");
417 while (offset
> start
&& path
[offset
-1] != '/') {
424 int gitfo_prettify_dir_path(char *buffer_out
, size_t size
, const char *path
, const char *base_path
)
426 int len
= 0, segment_len
, only_dots
, root_path_offset
, error
= GIT_SUCCESS
;
428 const char *buffer_out_start
, *buffer_end
;
430 current
= (char *)path
;
431 buffer_end
= path
+ strlen(path
);
432 buffer_out_start
= buffer_out
;
434 root_path_offset
= gitfo_retrieve_path_root_offset(path
);
435 if (root_path_offset
< 0) {
436 if (base_path
== NULL
) {
437 error
= gitfo_getcwd(buffer_out
, size
);
438 if (error
< GIT_SUCCESS
)
439 return error
; /* The callee already takes care of setting the correct error message. */
441 if (size
< (strlen(base_path
) + 1) * sizeof(char))
442 return git__throw(GIT_EOVERFLOW
, "Failed to prettify dir path: the base path is too long for the buffer.");
444 strcpy(buffer_out
, base_path
);
445 posixify_path(buffer_out
);
446 git__joinpath(buffer_out
, buffer_out
, "");
449 len
= strlen(buffer_out
);
453 while (current
< buffer_end
) {
454 /* Prevent multiple slashes from being added to the output */
455 if (*current
== '/' && len
> 0 && buffer_out_start
[len
- 1] == '/') {
463 /* Copy path segment to the output */
464 while (current
< buffer_end
&& *current
!= '/')
466 only_dots
&= (*current
== '.');
467 *buffer_out
++ = *current
++;
472 /* Skip current directory */
473 if (only_dots
&& segment_len
== 1)
476 buffer_out
-= segment_len
;
481 /* Handle the double-dot upward directory navigation */
482 if (only_dots
&& segment_len
== 2)
485 buffer_out
-= segment_len
;
488 len
= retrieve_previous_path_component_start(buffer_out_start
);
490 /* Are we escaping out of the root dir? */
492 return git__throw(GIT_EINVALIDPATH
, "Failed to normalize path `%s`. The path escapes out of the root directory", path
);
494 buffer_out
= (char *)buffer_out_start
+ len
;
498 /* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
499 if (only_dots
&& segment_len
> 0)
500 return git__throw(GIT_EINVALIDPATH
, "Failed to normalize path `%s`. The path contains a segment with three `.` or more", path
);
511 int gitfo_prettify_file_path(char *buffer_out
, size_t size
, const char *path
, const char *base_path
)
513 int error
, path_len
, i
, root_offset
;
514 const char* pattern
= "/..";
516 path_len
= strlen(path
);
518 /* Let's make sure the filename isn't empty nor a dot */
519 if (path_len
== 0 || (path_len
== 1 && *path
== '.'))
520 return git__throw(GIT_EINVALIDPATH
, "Failed to normalize file path `%s`. The path is either empty or equals `.`", path
);
522 /* Let's make sure the filename doesn't end with "/", "/." or "/.." */
523 for (i
= 1; path_len
> i
&& i
< 4; i
++) {
524 if (!strncmp(path
+ path_len
- i
, pattern
, i
))
525 return git__throw(GIT_EINVALIDPATH
, "Failed to normalize file path `%s`. The path points to a folder", path
);
528 error
= gitfo_prettify_dir_path(buffer_out
, size
, path
, base_path
);
529 if (error
< GIT_SUCCESS
)
530 return error
; /* The callee already takes care of setting the correct error message. */
532 path_len
= strlen(buffer_out
);
533 root_offset
= gitfo_retrieve_path_root_offset(buffer_out
) + 1;
534 if (path_len
== root_offset
)
535 return git__throw(GIT_EINVALIDPATH
, "Failed to normalize file path `%s`. The path points to a folder", path
);
537 /* Remove the trailing slash */
538 buffer_out
[path_len
- 1] = '\0';
543 int gitfo_cmp_path(const char *name1
, int len1
, int isdir1
,
544 const char *name2
, int len2
, int isdir2
)
546 int len
= len1
< len2
? len1
: len2
;
549 cmp
= memcmp(name1
, name2
, len
);
553 return ((!isdir1
&& !isdir2
) ? -1 :
554 (isdir1
? '/' - name2
[len1
] : name2
[len1
] - '/'));
556 return ((!isdir1
&& !isdir2
) ? 1 :
557 (isdir2
? name1
[len2
] - '/' : '/' - name1
[len2
]));
561 int gitfo_getcwd(char *buffer_out
, size_t size
)
565 assert(buffer_out
&& size
> 0);
568 cwd_buffer
= _getcwd(buffer_out
, size
);
570 cwd_buffer
= getcwd(buffer_out
, size
);
573 if (cwd_buffer
== NULL
)
574 return git__throw(GIT_EOSERR
, "Failed to retrieve current working directory");
576 posixify_path(buffer_out
);
578 git__joinpath(buffer_out
, buffer_out
, ""); //Ensure the path ends with a trailing slash