commit e01979620025666633250b3e2d545fe59c629f73
Author: Vladimír Čunát <vladimir.cunat@nic.cz>
Date:   Fri Nov 13 14:16:32 2020 +0100

    fix map() command on 32-bit platforms; regressed in 5.2.0
    
    LuaJIT FFI was using opendir() (etc.) variants with 32-bit inodes
    but the C parts was using them as 64-bit inode variants.
    Consequently the `struct dirent` layout didn't match and we were getting
    filenames shifted by eight bytes.
    
    Now the whole dir-listing lua function is written in C.

diff --git a/.luacheckrc b/.luacheckrc
index 0cf0b884..67bc18f6 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -20,6 +20,7 @@ new_read_globals = {
 	'user',
 	'verbose',
 	'worker',
+	'kluautil_list_dir',
 	-- Sandbox declarations
 	'kB',
 	'MB',
diff --git a/NEWS b/NEWS
index 2eae3082..9d8cde99 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,10 @@
 Knot Resolver X.Y.X (yyyy-mm-dd)
 ================================
 
+Bugfixes
+--------
+- fix map() command on 32-bit platforms; regressed in 5.2.0 (!1093)
+
 
 Knot Resolver 5.2.0 (2020-11-11)
 ================================
diff --git a/daemon/bindings/impl.c b/daemon/bindings/impl.c
index d10f4525..d9ad0774 100644
--- a/daemon/bindings/impl.c
+++ b/daemon/bindings/impl.c
@@ -2,6 +2,7 @@
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
+#include <dirent.h>
 #include <lua.h>
 #include <lauxlib.h>
 #include <string.h>
@@ -29,6 +30,29 @@ const char * lua_table_checkindices(lua_State *L, const char *keys[])
 	return NULL;
 }
 
+/** Return table listing filenames in a given directory (ls -A). */
+static int kluautil_list_dir(lua_State *L)
+{
+	lua_newtable(L); // empty table even on errors
+
+	const char *path = lua_tolstring(L, 1, NULL);
+	if (!path) return 1;
+	DIR *dir = opendir(path);
+	if (!dir) return 1;
+
+	struct dirent *entry;
+	int lua_i = 1;
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
+			lua_pushstring(L, entry->d_name);
+			lua_rawseti(L, -2, lua_i++);
+		}
+	}
+
+	closedir(dir);
+	return 1;
+}
+
 
 /* Each of these just creates the correspondingly named lua table of functions. */
 int kr_bindings_cache   (lua_State *L); /* ./cache.c   */
@@ -44,6 +68,9 @@ void kr_bindings_register(lua_State *L)
 	kr_bindings_modules(L);
 	kr_bindings_net(L);
 	kr_bindings_worker(L);
+
+	/* Finally some lua utils *written in C*, not really a binding. */
+	lua_register(L, "kluautil_list_dir", kluautil_list_dir);
 }
 
 void lua_error_p(lua_State *L, const char *fmt, ...)
diff --git a/daemon/lua/kluautil.lua b/daemon/lua/kluautil.lua
index 57912e7b..e73e952c 100644
--- a/daemon/lua/kluautil.lua
+++ b/daemon/lua/kluautil.lua
@@ -1,6 +1,5 @@
 -- SPDX-License-Identifier: GPL-3.0-or-later
 
-local ffi = require('ffi')
 local kluautil = {}
 
 -- Get length of table
@@ -28,14 +27,6 @@ function kluautil.kr_table_unpack(tab)
 	return unpack(tab, 1, tab.n)
 end
 
-ffi.cdef([[
-	typedef struct __dirstream DIR;
-	DIR *opendir(const char *name);
-	struct dirent *readdir(DIR *dirp);
-	int closedir(DIR *dirp);
-	char *strerror(int errnum);
-]])
-
 -- Fetch over HTTPS
 function kluautil.kr_https_fetch(url, out_file, ca_file)
 	local http_ok, http_request = pcall(require, 'http.request')
@@ -88,26 +79,6 @@ function kluautil.kr_https_fetch(url, out_file, ca_file)
 	return true
 end
 
--- List directory
-function kluautil.list_dir (path)
-	local results = {}
-	local dir = ffi.C.opendir(path)
-	if dir == nil then
-		return results
-	end
-
-	local entry = ffi.C.readdir(dir)
-	while entry ~= nil do
-		local entry_name = ffi.string(ffi.C.kr_dirent_name(entry))
-		if entry_name ~= '.' and entry_name ~= '..' then
-			table.insert(results, entry_name)
-		end
-		entry = ffi.C.readdir(dir)
-	end
-
-	ffi.C.closedir(dir)
-
-	return results
-end
+kluautil.list_dir = kluautil_list_dir
 
 return kluautil