diff --git a/osx.c b/osx.c index 98cb2c9..23879c1 100644 --- a/osx.c +++ b/osx.c @@ -24,11 +24,13 @@ #ifdef _WIN32 #include #include +#include #include #include #include #include #include +#include typedef struct __stat64 os_stat_type; @@ -75,6 +77,39 @@ static int truncate(const char *path, __int64 size) #define ftruncate(fd, size) _chsize_s(fd, size) #define fsync(fd) _commit(fd) +static int flocking(FILE *fh, int mode, __int64 ofs, __int64 len) +{ + if (ofs < 0 || len < 0 || len > LONG_MAX) { + errno = ERANGE; + return -1; + } + + _lock_file(fh); + if (_fseeki64_nolock(fh, ofs, SEEK_SET) != 0) + goto fail; + + if (len == 0) { + // NOTE: seek may flush write buffers, so file length must be taken now + len = _filelengthi64(_fileno(fh)); + if (len < 0) + goto fail; + if (len > LONG_MAX) { + errno = ERANGE; + goto fail; + } + } + + if (_locking(_fileno(fh), mode, len) != 0) + goto fail; + + _unlock_file(fh); + return 0; + +fail: + _unlock_file(fh); + return -1; +} + #else #include #include @@ -106,6 +141,39 @@ static inline int setmode(int fd, int mode) errno = ENOTSUP; return -1; } + +#define _LK_NBRLCK F_RDLCK +#define _LK_NBLCK F_WRLCK +#define _LK_UNLCK F_UNLCK + +static int flocking(FILE *fh, int mode, off_t ofs, off_t len) +{ + if (ofs < 0 || len < 0) { + errno = ERANGE; + return -1; + } + + flockfile(fh); + + if (fseeko(fh, ofs, SEEK_SET) != 0) + goto fail; + + struct flock lk = { + .l_whence = SEEK_SET, + .l_type = mode, + .l_start = ofs, + .l_len = len // NOTE: 0 has special meaning + }; + if (fcntl(fileno(fh), F_SETLK, &lk) != 0) + goto fail; + + funlockfile(fh); + return 0; + +fail: + funlockfile(fh); + return -1; +} #endif #ifdef __linux__ @@ -538,6 +606,20 @@ static int os_commit(lua_State *L) return os_result(L, ec, NULL); } +static int os_locking(lua_State *L) +{ + static const char *modes[] = { "r", "w", "u", NULL }; + static const int imodes[] = { _LK_NBRLCK, _LK_NBLCK, _LK_UNLCK }; + + luaL_Stream *stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); + int i = luaL_checkoption(L, 2, NULL, modes); + lua_Integer ofs = luaL_optinteger(L, 3, 0); + lua_Integer len = luaL_optinteger(L, 4, 0); + + int ec = flocking(stream->f, imodes[i], ofs, len); + return os_result(L, ec, NULL); +} + static void createmeta(lua_State *L) { static const luaL_Reg metameth[] = { @@ -579,6 +661,7 @@ DF_OSXMOD_API int luaopen_osx(lua_State *L) { "chsize", os_chsize }, { "fadvise", os_fadvise }, { "commit", os_commit }, + { "locking", os_locking }, { NULL, NULL } };