Skip to content
Open
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
61 changes: 56 additions & 5 deletions src/foundation/compat_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
#include <windows.h>

#include <aclapi.h>
#include <direct.h> /* _wmkdir */
#include <errno.h> /* errno for spawn-failure logging */
#include <fcntl.h> /* _O_RDONLY */
#include <io.h> /* _wunlink, _open_osfhandle, _close */
#include <stdint.h> /* intptr_t */
#include <direct.h> /* _wmkdir */
#include <errno.h> /* errno for spawn-failure logging */
#include <fcntl.h> /* _O_RDONLY */
#include <io.h> /* _wunlink, _open_osfhandle, _close */
#include <share.h> /* _SH_DENYRW */
#include <sys/stat.h> /* _S_IREAD */
#include <stdint.h> /* intptr_t */
#include "foundation/log.h"
#include "foundation/win_utf8.h"

Expand Down Expand Up @@ -581,6 +583,26 @@ int cbm_rmdir(const char *path) {
return ret;
}

int cbm_lockfile_open(const char *path, bool create) {
wchar_t *wpath = cbm_path_to_wide(path);
if (!wpath) {
errno = EINVAL;
return -1;
}
int flags = _O_RDWR | _O_BINARY | _O_NOINHERIT | (create ? _O_CREAT : 0);
/* _SH_DENYRW: every other open of this file, from any process, fails
* with EACCES until this descriptor closes -- including at death. */
int fd = _wsopen(wpath, flags, _SH_DENYRW, _S_IREAD | _S_IWRITE);
free(wpath);
return fd;
}

void cbm_lockfile_close(int fd) {
if (fd >= 0) {
(void)_close(fd);
}
}

/* Build a properly-quoted Windows command line from an argv array.
* Returns a heap-allocated wide string, or NULL on allocation failure.
* Quoting follows the MSVC CRT convention: arguments containing spaces,
Expand Down Expand Up @@ -730,6 +752,7 @@ int cbm_exec_no_shell(const char *const *argv) {
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
Expand Down Expand Up @@ -929,6 +952,34 @@ int cbm_rmdir(const char *path) {
return rmdir(path);
}

int cbm_lockfile_open(const char *path, bool create) {
int flags = O_RDWR | O_CLOEXEC | O_NOFOLLOW | (create ? O_CREAT : 0);
int fd;
do {
fd = open(path, flags, S_IRUSR | S_IWUSR);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
return -1;
}
int rc;
do {
rc = flock(fd, LOCK_EX | LOCK_NB);
} while (rc != 0 && errno == EINTR);
if (rc != 0) {
int saved = errno;
(void)close(fd);
errno = saved;
return -1;
}
return fd;
}

void cbm_lockfile_close(int fd) {
if (fd >= 0) {
(void)close(fd);
}
}

int cbm_exec_no_shell(const char *const *argv) {
if (!argv || !argv[0]) {
return CBM_NOT_FOUND;
Expand Down
12 changes: 12 additions & 0 deletions src/foundation/compat_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ int cbm_canonical_path(const char *path, char *out, size_t out_sz);
/* Delete an empty directory. Returns 0 on success. */
int cbm_rmdir(const char *path);

/* Exclusive lock file. Opens `path` and takes an exclusive lock the KERNEL
* releases on any process death -- POSIX open(O_CLOEXEC|O_NOFOLLOW) +
* flock(LOCK_EX|LOCK_NB); Windows _wsopen with _SH_DENYRW (deny every other
* open) and _O_NOINHERIT -- so ownership never outlives its holder and is
* never inherited by a spawned child. `create` false never creates the file.
* Returns the descriptor, or -1 with errno: EWOULDBLOCK/EAGAIN (POSIX) or
* EACCES (Windows sharing violation) when another holder is live, ENOENT
* when absent, otherwise the open error. Never use this on a SQLite file:
* on macOS an flock conflicts with SQLite's own fcntl byte locks. */
int cbm_lockfile_open(const char *path, bool create);
void cbm_lockfile_close(int fd);

/* Open a file by UTF-8 path.
* On Windows, converts to wide-char and calls _wfopen so paths with
* non-ASCII characters (accents, CJK, etc.) are handled correctly.
Expand Down
Loading
Loading