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 "common.h"
9 
10 #include "../posix.h"
11 #include "../futils.h"
12 #include "path.h"
13 #include "path_w32.h"
14 #include "utf-conv.h"
15 #include "repository.h"
16 #include "reparse.h"
17 #include "buffer.h"
18 #include <errno.h>
19 #include <io.h>
20 #include <fcntl.h>
21 #include <ws2tcpip.h>
22 
23 #ifndef FILE_NAME_NORMALIZED
24 # define FILE_NAME_NORMALIZED 0
25 #endif
26 
27 #ifndef IO_REPARSE_TAG_SYMLINK
28 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
29 #endif
30 
31 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
32 # define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x02
33 #endif
34 
35 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
36 # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x01
37 #endif
38 
39 /* Allowable mode bits on Win32.  Using mode bits that are not supported on
40  * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
41  * so we simply remove them.
42  */
43 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
44 
45 unsigned long git_win32__createfile_sharemode =
46  FILE_SHARE_READ | FILE_SHARE_WRITE;
47 int git_win32__retries = 10;
48 
set_errno(void)49 GIT_INLINE(void) set_errno(void)
50 {
51 	switch (GetLastError()) {
52 	case ERROR_FILE_NOT_FOUND:
53 	case ERROR_PATH_NOT_FOUND:
54 	case ERROR_INVALID_DRIVE:
55 	case ERROR_NO_MORE_FILES:
56 	case ERROR_BAD_NETPATH:
57 	case ERROR_BAD_NET_NAME:
58 	case ERROR_BAD_PATHNAME:
59 	case ERROR_FILENAME_EXCED_RANGE:
60 		errno = ENOENT;
61 		break;
62 	case ERROR_BAD_ENVIRONMENT:
63 		errno = E2BIG;
64 		break;
65 	case ERROR_BAD_FORMAT:
66 	case ERROR_INVALID_STARTING_CODESEG:
67 	case ERROR_INVALID_STACKSEG:
68 	case ERROR_INVALID_MODULETYPE:
69 	case ERROR_INVALID_EXE_SIGNATURE:
70 	case ERROR_EXE_MARKED_INVALID:
71 	case ERROR_BAD_EXE_FORMAT:
72 	case ERROR_ITERATED_DATA_EXCEEDS_64k:
73 	case ERROR_INVALID_MINALLOCSIZE:
74 	case ERROR_DYNLINK_FROM_INVALID_RING:
75 	case ERROR_IOPL_NOT_ENABLED:
76 	case ERROR_INVALID_SEGDPL:
77 	case ERROR_AUTODATASEG_EXCEEDS_64k:
78 	case ERROR_RING2SEG_MUST_BE_MOVABLE:
79 	case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
80 	case ERROR_INFLOOP_IN_RELOC_CHAIN:
81 		errno = ENOEXEC;
82 		break;
83 	case ERROR_INVALID_HANDLE:
84 	case ERROR_INVALID_TARGET_HANDLE:
85 	case ERROR_DIRECT_ACCESS_HANDLE:
86 		errno = EBADF;
87 		break;
88 	case ERROR_WAIT_NO_CHILDREN:
89 	case ERROR_CHILD_NOT_COMPLETE:
90 		errno = ECHILD;
91 		break;
92 	case ERROR_NO_PROC_SLOTS:
93 	case ERROR_MAX_THRDS_REACHED:
94 	case ERROR_NESTING_NOT_ALLOWED:
95 		errno = EAGAIN;
96 		break;
97 	case ERROR_ARENA_TRASHED:
98 	case ERROR_NOT_ENOUGH_MEMORY:
99 	case ERROR_INVALID_BLOCK:
100 	case ERROR_NOT_ENOUGH_QUOTA:
101 		errno = ENOMEM;
102 		break;
103 	case ERROR_ACCESS_DENIED:
104 	case ERROR_CURRENT_DIRECTORY:
105 	case ERROR_WRITE_PROTECT:
106 	case ERROR_BAD_UNIT:
107 	case ERROR_NOT_READY:
108 	case ERROR_BAD_COMMAND:
109 	case ERROR_CRC:
110 	case ERROR_BAD_LENGTH:
111 	case ERROR_SEEK:
112 	case ERROR_NOT_DOS_DISK:
113 	case ERROR_SECTOR_NOT_FOUND:
114 	case ERROR_OUT_OF_PAPER:
115 	case ERROR_WRITE_FAULT:
116 	case ERROR_READ_FAULT:
117 	case ERROR_GEN_FAILURE:
118 	case ERROR_SHARING_VIOLATION:
119 	case ERROR_LOCK_VIOLATION:
120 	case ERROR_WRONG_DISK:
121 	case ERROR_SHARING_BUFFER_EXCEEDED:
122 	case ERROR_NETWORK_ACCESS_DENIED:
123 	case ERROR_CANNOT_MAKE:
124 	case ERROR_FAIL_I24:
125 	case ERROR_DRIVE_LOCKED:
126 	case ERROR_SEEK_ON_DEVICE:
127 	case ERROR_NOT_LOCKED:
128 	case ERROR_LOCK_FAILED:
129 		errno = EACCES;
130 		break;
131 	case ERROR_FILE_EXISTS:
132 	case ERROR_ALREADY_EXISTS:
133 		errno = EEXIST;
134 		break;
135 	case ERROR_NOT_SAME_DEVICE:
136 		errno = EXDEV;
137 		break;
138 	case ERROR_INVALID_FUNCTION:
139 	case ERROR_INVALID_ACCESS:
140 	case ERROR_INVALID_DATA:
141 	case ERROR_INVALID_PARAMETER:
142 	case ERROR_NEGATIVE_SEEK:
143 		errno = EINVAL;
144 		break;
145 	case ERROR_TOO_MANY_OPEN_FILES:
146 		errno = EMFILE;
147 		break;
148 	case ERROR_DISK_FULL:
149 		errno = ENOSPC;
150 		break;
151 	case ERROR_BROKEN_PIPE:
152 		errno = EPIPE;
153 		break;
154 	case ERROR_DIR_NOT_EMPTY:
155 		errno = ENOTEMPTY;
156 		break;
157 	default:
158 		errno = EINVAL;
159 	}
160 }
161 
last_error_retryable(void)162 GIT_INLINE(bool) last_error_retryable(void)
163 {
164 	int os_error = GetLastError();
165 
166 	return (os_error == ERROR_SHARING_VIOLATION ||
167 		os_error == ERROR_ACCESS_DENIED);
168 }
169 
170 #define do_with_retries(fn, remediation) \
171 	do {                                                             \
172 		int __retry, __ret;                                          \
173 		for (__retry = git_win32__retries; __retry; __retry--) {     \
174 			if ((__ret = (fn)) != GIT_RETRY)                         \
175 				return __ret;                                        \
176 			if (__retry > 1 && (__ret = (remediation)) != 0) {       \
177 				if (__ret == GIT_RETRY)                              \
178 					continue;                                        \
179 				return __ret;                                        \
180 			}                                                        \
181 			Sleep(5);                                                \
182 		}                                                            \
183 		return -1;                                                   \
184 	} while (0)                                                      \
185 
ensure_writable(wchar_t * path)186 static int ensure_writable(wchar_t *path)
187 {
188 	DWORD attrs;
189 
190 	if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
191 		goto on_error;
192 
193 	if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
194 		return 0;
195 
196 	if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
197 		goto on_error;
198 
199 	return GIT_RETRY;
200 
201 on_error:
202 	set_errno();
203 	return -1;
204 }
205 
206 /**
207  * Truncate or extend file.
208  *
209  * We now take a "git_off_t" rather than "long" because
210  * files may be longer than 2Gb.
211  */
p_ftruncate(int fd,off64_t size)212 int p_ftruncate(int fd, off64_t size)
213 {
214 	if (size < 0) {
215 		errno = EINVAL;
216 		return -1;
217 	}
218 
219 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
220 	return ((_chsize_s(fd, size) == 0) ? 0 : -1);
221 #else
222 	/* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
223 	if (size > INT32_MAX) {
224 		errno = EFBIG;
225 		return -1;
226 	}
227 	return _chsize(fd, (long)size);
228 #endif
229 }
230 
p_mkdir(const char * path,mode_t mode)231 int p_mkdir(const char *path, mode_t mode)
232 {
233 	git_win32_path buf;
234 
235 	GIT_UNUSED(mode);
236 
237 	if (git_win32_path_from_utf8(buf, path) < 0)
238 		return -1;
239 
240 	return _wmkdir(buf);
241 }
242 
p_link(const char * old,const char * new)243 int p_link(const char *old, const char *new)
244 {
245 	GIT_UNUSED(old);
246 	GIT_UNUSED(new);
247 	errno = ENOSYS;
248 	return -1;
249 }
250 
unlink_once(const wchar_t * path)251 GIT_INLINE(int) unlink_once(const wchar_t *path)
252 {
253 	DWORD error;
254 
255 	if (DeleteFileW(path))
256 		return 0;
257 
258 	if ((error = GetLastError()) == ERROR_ACCESS_DENIED) {
259 		WIN32_FILE_ATTRIBUTE_DATA fdata;
260 		if (!GetFileAttributesExW(path, GetFileExInfoStandard, &fdata) ||
261 		    !(fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
262 		    !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
263 			goto out;
264 
265 		if (RemoveDirectoryW(path))
266 			return 0;
267 	}
268 
269 out:
270 	SetLastError(error);
271 
272 	if (last_error_retryable())
273 		return GIT_RETRY;
274 
275 	set_errno();
276 	return -1;
277 }
278 
p_unlink(const char * path)279 int p_unlink(const char *path)
280 {
281 	git_win32_path wpath;
282 
283 	if (git_win32_path_from_utf8(wpath, path) < 0)
284 		return -1;
285 
286 	do_with_retries(unlink_once(wpath), ensure_writable(wpath));
287 }
288 
p_fsync(int fd)289 int p_fsync(int fd)
290 {
291 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
292 
293 	p_fsync__cnt++;
294 
295 	if (fh == INVALID_HANDLE_VALUE) {
296 		errno = EBADF;
297 		return -1;
298 	}
299 
300 	if (!FlushFileBuffers(fh)) {
301 		DWORD code = GetLastError();
302 
303 		if (code == ERROR_INVALID_HANDLE)
304 			errno = EINVAL;
305 		else
306 			errno = EIO;
307 
308 		return -1;
309 	}
310 
311 	return 0;
312 }
313 
314 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
315 
lstat_w(wchar_t * path,struct stat * buf,bool posix_enotdir)316 static int lstat_w(
317 	wchar_t *path,
318 	struct stat *buf,
319 	bool posix_enotdir)
320 {
321 	WIN32_FILE_ATTRIBUTE_DATA fdata;
322 
323 	if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
324 		if (!buf)
325 			return 0;
326 
327 		return git_win32__file_attribute_to_stat(buf, &fdata, path);
328 	}
329 
330 	switch (GetLastError()) {
331 	case ERROR_ACCESS_DENIED:
332 		errno = EACCES;
333 		break;
334 	default:
335 		errno = ENOENT;
336 		break;
337 	}
338 
339 	/* To match POSIX behavior, set ENOTDIR when any of the folders in the
340 	 * file path is a regular file, otherwise set ENOENT.
341 	 */
342 	if (errno == ENOENT && posix_enotdir) {
343 		size_t path_len = wcslen(path);
344 
345 		/* scan up path until we find an existing item */
346 		while (1) {
347 			DWORD attrs;
348 
349 			/* remove last directory component */
350 			for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
351 
352 			if (path_len <= 0)
353 				break;
354 
355 			path[path_len] = L'\0';
356 			attrs = GetFileAttributesW(path);
357 
358 			if (attrs != INVALID_FILE_ATTRIBUTES) {
359 				if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
360 					errno = ENOTDIR;
361 				break;
362 			}
363 		}
364 	}
365 
366 	return -1;
367 }
368 
do_lstat(const char * path,struct stat * buf,bool posixly_correct)369 static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
370 {
371 	git_win32_path path_w;
372 	int len;
373 
374 	if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
375 		return -1;
376 
377 	git_win32_path_trim_end(path_w, len);
378 
379 	return lstat_w(path_w, buf, posixly_correct);
380 }
381 
p_lstat(const char * filename,struct stat * buf)382 int p_lstat(const char *filename, struct stat *buf)
383 {
384 	return do_lstat(filename, buf, false);
385 }
386 
p_lstat_posixly(const char * filename,struct stat * buf)387 int p_lstat_posixly(const char *filename, struct stat *buf)
388 {
389 	return do_lstat(filename, buf, true);
390 }
391 
p_readlink(const char * path,char * buf,size_t bufsiz)392 int p_readlink(const char *path, char *buf, size_t bufsiz)
393 {
394 	git_win32_path path_w, target_w;
395 	git_win32_utf8_path target;
396 	int len;
397 
398 	/* readlink(2) does not NULL-terminate the string written
399 	 * to the target buffer. Furthermore, the target buffer need
400 	 * not be large enough to hold the entire result. A truncated
401 	 * result should be written in this case. Since this truncation
402 	 * could occur in the middle of the encoding of a code point,
403 	 * we need to buffer the result on the stack. */
404 
405 	if (git_win32_path_from_utf8(path_w, path) < 0 ||
406 		git_win32_path_readlink_w(target_w, path_w) < 0 ||
407 		(len = git_win32_path_to_utf8(target, target_w)) < 0)
408 		return -1;
409 
410 	bufsiz = min((size_t)len, bufsiz);
411 	memcpy(buf, target, bufsiz);
412 
413 	return (int)bufsiz;
414 }
415 
target_is_dir(const char * target,const char * path)416 static bool target_is_dir(const char *target, const char *path)
417 {
418 	git_buf resolved = GIT_BUF_INIT;
419 	git_win32_path resolved_w;
420 	bool isdir = true;
421 
422 	if (git_path_is_absolute(target))
423 		git_win32_path_from_utf8(resolved_w, target);
424 	else if (git_path_dirname_r(&resolved, path) < 0 ||
425 		 git_path_apply_relative(&resolved, target) < 0 ||
426 		 git_win32_path_from_utf8(resolved_w, resolved.ptr) < 0)
427 		goto out;
428 
429 	isdir = GetFileAttributesW(resolved_w) & FILE_ATTRIBUTE_DIRECTORY;
430 
431 out:
432 	git_buf_dispose(&resolved);
433 	return isdir;
434 }
435 
p_symlink(const char * target,const char * path)436 int p_symlink(const char *target, const char *path)
437 {
438 	git_win32_path target_w, path_w;
439 	DWORD dwFlags;
440 
441 	/*
442 	 * Convert both target and path to Windows-style paths. Note that we do
443 	 * not want to use `git_win32_path_from_utf8` for converting the target,
444 	 * as that function will automatically pre-pend the current working
445 	 * directory in case the path is not absolute. As Git will instead use
446 	 * relative symlinks, this is not someting we want.
447 	 */
448 	if (git_win32_path_from_utf8(path_w, path) < 0 ||
449 	    git_win32_path_relative_from_utf8(target_w, target) < 0)
450 		return -1;
451 
452 	dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
453 	if (target_is_dir(target, path))
454 		dwFlags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
455 
456 	if (!CreateSymbolicLinkW(path_w, target_w, dwFlags))
457 		return -1;
458 
459 	return 0;
460 }
461 
462 struct open_opts {
463 	DWORD access;
464 	DWORD sharing;
465 	SECURITY_ATTRIBUTES security;
466 	DWORD creation_disposition;
467 	DWORD attributes;
468 	int osf_flags;
469 };
470 
open_opts_from_posix(struct open_opts * opts,int flags,mode_t mode)471 GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
472 {
473 	memset(opts, 0, sizeof(struct open_opts));
474 
475 	switch (flags & (O_WRONLY | O_RDWR)) {
476 	case O_WRONLY:
477 		opts->access = GENERIC_WRITE;
478 		break;
479 	case O_RDWR:
480 		opts->access = GENERIC_READ | GENERIC_WRITE;
481 		break;
482 	default:
483 		opts->access = GENERIC_READ;
484 		break;
485 	}
486 
487 	opts->sharing = (DWORD)git_win32__createfile_sharemode;
488 
489 	switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
490 	case O_CREAT | O_EXCL:
491 	case O_CREAT | O_TRUNC | O_EXCL:
492 		opts->creation_disposition = CREATE_NEW;
493 		break;
494 	case O_CREAT | O_TRUNC:
495 		opts->creation_disposition = CREATE_ALWAYS;
496 		break;
497 	case O_TRUNC:
498 		opts->creation_disposition = TRUNCATE_EXISTING;
499 		break;
500 	case O_CREAT:
501 		opts->creation_disposition = OPEN_ALWAYS;
502 		break;
503 	default:
504 		opts->creation_disposition = OPEN_EXISTING;
505 		break;
506 	}
507 
508 	opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
509 		FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
510 	opts->osf_flags = flags & (O_RDONLY | O_APPEND);
511 
512 	opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
513 	opts->security.lpSecurityDescriptor = NULL;
514 	opts->security.bInheritHandle = 0;
515 }
516 
open_once(const wchar_t * path,struct open_opts * opts)517 GIT_INLINE(int) open_once(
518 	const wchar_t *path,
519 	struct open_opts *opts)
520 {
521 	int fd;
522 
523 	HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
524 		&opts->security, opts->creation_disposition, opts->attributes, 0);
525 
526 	if (handle == INVALID_HANDLE_VALUE) {
527 		if (last_error_retryable())
528 			return GIT_RETRY;
529 
530 		set_errno();
531 		return -1;
532 	}
533 
534 	if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
535 		CloseHandle(handle);
536 
537 	return fd;
538 }
539 
p_open(const char * path,int flags,...)540 int p_open(const char *path, int flags, ...)
541 {
542 	git_win32_path wpath;
543 	mode_t mode = 0;
544 	struct open_opts opts = {0};
545 
546 	#ifdef GIT_DEBUG_STRICT_OPEN
547 	if (strstr(path, "//") != NULL) {
548 		errno = EACCES;
549 		return -1;
550 	}
551 	#endif
552 
553 	if (git_win32_path_from_utf8(wpath, path) < 0)
554 		return -1;
555 
556 	if (flags & O_CREAT) {
557 		va_list arg_list;
558 
559 		va_start(arg_list, flags);
560 		mode = (mode_t)va_arg(arg_list, int);
561 		va_end(arg_list);
562 	}
563 
564 	open_opts_from_posix(&opts, flags, mode);
565 
566 	do_with_retries(
567 		open_once(wpath, &opts),
568 		0);
569 }
570 
p_creat(const char * path,mode_t mode)571 int p_creat(const char *path, mode_t mode)
572 {
573 	return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
574 }
575 
p_utimes(const char * path,const struct p_timeval times[2])576 int p_utimes(const char *path, const struct p_timeval times[2])
577 {
578 	git_win32_path wpath;
579 	int fd, error;
580 	DWORD attrs_orig, attrs_new = 0;
581 	struct open_opts opts = { 0 };
582 
583 	if (git_win32_path_from_utf8(wpath, path) < 0)
584 		return -1;
585 
586 	attrs_orig = GetFileAttributesW(wpath);
587 
588 	if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
589 		attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
590 
591 		if (!SetFileAttributesW(wpath, attrs_new)) {
592 			git_error_set(GIT_ERROR_OS, "failed to set attributes");
593 			return -1;
594 		}
595 	}
596 
597 	open_opts_from_posix(&opts, O_RDWR, 0);
598 
599 	if ((fd = open_once(wpath, &opts)) < 0) {
600 		error = -1;
601 		goto done;
602 	}
603 
604 	error = p_futimes(fd, times);
605 	close(fd);
606 
607 done:
608 	if (attrs_orig != attrs_new) {
609 		DWORD os_error = GetLastError();
610 		SetFileAttributesW(wpath, attrs_orig);
611 		SetLastError(os_error);
612 	}
613 
614 	return error;
615 }
616 
p_futimes(int fd,const struct p_timeval times[2])617 int p_futimes(int fd, const struct p_timeval times[2])
618 {
619 	HANDLE handle;
620 	FILETIME atime = { 0 }, mtime = { 0 };
621 
622 	if (times == NULL) {
623 		SYSTEMTIME st;
624 
625 		GetSystemTime(&st);
626 		SystemTimeToFileTime(&st, &atime);
627 		SystemTimeToFileTime(&st, &mtime);
628 	}
629 	else {
630 		git_win32__timeval_to_filetime(&atime, times[0]);
631 		git_win32__timeval_to_filetime(&mtime, times[1]);
632 	}
633 
634 	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
635 		return -1;
636 
637 	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
638 		return -1;
639 
640 	return 0;
641 }
642 
p_getcwd(char * buffer_out,size_t size)643 int p_getcwd(char *buffer_out, size_t size)
644 {
645 	git_win32_path buf;
646 	wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
647 
648 	if (!cwd)
649 		return -1;
650 
651 	git_win32_path_remove_namespace(cwd, wcslen(cwd));
652 
653 	/* Convert the working directory back to UTF-8 */
654 	if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
655 		DWORD code = GetLastError();
656 
657 		if (code == ERROR_INSUFFICIENT_BUFFER)
658 			errno = ERANGE;
659 		else
660 			errno = EINVAL;
661 
662 		return -1;
663 	}
664 
665 	git_path_mkposix(buffer_out);
666 	return 0;
667 }
668 
getfinalpath_w(git_win32_path dest,const wchar_t * path)669 static int getfinalpath_w(
670 	git_win32_path dest,
671 	const wchar_t *path)
672 {
673 	HANDLE hFile;
674 	DWORD dwChars;
675 
676 	/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
677 	* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
678 	* target of the link. */
679 	hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
680 		NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
681 
682 	if (INVALID_HANDLE_VALUE == hFile)
683 		return -1;
684 
685 	/* Call GetFinalPathNameByHandle */
686 	dwChars = GetFinalPathNameByHandleW(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
687 	CloseHandle(hFile);
688 
689 	if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
690 		return -1;
691 
692 	/* The path may be delivered to us with a namespace prefix; remove */
693 	return (int)git_win32_path_remove_namespace(dest, dwChars);
694 }
695 
follow_and_lstat_link(git_win32_path path,struct stat * buf)696 static int follow_and_lstat_link(git_win32_path path, struct stat *buf)
697 {
698 	git_win32_path target_w;
699 
700 	if (getfinalpath_w(target_w, path) < 0)
701 		return -1;
702 
703 	return lstat_w(target_w, buf, false);
704 }
705 
p_fstat(int fd,struct stat * buf)706 int p_fstat(int fd, struct stat *buf)
707 {
708 	BY_HANDLE_FILE_INFORMATION fhInfo;
709 
710 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
711 
712 	if (fh == INVALID_HANDLE_VALUE ||
713 		!GetFileInformationByHandle(fh, &fhInfo)) {
714 		errno = EBADF;
715 		return -1;
716 	}
717 
718 	git_win32__file_information_to_stat(buf, &fhInfo);
719 	return 0;
720 }
721 
p_stat(const char * path,struct stat * buf)722 int p_stat(const char *path, struct stat *buf)
723 {
724 	git_win32_path path_w;
725 	int len;
726 
727 	if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
728 		lstat_w(path_w, buf, false) < 0)
729 		return -1;
730 
731 	/* The item is a symbolic link or mount point. No need to iterate
732 	 * to follow multiple links; use GetFinalPathNameFromHandle. */
733 	if (S_ISLNK(buf->st_mode))
734 		return follow_and_lstat_link(path_w, buf);
735 
736 	return 0;
737 }
738 
p_chdir(const char * path)739 int p_chdir(const char *path)
740 {
741 	git_win32_path buf;
742 
743 	if (git_win32_path_from_utf8(buf, path) < 0)
744 		return -1;
745 
746 	return _wchdir(buf);
747 }
748 
p_chmod(const char * path,mode_t mode)749 int p_chmod(const char *path, mode_t mode)
750 {
751 	git_win32_path buf;
752 
753 	if (git_win32_path_from_utf8(buf, path) < 0)
754 		return -1;
755 
756 	return _wchmod(buf, mode);
757 }
758 
p_rmdir(const char * path)759 int p_rmdir(const char *path)
760 {
761 	git_win32_path buf;
762 	int error;
763 
764 	if (git_win32_path_from_utf8(buf, path) < 0)
765 		return -1;
766 
767 	error = _wrmdir(buf);
768 
769 	if (error == -1) {
770 		switch (GetLastError()) {
771 			/* _wrmdir() is documented to return EACCES if "A program has an open
772 			 * handle to the directory."  This sounds like what everybody else calls
773 			 * EBUSY.  Let's convert appropriate error codes.
774 			 */
775 			case ERROR_SHARING_VIOLATION:
776 				errno = EBUSY;
777 				break;
778 
779 			/* This error can be returned when trying to rmdir an extant file. */
780 			case ERROR_DIRECTORY:
781 				errno = ENOTDIR;
782 				break;
783 		}
784 	}
785 
786 	return error;
787 }
788 
p_realpath(const char * orig_path,char * buffer)789 char *p_realpath(const char *orig_path, char *buffer)
790 {
791 	git_win32_path orig_path_w, buffer_w;
792 
793 	if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
794 		return NULL;
795 
796 	/* Note that if the path provided is a relative path, then the current directory
797 	 * is used to resolve the path -- which is a concurrency issue because the current
798 	 * directory is a process-wide variable. */
799 	if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
800 		if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
801 			errno = ENAMETOOLONG;
802 		else
803 			errno = EINVAL;
804 
805 		return NULL;
806 	}
807 
808 	/* The path must exist. */
809 	if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
810 		errno = ENOENT;
811 		return NULL;
812 	}
813 
814 	if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
815 		errno = ENOMEM;
816 		return NULL;
817 	}
818 
819 	/* Convert the path to UTF-8. If the caller provided a buffer, then it
820 	 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
821 	 * then we may overflow. */
822 	if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
823 		return NULL;
824 
825 	git_path_mkposix(buffer);
826 
827 	return buffer;
828 }
829 
p_vsnprintf(char * buffer,size_t count,const char * format,va_list argptr)830 int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
831 {
832 #if defined(_MSC_VER)
833 	int len;
834 
835 	if (count == 0)
836 		return _vscprintf(format, argptr);
837 
838 	#if _MSC_VER >= 1500
839 	len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
840 	#else
841 	len = _vsnprintf(buffer, count, format, argptr);
842 	#endif
843 
844 	if (len < 0)
845 		return _vscprintf(format, argptr);
846 
847 	return len;
848 #else /* MinGW */
849 	return vsnprintf(buffer, count, format, argptr);
850 #endif
851 }
852 
p_snprintf(char * buffer,size_t count,const char * format,...)853 int p_snprintf(char *buffer, size_t count, const char *format, ...)
854 {
855 	va_list va;
856 	int r;
857 
858 	va_start(va, format);
859 	r = p_vsnprintf(buffer, count, format, va);
860 	va_end(va);
861 
862 	return r;
863 }
864 
865 /* TODO: wut? */
p_mkstemp(char * tmp_path)866 int p_mkstemp(char *tmp_path)
867 {
868 #if defined(_MSC_VER) && _MSC_VER >= 1500
869 	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
870 		return -1;
871 #else
872 	if (_mktemp(tmp_path) == NULL)
873 		return -1;
874 #endif
875 
876 	return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); /* -V536 */
877 }
878 
p_access(const char * path,mode_t mode)879 int p_access(const char *path, mode_t mode)
880 {
881 	git_win32_path buf;
882 
883 	if (git_win32_path_from_utf8(buf, path) < 0)
884 		return -1;
885 
886 	return _waccess(buf, mode & WIN32_MODE_MASK);
887 }
888 
rename_once(const wchar_t * from,const wchar_t * to)889 GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
890 {
891 	if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
892 		return 0;
893 
894 	if (last_error_retryable())
895 		return GIT_RETRY;
896 
897 	set_errno();
898 	return -1;
899 }
900 
p_rename(const char * from,const char * to)901 int p_rename(const char *from, const char *to)
902 {
903 	git_win32_path wfrom, wto;
904 
905 	if (git_win32_path_from_utf8(wfrom, from) < 0 ||
906 		git_win32_path_from_utf8(wto, to) < 0)
907 		return -1;
908 
909 	do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
910 }
911 
p_recv(GIT_SOCKET socket,void * buffer,size_t length,int flags)912 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
913 {
914 	if ((size_t)((int)length) != length)
915 		return -1; /* git_error_set will be done by caller */
916 
917 	return recv(socket, buffer, (int)length, flags);
918 }
919 
p_send(GIT_SOCKET socket,const void * buffer,size_t length,int flags)920 int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
921 {
922 	if ((size_t)((int)length) != length)
923 		return -1; /* git_error_set will be done by caller */
924 
925 	return send(socket, buffer, (int)length, flags);
926 }
927 
928 /**
929  * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
930  * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
931  */
932 struct tm *
p_localtime_r(const time_t * timer,struct tm * result)933 p_localtime_r (const time_t *timer, struct tm *result)
934 {
935 	struct tm *local_result;
936 	local_result = localtime (timer);
937 
938 	if (local_result == NULL || result == NULL)
939 		return NULL;
940 
941 	memcpy (result, local_result, sizeof (struct tm));
942 	return result;
943 }
944 struct tm *
p_gmtime_r(const time_t * timer,struct tm * result)945 p_gmtime_r (const time_t *timer, struct tm *result)
946 {
947 	struct tm *local_result;
948 	local_result = gmtime (timer);
949 
950 	if (local_result == NULL || result == NULL)
951 		return NULL;
952 
953 	memcpy (result, local_result, sizeof (struct tm));
954 	return result;
955 }
956 
p_inet_pton(int af,const char * src,void * dst)957 int p_inet_pton(int af, const char *src, void *dst)
958 {
959 	struct sockaddr_storage sin;
960 	void *addr;
961 	int sin_len = sizeof(struct sockaddr_storage), addr_len;
962 	int error = 0;
963 
964 	if (af == AF_INET) {
965 		addr = &((struct sockaddr_in *)&sin)->sin_addr;
966 		addr_len = sizeof(struct in_addr);
967 	} else if (af == AF_INET6) {
968 		addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
969 		addr_len = sizeof(struct in6_addr);
970 	} else {
971 		errno = EAFNOSUPPORT;
972 		return -1;
973 	}
974 
975 	if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
976 		memcpy(dst, addr, addr_len);
977 		return 1;
978 	}
979 
980 	switch(WSAGetLastError()) {
981 	case WSAEINVAL:
982 		return 0;
983 	case WSAEFAULT:
984 		errno = ENOSPC;
985 		return -1;
986 	case WSA_NOT_ENOUGH_MEMORY:
987 		errno = ENOMEM;
988 		return -1;
989 	}
990 
991 	errno = EINVAL;
992 	return -1;
993 }
994 
p_pread(int fd,void * data,size_t size,off64_t offset)995 ssize_t p_pread(int fd, void *data, size_t size, off64_t offset)
996 {
997 	HANDLE fh;
998 	DWORD rsize = 0;
999 	OVERLAPPED ov = {0};
1000 	LARGE_INTEGER pos = {0};
1001 	off64_t final_offset = 0;
1002 
1003 	/* Fail if the final offset would have overflowed to match POSIX semantics. */
1004 	if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
1005 		errno = EINVAL;
1006 		return -1;
1007 	}
1008 
1009 	/*
1010 	 * Truncate large writes to the maximum allowable size: the caller
1011 	 * needs to always call this in a loop anyways.
1012 	 */
1013 	if (size > INT32_MAX) {
1014 		size = INT32_MAX;
1015 	}
1016 
1017 	pos.QuadPart = offset;
1018 	ov.Offset = pos.LowPart;
1019 	ov.OffsetHigh = pos.HighPart;
1020 	fh = (HANDLE)_get_osfhandle(fd);
1021 
1022 	if (ReadFile(fh, data, (DWORD)size, &rsize, &ov)) {
1023 		return (ssize_t)rsize;
1024 	}
1025 
1026 	set_errno();
1027 	return -1;
1028 }
1029 
p_pwrite(int fd,const void * data,size_t size,off64_t offset)1030 ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset)
1031 {
1032 	HANDLE fh;
1033 	DWORD wsize = 0;
1034 	OVERLAPPED ov = {0};
1035 	LARGE_INTEGER pos = {0};
1036 	off64_t final_offset = 0;
1037 
1038 	/* Fail if the final offset would have overflowed to match POSIX semantics. */
1039 	if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
1040 		errno = EINVAL;
1041 		return -1;
1042 	}
1043 
1044 	/*
1045 	 * Truncate large writes to the maximum allowable size: the caller
1046 	 * needs to always call this in a loop anyways.
1047 	 */
1048 	if (size > INT32_MAX) {
1049 		size = INT32_MAX;
1050 	}
1051 
1052 	pos.QuadPart = offset;
1053 	ov.Offset = pos.LowPart;
1054 	ov.OffsetHigh = pos.HighPart;
1055 	fh = (HANDLE)_get_osfhandle(fd);
1056 
1057 	if (WriteFile(fh, data, (DWORD)size, &wsize, &ov)) {
1058 		return (ssize_t)wsize;
1059 	}
1060 
1061 	set_errno();
1062 	return -1;
1063 }
1064