aboutsummaryrefslogtreecommitdiffstats
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rw-r--r--compat/compiler.h4
-rw-r--r--compat/fsmonitor/fsm-listen-darwin.c6
-rw-r--r--compat/fsmonitor/fsm-listen-win32.c10
-rw-r--r--compat/mingw.c182
-rw-r--r--compat/regex/regexec.c2
-rw-r--r--compat/simple-ipc/ipc-shared.c5
-rw-r--r--compat/simple-ipc/ipc-unix-socket.c30
-rw-r--r--compat/simple-ipc/ipc-win32.c48
-rw-r--r--compat/vcbuild/include/unistd.h4
9 files changed, 258 insertions, 33 deletions
diff --git a/compat/compiler.h b/compat/compiler.h
index e9ad9db84f..e12e426404 100644
--- a/compat/compiler.h
+++ b/compat/compiler.h
@@ -9,7 +9,7 @@
static inline void get_compiler_info(struct strbuf *info)
{
- int len = info->len;
+ size_t len = info->len;
#ifdef __clang__
strbuf_addf(info, "clang: %s\n", __clang_version__);
#elif defined(__GNUC__)
@@ -27,7 +27,7 @@ static inline void get_compiler_info(struct strbuf *info)
static inline void get_libc_info(struct strbuf *info)
{
- int len = info->len;
+ size_t len = info->len;
#ifdef __GLIBC__
strbuf_addf(info, "glibc: %s\n", gnu_get_libc_version());
diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index 2fc67442eb..dfa551459d 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -516,6 +516,12 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
}
data->stream_started = 1;
+ /*
+ * Our fs event listener is now running, so it's safe to start
+ * serving client requests.
+ */
+ ipc_server_start_async(state->ipc_server_data);
+
pthread_mutex_lock(&data->dq_lock);
pthread_cond_wait(&data->dq_finished, &data->dq_lock);
pthread_mutex_unlock(&data->dq_lock);
diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c
index 5a21dade7b..9a6efc9bea 100644
--- a/compat/fsmonitor/fsm-listen-win32.c
+++ b/compat/fsmonitor/fsm-listen-win32.c
@@ -431,9 +431,9 @@ static int recv_rdcw_watch(struct one_watch *watch)
* but I observed ERROR_ACCESS_DENIED (0x05) errors during
* testing.
*
- * Note that we only get notificaiton events for events
+ * Note that we only get notification events for events
* *within* the directory, not *on* the directory itself.
- * (These might be properies of the parent directory, for
+ * (These might be properties of the parent directory, for
* example).
*
* NEEDSWORK: We might try to check for the deleted directory
@@ -741,6 +741,12 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
start_rdcw_watch(data->watch_gitdir) == -1)
goto force_error_stop;
+ /*
+ * Now that we've established the rdcw watches, we can start
+ * serving clients.
+ */
+ ipc_server_start_async(state->ipc_server_data);
+
for (;;) {
dwWait = WaitForMultipleObjects(data->nr_listener_handles,
data->hListener,
diff --git a/compat/mingw.c b/compat/mingw.c
index 0e851ecae2..63f36c893b 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -502,7 +502,7 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
* to append to the file.
*/
handle = CreateFileW(wfilename, FILE_APPEND_DATA,
- FILE_SHARE_WRITE | FILE_SHARE_READ,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
@@ -533,6 +533,70 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
}
/*
+ * Ideally, we'd use `_wopen()` to implement this functionality so that we
+ * don't have to reimplement it, but unfortunately we do not have tight control
+ * over the share mode there. And while `_wsopen()` and friends exist that give
+ * us _some_ control over the share mode, this family of functions doesn't give
+ * us the ability to enable FILE_SHARE_DELETE, either. But this is a strict
+ * requirement for us though so that we can unlink or rename over files that
+ * are held open by another process.
+ *
+ * We are thus forced to implement our own emulation of `open()`. To make our
+ * life simpler we only implement basic support for this, namely opening
+ * existing files for reading and/or writing. This means that newly created
+ * files won't have their sharing mode set up correctly, but for now I couldn't
+ * find any case where this matters. We may have to revisit that in the future
+ * though based on our needs.
+ */
+static int mingw_open_existing(const wchar_t *filename, int oflags, ...)
+{
+ SECURITY_ATTRIBUTES security_attributes = {
+ .nLength = sizeof(security_attributes),
+ .bInheritHandle = !(oflags & O_NOINHERIT),
+ };
+ HANDLE handle;
+ DWORD access;
+ int fd;
+
+ /* We only support basic flags. */
+ if (oflags & ~(O_ACCMODE | O_NOINHERIT)) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ switch (oflags & O_ACCMODE) {
+ case O_RDWR:
+ access = GENERIC_READ | GENERIC_WRITE;
+ break;
+ case O_WRONLY:
+ access = GENERIC_WRITE;
+ break;
+ default:
+ access = GENERIC_READ;
+ break;
+ }
+
+ handle = CreateFileW(filename, access,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ &security_attributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ DWORD err = GetLastError();
+
+ /* See `mingw_open_append()` for why we have this conversion. */
+ if (err == ERROR_INVALID_PARAMETER)
+ err = ERROR_PATH_NOT_FOUND;
+
+ errno = err_win_to_posix(err);
+ return -1;
+ }
+
+ fd = _open_osfhandle((intptr_t)handle, oflags | O_BINARY);
+ if (fd < 0)
+ CloseHandle(handle);
+ return fd;
+}
+
+/*
* Does the pathname map to the local named pipe filesystem?
* That is, does it have a "//./pipe/" prefix?
*/
@@ -567,6 +631,8 @@ int mingw_open (const char *filename, int oflags, ...)
if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
open_fn = mingw_open_append;
+ else if (!(oflags & ~(O_ACCMODE | O_NOINHERIT)))
+ open_fn = mingw_open_existing;
else
open_fn = _wopen;
@@ -782,7 +848,7 @@ static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
*/
static int has_valid_directory_prefix(wchar_t *wfilename)
{
- int n = wcslen(wfilename);
+ size_t n = wcslen(wfilename);
while (n > 0) {
wchar_t c = wfilename[--n];
@@ -891,7 +957,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
*/
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
{
- int namelen;
+ size_t namelen;
char alt_name[PATH_MAX];
if (!do_lstat(follow, file_name, buf))
@@ -1006,7 +1072,7 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
osfilehandle = CreateFileW(wfilename,
FILE_WRITE_ATTRIBUTES,
- 0 /*FileShare.None*/,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
(attrs != INVALID_FILE_ATTRIBUTES &&
@@ -1274,7 +1340,8 @@ static const char *parse_interpreter(const char *cmd)
{
static char buf[100];
char *p, *opt;
- int n, fd;
+ ssize_t n; /* read() can return negative values */
+ int fd;
/* don't even try a .exe */
n = strlen(cmd);
@@ -1339,7 +1406,7 @@ static char *path_lookup(const char *cmd, int exe_only)
{
const char *path;
char *prog = NULL;
- int len = strlen(cmd);
+ size_t len = strlen(cmd);
int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
if (strpbrk(cmd, "/\\"))
@@ -1956,7 +2023,7 @@ char *mingw_getenv(const char *name)
#define GETENV_MAX_RETAIN 64
static char *values[GETENV_MAX_RETAIN];
static int value_counter;
- int len_key, len_value;
+ size_t len_key, len_value;
wchar_t *w_key;
char *value;
wchar_t w_value[32768];
@@ -1968,7 +2035,8 @@ char *mingw_getenv(const char *name)
/* We cannot use xcalloc() here because that uses getenv() itself */
w_key = calloc(len_key, sizeof(wchar_t));
if (!w_key)
- die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
+ die("Out of memory, (tried to allocate %"PRIuMAX" wchar_t's)",
+ (uintmax_t)len_key);
xutftowcs(w_key, name, len_key);
/* GetEnvironmentVariableW() only sets the last error upon failure */
SetLastError(ERROR_SUCCESS);
@@ -1983,7 +2051,8 @@ char *mingw_getenv(const char *name)
/* We cannot use xcalloc() here because that uses getenv() itself */
value = calloc(len_value, sizeof(char));
if (!value)
- die("Out of memory, (tried to allocate %u bytes)", len_value);
+ die("Out of memory, (tried to allocate %"PRIuMAX" bytes)",
+ (uintmax_t)len_value);
xwcstoutf(value, w_value, len_value);
/*
@@ -2001,7 +2070,7 @@ char *mingw_getenv(const char *name)
int mingw_putenv(const char *namevalue)
{
- int size;
+ size_t size;
wchar_t *wide, *equal;
BOOL result;
@@ -2011,7 +2080,8 @@ int mingw_putenv(const char *namevalue)
size = strlen(namevalue) * 2 + 1;
wide = calloc(size, sizeof(wchar_t));
if (!wide)
- die("Out of memory, (tried to allocate %u wchar_t's)", size);
+ die("Out of memory, (tried to allocate %" PRIuMAX " wchar_t's)",
+ (uintmax_t)size);
xutftowcs(wide, namevalue, size);
equal = wcschr(wide, L'=');
if (!equal)
@@ -2151,10 +2221,16 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
#undef rename
int mingw_rename(const char *pold, const char *pnew)
{
+ static int supports_file_rename_info_ex = 1;
DWORD attrs, gle;
int tries = 0;
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
- if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
+ int wpnew_len;
+
+ if (xutftowcs_path(wpold, pold) < 0)
+ return -1;
+ wpnew_len = xutftowcs_path(wpnew, pnew);
+ if (wpnew_len < 0)
return -1;
/*
@@ -2165,11 +2241,84 @@ int mingw_rename(const char *pold, const char *pnew)
return 0;
if (errno != EEXIST)
return -1;
+
repeat:
- if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
- return 0;
+ if (supports_file_rename_info_ex) {
+ /*
+ * Our minimum required Windows version is still set to Windows
+ * Vista. We thus have to declare required infrastructure for
+ * FileRenameInfoEx ourselves until we bump _WIN32_WINNT to
+ * 0x0A00. Furthermore, we have to handle cases where the
+ * FileRenameInfoEx call isn't supported yet.
+ */
+#define FILE_RENAME_FLAG_REPLACE_IF_EXISTS 0x00000001
+#define FILE_RENAME_FLAG_POSIX_SEMANTICS 0x00000002
+ FILE_INFO_BY_HANDLE_CLASS FileRenameInfoEx = 22;
+ struct {
+ /*
+ * This is usually an unnamed union, but that is not
+ * part of ISO C99. We thus inline the field, as we
+ * really only care for the Flags field anyway.
+ */
+ DWORD Flags;
+ HANDLE RootDirectory;
+ DWORD FileNameLength;
+ /*
+ * The actual structure is defined with a single-character
+ * flex array so that the structure has to be allocated on
+ * the heap. As we declare this structure ourselves though
+ * we can avoid the allocation and define FileName to have
+ * MAX_PATH bytes.
+ */
+ WCHAR FileName[MAX_PATH];
+ } rename_info = { 0 };
+ HANDLE old_handle = INVALID_HANDLE_VALUE;
+ BOOL success;
+
+ old_handle = CreateFileW(wpold, DELETE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (old_handle == INVALID_HANDLE_VALUE) {
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+ }
+
+ rename_info.Flags = FILE_RENAME_FLAG_REPLACE_IF_EXISTS |
+ FILE_RENAME_FLAG_POSIX_SEMANTICS;
+ rename_info.FileNameLength = wpnew_len * sizeof(WCHAR);
+ memcpy(rename_info.FileName, wpnew, wpnew_len * sizeof(WCHAR));
+
+ success = SetFileInformationByHandle(old_handle, FileRenameInfoEx,
+ &rename_info, sizeof(rename_info));
+ gle = GetLastError();
+ CloseHandle(old_handle);
+ if (success)
+ return 0;
+
+ /*
+ * When we see ERROR_INVALID_PARAMETER we can assume that the
+ * current system doesn't support FileRenameInfoEx. Keep us
+ * from using it in future calls and retry.
+ */
+ if (gle == ERROR_INVALID_PARAMETER) {
+ supports_file_rename_info_ex = 0;
+ goto repeat;
+ }
+
+ /*
+ * In theory, we shouldn't get ERROR_ACCESS_DENIED because we
+ * always open files with FILE_SHARE_DELETE But in practice we
+ * cannot assume that Git is the only one accessing files, and
+ * other applications may not set FILE_SHARE_DELETE. So we have
+ * to retry.
+ */
+ } else {
+ if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
+ return 0;
+ gle = GetLastError();
+ }
+
/* TODO: translate more errors */
- gle = GetLastError();
if (gle == ERROR_ACCESS_DENIED &&
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
@@ -3085,7 +3234,8 @@ static void maybe_redirect_std_handles(void)
*/
int wmain(int argc, const wchar_t **wargv)
{
- int i, maxlen, exit_status;
+ int i, exit_status;
+ size_t maxlen;
char *buffer, **save;
const char **argv;
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index e92be5741d..2eeec82f40 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -292,7 +292,7 @@ compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
concerned.
If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
- and all groups is stroed in REGS. (For the "_2" variants, the offsets are
+ and all groups are stored in REGS. (For the "_2" variants, the offsets are
computed relative to the concatenation, not relative to the individual
strings.)
diff --git a/compat/simple-ipc/ipc-shared.c b/compat/simple-ipc/ipc-shared.c
index cb176d966f..d1c21b49bd 100644
--- a/compat/simple-ipc/ipc-shared.c
+++ b/compat/simple-ipc/ipc-shared.c
@@ -16,11 +16,12 @@ int ipc_server_run(const char *path, const struct ipc_server_opts *opts,
struct ipc_server_data *server_data = NULL;
int ret;
- ret = ipc_server_run_async(&server_data, path, opts,
- application_cb, application_data);
+ ret = ipc_server_init_async(&server_data, path, opts,
+ application_cb, application_data);
if (ret)
return ret;
+ ipc_server_start_async(server_data);
ret = ipc_server_await(server_data);
ipc_server_free(server_data);
diff --git a/compat/simple-ipc/ipc-unix-socket.c b/compat/simple-ipc/ipc-unix-socket.c
index 9b3f2cdf8c..7db3b2a897 100644
--- a/compat/simple-ipc/ipc-unix-socket.c
+++ b/compat/simple-ipc/ipc-unix-socket.c
@@ -328,6 +328,7 @@ struct ipc_server_data {
int back_pos;
int front_pos;
+ int started;
int shutdown_requested;
int is_stopped;
};
@@ -712,7 +713,7 @@ static int accept_thread__wait_for_connection(
* Block SIGPIPE in this thread for the life of the thread. This
* avoids any stray SIGPIPE signals when closing pipe fds under
* extremely heavy loads (such as when the fifo queue is full and we
- * drop incomming connections).
+ * drop incoming connections).
*/
static void *accept_thread_proc(void *_accept_thread_data)
{
@@ -824,10 +825,10 @@ static int setup_listener_socket(
/*
* Start IPC server in a pool of background threads.
*/
-int ipc_server_run_async(struct ipc_server_data **returned_server_data,
- const char *path, const struct ipc_server_opts *opts,
- ipc_server_application_cb *application_cb,
- void *application_data)
+int ipc_server_init_async(struct ipc_server_data **returned_server_data,
+ const char *path, const struct ipc_server_opts *opts,
+ ipc_server_application_cb *application_cb,
+ void *application_data)
{
struct unix_ss_socket *server_socket = NULL;
struct ipc_server_data *server_data;
@@ -888,6 +889,12 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
server_data->accept_thread->fd_send_shutdown = sv[0];
server_data->accept_thread->fd_wait_shutdown = sv[1];
+ /*
+ * Hold work-available mutex so that no work can start until
+ * we unlock it.
+ */
+ pthread_mutex_lock(&server_data->work_available_mutex);
+
if (pthread_create(&server_data->accept_thread->pthread_id, NULL,
accept_thread_proc, server_data->accept_thread))
die_errno(_("could not start accept_thread '%s'"), path);
@@ -918,6 +925,15 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
return 0;
}
+void ipc_server_start_async(struct ipc_server_data *server_data)
+{
+ if (!server_data || server_data->started)
+ return;
+
+ server_data->started = 1;
+ pthread_mutex_unlock(&server_data->work_available_mutex);
+}
+
/*
* Gently tell the IPC server treads to shutdown.
* Can be run on any thread.
@@ -933,7 +949,9 @@ int ipc_server_stop_async(struct ipc_server_data *server_data)
trace2_region_enter("ipc-server", "server-stop-async", NULL);
- pthread_mutex_lock(&server_data->work_available_mutex);
+ /* If we haven't started yet, we are already holding lock. */
+ if (server_data->started)
+ pthread_mutex_lock(&server_data->work_available_mutex);
server_data->shutdown_requested = 1;
diff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c
index 8bfe51248e..a8fc812adf 100644
--- a/compat/simple-ipc/ipc-win32.c
+++ b/compat/simple-ipc/ipc-win32.c
@@ -371,6 +371,9 @@ struct ipc_server_data {
HANDLE hEventStopRequested;
struct ipc_server_thread_data *thread_list;
int is_stopped;
+
+ pthread_mutex_t startup_barrier;
+ int started;
};
enum connect_result {
@@ -526,6 +529,16 @@ static int use_connection(struct ipc_server_thread_data *server_thread_data)
return ret;
}
+static void wait_for_startup_barrier(struct ipc_server_data *server_data)
+{
+ /*
+ * Temporarily hold the startup_barrier mutex before starting,
+ * which lets us know that it's OK to start serving requests.
+ */
+ pthread_mutex_lock(&server_data->startup_barrier);
+ pthread_mutex_unlock(&server_data->startup_barrier);
+}
+
/*
* Thread proc for an IPC server worker thread. It handles a series of
* connections from clients. It cleans and reuses the hPipe between each
@@ -550,6 +563,8 @@ static void *server_thread_proc(void *_server_thread_data)
memset(&oConnect, 0, sizeof(oConnect));
oConnect.hEvent = hEventConnected;
+ wait_for_startup_barrier(server_thread_data->server_data);
+
for (;;) {
cr = wait_for_connection(server_thread_data, &oConnect);
@@ -752,10 +767,10 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
return hPipe;
}
-int ipc_server_run_async(struct ipc_server_data **returned_server_data,
- const char *path, const struct ipc_server_opts *opts,
- ipc_server_application_cb *application_cb,
- void *application_data)
+int ipc_server_init_async(struct ipc_server_data **returned_server_data,
+ const char *path, const struct ipc_server_opts *opts,
+ ipc_server_application_cb *application_cb,
+ void *application_data)
{
struct ipc_server_data *server_data;
wchar_t wpath[MAX_PATH];
@@ -787,6 +802,13 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
strbuf_addstr(&server_data->buf_path, path);
wcscpy(server_data->wpath, wpath);
+ /*
+ * Hold the startup_barrier lock so that no threads will progress
+ * until ipc_server_start_async() is called.
+ */
+ pthread_mutex_init(&server_data->startup_barrier, NULL);
+ pthread_mutex_lock(&server_data->startup_barrier);
+
if (nr_threads < 1)
nr_threads = 1;
@@ -837,6 +859,15 @@ int ipc_server_run_async(struct ipc_server_data **returned_server_data,
return 0;
}
+void ipc_server_start_async(struct ipc_server_data *server_data)
+{
+ if (!server_data || server_data->started)
+ return;
+
+ server_data->started = 1;
+ pthread_mutex_unlock(&server_data->startup_barrier);
+}
+
int ipc_server_stop_async(struct ipc_server_data *server_data)
{
if (!server_data)
@@ -850,6 +881,13 @@ int ipc_server_stop_async(struct ipc_server_data *server_data)
* We DO NOT attempt to force them to drop an active connection.
*/
SetEvent(server_data->hEventStopRequested);
+
+ /*
+ * If we haven't yet told the threads they are allowed to run,
+ * do so now, so they can receive the shutdown event.
+ */
+ ipc_server_start_async(server_data);
+
return 0;
}
@@ -900,5 +938,7 @@ void ipc_server_free(struct ipc_server_data *server_data)
free(std);
}
+ pthread_mutex_destroy(&server_data->startup_barrier);
+
free(server_data);
}
diff --git a/compat/vcbuild/include/unistd.h b/compat/vcbuild/include/unistd.h
index 3a959d124c..a261a925b7 100644
--- a/compat/vcbuild/include/unistd.h
+++ b/compat/vcbuild/include/unistd.h
@@ -14,7 +14,11 @@ typedef _mode_t mode_t;
#ifndef _SSIZE_T_
#define _SSIZE_T_
+#ifdef _WIN64
+typedef __int64 _ssize_t;
+#else
typedef long _ssize_t;
+#endif /* _WIN64 */
#ifndef _OFF_T_
#define _OFF_T_