diff options
author | Adam Harrison <adamdharrison@gmail.com> | 2023-01-10 23:03:16 -0500 |
---|---|---|
committer | Adam Harrison <adamdharrison@gmail.com> | 2023-01-10 23:03:16 -0500 |
commit | c855c3ccc39ed5cb1669170aebf2a88de0da97d3 (patch) | |
tree | 03ee98db51f37ed77a24e936214f4b80165aec5a | |
parent | 5e3f95685886bd4bd63ac3d39fb2e024f619b4cd (diff) | |
download | lite-xl-plugin-manager-c855c3ccc39ed5cb1669170aebf2a88de0da97d3.tar.gz lite-xl-plugin-manager-c855c3ccc39ed5cb1669170aebf2a88de0da97d3.zip |
Added in file locking.
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/lpm.c | 129 | ||||
-rw-r--r-- | src/lpm.lua | 79 |
3 files changed, 124 insertions, 86 deletions
@@ -104,7 +104,7 @@ lpm --help ### Linux -> Windows ``` -./build.sh clean && CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar WINDRES=x86_64-w64-mingw32-windres +./build.sh clean && CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar WINDRES=x86_64-w64-mingw32-windres \ CMAKE_DEFAULT_FLAGS="-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER\ -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=NEVER -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_INCLUDE_PATH=/usr/share/mingw-w64/include"\ GIT2_CONFIGURE="-DDLLTOOL=x86_64-w64-mingw32-dlltool" ./build.sh -DLPM_STATIC -DLPM_VERSION='"'$VERSION-x86_64-windows-`git rev-parse --short HEAD`'"' ``` @@ -24,6 +24,7 @@ #include <fcntl.h> #include <sys/stat.h> +#include <sys/file.h> #include <git2.h> #include <mbedtls/sha256.h> #include <mbedtls/x509.h> @@ -98,38 +99,43 @@ int lpm_chmod(lua_State* L) { return 0; } -/** BEGIN STOLEN LITE CODE **/ #if _WIN32 -static LPWSTR utfconv_utf8towc(const char *str) { - LPWSTR output; +static LPCWSTR lua_toutf16(lua_State* L, const char* str) { + if (str && str[0] == 0) + return L""; int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - if (len == 0) - return NULL; - output = (LPWSTR) malloc(sizeof(WCHAR) * len); - if (output == NULL) - return NULL; - len = MultiByteToWideChar(CP_UTF8, 0, str, -1, output, len); - if (len == 0) { - free(output); - return NULL; + if (len > 0) { + LPWSTR output = (LPWSTR) malloc(sizeof(WCHAR) * len); + if (output) { + len = MultiByteToWideChar(CP_UTF8, 0, str, -1, output, len); + if (len > 0) { + lua_pushlstring(L, (char*)output, len * 2); + free(output); + return (LPCWSTR)lua_tostring(L, -1); + } + free(output); + } } - return output; + luaL_error(L, "can't convert utf8 string"); + return NULL; } -static char *utfconv_wctoutf8(LPCWSTR str) { - char *output; +static const char* lua_toutf8(lua_State* L, LPCWSTR str) { int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - if (len == 0) - return NULL; - output = (char *) malloc(sizeof(char) * len); - if (output == NULL) - return NULL; - len = WideCharToMultiByte(CP_UTF8, 0, str, -1, output, len, NULL, NULL); - if (len == 0) { - free(output); - return NULL; + if (len > 0) { + char* output = (char *) malloc(sizeof(char) * len); + if (output) { + len = WideCharToMultiByte(CP_UTF8, 0, str, -1, output, len, NULL, NULL); + if (len) { + lua_pushlstring(L, output, len); + free(output); + return lua_tostring(L, -1); + } + free(output); + } } - return output; + luaL_error(L, "can't convert utf16 string"); + return NULL; } #endif @@ -141,22 +147,16 @@ static int lpm_ls(lua_State *L) { lua_pushstring(L, path[0] == 0 || strchr("\\/", path[strlen(path) - 1]) != NULL ? "*" : "\\*"); lua_concat(L, 2); path = lua_tostring(L, -1); - - LPWSTR wpath = utfconv_utf8towc(path); - if (wpath == NULL) - return luaL_error(L, "can't ls %s: invalid utf8 character conversion", path); WIN32_FIND_DATAW fd; - HANDLE find_handle = FindFirstFileExW(wpath, FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0); - free(wpath); + HANDLE find_handle = FindFirstFileExW(lua_toutf16(L, path), FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0); if (find_handle == INVALID_HANDLE_VALUE) return luaL_error(L, "can't ls %s: %d", path, GetLastError()); char mbpath[MAX_PATH * 4]; // utf-8 spans 4 bytes at most int len, i = 1; lua_newtable(L); - do - { + do { if (wcscmp(fd.cFileName, L".") == 0) { continue; } if (wcscmp(fd.cFileName, L"..") == 0) { continue; } @@ -193,10 +193,7 @@ static int lpm_ls(lua_State *L) { static int lpm_rmdir(lua_State *L) { const char *path = luaL_checkstring(L, 1); #ifdef _WIN32 - LPWSTR wpath = utfconv_utf8towc(path); - int deleted = RemoveDirectoryW(wpath); - free(wpath); - if (!deleted) + if (!RemoveDirectoryW(lua_toutf16(L, path))) return luaL_error(L, "can't rmdir %s: %d", path, GetLastError()); #else if (remove(path)) @@ -208,11 +205,7 @@ static int lpm_rmdir(lua_State *L) { static int lpm_mkdir(lua_State *L) { const char *path = luaL_checkstring(L, 1); #ifdef _WIN32 - LPWSTR wpath = utfconv_utf8towc(path); - if (wpath == NULL) - return luaL_error(L, "can't mkdir %s: invalid utf8 character conversion", path); - int err = _wmkdir(wpath); - free(wpath); + int err = _wmkdir(lua_toutf16(L, path)); #else int err = mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); #endif @@ -223,29 +216,25 @@ static int lpm_mkdir(lua_State *L) { static int lpm_stat(lua_State *L) { const char *path = luaL_checkstring(L, 1); - lua_newtable(L); #ifdef _WIN32 #define realpath(x, y) _wfullpath(y, x, MAX_PATH) struct _stat s; - LPWSTR wpath = utfconv_utf8towc(path); - if (wpath == NULL) - return luaL_error(L, "can't stat %s: invalid utf8 character conversion", path); + LPCWSTR wpath = lua_toutf16(L, path); int err = _wstat(wpath, &s); - LPWSTR wfullpath = realpath(wpath, NULL); - free(wpath); + LPCWSTR wfullpath = realpath(wpath, NULL); if (!wfullpath) return 0; - char *abs_path = utfconv_wctoutf8(wfullpath); - free(wfullpath); + const char *abs_path = lua_toutf8(L, wfullpath); #else struct stat s; int err = lstat(path, &s); - char *abs_path = realpath(path, NULL); + const char *abs_path = realpath(path, NULL); #endif if (err || !abs_path) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } + lua_newtable(L); lua_pushstring(L, abs_path); lua_setfield(L, -2, "abs_path"); lua_pushvalue(L, 1); lua_setfield(L, -2, "path"); @@ -658,7 +647,7 @@ static int lpm_extract(lua_State* L) { tar.close = gzip_close;*/ char buffer[8192]; int len = strlen(src); - strncpy(actual_src, src, len - 3); + strncpy(actual_src, src, min(len - 3, PATH_MAX)); actual_src[len-3] = 0; FILE* file = fopen(actual_src, "wb"); while (1) { @@ -951,6 +940,41 @@ static int lpm_pwd(lua_State* L) { return 1; } +int lpm_flock(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + #ifdef _WIN32 + HANDLE file = CreateFileW(lua_toutf16(L, path), FILE_SHARE_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (!file || file == INVALID_HANDLE_VALUE) + return luaL_error(L, "can't open for flock %s: %d", path, GetLastError()); + OVERLAPPED overlapped = {0}; + if (!LockFileEx(file, LOCKFILE_EXCLUSIVE_LOCK, 0, 0, 1, &overlapped)) { + CloseHandle(file); + return luaL_error(L, "can't flock %s: %d", path, GetLastError()); + } + #else + int fd = open(path, 0); + if (fd == -1) + return luaL_error(L, "can't flock %s: %s", path, strerror(errno)); + if (flock(fd, LOCK_EX) == -1) { + close(fd); + return luaL_error(L, "can't acquire exclusive lock on %s: %s", strerror(errno)); + } + #endif + lua_pushvalue(L, 2); + lua_pushvalue(L, 1); + int err = lua_pcall(L, 1, 0, 0); + #ifdef _WIN32 + UnlockFile(file, 0, 0, 1, 0); + CloseHandle(file); + #else + close(fd); + #endif + if (err) + return lua_error(L); + return 0; +} + static const luaL_Reg system_lib[] = { { "ls", lpm_ls }, // Returns an array of files. { "stat", lpm_stat }, // Returns info about a single file. @@ -969,6 +993,7 @@ static const luaL_Reg system_lib[] = { { "certs", lpm_certs }, // Sets the SSL certificate chain folder/file. { "chdir", lpm_chdir }, // Changes directory. Only use for --post actions. { "pwd", lpm_pwd }, // Gets existing directory. Only use for --post actions. + { "flock", lpm_flock }, // Locks a file. { NULL, NULL } }; diff --git a/src/lpm.lua b/src/lpm.lua index 5089463..33929b0 100644 --- a/src/lpm.lua +++ b/src/lpm.lua @@ -455,6 +455,13 @@ end local HOME, USERDIR, CACHEDIR, JSON, VERBOSE, MOD_VERSION, QUIET, FORCE, AUTO_PULL_REMOTES, ARCH, ASSUME_YES, NO_INSTALL_OPTIONAL, TMPDIR, DATADIR, BINARY, POST, repositories, lite_xls, system_bottle, progress_bar_label, write_progress_bar +local function engage_locks(func) + if not system.stat(USERDIR) then common.mkdirp(USERDIR) end + local lockfile = USERDIR .. PATHSEP .. ".lock" + if not system.stat(lockfile) then common.write(lockfile, "") end + system.flock(lockfile, func) +end + local Plugin, Repository, LiteXL, Bottle = {}, {}, {}, {} local actions, warnings = {}, {} @@ -1817,44 +1824,48 @@ in any circumstance unless explicitly supplied. end -- Base setup; initialize default repos if applicable, read them in. Determine Lite XL system binary if not specified, and pull in a list of all local lite-xl's. - lpm_repo_init() - repositories, lite_xls = {}, {} - if system.stat(CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. "list") then - for url in io.lines(CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. "list") do - table.insert(repositories, Repository.url(url)) - repositories[#repositories]:parse_manifest() + engage_locks(function() + lpm_repo_init() + repositories, lite_xls = {}, {} + if system.stat(CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. "list") then + for url in io.lines(CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. "list") do + table.insert(repositories, Repository.url(url)) + repositories[#repositories]:parse_manifest() + end end - end - if system.stat(CACHEDIR .. PATHSEP .. "lite_xls" .. PATHSEP .. ARCH .. PATHSEP .. "locals.json") then - for i, lite_xl in ipairs(json.decode(common.read(CACHEDIR .. PATHSEP .. "lite_xls" .. PATHSEP .. ARCH .. PATHSEP .. "locals.json"))) do - table.insert(lite_xls, LiteXL.new(nil, { version = lite_xl.version, mod_version = lite_xl.mod_version, path = lite_xl.path, tags = { "local" } })) + if system.stat(CACHEDIR .. PATHSEP .. "lite_xls" .. PATHSEP .. ARCH .. PATHSEP .. "locals.json") then + for i, lite_xl in ipairs(json.decode(common.read(CACHEDIR .. PATHSEP .. "lite_xls" .. PATHSEP .. ARCH .. PATHSEP .. "locals.json"))) do + table.insert(lite_xls, LiteXL.new(nil, { version = lite_xl.version, mod_version = lite_xl.mod_version, path = lite_xl.path, tags = { "local" } })) + end end - end - local lite_xl_binary = BINARY or common.path("lite-xl") - if lite_xl_binary then - local stat = system.stat(lite_xl_binary) - if not stat then error("can't find lite-xl binary " .. lite_xl_binary) end - lite_xl_binary = stat.symlink or lite_xl_binary - local directory = common.dirname(lite_xl_binary) - local hash = system.hash(lite_xl_binary, "file") - local system_lite_xl = common.first(common.concat(common.flat_map(repositories, function(r) return r.lite_xls end), lite_xls), function(lite_xl) return lite_xl.local_path == directory end) - if not system_lite_xl then - system_lite_xl = common.first(lite_xls, function(e) return e.version == "system" end) - if system_lite_xl then error("can't find existing system lite (does " .. system_lite_xl.binary_path .. " exist? was it moved?); run `lpm purge`, or specify --binary and --datadir.") end - system_lite_xl = LiteXL.new(nil, { datadir_path = DATADIR, binary_path = BINARY, mod_version = MOD_VERSION or 3, version = "system", tags = { "system", "local" } }) - table.insert(lite_xls, system_lite_xl) - lpm_lite_xl_save() + local lite_xl_binary = BINARY or common.path("lite-xl") + if lite_xl_binary then + local stat = system.stat(lite_xl_binary) + if not stat then error("can't find lite-xl binary " .. lite_xl_binary) end + lite_xl_binary = stat.symlink or lite_xl_binary + local directory = common.dirname(lite_xl_binary) + local hash = system.hash(lite_xl_binary, "file") + local system_lite_xl = common.first(common.concat(common.flat_map(repositories, function(r) return r.lite_xls end), lite_xls), function(lite_xl) return lite_xl.local_path == directory end) + if not system_lite_xl then + system_lite_xl = common.first(lite_xls, function(e) return e.version == "system" end) + if system_lite_xl then error("can't find existing system lite (does " .. system_lite_xl.binary_path .. " exist? was it moved?); run `lpm purge`, or specify --binary and --datadir.") end + system_lite_xl = LiteXL.new(nil, { datadir_path = DATADIR, binary_path = BINARY, mod_version = MOD_VERSION or 3, version = "system", tags = { "system", "local" } }) + table.insert(lite_xls, system_lite_xl) + lpm_lite_xl_save() + else + table.insert(system_lite_xl.tags, "system") + end + system_bottle = Bottle.new(system_lite_xl, nil, true) else - table.insert(system_lite_xl.tags, "system") + system_bottle = Bottle.new(LiteXL.new(nil, { mod_version = MOD_VERSION or 3, version = "system", tags = { "system", "local" } }), nil, true) end - system_bottle = Bottle.new(system_lite_xl, nil, true) - else - system_bottle = Bottle.new(LiteXL.new(nil, { mod_version = MOD_VERSION or 3, version = "system", tags = { "system", "local" } }), nil, true) - end - if not system_bottle then system_bottle = Bottle.new(nil, nil, true) end + if not system_bottle then system_bottle = Bottle.new(nil, nil, true) end + end) if ARGS[2] ~= '-' then - run_command(ARGS) + engage_locks(function() + run_command(ARGS) + end) else while true do local line = io.stdin:read("*line") @@ -1868,7 +1879,9 @@ in any circumstance unless explicitly supplied. s = e + 1 end xpcall(function() - run_command(args) + engage_locks(function() + run_command(args) + end) end, error_handler) actions, warnings = {}, {} end |