|
Packit |
ae9e2a |
/*
|
|
Packit |
ae9e2a |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit |
ae9e2a |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
#include "../posix.h"
|
|
Packit |
ae9e2a |
#include "../fileops.h"
|
|
Packit |
ae9e2a |
#include "path.h"
|
|
Packit |
ae9e2a |
#include "path_w32.h"
|
|
Packit |
ae9e2a |
#include "utf-conv.h"
|
|
Packit |
ae9e2a |
#include "repository.h"
|
|
Packit |
ae9e2a |
#include "reparse.h"
|
|
Packit |
ae9e2a |
#include "global.h"
|
|
Packit |
ae9e2a |
#include "buffer.h"
|
|
Packit |
ae9e2a |
#include <errno.h>
|
|
Packit |
ae9e2a |
#include <io.h>
|
|
Packit |
ae9e2a |
#include <fcntl.h>
|
|
Packit |
ae9e2a |
#include <ws2tcpip.h>
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#ifndef FILE_NAME_NORMALIZED
|
|
Packit |
ae9e2a |
# define FILE_NAME_NORMALIZED 0
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#ifndef IO_REPARSE_TAG_SYMLINK
|
|
Packit |
ae9e2a |
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Allowable mode bits on Win32. Using mode bits that are not supported on
|
|
Packit |
ae9e2a |
* Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
|
|
Packit |
ae9e2a |
* so we simply remove them.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
#define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* GetFinalPathNameByHandleW signature */
|
|
Packit |
ae9e2a |
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
unsigned long git_win32__createfile_sharemode =
|
|
Packit |
ae9e2a |
FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
Packit |
ae9e2a |
int git_win32__retries = 10;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(void) set_errno(void)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
switch (GetLastError()) {
|
|
Packit |
ae9e2a |
case ERROR_FILE_NOT_FOUND:
|
|
Packit |
ae9e2a |
case ERROR_PATH_NOT_FOUND:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_DRIVE:
|
|
Packit |
ae9e2a |
case ERROR_NO_MORE_FILES:
|
|
Packit |
ae9e2a |
case ERROR_BAD_NETPATH:
|
|
Packit |
ae9e2a |
case ERROR_BAD_NET_NAME:
|
|
Packit |
ae9e2a |
case ERROR_BAD_PATHNAME:
|
|
Packit |
ae9e2a |
case ERROR_FILENAME_EXCED_RANGE:
|
|
Packit |
ae9e2a |
errno = ENOENT;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_BAD_ENVIRONMENT:
|
|
Packit |
ae9e2a |
errno = E2BIG;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_BAD_FORMAT:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_STARTING_CODESEG:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_STACKSEG:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_MODULETYPE:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_EXE_SIGNATURE:
|
|
Packit |
ae9e2a |
case ERROR_EXE_MARKED_INVALID:
|
|
Packit |
ae9e2a |
case ERROR_BAD_EXE_FORMAT:
|
|
Packit |
ae9e2a |
case ERROR_ITERATED_DATA_EXCEEDS_64k:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_MINALLOCSIZE:
|
|
Packit |
ae9e2a |
case ERROR_DYNLINK_FROM_INVALID_RING:
|
|
Packit |
ae9e2a |
case ERROR_IOPL_NOT_ENABLED:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_SEGDPL:
|
|
Packit |
ae9e2a |
case ERROR_AUTODATASEG_EXCEEDS_64k:
|
|
Packit |
ae9e2a |
case ERROR_RING2SEG_MUST_BE_MOVABLE:
|
|
Packit |
ae9e2a |
case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
|
|
Packit |
ae9e2a |
case ERROR_INFLOOP_IN_RELOC_CHAIN:
|
|
Packit |
ae9e2a |
errno = ENOEXEC;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_INVALID_HANDLE:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_TARGET_HANDLE:
|
|
Packit |
ae9e2a |
case ERROR_DIRECT_ACCESS_HANDLE:
|
|
Packit |
ae9e2a |
errno = EBADF;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_WAIT_NO_CHILDREN:
|
|
Packit |
ae9e2a |
case ERROR_CHILD_NOT_COMPLETE:
|
|
Packit |
ae9e2a |
errno = ECHILD;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_NO_PROC_SLOTS:
|
|
Packit |
ae9e2a |
case ERROR_MAX_THRDS_REACHED:
|
|
Packit |
ae9e2a |
case ERROR_NESTING_NOT_ALLOWED:
|
|
Packit |
ae9e2a |
errno = EAGAIN;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_ARENA_TRASHED:
|
|
Packit |
ae9e2a |
case ERROR_NOT_ENOUGH_MEMORY:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_BLOCK:
|
|
Packit |
ae9e2a |
case ERROR_NOT_ENOUGH_QUOTA:
|
|
Packit |
ae9e2a |
errno = ENOMEM;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_ACCESS_DENIED:
|
|
Packit |
ae9e2a |
case ERROR_CURRENT_DIRECTORY:
|
|
Packit |
ae9e2a |
case ERROR_WRITE_PROTECT:
|
|
Packit |
ae9e2a |
case ERROR_BAD_UNIT:
|
|
Packit |
ae9e2a |
case ERROR_NOT_READY:
|
|
Packit |
ae9e2a |
case ERROR_BAD_COMMAND:
|
|
Packit |
ae9e2a |
case ERROR_CRC:
|
|
Packit |
ae9e2a |
case ERROR_BAD_LENGTH:
|
|
Packit |
ae9e2a |
case ERROR_SEEK:
|
|
Packit |
ae9e2a |
case ERROR_NOT_DOS_DISK:
|
|
Packit |
ae9e2a |
case ERROR_SECTOR_NOT_FOUND:
|
|
Packit |
ae9e2a |
case ERROR_OUT_OF_PAPER:
|
|
Packit |
ae9e2a |
case ERROR_WRITE_FAULT:
|
|
Packit |
ae9e2a |
case ERROR_READ_FAULT:
|
|
Packit |
ae9e2a |
case ERROR_GEN_FAILURE:
|
|
Packit |
ae9e2a |
case ERROR_SHARING_VIOLATION:
|
|
Packit |
ae9e2a |
case ERROR_LOCK_VIOLATION:
|
|
Packit |
ae9e2a |
case ERROR_WRONG_DISK:
|
|
Packit |
ae9e2a |
case ERROR_SHARING_BUFFER_EXCEEDED:
|
|
Packit |
ae9e2a |
case ERROR_NETWORK_ACCESS_DENIED:
|
|
Packit |
ae9e2a |
case ERROR_CANNOT_MAKE:
|
|
Packit |
ae9e2a |
case ERROR_FAIL_I24:
|
|
Packit |
ae9e2a |
case ERROR_DRIVE_LOCKED:
|
|
Packit |
ae9e2a |
case ERROR_SEEK_ON_DEVICE:
|
|
Packit |
ae9e2a |
case ERROR_NOT_LOCKED:
|
|
Packit |
ae9e2a |
case ERROR_LOCK_FAILED:
|
|
Packit |
ae9e2a |
errno = EACCES;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_FILE_EXISTS:
|
|
Packit |
ae9e2a |
case ERROR_ALREADY_EXISTS:
|
|
Packit |
ae9e2a |
errno = EEXIST;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_NOT_SAME_DEVICE:
|
|
Packit |
ae9e2a |
errno = EXDEV;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_INVALID_FUNCTION:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_ACCESS:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_DATA:
|
|
Packit |
ae9e2a |
case ERROR_INVALID_PARAMETER:
|
|
Packit |
ae9e2a |
case ERROR_NEGATIVE_SEEK:
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_TOO_MANY_OPEN_FILES:
|
|
Packit |
ae9e2a |
errno = EMFILE;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_DISK_FULL:
|
|
Packit |
ae9e2a |
errno = ENOSPC;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_BROKEN_PIPE:
|
|
Packit |
ae9e2a |
errno = EPIPE;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case ERROR_DIR_NOT_EMPTY:
|
|
Packit |
ae9e2a |
errno = ENOTEMPTY;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
default:
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(bool) last_error_retryable(void)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
int os_error = GetLastError();
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return (os_error == ERROR_SHARING_VIOLATION ||
|
|
Packit |
ae9e2a |
os_error == ERROR_ACCESS_DENIED);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#define do_with_retries(fn, remediation) \
|
|
Packit |
ae9e2a |
do { \
|
|
Packit |
ae9e2a |
int __tries, __ret; \
|
|
Packit |
ae9e2a |
for (__tries = 0; __tries < git_win32__retries; __tries++) { \
|
|
Packit |
ae9e2a |
if (__tries && (__ret = (remediation)) != 0) \
|
|
Packit |
ae9e2a |
return __ret; \
|
|
Packit |
ae9e2a |
if ((__ret = (fn)) != GIT_RETRY) \
|
|
Packit |
ae9e2a |
return __ret; \
|
|
Packit |
ae9e2a |
Sleep(5); \
|
|
Packit |
ae9e2a |
} \
|
|
Packit |
ae9e2a |
return -1; \
|
|
Packit |
ae9e2a |
} while (0) \
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static int ensure_writable(wchar_t *path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
DWORD attrs;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
|
|
Packit |
ae9e2a |
goto on_error;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
|
|
Packit |
ae9e2a |
goto on_error;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
on_error:
|
|
Packit |
ae9e2a |
set_errno();
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Truncate or extend file.
|
|
Packit |
ae9e2a |
*
|
|
Packit |
ae9e2a |
* We now take a "git_off_t" rather than "long" because
|
|
Packit |
ae9e2a |
* files may be longer than 2Gb.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
int p_ftruncate(int fd, git_off_t size)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
if (size < 0) {
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
|
|
Packit |
ae9e2a |
return ((_chsize_s(fd, size) == 0) ? 0 : -1);
|
|
Packit |
ae9e2a |
#else
|
|
Packit |
ae9e2a |
/* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
|
|
Packit |
ae9e2a |
if (size > INT32_MAX) {
|
|
Packit |
ae9e2a |
errno = EFBIG;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
return _chsize(fd, (long)size);
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_mkdir(const char *path, mode_t mode)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_UNUSED(mode);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(buf, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return _wmkdir(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_link(const char *old, const char *new)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
GIT_UNUSED(old);
|
|
Packit |
ae9e2a |
GIT_UNUSED(new);
|
|
Packit |
ae9e2a |
errno = ENOSYS;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(int) unlink_once(const wchar_t *path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
if (DeleteFileW(path))
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (last_error_retryable())
|
|
Packit |
ae9e2a |
return GIT_RETRY;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
set_errno();
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_unlink(const char *path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path wpath;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(wpath, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
do_with_retries(unlink_once(wpath), ensure_writable(wpath));
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_fsync(int fd)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
p_fsync__cnt++;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (fh == INVALID_HANDLE_VALUE) {
|
|
Packit |
ae9e2a |
errno = EBADF;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!FlushFileBuffers(fh)) {
|
|
Packit |
ae9e2a |
DWORD code = GetLastError();
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (code == ERROR_INVALID_HANDLE)
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
else
|
|
Packit |
ae9e2a |
errno = EIO;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static int lstat_w(
|
|
Packit |
ae9e2a |
wchar_t *path,
|
|
Packit |
ae9e2a |
struct stat *buf,
|
|
Packit |
ae9e2a |
bool posix_enotdir)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
|
|
Packit |
ae9e2a |
if (!buf)
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return git_win32__file_attribute_to_stat(buf, &fdata, path);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
switch (GetLastError()) {
|
|
Packit |
ae9e2a |
case ERROR_ACCESS_DENIED:
|
|
Packit |
ae9e2a |
errno = EACCES;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
default:
|
|
Packit |
ae9e2a |
errno = ENOENT;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* To match POSIX behavior, set ENOTDIR when any of the folders in the
|
|
Packit |
ae9e2a |
* file path is a regular file, otherwise set ENOENT.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
if (errno == ENOENT && posix_enotdir) {
|
|
Packit |
ae9e2a |
size_t path_len = wcslen(path);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* scan up path until we find an existing item */
|
|
Packit |
ae9e2a |
while (1) {
|
|
Packit |
ae9e2a |
DWORD attrs;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* remove last directory component */
|
|
Packit |
ae9e2a |
for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (path_len <= 0)
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
path[path_len] = L'\0';
|
|
Packit |
ae9e2a |
attrs = GetFileAttributesW(path);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (attrs != INVALID_FILE_ATTRIBUTES) {
|
|
Packit |
ae9e2a |
if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
|
|
Packit |
ae9e2a |
errno = ENOTDIR;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path path_w;
|
|
Packit |
ae9e2a |
int len;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
git_win32__path_trim_end(path_w, len);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return lstat_w(path_w, buf, posixly_correct);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_lstat(const char *filename, struct stat *buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
return do_lstat(filename, buf, false);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_lstat_posixly(const char *filename, struct stat *buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
return do_lstat(filename, buf, true);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_readlink(const char *path, char *buf, size_t bufsiz)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path path_w, target_w;
|
|
Packit |
ae9e2a |
git_win32_utf8_path target;
|
|
Packit |
ae9e2a |
int len;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* readlink(2) does not NULL-terminate the string written
|
|
Packit |
ae9e2a |
* to the target buffer. Furthermore, the target buffer need
|
|
Packit |
ae9e2a |
* not be large enough to hold the entire result. A truncated
|
|
Packit |
ae9e2a |
* result should be written in this case. Since this truncation
|
|
Packit |
ae9e2a |
* could occur in the middle of the encoding of a code point,
|
|
Packit |
ae9e2a |
* we need to buffer the result on the stack. */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(path_w, path) < 0 ||
|
|
Packit |
ae9e2a |
git_win32_path_readlink_w(target_w, path_w) < 0 ||
|
|
Packit |
ae9e2a |
(len = git_win32_path_to_utf8(target, target_w)) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
bufsiz = min((size_t)len, bufsiz);
|
|
Packit |
ae9e2a |
memcpy(buf, target, bufsiz);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return (int)bufsiz;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_symlink(const char *old, const char *new)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
/* Real symlinks on NTFS require admin privileges. Until this changes,
|
|
Packit |
ae9e2a |
* libgit2 just creates a text file with the link target in the contents.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
return git_futils_fake_symlink(old, new);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
struct open_opts {
|
|
Packit |
ae9e2a |
DWORD access;
|
|
Packit |
ae9e2a |
DWORD sharing;
|
|
Packit |
ae9e2a |
SECURITY_ATTRIBUTES security;
|
|
Packit |
ae9e2a |
DWORD creation_disposition;
|
|
Packit |
ae9e2a |
DWORD attributes;
|
|
Packit |
ae9e2a |
int osf_flags;
|
|
Packit |
ae9e2a |
};
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
memset(opts, 0, sizeof(struct open_opts));
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
switch (flags & (O_WRONLY | O_RDWR)) {
|
|
Packit |
ae9e2a |
case O_WRONLY:
|
|
Packit |
ae9e2a |
opts->access = GENERIC_WRITE;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case O_RDWR:
|
|
Packit |
ae9e2a |
opts->access = GENERIC_READ | GENERIC_WRITE;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
default:
|
|
Packit |
ae9e2a |
opts->access = GENERIC_READ;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
opts->sharing = (DWORD)git_win32__createfile_sharemode;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
|
|
Packit |
ae9e2a |
case O_CREAT | O_EXCL:
|
|
Packit |
ae9e2a |
case O_CREAT | O_TRUNC | O_EXCL:
|
|
Packit |
ae9e2a |
opts->creation_disposition = CREATE_NEW;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case O_CREAT | O_TRUNC:
|
|
Packit |
ae9e2a |
opts->creation_disposition = CREATE_ALWAYS;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case O_TRUNC:
|
|
Packit |
ae9e2a |
opts->creation_disposition = TRUNCATE_EXISTING;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
case O_CREAT:
|
|
Packit |
ae9e2a |
opts->creation_disposition = OPEN_ALWAYS;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
default:
|
|
Packit |
ae9e2a |
opts->creation_disposition = OPEN_EXISTING;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
|
|
Packit |
ae9e2a |
FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
|
|
Packit |
ae9e2a |
opts->osf_flags = flags & (O_RDONLY | O_APPEND);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
Packit |
ae9e2a |
opts->security.lpSecurityDescriptor = NULL;
|
|
Packit |
ae9e2a |
opts->security.bInheritHandle = 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(int) open_once(
|
|
Packit |
ae9e2a |
const wchar_t *path,
|
|
Packit |
ae9e2a |
struct open_opts *opts)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
int fd;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
|
|
Packit |
ae9e2a |
&opts->security, opts->creation_disposition, opts->attributes, 0);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (handle == INVALID_HANDLE_VALUE) {
|
|
Packit |
ae9e2a |
if (last_error_retryable())
|
|
Packit |
ae9e2a |
return GIT_RETRY;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
set_errno();
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
|
|
Packit |
ae9e2a |
CloseHandle(handle);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return fd;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_open(const char *path, int flags, ...)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path wpath;
|
|
Packit |
ae9e2a |
mode_t mode = 0;
|
|
Packit |
ae9e2a |
struct open_opts opts = {0};
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(wpath, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (flags & O_CREAT) {
|
|
Packit |
ae9e2a |
va_list arg_list;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
va_start(arg_list, flags);
|
|
Packit |
ae9e2a |
mode = (mode_t)va_arg(arg_list, int);
|
|
Packit |
ae9e2a |
va_end(arg_list);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
open_opts_from_posix(&opts, flags, mode);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
do_with_retries(
|
|
Packit |
ae9e2a |
open_once(wpath, &opts),
|
|
Packit |
ae9e2a |
0);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_creat(const char *path, mode_t mode)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_utimes(const char *path, const struct p_timeval times[2])
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path wpath;
|
|
Packit |
ae9e2a |
int fd, error;
|
|
Packit |
ae9e2a |
DWORD attrs_orig, attrs_new = 0;
|
|
Packit |
ae9e2a |
struct open_opts opts = { 0 };
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(wpath, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
attrs_orig = GetFileAttributesW(wpath);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
|
|
Packit |
ae9e2a |
attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!SetFileAttributesW(wpath, attrs_new)) {
|
|
Packit |
ae9e2a |
giterr_set(GITERR_OS, "failed to set attributes");
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
open_opts_from_posix(&opts, O_RDWR, 0);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((fd = open_once(wpath, &opts)) < 0) {
|
|
Packit |
ae9e2a |
error = -1;
|
|
Packit |
ae9e2a |
goto done;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
error = p_futimes(fd, times);
|
|
Packit |
ae9e2a |
close(fd);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
done:
|
|
Packit |
ae9e2a |
if (attrs_orig != attrs_new) {
|
|
Packit |
ae9e2a |
DWORD os_error = GetLastError();
|
|
Packit |
ae9e2a |
SetFileAttributesW(wpath, attrs_orig);
|
|
Packit |
ae9e2a |
SetLastError(os_error);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return error;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_futimes(int fd, const struct p_timeval times[2])
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
HANDLE handle;
|
|
Packit |
ae9e2a |
FILETIME atime = { 0 }, mtime = { 0 };
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (times == NULL) {
|
|
Packit |
ae9e2a |
SYSTEMTIME st;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GetSystemTime(&st);
|
|
Packit |
ae9e2a |
SystemTimeToFileTime(&st, &atime);
|
|
Packit |
ae9e2a |
SystemTimeToFileTime(&st, &mtime);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
else {
|
|
Packit |
ae9e2a |
git_win32__timeval_to_filetime(&atime, times[0]);
|
|
Packit |
ae9e2a |
git_win32__timeval_to_filetime(&mtime, times[1]);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_getcwd(char *buffer_out, size_t size)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!cwd)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Convert the working directory back to UTF-8 */
|
|
Packit |
ae9e2a |
if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
|
|
Packit |
ae9e2a |
DWORD code = GetLastError();
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (code == ERROR_INSUFFICIENT_BUFFER)
|
|
Packit |
ae9e2a |
errno = ERANGE;
|
|
Packit |
ae9e2a |
else
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/*
|
|
Packit |
ae9e2a |
* Returns the address of the GetFinalPathNameByHandleW function.
|
|
Packit |
ae9e2a |
* This function is available on Windows Vista and higher.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
static PFGetFinalPathNameByHandleW pFunc = NULL;
|
|
Packit |
ae9e2a |
PFGetFinalPathNameByHandleW toReturn = pFunc;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!toReturn) {
|
|
Packit |
ae9e2a |
HMODULE hModule = GetModuleHandleW(L"kernel32");
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (hModule)
|
|
Packit |
ae9e2a |
toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
pFunc = toReturn;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
assert(toReturn);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return toReturn;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static int getfinalpath_w(
|
|
Packit |
ae9e2a |
git_win32_path dest,
|
|
Packit |
ae9e2a |
const wchar_t *path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
|
|
Packit |
ae9e2a |
HANDLE hFile;
|
|
Packit |
ae9e2a |
DWORD dwChars;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!pgfp)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
|
|
Packit |
ae9e2a |
* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
|
|
Packit |
ae9e2a |
* target of the link. */
|
|
Packit |
ae9e2a |
hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
Packit |
ae9e2a |
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (INVALID_HANDLE_VALUE == hFile)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Call GetFinalPathNameByHandle */
|
|
Packit |
ae9e2a |
dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
|
|
Packit |
ae9e2a |
CloseHandle(hFile);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* The path may be delivered to us with a prefix; canonicalize */
|
|
Packit |
ae9e2a |
return (int)git_win32__canonicalize_path(dest, dwChars);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path target_w;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (getfinalpath_w(target_w, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return lstat_w(target_w, buf, false);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_fstat(int fd, struct stat *buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
BY_HANDLE_FILE_INFORMATION fhInfo;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (fh == INVALID_HANDLE_VALUE ||
|
|
Packit |
ae9e2a |
!GetFileInformationByHandle(fh, &fhInfo)) {
|
|
Packit |
ae9e2a |
errno = EBADF;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
git_win32__file_information_to_stat(buf, &fhInfo);
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_stat(const char* path, struct stat* buf)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path path_w;
|
|
Packit |
ae9e2a |
int len;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
|
|
Packit |
ae9e2a |
lstat_w(path_w, buf, false) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* The item is a symbolic link or mount point. No need to iterate
|
|
Packit |
ae9e2a |
* to follow multiple links; use GetFinalPathNameFromHandle. */
|
|
Packit |
ae9e2a |
if (S_ISLNK(buf->st_mode))
|
|
Packit |
ae9e2a |
return follow_and_lstat_link(path_w, buf);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_chdir(const char* path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(buf, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return _wchdir(buf);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_chmod(const char* path, mode_t mode)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(buf, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return _wchmod(buf, mode);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_rmdir(const char* path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
int error;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(buf, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
error = _wrmdir(buf);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (error == -1) {
|
|
Packit |
ae9e2a |
switch (GetLastError()) {
|
|
Packit |
ae9e2a |
/* _wrmdir() is documented to return EACCES if "A program has an open
|
|
Packit |
ae9e2a |
* handle to the directory." This sounds like what everybody else calls
|
|
Packit |
ae9e2a |
* EBUSY. Let's convert appropriate error codes.
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
case ERROR_SHARING_VIOLATION:
|
|
Packit |
ae9e2a |
errno = EBUSY;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* This error can be returned when trying to rmdir an extant file. */
|
|
Packit |
ae9e2a |
case ERROR_DIRECTORY:
|
|
Packit |
ae9e2a |
errno = ENOTDIR;
|
|
Packit |
ae9e2a |
break;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return error;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
char *p_realpath(const char *orig_path, char *buffer)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path orig_path_w, buffer_w;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Note that if the path provided is a relative path, then the current directory
|
|
Packit |
ae9e2a |
* is used to resolve the path -- which is a concurrency issue because the current
|
|
Packit |
ae9e2a |
* directory is a process-wide variable. */
|
|
Packit |
ae9e2a |
if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
|
|
Packit |
ae9e2a |
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
Packit |
ae9e2a |
errno = ENAMETOOLONG;
|
|
Packit |
ae9e2a |
else
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* The path must exist. */
|
|
Packit |
ae9e2a |
if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
|
|
Packit |
ae9e2a |
errno = ENOENT;
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
|
|
Packit |
ae9e2a |
errno = ENOMEM;
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* Convert the path to UTF-8. If the caller provided a buffer, then it
|
|
Packit |
ae9e2a |
* is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
|
|
Packit |
ae9e2a |
* then we may overflow. */
|
|
Packit |
ae9e2a |
if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
git_path_mkposix(buffer);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return buffer;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
#if defined(_MSC_VER)
|
|
Packit |
ae9e2a |
int len;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (count == 0)
|
|
Packit |
ae9e2a |
return _vscprintf(format, argptr);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
#if _MSC_VER >= 1500
|
|
Packit |
ae9e2a |
len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
|
|
Packit |
ae9e2a |
#else
|
|
Packit |
ae9e2a |
len = _vsnprintf(buffer, count, format, argptr);
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (len < 0)
|
|
Packit |
ae9e2a |
return _vscprintf(format, argptr);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return len;
|
|
Packit |
ae9e2a |
#else /* MinGW */
|
|
Packit |
ae9e2a |
return vsnprintf(buffer, count, format, argptr);
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_snprintf(char *buffer, size_t count, const char *format, ...)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
va_list va;
|
|
Packit |
ae9e2a |
int r;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
va_start(va, format);
|
|
Packit |
ae9e2a |
r = p_vsnprintf(buffer, count, format, va);
|
|
Packit |
ae9e2a |
va_end(va);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return r;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/* TODO: wut? */
|
|
Packit |
ae9e2a |
int p_mkstemp(char *tmp_path)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
|
Packit |
ae9e2a |
if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
#else
|
|
Packit |
ae9e2a |
if (_mktemp(tmp_path) == NULL)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
#endif
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_access(const char* path, mode_t mode)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path buf;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(buf, path) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return _waccess(buf, mode & WIN32_MODE_MASK);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (last_error_retryable())
|
|
Packit |
ae9e2a |
return GIT_RETRY;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
set_errno();
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_rename(const char *from, const char *to)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
git_win32_path wfrom, wto;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (git_win32_path_from_utf8(wfrom, from) < 0 ||
|
|
Packit |
ae9e2a |
git_win32_path_from_utf8(wto, to) < 0)
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
if ((size_t)((int)length) != length)
|
|
Packit |
ae9e2a |
return -1; /* giterr_set will be done by caller */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return recv(socket, buffer, (int)length, flags);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
if ((size_t)((int)length) != length)
|
|
Packit |
ae9e2a |
return -1; /* giterr_set will be done by caller */
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
return send(socket, buffer, (int)length, flags);
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
/**
|
|
Packit |
ae9e2a |
* Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
|
|
Packit |
ae9e2a |
* On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
|
|
Packit |
ae9e2a |
*/
|
|
Packit |
ae9e2a |
struct tm *
|
|
Packit |
ae9e2a |
p_localtime_r (const time_t *timer, struct tm *result)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
struct tm *local_result;
|
|
Packit |
ae9e2a |
local_result = localtime (timer);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (local_result == NULL || result == NULL)
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
memcpy (result, local_result, sizeof (struct tm));
|
|
Packit |
ae9e2a |
return result;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
struct tm *
|
|
Packit |
ae9e2a |
p_gmtime_r (const time_t *timer, struct tm *result)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
struct tm *local_result;
|
|
Packit |
ae9e2a |
local_result = gmtime (timer);
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (local_result == NULL || result == NULL)
|
|
Packit |
ae9e2a |
return NULL;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
memcpy (result, local_result, sizeof (struct tm));
|
|
Packit |
ae9e2a |
return result;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
int p_inet_pton(int af, const char *src, void *dst)
|
|
Packit |
ae9e2a |
{
|
|
Packit |
ae9e2a |
struct sockaddr_storage sin;
|
|
Packit |
ae9e2a |
void *addr;
|
|
Packit |
ae9e2a |
int sin_len = sizeof(struct sockaddr_storage), addr_len;
|
|
Packit |
ae9e2a |
int error = 0;
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if (af == AF_INET) {
|
|
Packit |
ae9e2a |
addr = &((struct sockaddr_in *)&sin)->sin_addr;
|
|
Packit |
ae9e2a |
addr_len = sizeof(struct in_addr);
|
|
Packit |
ae9e2a |
} else if (af == AF_INET6) {
|
|
Packit |
ae9e2a |
addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
|
|
Packit |
ae9e2a |
addr_len = sizeof(struct in6_addr);
|
|
Packit |
ae9e2a |
} else {
|
|
Packit |
ae9e2a |
errno = EAFNOSUPPORT;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
|
|
Packit |
ae9e2a |
memcpy(dst, addr, addr_len);
|
|
Packit |
ae9e2a |
return 1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
switch(WSAGetLastError()) {
|
|
Packit |
ae9e2a |
case WSAEINVAL:
|
|
Packit |
ae9e2a |
return 0;
|
|
Packit |
ae9e2a |
case WSAEFAULT:
|
|
Packit |
ae9e2a |
errno = ENOSPC;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
case WSA_NOT_ENOUGH_MEMORY:
|
|
Packit |
ae9e2a |
errno = ENOMEM;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|
|
Packit |
ae9e2a |
|
|
Packit |
ae9e2a |
errno = EINVAL;
|
|
Packit |
ae9e2a |
return -1;
|
|
Packit |
ae9e2a |
}
|