aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2023-12-13 14:31:02 -0800
committerGitHub <noreply@github.com>2023-12-13 19:31:02 -0300
commit89daf1f1588189f0fedc0b050408d785729a6833 (patch)
treeebf84abf3f1981630cddf26af6bf3253109b8798
parentd81020338c99ff4116431ee496a7db516eb91f00 (diff)
downloadluarocks-89daf1f1588189f0fedc0b050408d785729a6833.tar.gz
luarocks-89daf1f1588189f0fedc0b050408d785729a6833.tar.bz2
luarocks-89daf1f1588189f0fedc0b050408d785729a6833.zip
Introduce locking for concurrent access control (#1557)
Fixes #1540
-rw-r--r--src/luarocks/cmd.lua21
-rw-r--r--src/luarocks/cmd/build.lua2
-rw-r--r--src/luarocks/cmd/init.lua2
-rw-r--r--src/luarocks/cmd/install.lua2
-rw-r--r--src/luarocks/cmd/make.lua2
-rw-r--r--src/luarocks/cmd/purge.lua12
-rw-r--r--src/luarocks/cmd/remove.lua2
-rw-r--r--src/luarocks/core/cfg.lua1
-rw-r--r--src/luarocks/fetch.lua6
-rw-r--r--src/luarocks/fs/lua.lua12
-rw-r--r--src/luarocks/fs/unix/tools.lua39
-rw-r--r--src/luarocks/fs/win32/tools.lua9
12 files changed, 100 insertions, 10 deletions
diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua
index ca79c316..2db0c039 100644
--- a/src/luarocks/cmd.lua
+++ b/src/luarocks/cmd.lua
@@ -26,6 +26,7 @@ cmd.errorcodes = {
26 UNSPECIFIED = 1, 26 UNSPECIFIED = 1,
27 PERMISSIONDENIED = 2, 27 PERMISSIONDENIED = 2,
28 CONFIGFILE = 3, 28 CONFIGFILE = 3,
29 LOCK = 4,
29 CRASH = 99 30 CRASH = 99
30} 31}
31 32
@@ -477,6 +478,8 @@ Enabling completion for Fish:
477 "To enable it, see '"..program.." help path'.") 478 "To enable it, see '"..program.." help path'.")
478 parser:flag("--global", "Use the system tree when `local_by_default` is `true`.") 479 parser:flag("--global", "Use the system tree when `local_by_default` is `true`.")
479 parser:flag("--no-project", "Do not use project tree even if running from a project folder.") 480 parser:flag("--no-project", "Do not use project tree even if running from a project folder.")
481 parser:flag("--force-lock", "Attempt to overwrite the lock for commands " ..
482 "that require exclusive access, such as 'install'")
480 parser:flag("--verbose", "Display verbose output of commands executed.") 483 parser:flag("--verbose", "Display verbose output of commands executed.")
481 parser:option("--timeout", "Timeout on network operations, in seconds.\n".. 484 parser:option("--timeout", "Timeout on network operations, in seconds.\n"..
482 "0 means no timeout (wait forever). Default is ".. 485 "0 means no timeout (wait forever). Default is "..
@@ -672,10 +675,28 @@ function cmd.run_command(description, commands, external_namespace, ...)
672 end 675 end
673 676
674 local cmd_mod = cmd_modules[args.command] 677 local cmd_mod = cmd_modules[args.command]
678
679 local lock
680 if cmd_mod.needs_lock then
681 lock, err = fs.lock_access(path.root_dir(cfg.root_dir), args.force_lock)
682 if not lock then
683 local try_force = args.force_lock
684 and (" - failed to force the lock" .. (err and ": " .. err or ""))
685 or " - use --force-lock to overwrite the lock"
686 die("command '" .. args.command .. "' " ..
687 "requires exclusive access to " .. cfg.root_dir ..
688 try_force, cmd.errorcodes.LOCK)
689 end
690 end
691
675 local call_ok, ok, err, exitcode = xpcall(function() 692 local call_ok, ok, err, exitcode = xpcall(function()
676 return cmd_mod.command(args) 693 return cmd_mod.command(args)
677 end, error_handler) 694 end, error_handler)
678 695
696 if lock then
697 fs.unlock_access(lock)
698 end
699
679 if not call_ok then 700 if not call_ok then
680 die(ok, cmd.errorcodes.CRASH) 701 die(ok, cmd.errorcodes.CRASH)
681 elseif not ok then 702 elseif not ok then
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua
index 56f0e757..16c0aff2 100644
--- a/src/luarocks/cmd/build.lua
+++ b/src/luarocks/cmd/build.lua
@@ -180,4 +180,6 @@ function cmd_build.command(args)
180 return name, version 180 return name, version
181end 181end
182 182
183cmd_build.needs_lock = true
184
183return cmd_build 185return cmd_build
diff --git a/src/luarocks/cmd/init.lua b/src/luarocks/cmd/init.lua
index 8bccb23f..dcd6b759 100644
--- a/src/luarocks/cmd/init.lua
+++ b/src/luarocks/cmd/init.lua
@@ -173,4 +173,6 @@ function init.command(args)
173 return true 173 return true
174end 174end
175 175
176init.needs_lock = true
177
176return init 178return init
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua
index c2e947b2..b4f15cb8 100644
--- a/src/luarocks/cmd/install.lua
+++ b/src/luarocks/cmd/install.lua
@@ -255,4 +255,6 @@ function install.command(args)
255 end 255 end
256end 256end
257 257
258install.needs_lock = true
259
258return install 260return install
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua
index 8b313bb9..8147b5df 100644
--- a/src/luarocks/cmd/make.lua
+++ b/src/luarocks/cmd/make.lua
@@ -155,4 +155,6 @@ function make.command(args)
155 end 155 end
156end 156end
157 157
158make.needs_lock = true
159
158return make 160return make
diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua
index c300e286..09380ef1 100644
--- a/src/luarocks/cmd/purge.lua
+++ b/src/luarocks/cmd/purge.lua
@@ -39,18 +39,10 @@ end
39function purge.command(args) 39function purge.command(args)
40 local tree = args.tree 40 local tree = args.tree
41 41
42 if type(tree) ~= "string" then
43 return nil, "The --tree argument is mandatory. "..util.see_help("purge")
44 end
45
46 local results = {}
47 if not fs.is_dir(tree) then
48 return nil, "Directory not found: "..tree
49 end
50
51 local ok, err = fs.check_command_permissions(args) 42 local ok, err = fs.check_command_permissions(args)
52 if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end 43 if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end
53 44
45 local results = {}
54 search.local_manifest_search(results, path.rocks_dir(tree), queries.all()) 46 search.local_manifest_search(results, path.rocks_dir(tree), queries.all())
55 47
56 local sort = function(a,b) return vers.compare_versions(b,a) end 48 local sort = function(a,b) return vers.compare_versions(b,a) end
@@ -81,4 +73,6 @@ function purge.command(args)
81 return writer.make_manifest(cfg.rocks_dir, "one") 73 return writer.make_manifest(cfg.rocks_dir, "one")
82end 74end
83 75
76purge.needs_lock = true
77
84return purge 78return purge
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua
index 6bb69ad6..affeda25 100644
--- a/src/luarocks/cmd/remove.lua
+++ b/src/luarocks/cmd/remove.lua
@@ -72,4 +72,6 @@ function cmd_remove.command(args)
72 return true 72 return true
73end 73end
74 74
75cmd_remove.needs_lock = true
76
75return cmd_remove 77return cmd_remove
diff --git a/src/luarocks/core/cfg.lua b/src/luarocks/core/cfg.lua
index 9c2728cc..81987cc9 100644
--- a/src/luarocks/core/cfg.lua
+++ b/src/luarocks/core/cfg.lua
@@ -238,6 +238,7 @@ local function make_defaults(lua_version, target_cpu, platforms, home)
238 MKDIR = "mkdir", 238 MKDIR = "mkdir",
239 RMDIR = "rmdir", 239 RMDIR = "rmdir",
240 CP = "cp", 240 CP = "cp",
241 LN = "ln",
241 LS = "ls", 242 LS = "ls",
242 RM = "rm", 243 RM = "rm",
243 FIND = "find", 244 FIND = "find",
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua
index 723af8b5..09f7a40b 100644
--- a/src/luarocks/fetch.lua
+++ b/src/luarocks/fetch.lua
@@ -33,21 +33,25 @@ function fetch.fetch_caching(url, mirroring)
33 local name = repo_url:gsub("[/:]","_") 33 local name = repo_url:gsub("[/:]","_")
34 local cache_dir = dir.path(cfg.local_cache, name) 34 local cache_dir = dir.path(cfg.local_cache, name)
35 local ok = fs.make_dir(cache_dir) 35 local ok = fs.make_dir(cache_dir)
36 if not ok then 36 local lock = ok and fs.lock_access(cache_dir)
37 if not (ok and lock) then
37 cfg.local_cache = fs.make_temp_dir("local_cache") 38 cfg.local_cache = fs.make_temp_dir("local_cache")
38 cache_dir = dir.path(cfg.local_cache, name) 39 cache_dir = dir.path(cfg.local_cache, name)
39 ok = fs.make_dir(cache_dir) 40 ok = fs.make_dir(cache_dir)
40 if not ok then 41 if not ok then
41 return nil, "Failed creating temporary cache directory "..cache_dir 42 return nil, "Failed creating temporary cache directory "..cache_dir
42 end 43 end
44 lock = fs.lock_access(cache_dir)
43 end 45 end
44 46
45 local cachefile = dir.path(cache_dir, filename) 47 local cachefile = dir.path(cache_dir, filename)
46 if cfg.aggressive_cache and (not name:match("^manifest")) and fs.exists(cachefile) then 48 if cfg.aggressive_cache and (not name:match("^manifest")) and fs.exists(cachefile) then
49 fs.unlock_access(lock)
47 return cachefile, nil, nil, true 50 return cachefile, nil, nil, true
48 end 51 end
49 52
50 local file, err, errcode, from_cache = fetch.fetch_url(url, cachefile, true, mirroring) 53 local file, err, errcode, from_cache = fetch.fetch_url(url, cachefile, true, mirroring)
54 fs.unlock_access(lock)
51 if not file then 55 if not file then
52 return nil, err or "Failed downloading "..url, errcode 56 return nil, err or "Failed downloading "..url, errcode
53 end 57 end
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua
index 174fdc88..ec497f88 100644
--- a/src/luarocks/fs/lua.lua
+++ b/src/luarocks/fs/lua.lua
@@ -283,6 +283,18 @@ end
283 283
284if lfs_ok then 284if lfs_ok then
285 285
286function fs_lua.lock_access(dirname, force)
287 fs.make_dir(dirname)
288 if force then
289 os.remove(dir.path(dirname, "lockfile.lfs"))
290 end
291 return lfs.lock_dir(dirname)
292end
293
294function fs_lua.unlock_access(lock)
295 return lock:free()
296end
297
286--- Run the given command. 298--- Run the given command.
287-- The command is executed in the current directory in the dir stack. 299-- The command is executed in the current directory in the dir stack.
288-- @param cmd string: No quoting/escaping is applied to the command. 300-- @param cmd string: No quoting/escaping is applied to the command.
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua
index b7fd4de5..7106ed81 100644
--- a/src/luarocks/fs/unix/tools.lua
+++ b/src/luarocks/fs/unix/tools.lua
@@ -315,4 +315,43 @@ function tools.is_superuser()
315 return fs.current_user() == "root" 315 return fs.current_user() == "root"
316end 316end
317 317
318function tools.lock_access(dirname, force)
319 fs.make_dir(dirname)
320
321 local tempfile = os.tmpname()
322 if not tempfile then
323 return nil, "failed creating temp file for locking"
324 end
325
326 local fd, fderr = io.open(tempfile, "w")
327 if not fd then
328 return nil, "failed opening temp file " .. tempfile .. " for locking: " .. fderr
329 end
330
331 local ok, werr = fd:write("lock file for " .. dirname)
332 if not ok then
333 return nil, "failed writing temp file " .. tempfile .. " for locking: " .. werr
334 end
335
336 fd:close()
337
338 local lockfile = dir.path(dirname, "lockfile.luarocks")
339
340 local force_flag = force and " -f" or ""
341
342 if fs.execute(vars.LN .. force_flag, tempfile, lockfile) then
343 return {
344 tempfile = tempfile,
345 lockfile = lockfile,
346 }
347 else
348 return nil, "File exists" -- same message as luafilesystem
349 end
350end
351
352function tools.unlock_access(lock)
353 os.remove(lock.lockfile)
354 os.remove(lock.tempfile)
355end
356
318return tools 357return tools
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua
index 9bd050c6..be63063b 100644
--- a/src/luarocks/fs/win32/tools.lua
+++ b/src/luarocks/fs/win32/tools.lua
@@ -316,4 +316,13 @@ function tools.set_time(filename, time)
316 return true -- FIXME 316 return true -- FIXME
317end 317end
318 318
319function tools.lock_access(dirname)
320 -- NYI
321 return {}
322end
323
324function tools.unlock_access(lock)
325 -- NYI
326end
327
319return tools 328return tools