Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ v0.25

### API additions

* You can now set the default share modes on Windows for opening files using
`GIT_OPT_SET_WINDOWS_SHAREMODE` option with `git_libgit2_opts()`.
It is the counterpart to `GIT_OPT_GET_WINDOWS_SHAREMODE`.

* You can now get the user-agent used by libgit2 using the
`GIT_OPT_GET_USER_AGENT` option with `git_libgit2_opts()`.
It is the counterpart to `GIT_OPT_SET_USER_AGENT`.
Expand Down
12 changes: 12 additions & 0 deletions include/git2/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ typedef enum {
GIT_OPT_GET_USER_AGENT,
GIT_OPT_ENABLE_OFS_DELTA,
GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION,
GIT_OPT_GET_WINDOWS_SHAREMODE,
GIT_OPT_SET_WINDOWS_SHAREMODE,
} git_libgit2_opt_t;

/**
Expand Down Expand Up @@ -284,6 +286,16 @@ typedef enum {
* > - `user_agent` is the value that will be delivered as the
* > User-Agent header on HTTP requests.
*
* * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, DWORD value)
*
* > Set the default share modes on Windows for opening files.
* > This can be used to add FILE_SHARE_DELETE.
* > The Windows default is: FILE_SHARE_READ | FILE_SHARE_WRITE.
*
* * opts(GIT_OPT_GET_WINDOWS_SHAREMODE, DWORD *value)
*
* > Get the default share modes on Windows for opening files.
*
* * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
*
* > Enable strict input validation when creating new objects
Expand Down
18 changes: 18 additions & 0 deletions src/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,24 @@ int git_libgit2_opts(int key, ...)
git_object__synchronous_writing = (va_arg(ap, int) != 0);
break;

case GIT_OPT_GET_WINDOWS_SHAREMODE:
#ifdef GIT_WIN32
*(va_arg(ap, DWORD *)) = git_posix_w32__windows_sharemode;
#else
giterr_set(GITERR_INVALID, "cannot set windows share mode on non Windows builds");
error = -1;
#endif
break;

case GIT_OPT_SET_WINDOWS_SHAREMODE:
#ifdef GIT_WIN32
git_posix_w32__windows_sharemode = (va_arg(ap, DWORD) & (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE));
#else
giterr_set(GITERR_INVALID, "cannot set windows share mode on non Windows builds");
error = -1;
#endif
break;

default:
giterr_set(GITERR_INVALID, "invalid option key");
error = -1;
Expand Down
2 changes: 2 additions & 0 deletions src/win32/posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "utf-conv.h"
#include "dir.h"

extern DWORD git_posix_w32__windows_sharemode;

typedef SOCKET GIT_SOCKET;

#define p_lseek(f,n,w) _lseeki64(f, n, w)
Expand Down
100 changes: 81 additions & 19 deletions src/win32/posix_w32.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <fcntl.h>
#include <ws2tcpip.h>

DWORD git_posix_w32__windows_sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;

#ifndef FILE_NAME_NORMALIZED
# define FILE_NAME_NORMALIZED 0
#endif
Expand All @@ -26,15 +28,6 @@
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
#endif

/* Options which we always provide to _wopen.
*
* _O_BINARY - Raw access; no translation of CR or LF characters
* _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
* The Windows default is 'not inheritable', but the CRT's default (following
* POSIX convention) is 'inheritable'. We have no desire for our handles to be
* inheritable on Windows, so specify the flag to get default behavior back. */
#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)

/* 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.
Expand Down Expand Up @@ -285,32 +278,101 @@ int p_symlink(const char *old, const char *new)
int p_open(const char *path, int flags, ...)
{
git_win32_path buf;
mode_t mode = 0;
HANDLE handle;
DWORD desired_access;
DWORD creation_disposition;
DWORD flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
int osfhandle_flags = 0;

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

if (flags & O_CREAT) {
va_list arg_list;
mode_t mode = 0;

va_start(arg_list, flags);
mode = (mode_t)va_arg(arg_list, int);
va_end(arg_list);

if (!(mode & _S_IWRITE))
flags_and_attributes = FILE_ATTRIBUTE_READONLY;
}

return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
}
/* we only support _O_BINARY and set this by default */
if (flags & (_O_TEXT | _O_WTEXT | _O_U16TEXT | _O_U8TEXT)) {
errno = EINVAL;
return -1;
}

int p_creat(const char *path, mode_t mode)
{
git_win32_path buf;
/* currently unsupported */
if (flags & (_O_SEQUENTIAL | _O_RANDOM | _O_TEMPORARY | _O_SHORT_LIVED)) {
errno = EINVAL;
return -1;
}

if (git_win32_path_from_utf8(buf, path) < 0)
switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR | _O_APPEND)) {
case _O_RDONLY:
desired_access = GENERIC_READ;
osfhandle_flags = _O_RDONLY;
break;
case _O_WRONLY | _O_APPEND:
osfhandle_flags = _O_APPEND;
case _O_WRONLY:
desired_access = GENERIC_WRITE;
break;
case _O_RDWR | _O_APPEND:
osfhandle_flags = _O_APPEND;
case _O_RDWR:
desired_access = GENERIC_READ | GENERIC_WRITE;
break;
default:
/* invalid or unsupported flag (combination) */
errno = EINVAL;
return -1;
}

return _wopen(buf,
_O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
mode & WIN32_MODE_MASK);
switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
case 0:
creation_disposition = OPEN_EXISTING;
break;

case _O_CREAT:
creation_disposition = OPEN_ALWAYS;
break;

case _O_CREAT | _O_TRUNC:
creation_disposition = CREATE_ALWAYS;
break;

case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
creation_disposition = CREATE_NEW;
break;

case _O_TRUNC:
creation_disposition = TRUNCATE_EXISTING;
break;

default:
/* _O_EXCL required O_CREAT to be set, IIRC this is no error in MSVC implementaton */
errno = EINVAL;
return -1;
}

handle = CreateFileW(buf, desired_access, git_posix_w32__windows_sharemode, NULL, creation_disposition, flags_and_attributes, 0);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}

return _open_osfhandle((intptr_t)handle, osfhandle_flags);
}

int p_creat(const char *path, mode_t mode)
{
return p_open(path, _O_WRONLY | _O_CREAT | _O_TRUNC, mode);
}

int p_getcwd(char *buffer_out, size_t size)
Expand Down
112 changes: 112 additions & 0 deletions src/win32/w32_dosmaperr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*
* struct doserrmap: auto-generated from winerror.h and errno.h using undoc'd _dosmaperr.
* Inspired by ReactOS reactos\sdk\lib\crt\stdlib\doserrmap.h
*/


#if !defined(_MSC_VER) || _MSC_VER >= 1900
#include <errno.h>
#include <winerror.h>

static const struct {
unsigned long winerr;
int en;
} doserrors[] =
{ { ERROR_FILE_NOT_FOUND, ENOENT },
{ ERROR_PATH_NOT_FOUND, ENOENT },
{ ERROR_TOO_MANY_OPEN_FILES, EMFILE },
{ ERROR_ACCESS_DENIED, EACCES },
{ ERROR_INVALID_HANDLE, EBADF },
{ ERROR_ARENA_TRASHED, ENOMEM },
{ ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
{ ERROR_INVALID_BLOCK, ENOMEM },
{ ERROR_BAD_ENVIRONMENT, E2BIG },
{ ERROR_BAD_FORMAT, ENOEXEC },
{ ERROR_INVALID_DRIVE, ENOENT },
{ ERROR_CURRENT_DIRECTORY, EACCES },
{ ERROR_NOT_SAME_DEVICE, EXDEV },
{ ERROR_NO_MORE_FILES, ENOENT },
{ ERROR_WRITE_PROTECT, EACCES },
{ ERROR_BAD_UNIT, EACCES },
{ ERROR_NOT_READY, EACCES },
{ ERROR_BAD_COMMAND, EACCES },
{ ERROR_CRC, EACCES },
{ ERROR_BAD_LENGTH, EACCES },
{ ERROR_SEEK, EACCES },
{ ERROR_NOT_DOS_DISK, EACCES },
{ ERROR_SECTOR_NOT_FOUND, EACCES },
{ ERROR_OUT_OF_PAPER, EACCES },
{ ERROR_WRITE_FAULT, EACCES },
{ ERROR_READ_FAULT, EACCES },
{ ERROR_GEN_FAILURE, EACCES },
{ ERROR_SHARING_VIOLATION, EACCES },
{ ERROR_LOCK_VIOLATION, EACCES },
{ ERROR_WRONG_DISK, EACCES },
{ ERROR_SHARING_BUFFER_EXCEEDED, EACCES },
{ ERROR_BAD_NETPATH, ENOENT },
{ ERROR_NETWORK_ACCESS_DENIED, EACCES },
{ ERROR_BAD_NET_NAME, ENOENT },
{ ERROR_FILE_EXISTS, EEXIST },
{ ERROR_CANNOT_MAKE, EACCES },
{ ERROR_FAIL_I24, EACCES },
{ ERROR_NO_PROC_SLOTS, EAGAIN },
{ ERROR_DRIVE_LOCKED, EACCES },
{ ERROR_BROKEN_PIPE, EPIPE },
{ ERROR_DISK_FULL, ENOSPC },
{ ERROR_INVALID_TARGET_HANDLE, EBADF },
{ ERROR_WAIT_NO_CHILDREN, ECHILD },
{ ERROR_CHILD_NOT_COMPLETE, ECHILD },
{ ERROR_DIRECT_ACCESS_HANDLE, EBADF },
{ ERROR_SEEK_ON_DEVICE, EACCES },
{ ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
{ ERROR_NOT_LOCKED, EACCES },
{ ERROR_BAD_PATHNAME, ENOENT },
{ ERROR_MAX_THRDS_REACHED, EAGAIN },
{ ERROR_LOCK_FAILED, EACCES },
{ ERROR_ALREADY_EXISTS, EEXIST },
{ ERROR_INVALID_STARTING_CODESEG, ENOEXEC },
{ ERROR_INVALID_STACKSEG, ENOEXEC },
{ ERROR_INVALID_MODULETYPE, ENOEXEC },
{ ERROR_INVALID_EXE_SIGNATURE, ENOEXEC },
{ ERROR_EXE_MARKED_INVALID, ENOEXEC },
{ ERROR_BAD_EXE_FORMAT, ENOEXEC },
{ ERROR_ITERATED_DATA_EXCEEDS_64k, ENOEXEC },
{ ERROR_INVALID_MINALLOCSIZE, ENOEXEC },
{ ERROR_DYNLINK_FROM_INVALID_RING, ENOEXEC },
{ ERROR_IOPL_NOT_ENABLED, ENOEXEC },
{ ERROR_INVALID_SEGDPL, ENOEXEC },
{ ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC },
{ ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC },
{ ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC },
{ ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC },
{ ERROR_FILENAME_EXCED_RANGE, ENOENT },
{ ERROR_NESTING_NOT_ALLOWED, EAGAIN },
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
};

void _dosmaperr(unsigned long e)
{
unsigned int i;
if (e == 0)
{
errno = 0;
return;
}

for (i = 0; i < sizeof(doserrors) / sizeof(doserrors[0]); ++i)
{
if (doserrors[i].winerr == e)
{
errno = doserrors[i].en;
return;
}
}

errno = EINVAL;
}
#endif