]> git.proxmox.com Git - libgit2.git/blame - src/fileops.c
Add git_config_refresh() API to reload config
[libgit2.git] / src / fileops.c
CommitLineData
bb742ede 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
bb742ede
VM
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 */
5ee2fe77 7#include "common.h"
ec250c6e 8#include "fileops.h"
2e29957a 9#include <ctype.h>
997579be
SS
10#if GIT_WIN32
11#include "win32/findfile.h"
12#endif
ec250c6e 13
ce8cd006 14int git_futils_mkpath2file(const char *file_path, const mode_t mode)
55ffebe3 15{
ca1b6e54
RB
16 return git_futils_mkdir(
17 file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
55ffebe3
VM
18}
19
97769280 20int git_futils_mktmp(git_buf *path_out, const char *filename)
72a3fe42
VM
21{
22 int fd;
23
97769280
RB
24 git_buf_sets(path_out, filename);
25 git_buf_puts(path_out, "_git2_XXXXXX");
26
27 if (git_buf_oom(path_out))
1a481123 28 return -1;
72a3fe42 29
1a481123
VM
30 if ((fd = p_mkstemp(path_out->ptr)) < 0) {
31 giterr_set(GITERR_OS,
ae9e29fd 32 "Failed to create temporary file '%s'", path_out->ptr);
1a481123
VM
33 return -1;
34 }
72a3fe42 35
f978b748 36 return fd;
72a3fe42
VM
37}
38
ce8cd006 39int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
7dd8a9f7 40{
1a481123 41 int fd;
55ffebe3 42
1a481123
VM
43 if (git_futils_mkpath2file(path, dirmode) < 0)
44 return -1;
45
46 fd = p_creat(path, mode);
47 if (fd < 0) {
ae9e29fd 48 giterr_set(GITERR_OS, "Failed to create file '%s'", path);
1a481123
VM
49 return -1;
50 }
51
52 return fd;
55ffebe3
VM
53}
54
33127043 55int git_futils_creat_locked(const char *path, const mode_t mode)
1549cba9 56{
09719c50 57 int fd;
58
59#ifdef GIT_WIN32
6813169a 60 wchar_t buf[GIT_WIN_PATH];
09719c50 61
0f4c6175 62 git__utf8_to_16(buf, GIT_WIN_PATH, path);
09719c50 63 fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
09719c50 64#else
65 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
66#endif
67
1a481123 68 if (fd < 0) {
ae9e29fd 69 giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
1a481123
VM
70 return -1;
71 }
72
73 return fd;
1549cba9
RG
74}
75
ce8cd006 76int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
1549cba9 77{
1a481123
VM
78 if (git_futils_mkpath2file(path, dirmode) < 0)
79 return -1;
1549cba9 80
f79026b4 81 return git_futils_creat_locked(path, mode);
ec250c6e
AE
82}
83
deafee7b
RB
84int git_futils_open_ro(const char *path)
85{
86 int fd = p_open(path, O_RDONLY);
87 if (fd < 0) {
d0a920a6 88 if (errno == ENOENT || errno == ENOTDIR)
deafee7b
RB
89 fd = GIT_ENOTFOUND;
90 giterr_set(GITERR_OS, "Failed to open '%s'", path);
91 }
92 return fd;
93}
94
f79026b4 95git_off_t git_futils_filesize(git_file fd)
ec250c6e 96{
7dd8a9f7 97 struct stat sb;
deafee7b
RB
98
99 if (p_fstat(fd, &sb)) {
100 giterr_set(GITERR_OS, "Failed to stat file descriptor");
101 return -1;
102 }
5ad739e8 103
ec250c6e
AE
104 return sb.st_size;
105}
4188d28f 106
b6c93aef
RB
107mode_t git_futils_canonical_mode(mode_t raw_mode)
108{
109 if (S_ISREG(raw_mode))
110 return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
111 else if (S_ISLNK(raw_mode))
112 return S_IFLNK;
b6c93aef
RB
113 else if (S_ISGITLINK(raw_mode))
114 return S_IFGITLINK;
7c7ff7d1
RB
115 else if (S_ISDIR(raw_mode))
116 return S_IFDIR;
b6c93aef
RB
117 else
118 return 0;
119}
120
60b9d3fc
RB
121int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
122{
1f35e89d 123 ssize_t read_size;
60b9d3fc
RB
124
125 git_buf_clear(buf);
126
127 if (git_buf_grow(buf, len + 1) < 0)
128 return -1;
129
1f35e89d
RB
130 /* p_read loops internally to read len bytes */
131 read_size = p_read(fd, buf->ptr, len);
60b9d3fc 132
c859184b 133 if (read_size != (ssize_t)len) {
1f35e89d
RB
134 giterr_set(GITERR_OS, "Failed to read descriptor");
135 return -1;
60b9d3fc
RB
136 }
137
1f35e89d
RB
138 buf->ptr[read_size] = '\0';
139 buf->size = read_size;
140
60b9d3fc
RB
141 return 0;
142}
143
144int git_futils_readbuffer_updated(
744cc03e 145 git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
75d58430
RJ
146{
147 git_file fd;
c3da9f06 148 struct stat st;
744cc03e 149 bool changed = false;
75d58430 150
13224ea4 151 assert(buf && path && *path);
75d58430 152
c3da9f06
CMN
153 if (updated != NULL)
154 *updated = 0;
75d58430 155
ae9e29fd
RB
156 if ((fd = git_futils_open_ro(path)) < 0)
157 return fd;
c3da9f06 158
13224ea4 159 if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
e1de726c 160 p_close(fd);
1a481123
VM
161 giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
162 return -1;
13224ea4 163 }
c3da9f06
CMN
164
165 /*
744cc03e
RB
166 * If we were given a time and/or a size, we only want to read the file
167 * if it has been modified.
c3da9f06 168 */
744cc03e
RB
169 if (size && *size != (size_t)st.st_size)
170 changed = true;
171 if (mtime && *mtime != st.st_mtime)
172 changed = true;
173 if (!size && !mtime)
174 changed = true;
175
176 if (!changed) {
e1de726c 177 p_close(fd);
13224ea4
VM
178 return 0;
179 }
c3da9f06
CMN
180
181 if (mtime != NULL)
182 *mtime = st.st_mtime;
744cc03e
RB
183 if (size != NULL)
184 *size = (size_t)st.st_size;
c3da9f06 185
60b9d3fc 186 if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
e1de726c
RB
187 p_close(fd);
188 return -1;
75d58430
RJ
189 }
190
f79026b4 191 p_close(fd);
75d58430 192
c3da9f06
CMN
193 if (updated != NULL)
194 *updated = 1;
195
13224ea4 196 return 0;
c3da9f06
CMN
197}
198
13224ea4 199int git_futils_readbuffer(git_buf *buf, const char *path)
97769280 200{
744cc03e 201 return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
97769280
RB
202}
203
ce8cd006 204int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
19a30a3f 205{
deafee7b
RB
206 if (git_futils_mkpath2file(to, dirmode) < 0)
207 return -1;
19a30a3f 208
deafee7b
RB
209 if (p_rename(from, to) < 0) {
210 giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
211 return -1;
212 }
213
214 return 0;
19a30a3f
VM
215}
216
f79026b4 217int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
20e7f426 218{
f79026b4 219 return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
20e7f426
SP
220}
221
74fa4bfa
RB
222int git_futils_mmap_ro_file(git_map *out, const char *path)
223{
0d0fa7c3
RB
224 git_file fd = git_futils_open_ro(path);
225 git_off_t len;
da9abdd6 226 int result;
0d0fa7c3
RB
227
228 if (fd < 0)
229 return fd;
230
231 len = git_futils_filesize(fd);
deafee7b
RB
232 if (!git__is_sizet(len)) {
233 giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
234 return -1;
235 }
0d0fa7c3 236
da9abdd6 237 result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
74fa4bfa
RB
238 p_close(fd);
239 return result;
240}
241
f79026b4 242void git_futils_mmap_free(git_map *out)
20e7f426 243{
f79026b4 244 p_munmap(out);
20e7f426
SP
245}
246
ca1b6e54
RB
247int git_futils_mkdir(
248 const char *path,
249 const char *base,
250 mode_t mode,
251 uint32_t flags)
1a5204a7 252{
97769280 253 git_buf make_path = GIT_BUF_INIT;
ca1b6e54
RB
254 ssize_t root = 0;
255 char lastch, *tail;
dc07184f 256
ca1b6e54
RB
257 /* build path and find "root" where we should start calling mkdir */
258 if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
259 return -1;
dc07184f 260
ca1b6e54
RB
261 if (make_path.size == 0) {
262 giterr_set(GITERR_OS, "Attempt to create empty path");
263 goto fail;
97769280 264 }
f0b2bfe5 265
ca1b6e54
RB
266 /* remove trailing slashes on path */
267 while (make_path.ptr[make_path.size - 1] == '/') {
268 make_path.size--;
269 make_path.ptr[make_path.size] = '\0';
270 }
412de9a6 271
ca1b6e54
RB
272 /* if we are not supposed to made the last element, truncate it */
273 if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
274 git_buf_rtruncate_at_char(&make_path, '/');
275
276 /* if we are not supposed to make the whole path, reset root */
277 if ((flags & GIT_MKDIR_PATH) == 0)
278 root = git_buf_rfind(&make_path, '/');
279
280 /* clip root to make_path length */
281 if (root >= (ssize_t)make_path.size)
282 root = (ssize_t)make_path.size - 1;
0e26202c
RB
283 if (root < 0)
284 root = 0;
ca1b6e54
RB
285
286 tail = & make_path.ptr[root];
287
288 while (*tail) {
289 /* advance tail to include next path component */
290 while (*tail == '/')
291 tail++;
292 while (*tail && *tail != '/')
293 tail++;
294
295 /* truncate path at next component */
296 lastch = *tail;
297 *tail = '\0';
298
299 /* make directory */
300 if (p_mkdir(make_path.ptr, mode) < 0 &&
301 (errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
302 {
303 giterr_set(GITERR_OS, "Failed to make directory '%s'",
304 make_path.ptr);
305 goto fail;
d5f25204 306 }
40c44d2f 307
ca1b6e54
RB
308 /* chmod if requested */
309 if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
310 ((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
311 {
312 if (p_chmod(make_path.ptr, mode) < 0) {
313 giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
314 make_path.ptr);
315 goto fail;
316 }
317 }
f0b2bfe5 318
ca1b6e54 319 *tail = lastch;
a993e4fe 320 }
40c44d2f 321
97769280 322 git_buf_free(&make_path);
ca1b6e54 323 return 0;
77c3999c 324
ca1b6e54
RB
325fail:
326 git_buf_free(&make_path);
327 return -1;
328}
77c3999c 329
ca1b6e54
RB
330int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
331{
332 return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
170d3f2f 333}
334
97769280 335static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
42b3a460 336{
1a2b8725 337 git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
555aa453 338
1a481123 339 if (git_path_isdir(path->ptr) == true) {
deafee7b
RB
340 if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
341 return -1;
342
343 if (p_rmdir(path->ptr) < 0) {
54bdc64a 344 if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
555aa453 345 return 0;
346
deafee7b
RB
347 giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
348 return -1;
349 }
42b3a460 350
deafee7b 351 return 0;
858dba58
VM
352 }
353
555aa453 354 if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
deafee7b
RB
355 if (p_unlink(path->ptr) < 0) {
356 giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
357 return -1;
358 }
359
360 return 0;
361 }
362
555aa453 363 if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
364 giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
365 return -1;
366 }
367
368 return 0;
42b3a460
MS
369}
370
0d64bef9
RB
371int git_futils_rmdir_r(
372 const char *path, const char *base, git_directory_removal_type removal_type)
42b3a460 373{
97769280 374 int error;
0d64bef9
RB
375 git_buf fullpath = GIT_BUF_INIT;
376
377 assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
378 || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
379 || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
380
381 /* build path and find "root" where we should start calling mkdir */
382 if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
383 return -1;
384
385 error = _rmdir_recurs_foreach(&removal_type, &fullpath);
386
387 git_buf_free(&fullpath);
97769280 388
97769280 389 return error;
42b3a460
MS
390}
391
32a4e3b7
SS
392int git_futils_find_system_file(git_buf *path, const char *filename)
393{
394#ifdef GIT_WIN32
395 // try to find git.exe/git.cmd on path
dee18b82 396 if (!win32_find_system_file_using_path(path, filename))
32a4e3b7
SS
397 return 0;
398
399 // try to find msysgit installation path using registry
dee18b82 400 if (!win32_find_system_file_using_registry(path, filename))
32a4e3b7 401 return 0;
349fb6d7 402#else
cb8a7961
VM
403 if (git_buf_joinpath(path, "/etc", filename) < 0)
404 return -1;
73b51450 405
1a481123 406 if (git_path_exists(path->ptr) == true)
deafee7b 407 return 0;
32a4e3b7 408#endif
73b51450
RB
409
410 git_buf_clear(path);
0b956819 411 giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename);
349fb6d7 412 return GIT_ENOTFOUND;
349fb6d7 413}
73b51450 414
349fb6d7
VM
415int git_futils_find_global_file(git_buf *path, const char *filename)
416{
73b51450 417#ifdef GIT_WIN32
349fb6d7 418 struct win32_path root;
0d422ec9
RB
419 static const wchar_t *tmpls[4] = {
420 L"%HOME%\\",
421 L"%HOMEDRIVE%%HOMEPATH%\\",
422 L"%USERPROFILE%\\",
423 NULL,
424 };
425 const wchar_t **tmpl;
426
427 for (tmpl = tmpls; *tmpl != NULL; tmpl++) {
428 /* try to expand environment variable, skipping if not set */
429 if (win32_expand_path(&root, *tmpl) != 0 || root.path[0] == L'%')
430 continue;
431
432 /* try to look up file under path */
433 if (!win32_find_file(path, &root, filename))
aed8f8a1 434 return 0;
aed8f8a1 435
0d422ec9
RB
436 /* No error if file not found under %HOME%, b/c we don't trust it,
437 * but do error if another var is set and yet file is not found.
438 */
439 if (tmpl != tmpls)
440 break;
349fb6d7
VM
441 }
442
0d422ec9
RB
443 giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
444 git_buf_clear(path);
29ef309e 445
0d422ec9 446 return GIT_ENOTFOUND;
73b51450 447#else
0d422ec9
RB
448 const char *home = getenv("HOME");
449
349fb6d7
VM
450 if (home == NULL) {
451 giterr_set(GITERR_OS, "Global file lookup failed. "
452 "Cannot locate the user's home directory");
18217e7e 453 return GIT_ENOTFOUND;
349fb6d7
VM
454 }
455
456 if (git_buf_joinpath(path, home, filename) < 0)
457 return -1;
458
459 if (git_path_exists(path->ptr) == false) {
0b956819 460 giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
349fb6d7
VM
461 git_buf_clear(path);
462 return GIT_ENOTFOUND;
463 }
464
465 return 0;
73b51450
RB
466#endif
467}
8651c10f
BS
468
469int git_futils_fake_symlink(const char *old, const char *new)
470{
471 int retcode = GIT_ERROR;
472 int fd = git_futils_creat_withpath(new, 0755, 0644);
473 if (fd >= 0) {
474 retcode = p_write(fd, old, strlen(old));
475 p_close(fd);
476 }
477 return retcode;
478}
ca1b6e54 479
85bd1746 480static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
ca1b6e54
RB
481{
482 int error = 0;
483 char buffer[4096];
484 ssize_t len = 0;
485
486 while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
487 /* p_write() does not have the same semantics as write(). It loops
488 * internally and will return 0 when it has completed writing.
489 */
490 error = p_write(ofd, buffer, len);
491
492 if (len < 0) {
493 giterr_set(GITERR_OS, "Read error while copying file");
494 error = (int)len;
495 }
496
85bd1746 497 if (close_fd_when_done) {
ca1b6e54
RB
498 p_close(ifd);
499 p_close(ofd);
500 }
501
502 return error;
503}
504
85bd1746 505int git_futils_cp(const char *from, const char *to, mode_t filemode)
ca1b6e54
RB
506{
507 int ifd, ofd;
508
ca1b6e54
RB
509 if ((ifd = git_futils_open_ro(from)) < 0)
510 return ifd;
511
512 if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
513 if (errno == ENOENT || errno == ENOTDIR)
514 ofd = GIT_ENOTFOUND;
515 giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
516 p_close(ifd);
517 return ofd;
518 }
519
85bd1746 520 return cp_by_fd(ifd, ofd, true);
ca1b6e54
RB
521}
522
85bd1746 523static int cp_link(const char *from, const char *to, size_t link_size)
ca1b6e54
RB
524{
525 int error = 0;
526 ssize_t read_len;
85bd1746 527 char *link_data = git__malloc(link_size + 1);
ca1b6e54
RB
528 GITERR_CHECK_ALLOC(link_data);
529
85bd1746
RB
530 read_len = p_readlink(from, link_data, link_size);
531 if (read_len != (ssize_t)link_size) {
ca1b6e54
RB
532 giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
533 error = -1;
534 }
535 else {
536 link_data[read_len] = '\0';
537
538 if (p_symlink(link_data, to) < 0) {
539 giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
540 link_data, to);
541 error = -1;
542 }
543 }
544
545 git__free(link_data);
546 return error;
547}
548
549typedef struct {
550 const char *to_root;
551 git_buf to;
552 ssize_t from_prefix;
553 uint32_t flags;
554 uint32_t mkdir_flags;
555 mode_t dirmode;
556} cp_r_info;
557
558static int _cp_r_callback(void *ref, git_buf *from)
559{
560 cp_r_info *info = ref;
561 struct stat from_st, to_st;
562 bool exists = false;
563
564 if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
565 from->ptr[git_path_basename_offset(from)] == '.')
566 return 0;
567
568 if (git_buf_joinpath(
569 &info->to, info->to_root, from->ptr + info->from_prefix) < 0)
570 return -1;
571
572 if (p_lstat(info->to.ptr, &to_st) < 0) {
2eb4edf5 573 if (errno != ENOENT && errno != ENOTDIR) {
ca1b6e54
RB
574 giterr_set(GITERR_OS,
575 "Could not access %s while copying files", info->to.ptr);
576 return -1;
577 }
578 } else
579 exists = true;
580
581 if (git_path_lstat(from->ptr, &from_st) < 0)
582 return -1;
583
584 if (S_ISDIR(from_st.st_mode)) {
585 int error = 0;
586 mode_t oldmode = info->dirmode;
587
588 /* if we are not chmod'ing, then overwrite dirmode */
589 if ((info->flags & GIT_CPDIR_CHMOD) == 0)
590 info->dirmode = from_st.st_mode;
591
592 /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
593 if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
594 error = git_futils_mkdir(
595 info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
596
597 /* recurse onto target directory */
598 if (!exists || S_ISDIR(to_st.st_mode))
599 error = git_path_direach(from, _cp_r_callback, info);
600
601 if (oldmode != 0)
602 info->dirmode = oldmode;
603
604 return error;
605 }
606
607 if (exists) {
608 if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
609 return 0;
610
611 if (p_unlink(info->to.ptr) < 0) {
612 giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
613 info->to.ptr);
614 return -1;
615 }
616 }
617
618 /* Done if this isn't a regular file or a symlink */
619 if (!S_ISREG(from_st.st_mode) &&
620 (!S_ISLNK(from_st.st_mode) ||
621 (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
622 return 0;
623
624 /* Make container directory on demand if needed */
625 if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
626 git_futils_mkdir(
627 info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
628 return -1;
629
630 /* make symlink or regular file */
631 if (S_ISLNK(from_st.st_mode))
85bd1746 632 return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
ca1b6e54 633 else
85bd1746 634 return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
ca1b6e54
RB
635}
636
637int git_futils_cp_r(
638 const char *from,
639 const char *to,
640 uint32_t flags,
641 mode_t dirmode)
642{
643 int error;
644 git_buf path = GIT_BUF_INIT;
645 cp_r_info info;
646
647 if (git_buf_sets(&path, from) < 0)
648 return -1;
649
650 info.to_root = to;
651 info.flags = flags;
652 info.dirmode = dirmode;
653 info.from_prefix = path.size;
654 git_buf_init(&info.to, 0);
655
656 /* precalculate mkdir flags */
657 if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
658 info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
659 if ((flags & GIT_CPDIR_CHMOD) != 0)
660 info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
661 } else {
662 info.mkdir_flags =
663 ((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
664 }
665
666 error = _cp_r_callback(&info, &path);
667
668 git_buf_free(&path);
07c06f7a 669 git_buf_free(&info.to);
ca1b6e54
RB
670
671 return error;
672}
744cc03e
RB
673
674int git_futils_stat_sig_needs_reload(
675 git_futils_stat_sig *sig, const char *path)
676{
677 struct stat st;
678
679 /* if the sig is NULL, then alway reload */
680 if (sig == NULL)
681 return 1;
682
683 if (p_stat(path, &st) < 0)
684 return GIT_ENOTFOUND;
685
686 if ((git_time_t)st.st_mtime == sig->seconds &&
687 (git_off_t)st.st_size == sig->size &&
688 (unsigned int)st.st_ino == sig->ino)
689 return 0;
690
691 sig->seconds = (git_time_t)st.st_mtime;
692 sig->size = (git_off_t)st.st_size;
693 sig->ino = (unsigned int)st.st_ino;
694
695 return 1;
696}
697