2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
11 #include "win32/findfile.h"
14 int git_futils_mkpath2file(const char *file_path
, const mode_t mode
)
16 return git_futils_mkdir(
17 file_path
, NULL
, mode
, GIT_MKDIR_PATH
| GIT_MKDIR_SKIP_LAST
);
20 int git_futils_mktmp(git_buf
*path_out
, const char *filename
)
24 git_buf_sets(path_out
, filename
);
25 git_buf_puts(path_out
, "_git2_XXXXXX");
27 if (git_buf_oom(path_out
))
30 if ((fd
= p_mkstemp(path_out
->ptr
)) < 0) {
32 "Failed to create temporary file '%s'", path_out
->ptr
);
39 int git_futils_creat_withpath(const char *path
, const mode_t dirmode
, const mode_t mode
)
43 if (git_futils_mkpath2file(path
, dirmode
) < 0)
46 fd
= p_creat(path
, mode
);
48 giterr_set(GITERR_OS
, "Failed to create file '%s'", path
);
55 int git_futils_creat_locked(const char *path
, const mode_t mode
)
60 wchar_t buf
[GIT_WIN_PATH
];
62 git__utf8_to_16(buf
, GIT_WIN_PATH
, path
);
63 fd
= _wopen(buf
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
| O_EXCL
, mode
);
65 fd
= open(path
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
| O_EXCL
, mode
);
69 giterr_set(GITERR_OS
, "Failed to create locked file '%s'", path
);
76 int git_futils_creat_locked_withpath(const char *path
, const mode_t dirmode
, const mode_t mode
)
78 if (git_futils_mkpath2file(path
, dirmode
) < 0)
81 return git_futils_creat_locked(path
, mode
);
84 int git_futils_open_ro(const char *path
)
86 int fd
= p_open(path
, O_RDONLY
);
88 if (errno
== ENOENT
|| errno
== ENOTDIR
)
90 giterr_set(GITERR_OS
, "Failed to open '%s'", path
);
95 git_off_t
git_futils_filesize(git_file fd
)
99 if (p_fstat(fd
, &sb
)) {
100 giterr_set(GITERR_OS
, "Failed to stat file descriptor");
107 mode_t
git_futils_canonical_mode(mode_t raw_mode
)
109 if (S_ISREG(raw_mode
))
110 return S_IFREG
| GIT_CANONICAL_PERMS(raw_mode
);
111 else if (S_ISLNK(raw_mode
))
113 else if (S_ISGITLINK(raw_mode
))
115 else if (S_ISDIR(raw_mode
))
121 int git_futils_readbuffer_fd(git_buf
*buf
, git_file fd
, size_t len
)
127 if (git_buf_grow(buf
, len
+ 1) < 0)
130 /* p_read loops internally to read len bytes */
131 read_size
= p_read(fd
, buf
->ptr
, len
);
133 if (read_size
!= (ssize_t
)len
) {
134 giterr_set(GITERR_OS
, "Failed to read descriptor");
138 buf
->ptr
[read_size
] = '\0';
139 buf
->size
= read_size
;
144 int git_futils_readbuffer_updated(
145 git_buf
*buf
, const char *path
, time_t *mtime
, int *updated
)
150 assert(buf
&& path
&& *path
);
155 if ((fd
= git_futils_open_ro(path
)) < 0)
158 if (p_fstat(fd
, &st
) < 0 || S_ISDIR(st
.st_mode
) || !git__is_sizet(st
.st_size
+1)) {
160 giterr_set(GITERR_OS
, "Invalid regular file stat for '%s'", path
);
165 * If we were given a time, we only want to read the file if it
168 if (mtime
!= NULL
&& *mtime
>= st
.st_mtime
) {
174 *mtime
= st
.st_mtime
;
176 if (git_futils_readbuffer_fd(buf
, fd
, (size_t)st
.st_size
) < 0) {
189 int git_futils_readbuffer(git_buf
*buf
, const char *path
)
191 return git_futils_readbuffer_updated(buf
, path
, NULL
, NULL
);
194 int git_futils_mv_withpath(const char *from
, const char *to
, const mode_t dirmode
)
196 if (git_futils_mkpath2file(to
, dirmode
) < 0)
199 if (p_rename(from
, to
) < 0) {
200 giterr_set(GITERR_OS
, "Failed to rename '%s' to '%s'", from
, to
);
207 int git_futils_mmap_ro(git_map
*out
, git_file fd
, git_off_t begin
, size_t len
)
209 return p_mmap(out
, len
, GIT_PROT_READ
, GIT_MAP_SHARED
, fd
, begin
);
212 int git_futils_mmap_ro_file(git_map
*out
, const char *path
)
214 git_file fd
= git_futils_open_ro(path
);
221 len
= git_futils_filesize(fd
);
222 if (!git__is_sizet(len
)) {
223 giterr_set(GITERR_OS
, "File `%s` too large to mmap", path
);
227 result
= git_futils_mmap_ro(out
, fd
, 0, (size_t)len
);
232 void git_futils_mmap_free(git_map
*out
)
237 int git_futils_mkdir(
243 git_buf make_path
= GIT_BUF_INIT
;
247 /* build path and find "root" where we should start calling mkdir */
248 if (git_path_join_unrooted(&make_path
, path
, base
, &root
) < 0)
251 if (make_path
.size
== 0) {
252 giterr_set(GITERR_OS
, "Attempt to create empty path");
256 /* remove trailing slashes on path */
257 while (make_path
.ptr
[make_path
.size
- 1] == '/') {
259 make_path
.ptr
[make_path
.size
] = '\0';
262 /* if we are not supposed to made the last element, truncate it */
263 if ((flags
& GIT_MKDIR_SKIP_LAST
) != 0)
264 git_buf_rtruncate_at_char(&make_path
, '/');
266 /* if we are not supposed to make the whole path, reset root */
267 if ((flags
& GIT_MKDIR_PATH
) == 0)
268 root
= git_buf_rfind(&make_path
, '/');
270 /* clip root to make_path length */
271 if (root
>= (ssize_t
)make_path
.size
)
272 root
= (ssize_t
)make_path
.size
- 1;
276 tail
= & make_path
.ptr
[root
];
279 /* advance tail to include next path component */
282 while (*tail
&& *tail
!= '/')
285 /* truncate path at next component */
290 if (p_mkdir(make_path
.ptr
, mode
) < 0 &&
291 (errno
!= EEXIST
|| (flags
& GIT_MKDIR_EXCL
) != 0))
293 giterr_set(GITERR_OS
, "Failed to make directory '%s'",
298 /* chmod if requested */
299 if ((flags
& GIT_MKDIR_CHMOD_PATH
) != 0 ||
300 ((flags
& GIT_MKDIR_CHMOD
) != 0 && lastch
== '\0'))
302 if (p_chmod(make_path
.ptr
, mode
) < 0) {
303 giterr_set(GITERR_OS
, "Failed to set permissions on '%s'",
312 git_buf_free(&make_path
);
316 git_buf_free(&make_path
);
320 int git_futils_mkdir_r(const char *path
, const char *base
, const mode_t mode
)
322 return git_futils_mkdir(path
, base
, mode
, GIT_MKDIR_PATH
);
325 static int _rmdir_recurs_foreach(void *opaque
, git_buf
*path
)
327 git_directory_removal_type removal_type
= *(git_directory_removal_type
*)opaque
;
329 if (git_path_isdir(path
->ptr
) == true) {
330 if (git_path_direach(path
, _rmdir_recurs_foreach
, opaque
) < 0)
333 if (p_rmdir(path
->ptr
) < 0) {
334 if (removal_type
== GIT_DIRREMOVAL_ONLY_EMPTY_DIRS
&& (errno
== ENOTEMPTY
|| errno
== EEXIST
))
337 giterr_set(GITERR_OS
, "Could not remove directory '%s'", path
->ptr
);
344 if (removal_type
== GIT_DIRREMOVAL_FILES_AND_DIRS
) {
345 if (p_unlink(path
->ptr
) < 0) {
346 giterr_set(GITERR_OS
, "Could not remove directory. File '%s' cannot be removed", path
->ptr
);
353 if (removal_type
== GIT_DIRREMOVAL_EMPTY_HIERARCHY
) {
354 giterr_set(GITERR_OS
, "Could not remove directory. File '%s' still present", path
->ptr
);
361 int git_futils_rmdir_r(
362 const char *path
, const char *base
, git_directory_removal_type removal_type
)
365 git_buf fullpath
= GIT_BUF_INIT
;
367 assert(removal_type
== GIT_DIRREMOVAL_EMPTY_HIERARCHY
368 || removal_type
== GIT_DIRREMOVAL_FILES_AND_DIRS
369 || removal_type
== GIT_DIRREMOVAL_ONLY_EMPTY_DIRS
);
371 /* build path and find "root" where we should start calling mkdir */
372 if (git_path_join_unrooted(&fullpath
, path
, base
, NULL
) < 0)
375 error
= _rmdir_recurs_foreach(&removal_type
, &fullpath
);
377 git_buf_free(&fullpath
);
382 int git_futils_find_system_file(git_buf
*path
, const char *filename
)
385 // try to find git.exe/git.cmd on path
386 if (!win32_find_system_file_using_path(path
, filename
))
389 // try to find msysgit installation path using registry
390 if (!win32_find_system_file_using_registry(path
, filename
))
393 if (git_buf_joinpath(path
, "/etc", filename
) < 0)
396 if (git_path_exists(path
->ptr
) == true)
401 giterr_set(GITERR_OS
, "The system file '%s' doesn't exist", filename
);
402 return GIT_ENOTFOUND
;
405 int git_futils_find_global_file(git_buf
*path
, const char *filename
)
407 const char *home
= getenv("HOME");
410 struct win32_path root
;
413 if (git_buf_joinpath(path
, home
, filename
) < 0)
416 if (git_path_exists(path
->ptr
)) {
421 if (getenv("HOMEPATH") != NULL
) {
422 if (win32_expand_path(&root
, L
"%HOMEDRIVE%%HOMEPATH%\\") < 0 ||
423 root
.path
[0] == L
'%') /* i.e. no expansion happened */
425 giterr_set(GITERR_OS
, "Cannot locate the user's profile directory");
426 return GIT_ENOTFOUND
;
429 if (win32_expand_path(&root
, L
"%USERPROFILE%\\") < 0 ||
430 root
.path
[0] == L
'%') /* i.e. no expansion happened */
432 giterr_set(GITERR_OS
, "Cannot locate the user's profile directory");
433 return GIT_ENOTFOUND
;
437 if (win32_find_file(path
, &root
, filename
) < 0) {
438 giterr_set(GITERR_OS
, "The global file '%s' doesn't exist", filename
);
440 return GIT_ENOTFOUND
;
446 giterr_set(GITERR_OS
, "Global file lookup failed. "
447 "Cannot locate the user's home directory");
448 return GIT_ENOTFOUND
;
451 if (git_buf_joinpath(path
, home
, filename
) < 0)
454 if (git_path_exists(path
->ptr
) == false) {
455 giterr_set(GITERR_OS
, "The global file '%s' doesn't exist", filename
);
457 return GIT_ENOTFOUND
;
464 int git_futils_fake_symlink(const char *old
, const char *new)
466 int retcode
= GIT_ERROR
;
467 int fd
= git_futils_creat_withpath(new, 0755, 0644);
469 retcode
= p_write(fd
, old
, strlen(old
));
475 static int cp_by_fd(int ifd
, int ofd
, bool close_fd_when_done
)
481 while (!error
&& (len
= p_read(ifd
, buffer
, sizeof(buffer
))) > 0)
482 /* p_write() does not have the same semantics as write(). It loops
483 * internally and will return 0 when it has completed writing.
485 error
= p_write(ofd
, buffer
, len
);
488 giterr_set(GITERR_OS
, "Read error while copying file");
492 if (close_fd_when_done
) {
500 int git_futils_cp(const char *from
, const char *to
, mode_t filemode
)
504 if ((ifd
= git_futils_open_ro(from
)) < 0)
507 if ((ofd
= p_open(to
, O_WRONLY
| O_CREAT
| O_EXCL
, filemode
)) < 0) {
508 if (errno
== ENOENT
|| errno
== ENOTDIR
)
510 giterr_set(GITERR_OS
, "Failed to open '%s' for writing", to
);
515 return cp_by_fd(ifd
, ofd
, true);
518 static int cp_link(const char *from
, const char *to
, size_t link_size
)
522 char *link_data
= git__malloc(link_size
+ 1);
523 GITERR_CHECK_ALLOC(link_data
);
525 read_len
= p_readlink(from
, link_data
, link_size
);
526 if (read_len
!= (ssize_t
)link_size
) {
527 giterr_set(GITERR_OS
, "Failed to read symlink data for '%s'", from
);
531 link_data
[read_len
] = '\0';
533 if (p_symlink(link_data
, to
) < 0) {
534 giterr_set(GITERR_OS
, "Could not symlink '%s' as '%s'",
540 git__free(link_data
);
549 uint32_t mkdir_flags
;
553 static int _cp_r_callback(void *ref
, git_buf
*from
)
555 cp_r_info
*info
= ref
;
556 struct stat from_st
, to_st
;
559 if ((info
->flags
& GIT_CPDIR_COPY_DOTFILES
) == 0 &&
560 from
->ptr
[git_path_basename_offset(from
)] == '.')
563 if (git_buf_joinpath(
564 &info
->to
, info
->to_root
, from
->ptr
+ info
->from_prefix
) < 0)
567 if (p_lstat(info
->to
.ptr
, &to_st
) < 0) {
568 if (errno
!= ENOENT
&& errno
!= ENOTDIR
) {
569 giterr_set(GITERR_OS
,
570 "Could not access %s while copying files", info
->to
.ptr
);
576 if (git_path_lstat(from
->ptr
, &from_st
) < 0)
579 if (S_ISDIR(from_st
.st_mode
)) {
581 mode_t oldmode
= info
->dirmode
;
583 /* if we are not chmod'ing, then overwrite dirmode */
584 if ((info
->flags
& GIT_CPDIR_CHMOD
) == 0)
585 info
->dirmode
= from_st
.st_mode
;
587 /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
588 if (!exists
&& (info
->flags
& GIT_CPDIR_CREATE_EMPTY_DIRS
) != 0)
589 error
= git_futils_mkdir(
590 info
->to
.ptr
, NULL
, info
->dirmode
, info
->mkdir_flags
);
592 /* recurse onto target directory */
593 if (!exists
|| S_ISDIR(to_st
.st_mode
))
594 error
= git_path_direach(from
, _cp_r_callback
, info
);
597 info
->dirmode
= oldmode
;
603 if ((info
->flags
& GIT_CPDIR_OVERWRITE
) == 0)
606 if (p_unlink(info
->to
.ptr
) < 0) {
607 giterr_set(GITERR_OS
, "Cannot overwrite existing file '%s'",
613 /* Done if this isn't a regular file or a symlink */
614 if (!S_ISREG(from_st
.st_mode
) &&
615 (!S_ISLNK(from_st
.st_mode
) ||
616 (info
->flags
& GIT_CPDIR_COPY_SYMLINKS
) == 0))
619 /* Make container directory on demand if needed */
620 if ((info
->flags
& GIT_CPDIR_CREATE_EMPTY_DIRS
) == 0 &&
622 info
->to
.ptr
, NULL
, info
->dirmode
, info
->mkdir_flags
) < 0)
625 /* make symlink or regular file */
626 if (S_ISLNK(from_st
.st_mode
))
627 return cp_link(from
->ptr
, info
->to
.ptr
, (size_t)from_st
.st_size
);
629 return git_futils_cp(from
->ptr
, info
->to
.ptr
, from_st
.st_mode
);
639 git_buf path
= GIT_BUF_INIT
;
642 if (git_buf_sets(&path
, from
) < 0)
647 info
.dirmode
= dirmode
;
648 info
.from_prefix
= path
.size
;
649 git_buf_init(&info
.to
, 0);
651 /* precalculate mkdir flags */
652 if ((flags
& GIT_CPDIR_CREATE_EMPTY_DIRS
) == 0) {
653 info
.mkdir_flags
= GIT_MKDIR_PATH
| GIT_MKDIR_SKIP_LAST
;
654 if ((flags
& GIT_CPDIR_CHMOD
) != 0)
655 info
.mkdir_flags
|= GIT_MKDIR_CHMOD_PATH
;
658 ((flags
& GIT_CPDIR_CHMOD
) != 0) ? GIT_MKDIR_CHMOD
: 0;
661 error
= _cp_r_callback(&info
, &path
);
664 git_buf_free(&info
.to
);