posix_w32.c 20.5 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
#include "../posix.h"
Russell Belfer committed
8
#include "../fileops.h"
9
#include "path.h"
10
#include "path_w32.h"
11
#include "utf-conv.h"
12
#include "repository.h"
13
#include "reparse.h"
14 15
#include "global.h"
#include "buffer.h"
Vicent Marti committed
16
#include <errno.h>
17
#include <io.h>
18
#include <fcntl.h>
19
#include <ws2tcpip.h>
Vicent Marti committed
20

21
#ifndef FILE_NAME_NORMALIZED
22 23 24
# define FILE_NAME_NORMALIZED 0
#endif

25 26 27 28
#ifndef IO_REPARSE_TAG_SYMLINK
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
#endif

29 30 31 32 33 34
/* 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)

35 36 37
/* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);

38 39
unsigned long git_win32__createfile_sharemode =
 FILE_SHARE_READ | FILE_SHARE_WRITE;
40
int git_win32__retries = 10;
41

42 43 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
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;
	}
}

155 156 157 158 159 160 161 162
GIT_INLINE(bool) last_error_retryable(void)
{
	int os_error = GetLastError();

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

163
#define do_with_retries(fn, remediation) \
164 165
	do {                                                             \
		int __tries, __ret;                                          \
166
		for (__tries = 0; __tries < git_win32__retries; __tries++) { \
167
			if (__tries && (__ret = (remediation)) != 0)             \
168 169 170 171 172 173 174 175
				return __ret;                                        \
			if ((__ret = (fn)) != GIT_RETRY)                         \
				return __ret;                                        \
			Sleep(5);                                                \
		}                                                            \
		return -1;                                                   \
	} while (0)                                                      \

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
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;

	return 0;

on_error:
	set_errno();
	return -1;
}

196 197 198 199 200 201 202
/**
 * 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)
203
{
204 205 206 207 208
	if (size < 0) {
		errno = EINVAL;
		return -1;
	}

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

221 222 223 224 225 226
int p_mkdir(const char *path, mode_t mode)
{
	git_win32_path buf;

	GIT_UNUSED(mode);

227
	if (git_win32_path_from_utf8(buf, path) < 0)
228 229 230 231 232
		return -1;

	return _wmkdir(buf);
}

233 234 235 236 237 238 239 240
int p_link(const char *old, const char *new)
{
	GIT_UNUSED(old);
	GIT_UNUSED(new);
	errno = ENOSYS;
	return -1;
}

241 242 243 244 245 246 247 248 249 250 251 252
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
253 254
int p_unlink(const char *path)
{
255
	git_win32_path wpath;
256

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

260
	do_with_retries(unlink_once(wpath), ensure_writable(wpath));
Vicent Marti committed
261 262 263 264 265 266
}

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

267 268
	p_fsync__cnt++;

Vicent Marti committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	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;
}

288 289 290 291 292 293 294 295 296 297
#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)) {
298 299 300
		if (!buf)
			return 0;

301
		return git_win32__file_attribute_to_stat(buf, &fdata, path);
Vicent Marti committed
302 303
	}

304 305 306 307 308 309 310 311
	switch (GetLastError()) {
	case ERROR_ACCESS_DENIED:
		errno = EACCES;
		break;
	default:
		errno = ENOENT;
		break;
	}
312

313 314
	/* To match POSIX behavior, set ENOTDIR when any of the folders in the
	 * file path is a regular file, otherwise set ENOENT.
315
	 */
316
	if (errno == ENOENT && posix_enotdir) {
317 318
		size_t path_len = wcslen(path);

319 320
		/* scan up path until we find an existing item */
		while (1) {
321 322
			DWORD attrs;

323
			/* remove last directory component */
324
			for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
325

326
			if (path_len <= 0)
327 328
				break;

329 330
			path[path_len] = L'\0';
			attrs = GetFileAttributesW(path);
331

332
			if (attrs != INVALID_FILE_ATTRIBUTES) {
333
				if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
Eduardo Bart committed
334
					errno = ENOTDIR;
335
				break;
336 337 338 339
			}
		}
	}

340
	return -1;
Vicent Marti committed
341 342
}

343
static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
Vicent Marti committed
344
{
345 346 347
	git_win32_path path_w;
	int len;

348
	if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
349 350 351 352 353
		return -1;

	git_win32__path_trim_end(path_w, len);

	return lstat_w(path_w, buf, posixly_correct);
354
}
Vicent Marti committed
355

356
int p_lstat(const char *filename, struct stat *buf)
357
{
358
	return do_lstat(filename, buf, false);
Vicent Marti committed
359 360
}

361
int p_lstat_posixly(const char *filename, struct stat *buf)
362
{
363
	return do_lstat(filename, buf, true);
364
}
365

366
int p_readlink(const char *path, char *buf, size_t bufsiz)
Vicent Marti committed
367
{
368 369 370 371 372 373 374 375 376 377 378
	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. */

379
	if (git_win32_path_from_utf8(path_w, path) < 0 ||
380
		git_win32_path_readlink_w(target_w, path_w) < 0 ||
381 382
		(len = git_win32_path_to_utf8(target, target_w)) < 0)
		return -1;
383

384 385
	bufsiz = min((size_t)len, bufsiz);
	memcpy(buf, target, bufsiz);
386

387
	return (int)bufsiz;
Vicent Marti committed
388 389
}

Ben Straub committed
390 391
int p_symlink(const char *old, const char *new)
{
392 393 394 395
	/* 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
396 397
}

398 399 400 401 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
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;
}

453 454
GIT_INLINE(int) open_once(
	const wchar_t *path,
455
	struct open_opts *opts)
456
{
457 458
	int fd;

459 460
	HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
		&opts->security, opts->creation_disposition, opts->attributes, 0);
461

462 463 464 465 466 467 468 469
	if (handle == INVALID_HANDLE_VALUE) {
		if (last_error_retryable())
			return GIT_RETRY;

		set_errno();
		return -1;
	}

470 471 472 473
	if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
		CloseHandle(handle);

	return fd;
474 475
}

476
int p_open(const char *path, int flags, ...)
477
{
478
	git_win32_path wpath;
479
	mode_t mode = 0;
480
	struct open_opts opts = {0};
481

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

485
	if (flags & O_CREAT) {
486 487 488
		va_list arg_list;

		va_start(arg_list, flags);
liyuray committed
489
		mode = (mode_t)va_arg(arg_list, int);
490 491 492
		va_end(arg_list);
	}

493 494 495 496 497 498 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
	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;
		}
524 525
	}

526
	open_opts_from_posix(&opts, O_RDWR, 0);
527

528 529 530
	if ((fd = open_once(wpath, &opts)) < 0) {
		error = -1;
		goto done;
531 532
	}

533 534
	error = p_futimes(fd, times);
	close(fd);
535

536 537 538 539 540 541 542 543
done:
	if (attrs_orig != attrs_new) {
		DWORD os_error = GetLastError();
		SetFileAttributesW(wpath, attrs_orig);
		SetLastError(os_error);
	}

	return error;
544 545
}

546
int p_futimes(int fd, const struct p_timeval times[2])
547
{
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
	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;
570 571 572 573
}

int p_getcwd(char *buffer_out, size_t size)
{
574 575
	git_win32_path buf;
	wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
576

577
	if (!cwd)
578 579
		return -1;

580 581 582
	/* Convert the working directory back to UTF-8 */
	if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
		DWORD code = GetLastError();
583

584 585 586 587
		if (code == ERROR_INSUFFICIENT_BUFFER)
			errno = ERANGE;
		else
			errno = EINVAL;
588

589 590
		return -1;
	}
591

592
	return 0;
593 594
}

595 596 597 598 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
/*
 * 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);

635
	if (INVALID_HANDLE_VALUE == hFile)
636 637 638 639
		return -1;

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

642
	if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
643 644 645
		return -1;

	/* The path may be delivered to us with a prefix; canonicalize */
646
	return (int)git_win32__canonicalize_path(dest, dwChars);
647 648 649 650 651 652 653 654 655 656 657 658
}

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);
}

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
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;
}

675 676
int p_stat(const char* path, struct stat* buf)
{
677 678
	git_win32_path path_w;
	int len;
679

680 681
	if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
		lstat_w(path_w, buf, false) < 0)
682 683 684 685 686 687 688 689
		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;
690 691 692 693
}

int p_chdir(const char* path)
{
694
	git_win32_path buf;
695

696
	if (git_win32_path_from_utf8(buf, path) < 0)
697 698
		return -1;

699
	return _wchdir(buf);
700 701
}

702
int p_chmod(const char* path, mode_t mode)
703
{
704
	git_win32_path buf;
705

706
	if (git_win32_path_from_utf8(buf, path) < 0)
707 708
		return -1;

709
	return _wchmod(buf, mode);
710 711 712 713
}

int p_rmdir(const char* path)
{
714
	git_win32_path buf;
715 716
	int error;

717
	if (git_win32_path_from_utf8(buf, path) < 0)
718
		return -1;
719 720 721

	error = _wrmdir(buf);

722
	if (error == -1) {
723 724 725 726 727 728 729 730
		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;
731

732 733 734 735 736 737
			/* This error can be returned when trying to rmdir an extant file. */
			case ERROR_DIRECTORY:
				errno = ENOTDIR;
				break;
		}
	}
738

739
	return error;
Vicent Marti committed
740 741
}

742
char *p_realpath(const char *orig_path, char *buffer)
743
{
744
	git_win32_path orig_path_w, buffer_w;
745

746
	if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
747
		return NULL;
748

749 750 751 752 753 754 755 756
	/* 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;
757

758 759
		return NULL;
	}
760

761
	/* The path must exist. */
762
	if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
763
		errno = ENOENT;
764
		return NULL;
765
	}
766

767 768 769
	if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
		errno = ENOMEM;
		return NULL;
770 771
	}

772 773 774 775 776 777
	/* 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;

778
	git_path_mkposix(buffer);
779

780
	return buffer;
781 782
}

783 784
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
785
#if defined(_MSC_VER)
786 787
	int len;

788 789 790 791 792 793 794 795 796 797
	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)
798
		return _vscprintf(format, argptr);
799 800

	return len;
801 802 803 804
#else /* MinGW */
	return vsnprintf(buffer, count, format, argptr);
#endif
}
805 806 807 808 809 810 811 812 813 814 815 816

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;
}
817

818
/* TODO: wut? */
819 820
int p_mkstemp(char *tmp_path)
{
821
#if defined(_MSC_VER) && _MSC_VER >= 1500
822
	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
823
		return -1;
824
#else
Vicent Marti committed
825
	if (_mktemp(tmp_path) == NULL)
826
		return -1;
Vicent Marti committed
827
#endif
828

829
	return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
830
}
831

832
int p_access(const char* path, mode_t mode)
833
{
834
	git_win32_path buf;
835

836
	if (git_win32_path_from_utf8(buf, path) < 0)
837 838
		return -1;

839
	return _waccess(buf, mode & WIN32_MODE_MASK);
840
}
841

842 843 844 845 846 847 848 849 850 851
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;
852 853
}

854
int p_rename(const char *from, const char *to)
855
{
856
	git_win32_path wfrom, wto;
857

858 859
	if (git_win32_path_from_utf8(wfrom, from) < 0 ||
		git_win32_path_from_utf8(wto, to) < 0)
860
		return -1;
861

862
	do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
863
}
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879

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
880 881 882 883 884

/**
 * 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
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
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
908 909
}

910
int p_inet_pton(int af, const char *src, void *dst)
911
{
912 913 914 915
	struct sockaddr_storage sin;
	void *addr;
	int sin_len = sizeof(struct sockaddr_storage), addr_len;
	int error = 0;
916

917 918 919 920 921 922 923 924 925
	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;
926 927
	}

928 929 930
	if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
		memcpy(dst, addr, addr_len);
		return 1;
931 932
	}

933 934 935 936 937 938 939 940 941
	switch(WSAGetLastError()) {
	case WSAEINVAL:
		return 0;
	case WSAEFAULT:
		errno = ENOSPC;
		return -1;
	case WSA_NOT_ENOUGH_MEMORY:
		errno = ENOMEM;
		return -1;
942 943
	}

944 945
	errno = EINVAL;
	return -1;
946
}