posix_w32.c 20.7 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
Vicent Marti committed
3 4 5 6
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */
7 8 9

#include "common.h"

10
#include "../posix.h"
Russell Belfer committed
11
#include "../fileops.h"
12
#include "path.h"
13
#include "path_w32.h"
14
#include "utf-conv.h"
15
#include "repository.h"
16
#include "reparse.h"
17 18
#include "global.h"
#include "buffer.h"
Vicent Marti committed
19
#include <errno.h>
20
#include <io.h>
21
#include <fcntl.h>
22
#include <ws2tcpip.h>
Vicent Marti committed
23

24
#ifndef FILE_NAME_NORMALIZED
25 26 27
# define FILE_NAME_NORMALIZED 0
#endif

28 29 30 31
#ifndef IO_REPARSE_TAG_SYMLINK
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
#endif

32 33 34 35 36 37
/* Allowable mode bits on Win32.  Using mode bits that are not supported on
 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
 * so we simply remove them.
 */
#define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)

38 39 40
/* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);

41 42
unsigned long git_win32__createfile_sharemode =
 FILE_SHARE_READ | FILE_SHARE_WRITE;
43
int git_win32__retries = 10;
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
GIT_INLINE(void) set_errno(void)
{
	switch (GetLastError()) {
	case ERROR_FILE_NOT_FOUND:
	case ERROR_PATH_NOT_FOUND:
	case ERROR_INVALID_DRIVE:
	case ERROR_NO_MORE_FILES:
	case ERROR_BAD_NETPATH:
	case ERROR_BAD_NET_NAME:
	case ERROR_BAD_PATHNAME:
	case ERROR_FILENAME_EXCED_RANGE:
		errno = ENOENT;
		break;
	case ERROR_BAD_ENVIRONMENT:
		errno = E2BIG;
		break;
	case ERROR_BAD_FORMAT:
	case ERROR_INVALID_STARTING_CODESEG:
	case ERROR_INVALID_STACKSEG:
	case ERROR_INVALID_MODULETYPE:
	case ERROR_INVALID_EXE_SIGNATURE:
	case ERROR_EXE_MARKED_INVALID:
	case ERROR_BAD_EXE_FORMAT:
	case ERROR_ITERATED_DATA_EXCEEDS_64k:
	case ERROR_INVALID_MINALLOCSIZE:
	case ERROR_DYNLINK_FROM_INVALID_RING:
	case ERROR_IOPL_NOT_ENABLED:
	case ERROR_INVALID_SEGDPL:
	case ERROR_AUTODATASEG_EXCEEDS_64k:
	case ERROR_RING2SEG_MUST_BE_MOVABLE:
	case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
	case ERROR_INFLOOP_IN_RELOC_CHAIN:
		errno = ENOEXEC;
		break;
	case ERROR_INVALID_HANDLE:
	case ERROR_INVALID_TARGET_HANDLE:
	case ERROR_DIRECT_ACCESS_HANDLE:
		errno = EBADF;
		break;
	case ERROR_WAIT_NO_CHILDREN:
	case ERROR_CHILD_NOT_COMPLETE:
		errno = ECHILD;
		break;
	case ERROR_NO_PROC_SLOTS:
	case ERROR_MAX_THRDS_REACHED:
	case ERROR_NESTING_NOT_ALLOWED:
		errno = EAGAIN;
		break;
	case ERROR_ARENA_TRASHED:
	case ERROR_NOT_ENOUGH_MEMORY:
	case ERROR_INVALID_BLOCK:
	case ERROR_NOT_ENOUGH_QUOTA:
		errno = ENOMEM;
		break;
	case ERROR_ACCESS_DENIED:
	case ERROR_CURRENT_DIRECTORY:
	case ERROR_WRITE_PROTECT:
	case ERROR_BAD_UNIT:
	case ERROR_NOT_READY:
	case ERROR_BAD_COMMAND:
	case ERROR_CRC:
	case ERROR_BAD_LENGTH:
	case ERROR_SEEK:
	case ERROR_NOT_DOS_DISK:
	case ERROR_SECTOR_NOT_FOUND:
	case ERROR_OUT_OF_PAPER:
	case ERROR_WRITE_FAULT:
	case ERROR_READ_FAULT:
	case ERROR_GEN_FAILURE:
	case ERROR_SHARING_VIOLATION:
	case ERROR_LOCK_VIOLATION:
	case ERROR_WRONG_DISK:
	case ERROR_SHARING_BUFFER_EXCEEDED:
	case ERROR_NETWORK_ACCESS_DENIED:
	case ERROR_CANNOT_MAKE:
	case ERROR_FAIL_I24:
	case ERROR_DRIVE_LOCKED:
	case ERROR_SEEK_ON_DEVICE:
	case ERROR_NOT_LOCKED:
	case ERROR_LOCK_FAILED:
		errno = EACCES;
		break;
	case ERROR_FILE_EXISTS:
	case ERROR_ALREADY_EXISTS:
		errno = EEXIST;
		break;
	case ERROR_NOT_SAME_DEVICE:
		errno = EXDEV;
		break;
	case ERROR_INVALID_FUNCTION:
	case ERROR_INVALID_ACCESS:
	case ERROR_INVALID_DATA:
	case ERROR_INVALID_PARAMETER:
	case ERROR_NEGATIVE_SEEK:
		errno = EINVAL;
		break;
	case ERROR_TOO_MANY_OPEN_FILES:
		errno = EMFILE;
		break;
	case ERROR_DISK_FULL:
		errno = ENOSPC;
		break;
	case ERROR_BROKEN_PIPE:
		errno = EPIPE;
		break;
	case ERROR_DIR_NOT_EMPTY:
		errno = ENOTEMPTY;
		break;
	default:
		errno = EINVAL;
	}
}

158 159 160 161 162 163 164 165
GIT_INLINE(bool) last_error_retryable(void)
{
	int os_error = GetLastError();

	return (os_error == ERROR_SHARING_VIOLATION ||
		os_error == ERROR_ACCESS_DENIED);
}

166
#define do_with_retries(fn, remediation) \
167
	do {                                                             \
168 169
		int __retry, __ret;                                          \
		for (__retry = git_win32__retries; __retry; __retry--) {     \
170 171
			if ((__ret = (fn)) != GIT_RETRY)                         \
				return __ret;                                        \
172 173 174 175 176
			if (__retry > 1 && (__ret = (remediation)) != 0) {       \
				if (__ret == GIT_RETRY)                              \
					continue;                                        \
				return __ret;                                        \
			}                                                        \
177 178 179 180 181
			Sleep(5);                                                \
		}                                                            \
		return -1;                                                   \
	} while (0)                                                      \

182 183 184 185 186 187 188 189 190 191 192 193 194
static int ensure_writable(wchar_t *path)
{
	DWORD attrs;

	if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
		goto on_error;

	if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
		return 0;

	if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
		goto on_error;

195
	return GIT_RETRY;
196 197 198 199 200 201

on_error:
	set_errno();
	return -1;
}

202 203 204 205 206 207 208
/**
 * Truncate or extend file.
 *
 * We now take a "git_off_t" rather than "long" because
 * files may be longer than 2Gb.
 */
int p_ftruncate(int fd, git_off_t size)
209
{
210 211 212 213 214
	if (size < 0) {
		errno = EINVAL;
		return -1;
	}

215
#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
216
	return ((_chsize_s(fd, size) == 0) ? 0 : -1);
217
#else
218
	/* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
219 220 221 222 223
	if (size > INT32_MAX) {
		errno = EFBIG;
		return -1;
	}
	return _chsize(fd, (long)size);
224 225 226
#endif
}

227 228 229 230 231 232
int p_mkdir(const char *path, mode_t mode)
{
	git_win32_path buf;

	GIT_UNUSED(mode);

233
	if (git_win32_path_from_utf8(buf, path) < 0)
234 235 236 237 238
		return -1;

	return _wmkdir(buf);
}

239 240 241 242 243 244 245 246
int p_link(const char *old, const char *new)
{
	GIT_UNUSED(old);
	GIT_UNUSED(new);
	errno = ENOSYS;
	return -1;
}

247 248 249 250 251 252 253 254 255 256 257 258
GIT_INLINE(int) unlink_once(const wchar_t *path)
{
	if (DeleteFileW(path))
		return 0;

	if (last_error_retryable())
		return GIT_RETRY;

	set_errno();
	return -1;
}

Vicent Marti committed
259 260
int p_unlink(const char *path)
{
261
	git_win32_path wpath;
262

263
	if (git_win32_path_from_utf8(wpath, path) < 0)
264 265
		return -1;

266
	do_with_retries(unlink_once(wpath), ensure_writable(wpath));
Vicent Marti committed
267 268 269 270 271 272
}

int p_fsync(int fd)
{
	HANDLE fh = (HANDLE)_get_osfhandle(fd);

273 274
	p_fsync__cnt++;

Vicent Marti committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	if (fh == INVALID_HANDLE_VALUE) {
		errno = EBADF;
		return -1;
	}

	if (!FlushFileBuffers(fh)) {
		DWORD code = GetLastError();

		if (code == ERROR_INVALID_HANDLE)
			errno = EINVAL;
		else
			errno = EIO;

		return -1;
	}

	return 0;
}

294 295 296 297 298 299 300 301 302 303
#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')

static int lstat_w(
	wchar_t *path,
	struct stat *buf,
	bool posix_enotdir)
{
	WIN32_FILE_ATTRIBUTE_DATA fdata;

	if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
304 305 306
		if (!buf)
			return 0;

307
		return git_win32__file_attribute_to_stat(buf, &fdata, path);
Vicent Marti committed
308 309
	}

310 311 312 313 314 315 316 317
	switch (GetLastError()) {
	case ERROR_ACCESS_DENIED:
		errno = EACCES;
		break;
	default:
		errno = ENOENT;
		break;
	}
318

319 320
	/* To match POSIX behavior, set ENOTDIR when any of the folders in the
	 * file path is a regular file, otherwise set ENOENT.
321
	 */
322
	if (errno == ENOENT && posix_enotdir) {
323 324
		size_t path_len = wcslen(path);

325 326
		/* scan up path until we find an existing item */
		while (1) {
327 328
			DWORD attrs;

329
			/* remove last directory component */
330
			for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
331

332
			if (path_len <= 0)
333 334
				break;

335 336
			path[path_len] = L'\0';
			attrs = GetFileAttributesW(path);
337

338
			if (attrs != INVALID_FILE_ATTRIBUTES) {
339
				if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
Eduardo Bart committed
340
					errno = ENOTDIR;
341
				break;
342 343 344 345
			}
		}
	}

346
	return -1;
Vicent Marti committed
347 348
}

349
static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
Vicent Marti committed
350
{
351 352 353
	git_win32_path path_w;
	int len;

354
	if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
355 356 357 358 359
		return -1;

	git_win32__path_trim_end(path_w, len);

	return lstat_w(path_w, buf, posixly_correct);
360
}
Vicent Marti committed
361

362
int p_lstat(const char *filename, struct stat *buf)
363
{
364
	return do_lstat(filename, buf, false);
Vicent Marti committed
365 366
}

367
int p_lstat_posixly(const char *filename, struct stat *buf)
368
{
369
	return do_lstat(filename, buf, true);
370
}
371

372
int p_readlink(const char *path, char *buf, size_t bufsiz)
Vicent Marti committed
373
{
374 375 376 377 378 379 380 381 382 383 384
	git_win32_path path_w, target_w;
	git_win32_utf8_path target;
	int len;

	/* readlink(2) does not NULL-terminate the string written
	 * to the target buffer. Furthermore, the target buffer need
	 * not be large enough to hold the entire result. A truncated
	 * result should be written in this case. Since this truncation
	 * could occur in the middle of the encoding of a code point,
	 * we need to buffer the result on the stack. */

385
	if (git_win32_path_from_utf8(path_w, path) < 0 ||
386
		git_win32_path_readlink_w(target_w, path_w) < 0 ||
387 388
		(len = git_win32_path_to_utf8(target, target_w)) < 0)
		return -1;
389

390 391
	bufsiz = min((size_t)len, bufsiz);
	memcpy(buf, target, bufsiz);
392

393
	return (int)bufsiz;
Vicent Marti committed
394 395
}

Ben Straub committed
396 397
int p_symlink(const char *old, const char *new)
{
398 399 400 401
	/* Real symlinks on NTFS require admin privileges. Until this changes,
	 * libgit2 just creates a text file with the link target in the contents.
	 */
	return git_futils_fake_symlink(old, new);
Ben Straub committed
402 403
}

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
struct open_opts {
	DWORD access;
	DWORD sharing;
	SECURITY_ATTRIBUTES security;
	DWORD creation_disposition;
	DWORD attributes;
	int osf_flags;
};

GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
{
	memset(opts, 0, sizeof(struct open_opts));

	switch (flags & (O_WRONLY | O_RDWR)) {
	case O_WRONLY:
		opts->access = GENERIC_WRITE;
		break;
	case O_RDWR:
		opts->access = GENERIC_READ | GENERIC_WRITE;
		break;
	default:
		opts->access = GENERIC_READ;
		break;
	}

	opts->sharing = (DWORD)git_win32__createfile_sharemode;

	switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
	case O_CREAT | O_EXCL:
	case O_CREAT | O_TRUNC | O_EXCL:
		opts->creation_disposition = CREATE_NEW;
		break;
	case O_CREAT | O_TRUNC:
		opts->creation_disposition = CREATE_ALWAYS;
		break;
	case O_TRUNC:
		opts->creation_disposition = TRUNCATE_EXISTING;
		break;
	case O_CREAT:
		opts->creation_disposition = OPEN_ALWAYS;
		break;
	default:
		opts->creation_disposition = OPEN_EXISTING;
		break;
	}

	opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
		FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
	opts->osf_flags = flags & (O_RDONLY | O_APPEND);

	opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
	opts->security.lpSecurityDescriptor = NULL;
	opts->security.bInheritHandle = 0;
}

459 460
GIT_INLINE(int) open_once(
	const wchar_t *path,
461
	struct open_opts *opts)
462
{
463 464
	int fd;

465 466
	HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
		&opts->security, opts->creation_disposition, opts->attributes, 0);
467

468 469 470 471 472 473 474 475
	if (handle == INVALID_HANDLE_VALUE) {
		if (last_error_retryable())
			return GIT_RETRY;

		set_errno();
		return -1;
	}

476 477 478 479
	if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
		CloseHandle(handle);

	return fd;
480 481
}

482
int p_open(const char *path, int flags, ...)
483
{
484
	git_win32_path wpath;
485
	mode_t mode = 0;
486
	struct open_opts opts = {0};
487

488
	if (git_win32_path_from_utf8(wpath, path) < 0)
489
		return -1;
490

491
	if (flags & O_CREAT) {
492 493 494
		va_list arg_list;

		va_start(arg_list, flags);
liyuray committed
495
		mode = (mode_t)va_arg(arg_list, int);
496 497 498
		va_end(arg_list);
	}

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
	open_opts_from_posix(&opts, flags, mode);

	do_with_retries(
		open_once(wpath, &opts),
		0);
}

int p_creat(const char *path, mode_t mode)
{
	return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
}

int p_utimes(const char *path, const struct p_timeval times[2])
{
	git_win32_path wpath;
	int fd, error;
	DWORD attrs_orig, attrs_new = 0;
	struct open_opts opts = { 0 };

	if (git_win32_path_from_utf8(wpath, path) < 0)
		return -1;

	attrs_orig = GetFileAttributesW(wpath);

	if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
		attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;

		if (!SetFileAttributesW(wpath, attrs_new)) {
			giterr_set(GITERR_OS, "failed to set attributes");
			return -1;
		}
530 531
	}

532
	open_opts_from_posix(&opts, O_RDWR, 0);
533

534 535 536
	if ((fd = open_once(wpath, &opts)) < 0) {
		error = -1;
		goto done;
537 538
	}

539 540
	error = p_futimes(fd, times);
	close(fd);
541

542 543 544 545 546 547 548 549
done:
	if (attrs_orig != attrs_new) {
		DWORD os_error = GetLastError();
		SetFileAttributesW(wpath, attrs_orig);
		SetLastError(os_error);
	}

	return error;
550 551
}

552
int p_futimes(int fd, const struct p_timeval times[2])
553
{
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
	HANDLE handle;
	FILETIME atime = { 0 }, mtime = { 0 };

	if (times == NULL) {
		SYSTEMTIME st;

		GetSystemTime(&st);
		SystemTimeToFileTime(&st, &atime);
		SystemTimeToFileTime(&st, &mtime);
	}
	else {
		git_win32__timeval_to_filetime(&atime, times[0]);
		git_win32__timeval_to_filetime(&mtime, times[1]);
	}

	if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
		return -1;

	if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
		return -1;

	return 0;
576 577 578 579
}

int p_getcwd(char *buffer_out, size_t size)
{
580 581
	git_win32_path buf;
	wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
582

583
	if (!cwd)
584 585
		return -1;

586 587 588
	/* Convert the working directory back to UTF-8 */
	if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
		DWORD code = GetLastError();
589

590 591 592 593
		if (code == ERROR_INSUFFICIENT_BUFFER)
			errno = ERANGE;
		else
			errno = EINVAL;
594

595 596
		return -1;
	}
597

598
	return 0;
599 600
}

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
/*
 * Returns the address of the GetFinalPathNameByHandleW function.
 * This function is available on Windows Vista and higher.
 */
static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
{
	static PFGetFinalPathNameByHandleW pFunc = NULL;
	PFGetFinalPathNameByHandleW toReturn = pFunc;

	if (!toReturn) {
		HMODULE hModule = GetModuleHandleW(L"kernel32");

		if (hModule)
			toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");

		pFunc = toReturn;
	}

	assert(toReturn);

	return toReturn;
}

static int getfinalpath_w(
	git_win32_path dest,
	const wchar_t *path)
{
	PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
	HANDLE hFile;
	DWORD dwChars;

	if (!pgfp)
		return -1;

	/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
	* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
	* target of the link. */
	hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
		NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

641
	if (INVALID_HANDLE_VALUE == hFile)
642 643 644 645
		return -1;

	/* Call GetFinalPathNameByHandle */
	dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
646
	CloseHandle(hFile);
647

648
	if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
649 650 651
		return -1;

	/* The path may be delivered to us with a prefix; canonicalize */
652
	return (int)git_win32__canonicalize_path(dest, dwChars);
653 654 655 656 657 658 659 660 661 662 663 664
}

static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
{
	git_win32_path target_w;

	if (getfinalpath_w(target_w, path) < 0)
		return -1;

	return lstat_w(target_w, buf, false);
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
int p_fstat(int fd, struct stat *buf)
{
	BY_HANDLE_FILE_INFORMATION fhInfo;

	HANDLE fh = (HANDLE)_get_osfhandle(fd);

	if (fh == INVALID_HANDLE_VALUE ||
		!GetFileInformationByHandle(fh, &fhInfo)) {
		errno = EBADF;
		return -1;
	}

	git_win32__file_information_to_stat(buf, &fhInfo);
	return 0;
}

681 682
int p_stat(const char* path, struct stat* buf)
{
683 684
	git_win32_path path_w;
	int len;
685

686 687
	if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
		lstat_w(path_w, buf, false) < 0)
688 689 690 691 692 693 694 695
		return -1;

	/* The item is a symbolic link or mount point. No need to iterate
	 * to follow multiple links; use GetFinalPathNameFromHandle. */
	if (S_ISLNK(buf->st_mode))
		return follow_and_lstat_link(path_w, buf);

	return 0;
696 697 698 699
}

int p_chdir(const char* path)
{
700
	git_win32_path buf;
701

702
	if (git_win32_path_from_utf8(buf, path) < 0)
703 704
		return -1;

705
	return _wchdir(buf);
706 707
}

708
int p_chmod(const char* path, mode_t mode)
709
{
710
	git_win32_path buf;
711

712
	if (git_win32_path_from_utf8(buf, path) < 0)
713 714
		return -1;

715
	return _wchmod(buf, mode);
716 717 718 719
}

int p_rmdir(const char* path)
{
720
	git_win32_path buf;
721 722
	int error;

723
	if (git_win32_path_from_utf8(buf, path) < 0)
724
		return -1;
725 726 727

	error = _wrmdir(buf);

728
	if (error == -1) {
729 730 731 732 733 734 735 736
		switch (GetLastError()) {
			/* _wrmdir() is documented to return EACCES if "A program has an open
			 * handle to the directory."  This sounds like what everybody else calls
			 * EBUSY.  Let's convert appropriate error codes.
			 */
			case ERROR_SHARING_VIOLATION:
				errno = EBUSY;
				break;
737

738 739 740 741 742 743
			/* This error can be returned when trying to rmdir an extant file. */
			case ERROR_DIRECTORY:
				errno = ENOTDIR;
				break;
		}
	}
744

745
	return error;
Vicent Marti committed
746 747
}

748
char *p_realpath(const char *orig_path, char *buffer)
749
{
750
	git_win32_path orig_path_w, buffer_w;
751

752
	if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
753
		return NULL;
754

755 756 757 758 759 760 761 762
	/* Note that if the path provided is a relative path, then the current directory
	 * is used to resolve the path -- which is a concurrency issue because the current
	 * directory is a process-wide variable. */
	if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
		if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
			errno = ENAMETOOLONG;
		else
			errno = EINVAL;
763

764 765
		return NULL;
	}
766

767
	/* The path must exist. */
768
	if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
769
		errno = ENOENT;
770
		return NULL;
771
	}
772

773 774 775
	if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
		errno = ENOMEM;
		return NULL;
776 777
	}

778 779 780 781 782 783
	/* Convert the path to UTF-8. If the caller provided a buffer, then it
	 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
	 * then we may overflow. */
	if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
		return NULL;

784
	git_path_mkposix(buffer);
785

786
	return buffer;
787 788
}

789 790
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
791
#if defined(_MSC_VER)
792 793
	int len;

794 795 796 797 798 799 800 801 802 803
	if (count == 0)
		return _vscprintf(format, argptr);

	#if _MSC_VER >= 1500
	len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
	#else
	len = _vsnprintf(buffer, count, format, argptr);
	#endif

	if (len < 0)
804
		return _vscprintf(format, argptr);
805 806

	return len;
807 808 809 810
#else /* MinGW */
	return vsnprintf(buffer, count, format, argptr);
#endif
}
811 812 813 814 815 816 817 818 819 820 821 822

int p_snprintf(char *buffer, size_t count, const char *format, ...)
{
	va_list va;
	int r;

	va_start(va, format);
	r = p_vsnprintf(buffer, count, format, va);
	va_end(va);

	return r;
}
823

824
/* TODO: wut? */
825 826
int p_mkstemp(char *tmp_path)
{
827
#if defined(_MSC_VER) && _MSC_VER >= 1500
828
	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
829
		return -1;
830
#else
Vicent Marti committed
831
	if (_mktemp(tmp_path) == NULL)
832
		return -1;
Vicent Marti committed
833
#endif
834

835
	return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
836
}
837

838
int p_access(const char* path, mode_t mode)
839
{
840
	git_win32_path buf;
841

842
	if (git_win32_path_from_utf8(buf, path) < 0)
843 844
		return -1;

845
	return _waccess(buf, mode & WIN32_MODE_MASK);
846
}
847

848 849 850 851 852 853 854 855 856 857
GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
{
	if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
		return 0;

	if (last_error_retryable())
		return GIT_RETRY;

	set_errno();
	return -1;
858 859
}

860
int p_rename(const char *from, const char *to)
861
{
862
	git_win32_path wfrom, wto;
863

864 865
	if (git_win32_path_from_utf8(wfrom, from) < 0 ||
		git_win32_path_from_utf8(wto, to) < 0)
866
		return -1;
867

868
	do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
869
}
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885

int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
{
	if ((size_t)((int)length) != length)
		return -1; /* giterr_set will be done by caller */

	return recv(socket, buffer, (int)length, flags);
}

int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
{
	if ((size_t)((int)length) != length)
		return -1; /* giterr_set will be done by caller */

	return send(socket, buffer, (int)length, flags);
}
Ben Straub committed
886 887 888 889 890

/**
 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
 */
Linquize committed
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
struct tm *
p_localtime_r (const time_t *timer, struct tm *result)
{
	struct tm *local_result;
	local_result = localtime (timer);

	if (local_result == NULL || result == NULL)
		return NULL;

	memcpy (result, local_result, sizeof (struct tm));
	return result;
}
struct tm *
p_gmtime_r (const time_t *timer, struct tm *result)
{
	struct tm *local_result;
	local_result = gmtime (timer);

	if (local_result == NULL || result == NULL)
		return NULL;

	memcpy (result, local_result, sizeof (struct tm));
	return result;
Ben Straub committed
914 915
}

916
int p_inet_pton(int af, const char *src, void *dst)
917
{
918 919 920 921
	struct sockaddr_storage sin;
	void *addr;
	int sin_len = sizeof(struct sockaddr_storage), addr_len;
	int error = 0;
922

923 924 925 926 927 928 929 930 931
	if (af == AF_INET) {
		addr = &((struct sockaddr_in *)&sin)->sin_addr;
		addr_len = sizeof(struct in_addr);
	} else if (af == AF_INET6) {
		addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
		addr_len = sizeof(struct in6_addr);
	} else {
		errno = EAFNOSUPPORT;
		return -1;
932 933
	}

934 935 936
	if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
		memcpy(dst, addr, addr_len);
		return 1;
937 938
	}

939 940 941 942 943 944 945 946 947
	switch(WSAGetLastError()) {
	case WSAEINVAL:
		return 0;
	case WSAEFAULT:
		errno = ENOSPC;
		return -1;
	case WSA_NOT_ENOUGH_MEMORY:
		errno = ENOMEM;
		return -1;
948 949
	}

950 951
	errno = EINVAL;
	return -1;
952
}