xref: /freebsd/libexec/flua/modules/lfs.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1506f3640SKyle Evans /*-
2506f3640SKyle Evans  * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
3506f3640SKyle Evans  * All rights reserved.
4506f3640SKyle Evans  *
5506f3640SKyle Evans  * Redistribution and use in source and binary forms, with or without
6506f3640SKyle Evans  * modification, are permitted provided that the following conditions
7506f3640SKyle Evans  * are met:
8506f3640SKyle Evans  * 1. Redistributions of source code must retain the above copyright
9506f3640SKyle Evans  *    notice, this list of conditions and the following disclaimer.
10506f3640SKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
11506f3640SKyle Evans  *    notice, this list of conditions and the following disclaimer in the
12506f3640SKyle Evans  *    documentation and/or other materials provided with the distribution.
13506f3640SKyle Evans  *
14506f3640SKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15506f3640SKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16506f3640SKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17506f3640SKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18506f3640SKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19506f3640SKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20506f3640SKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21506f3640SKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22506f3640SKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23506f3640SKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24506f3640SKyle Evans  * SUCH DAMAGE.
25506f3640SKyle Evans  *
26506f3640SKyle Evans  * Portions derived from https://github.com/keplerproject/luafilesystem under
27506f3640SKyle Evans  * the terms of the MIT license:
28506f3640SKyle Evans  *
29506f3640SKyle Evans  * Copyright (c) 2003-2014 Kepler Project.
30506f3640SKyle Evans  *
31506f3640SKyle Evans  * Permission is hereby granted, free of charge, to any person
32506f3640SKyle Evans  * obtaining a copy of this software and associated documentation
33506f3640SKyle Evans  * files (the "Software"), to deal in the Software without
34506f3640SKyle Evans  * restriction, including without limitation the rights to use, copy,
35506f3640SKyle Evans  * modify, merge, publish, distribute, sublicense, and/or sell copies
36506f3640SKyle Evans  * of the Software, and to permit persons to whom the Software is
37506f3640SKyle Evans  * furnished to do so, subject to the following conditions:
38506f3640SKyle Evans  *
39506f3640SKyle Evans  * The above copyright notice and this permission notice shall be
40506f3640SKyle Evans  * included in all copies or substantial portions of the Software.
41506f3640SKyle Evans  *
42506f3640SKyle Evans  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43506f3640SKyle Evans  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44506f3640SKyle Evans  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45506f3640SKyle Evans  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
46506f3640SKyle Evans  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
47506f3640SKyle Evans  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
48506f3640SKyle Evans  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49506f3640SKyle Evans  * SOFTWARE.
50506f3640SKyle Evans  */
51506f3640SKyle Evans 
52506f3640SKyle Evans #include <sys/cdefs.h>
53506f3640SKyle Evans #ifndef _STANDALONE
54506f3640SKyle Evans #include <sys/stat.h>
55506f3640SKyle Evans #include <dirent.h>
56506f3640SKyle Evans #include <errno.h>
57506f3640SKyle Evans #include <unistd.h>
58506f3640SKyle Evans #include <stdio.h>
59506f3640SKyle Evans #include <string.h>
60506f3640SKyle Evans #endif
61506f3640SKyle Evans 
62506f3640SKyle Evans #include <lua.h>
63506f3640SKyle Evans #include "lauxlib.h"
64506f3640SKyle Evans #include "lfs.h"
65506f3640SKyle Evans 
66506f3640SKyle Evans #ifdef _STANDALONE
67506f3640SKyle Evans #include "lstd.h"
68506f3640SKyle Evans #include "lutils.h"
69506f3640SKyle Evans #include "bootstrap.h"
70506f3640SKyle Evans #endif
71506f3640SKyle Evans 
72506f3640SKyle Evans #ifndef nitems
73506f3640SKyle Evans #define	nitems(x)	(sizeof((x)) / sizeof((x)[0]))
74506f3640SKyle Evans #endif
75506f3640SKyle Evans 
76506f3640SKyle Evans /*
77506f3640SKyle Evans  * The goal is to emulate a subset of the upstream Lua FileSystem library, as
78506f3640SKyle Evans  * faithfully as possible in the boot environment.  Only APIs that seem useful
79506f3640SKyle Evans  * need to emulated.
80506f3640SKyle Evans  *
81506f3640SKyle Evans  * Example usage:
82506f3640SKyle Evans  *
83506f3640SKyle Evans  *     for file in lfs.dir("/boot") do
84506f3640SKyle Evans  *         print("\t"..file)
85506f3640SKyle Evans  *     end
86506f3640SKyle Evans  *
87506f3640SKyle Evans  * Prints:
88506f3640SKyle Evans  *     .
89506f3640SKyle Evans  *     ..
90506f3640SKyle Evans  * (etc.)
91506f3640SKyle Evans  *
92506f3640SKyle Evans  * The other available API is lfs.attributes(), which functions somewhat like
93506f3640SKyle Evans  * stat(2) and returns a table of values.  Example code:
94506f3640SKyle Evans  *
95506f3640SKyle Evans  *     attrs, errormsg, errorcode = lfs.attributes("/boot")
96506f3640SKyle Evans  *     if attrs == nil then
97506f3640SKyle Evans  *         print(errormsg)
98506f3640SKyle Evans  *         return errorcode
99506f3640SKyle Evans  *     end
100506f3640SKyle Evans  *
101506f3640SKyle Evans  *     for k, v in pairs(attrs) do
102506f3640SKyle Evans  *         print(k .. ":\t" .. v)
103506f3640SKyle Evans  *     end
104506f3640SKyle Evans  *     return 0
105506f3640SKyle Evans  *
106506f3640SKyle Evans  * Prints (on success):
107506f3640SKyle Evans  *     gid:    0
108506f3640SKyle Evans  *     change: 140737488342640
109506f3640SKyle Evans  *     mode:   directory
110506f3640SKyle Evans  *     rdev:   0
111506f3640SKyle Evans  *     ino:    4199275
112506f3640SKyle Evans  *     dev:    140737488342544
113506f3640SKyle Evans  *     modification:   140737488342576
114506f3640SKyle Evans  *     size:   512
115506f3640SKyle Evans  *     access: 140737488342560
116506f3640SKyle Evans  *     permissions:    755
117506f3640SKyle Evans  *     nlink:  58283552
118506f3640SKyle Evans  *     uid:    1001
119506f3640SKyle Evans  */
120506f3640SKyle Evans 
121506f3640SKyle Evans #define DIR_METATABLE "directory iterator metatable"
122506f3640SKyle Evans 
123506f3640SKyle Evans static int
lua_dir_iter_pushtype(lua_State * L __unused,const struct dirent * ent __unused)124*e25ee296SKyle Evans lua_dir_iter_pushtype(lua_State *L __unused, const struct dirent *ent __unused)
125*e25ee296SKyle Evans {
126*e25ee296SKyle Evans 
127*e25ee296SKyle Evans 	/*
128*e25ee296SKyle Evans 	 * This is a non-standard extension to luafilesystem for loader's
129*e25ee296SKyle Evans 	 * benefit.  The extra stat() calls to determine the entry type can
130*e25ee296SKyle Evans 	 * be quite expensive on some systems, so this speeds up enumeration of
131*e25ee296SKyle Evans 	 * /boot greatly by providing the type up front.
132*e25ee296SKyle Evans 	 *
133*e25ee296SKyle Evans 	 * This extension is compatible enough with luafilesystem, in that we're
134*e25ee296SKyle Evans 	 * just using an extra return value for the iterator.
135*e25ee296SKyle Evans 	 */
136*e25ee296SKyle Evans #ifdef _STANDALONE
137*e25ee296SKyle Evans 	lua_pushinteger(L, ent->d_type);
138*e25ee296SKyle Evans 	return 1;
139*e25ee296SKyle Evans #else
140*e25ee296SKyle Evans 	return 0;
141*e25ee296SKyle Evans #endif
142*e25ee296SKyle Evans }
143*e25ee296SKyle Evans 
144*e25ee296SKyle Evans static int
lua_dir_iter_next(lua_State * L)145506f3640SKyle Evans lua_dir_iter_next(lua_State *L)
146506f3640SKyle Evans {
147506f3640SKyle Evans 	struct dirent *entry;
148506f3640SKyle Evans 	DIR *dp, **dpp;
149506f3640SKyle Evans 
150506f3640SKyle Evans 	dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
151506f3640SKyle Evans 	dp = *dpp;
152506f3640SKyle Evans 	luaL_argcheck(L, dp != NULL, 1, "closed directory");
153506f3640SKyle Evans 
154506f3640SKyle Evans #ifdef _STANDALONE
155506f3640SKyle Evans 	entry = readdirfd(dp->fd);
156506f3640SKyle Evans #else
157506f3640SKyle Evans 	entry = readdir(dp);
158506f3640SKyle Evans #endif
159506f3640SKyle Evans 	if (entry == NULL) {
160506f3640SKyle Evans 		closedir(dp);
161506f3640SKyle Evans 		*dpp = NULL;
162506f3640SKyle Evans 		return 0;
163506f3640SKyle Evans 	}
164506f3640SKyle Evans 
165506f3640SKyle Evans 	lua_pushstring(L, entry->d_name);
166*e25ee296SKyle Evans 	return 1 + lua_dir_iter_pushtype(L, entry);
167506f3640SKyle Evans }
168506f3640SKyle Evans 
169506f3640SKyle Evans static int
lua_dir_iter_close(lua_State * L)170506f3640SKyle Evans lua_dir_iter_close(lua_State *L)
171506f3640SKyle Evans {
172506f3640SKyle Evans 	DIR *dp, **dpp;
173506f3640SKyle Evans 
174506f3640SKyle Evans 	dpp = (DIR **)lua_touserdata(L, 1);
175506f3640SKyle Evans 	dp = *dpp;
176506f3640SKyle Evans 	if (dp == NULL)
177506f3640SKyle Evans 		return 0;
178506f3640SKyle Evans 
179506f3640SKyle Evans 	closedir(dp);
180506f3640SKyle Evans 	*dpp = NULL;
181506f3640SKyle Evans 	return 0;
182506f3640SKyle Evans }
183506f3640SKyle Evans 
184506f3640SKyle Evans static int
lua_dir(lua_State * L)185506f3640SKyle Evans lua_dir(lua_State *L)
186506f3640SKyle Evans {
187506f3640SKyle Evans 	const char *path;
188506f3640SKyle Evans 	DIR *dp;
189506f3640SKyle Evans 
190506f3640SKyle Evans 	if (lua_gettop(L) != 1) {
191506f3640SKyle Evans 		lua_pushnil(L);
192506f3640SKyle Evans 		return 1;
193506f3640SKyle Evans 	}
194506f3640SKyle Evans 
195506f3640SKyle Evans 	path = luaL_checkstring(L, 1);
196506f3640SKyle Evans 	dp = opendir(path);
197506f3640SKyle Evans 	if (dp == NULL) {
198506f3640SKyle Evans 		lua_pushnil(L);
199506f3640SKyle Evans 		return 1;
200506f3640SKyle Evans 	}
201506f3640SKyle Evans 
202506f3640SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_next);
203506f3640SKyle Evans 	*(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
204506f3640SKyle Evans 	luaL_getmetatable(L, DIR_METATABLE);
205506f3640SKyle Evans 	lua_setmetatable(L, -2);
206506f3640SKyle Evans 	return 2;
207506f3640SKyle Evans }
208506f3640SKyle Evans 
209506f3640SKyle Evans static void
register_metatable(lua_State * L)210506f3640SKyle Evans register_metatable(lua_State *L)
211506f3640SKyle Evans {
212506f3640SKyle Evans 	/*
213506f3640SKyle Evans 	 * Create so-called metatable for iterator object returned by
214506f3640SKyle Evans 	 * lfs.dir().
215506f3640SKyle Evans 	 */
216506f3640SKyle Evans 	luaL_newmetatable(L, DIR_METATABLE);
217506f3640SKyle Evans 
218506f3640SKyle Evans 	lua_newtable(L);
219506f3640SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_next);
220506f3640SKyle Evans 	lua_setfield(L, -2, "next");
221506f3640SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_close);
222506f3640SKyle Evans 	lua_setfield(L, -2, "close");
223506f3640SKyle Evans 
224506f3640SKyle Evans 	/* Magically associate anonymous method table with metatable. */
225506f3640SKyle Evans 	lua_setfield(L, -2, "__index");
226506f3640SKyle Evans 	/* Implement magic destructor method */
227506f3640SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_close);
228506f3640SKyle Evans 	lua_setfield(L, -2, "__gc");
229506f3640SKyle Evans 
230506f3640SKyle Evans 	lua_pop(L, 1);
231506f3640SKyle Evans }
232506f3640SKyle Evans 
233506f3640SKyle Evans #define PUSH_INTEGER(lname, stname)				\
234506f3640SKyle Evans static void							\
235506f3640SKyle Evans push_st_ ## lname (lua_State *L, struct stat *sb)		\
236506f3640SKyle Evans {								\
237506f3640SKyle Evans 	lua_pushinteger(L, (lua_Integer)sb->st_ ## stname);	\
238506f3640SKyle Evans }
PUSH_INTEGER(dev,dev)239506f3640SKyle Evans PUSH_INTEGER(dev, dev)
240506f3640SKyle Evans PUSH_INTEGER(ino, ino)
241506f3640SKyle Evans PUSH_INTEGER(nlink, nlink)
242506f3640SKyle Evans PUSH_INTEGER(uid, uid)
243506f3640SKyle Evans PUSH_INTEGER(gid, gid)
244506f3640SKyle Evans PUSH_INTEGER(rdev, rdev)
245506f3640SKyle Evans PUSH_INTEGER(access, atime)
246506f3640SKyle Evans PUSH_INTEGER(modification, mtime)
247506f3640SKyle Evans PUSH_INTEGER(change, ctime)
248506f3640SKyle Evans PUSH_INTEGER(size, size)
249506f3640SKyle Evans #undef PUSH_INTEGER
250506f3640SKyle Evans 
251506f3640SKyle Evans static void
252506f3640SKyle Evans push_st_mode(lua_State *L, struct stat *sb)
253506f3640SKyle Evans {
254506f3640SKyle Evans 	const char *mode_s;
255506f3640SKyle Evans 	mode_t mode;
256506f3640SKyle Evans 
257506f3640SKyle Evans 	mode = (sb->st_mode & S_IFMT);
258506f3640SKyle Evans 	if (S_ISREG(mode))
259506f3640SKyle Evans 		mode_s = "file";
260506f3640SKyle Evans 	else if (S_ISDIR(mode))
261506f3640SKyle Evans 		mode_s = "directory";
262506f3640SKyle Evans 	else if (S_ISLNK(mode))
263506f3640SKyle Evans 		mode_s = "link";
264506f3640SKyle Evans 	else if (S_ISSOCK(mode))
265506f3640SKyle Evans 		mode_s = "socket";
266506f3640SKyle Evans 	else if (S_ISFIFO(mode))
267506f3640SKyle Evans 		mode_s = "fifo";
268506f3640SKyle Evans 	else if (S_ISCHR(mode))
269506f3640SKyle Evans 		mode_s = "char device";
270506f3640SKyle Evans 	else if (S_ISBLK(mode))
271506f3640SKyle Evans 		mode_s = "block device";
272506f3640SKyle Evans 	else
273506f3640SKyle Evans 		mode_s = "other";
274506f3640SKyle Evans 
275506f3640SKyle Evans 	lua_pushstring(L, mode_s);
276506f3640SKyle Evans }
277506f3640SKyle Evans 
278506f3640SKyle Evans static void
push_st_permissions(lua_State * L,struct stat * sb)279506f3640SKyle Evans push_st_permissions(lua_State *L, struct stat *sb)
280506f3640SKyle Evans {
281506f3640SKyle Evans 	char buf[20];
282506f3640SKyle Evans 
283506f3640SKyle Evans 	/*
284506f3640SKyle Evans 	 * XXX
285506f3640SKyle Evans 	 * Could actually format as "-rwxrwxrwx" -- do we care?
286506f3640SKyle Evans 	 */
287506f3640SKyle Evans 	snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
288506f3640SKyle Evans 	lua_pushstring(L, buf);
289506f3640SKyle Evans }
290506f3640SKyle Evans 
291506f3640SKyle Evans #define PUSH_ENTRY(n)	{ #n, push_st_ ## n }
292506f3640SKyle Evans struct stat_members {
293506f3640SKyle Evans 	const char *name;
294506f3640SKyle Evans 	void (*push)(lua_State *, struct stat *);
295506f3640SKyle Evans } members[] = {
296506f3640SKyle Evans 	PUSH_ENTRY(mode),
297506f3640SKyle Evans 	PUSH_ENTRY(dev),
298506f3640SKyle Evans 	PUSH_ENTRY(ino),
299506f3640SKyle Evans 	PUSH_ENTRY(nlink),
300506f3640SKyle Evans 	PUSH_ENTRY(uid),
301506f3640SKyle Evans 	PUSH_ENTRY(gid),
302506f3640SKyle Evans 	PUSH_ENTRY(rdev),
303506f3640SKyle Evans 	PUSH_ENTRY(access),
304506f3640SKyle Evans 	PUSH_ENTRY(modification),
305506f3640SKyle Evans 	PUSH_ENTRY(change),
306506f3640SKyle Evans 	PUSH_ENTRY(size),
307506f3640SKyle Evans 	PUSH_ENTRY(permissions),
308506f3640SKyle Evans };
309506f3640SKyle Evans #undef PUSH_ENTRY
310506f3640SKyle Evans 
311506f3640SKyle Evans static int
lua_attributes(lua_State * L)312506f3640SKyle Evans lua_attributes(lua_State *L)
313506f3640SKyle Evans {
314506f3640SKyle Evans 	struct stat sb;
315506f3640SKyle Evans 	const char *path, *member;
316506f3640SKyle Evans 	size_t i;
317506f3640SKyle Evans 	int rc;
318506f3640SKyle Evans 
319506f3640SKyle Evans 	path = luaL_checkstring(L, 1);
320506f3640SKyle Evans 	if (path == NULL) {
321506f3640SKyle Evans 		lua_pushnil(L);
322506f3640SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
323506f3640SKyle Evans 		lua_pushinteger(L, EINVAL);
324506f3640SKyle Evans 		return 3;
325506f3640SKyle Evans 	}
326506f3640SKyle Evans 
327506f3640SKyle Evans 	rc = stat(path, &sb);
328506f3640SKyle Evans 	if (rc != 0) {
329506f3640SKyle Evans 		lua_pushnil(L);
330506f3640SKyle Evans 		lua_pushfstring(L,
331506f3640SKyle Evans 		    "cannot obtain information from file '%s': %s", path,
332506f3640SKyle Evans 		    strerror(errno));
333506f3640SKyle Evans 		lua_pushinteger(L, errno);
334506f3640SKyle Evans 		return 3;
335506f3640SKyle Evans 	}
336506f3640SKyle Evans 
337506f3640SKyle Evans 	if (lua_isstring(L, 2)) {
338506f3640SKyle Evans 		member = lua_tostring(L, 2);
339506f3640SKyle Evans 		for (i = 0; i < nitems(members); i++) {
340506f3640SKyle Evans 			if (strcmp(members[i].name, member) != 0)
341506f3640SKyle Evans 				continue;
342506f3640SKyle Evans 
343506f3640SKyle Evans 			members[i].push(L, &sb);
344506f3640SKyle Evans 			return 1;
345506f3640SKyle Evans 		}
346506f3640SKyle Evans 		return luaL_error(L, "invalid attribute name '%s'", member);
347506f3640SKyle Evans 	}
348506f3640SKyle Evans 
349506f3640SKyle Evans 	/* Create or reuse existing table */
350506f3640SKyle Evans 	lua_settop(L, 2);
351506f3640SKyle Evans 	if (!lua_istable(L, 2))
352506f3640SKyle Evans 		lua_newtable(L);
353506f3640SKyle Evans 
354506f3640SKyle Evans 	/* Export all stat data to caller */
355506f3640SKyle Evans 	for (i = 0; i < nitems(members); i++) {
356506f3640SKyle Evans 		lua_pushstring(L, members[i].name);
357506f3640SKyle Evans 		members[i].push(L, &sb);
358506f3640SKyle Evans 		lua_rawset(L, -3);
359506f3640SKyle Evans 	}
360506f3640SKyle Evans 	return 1;
361506f3640SKyle Evans }
362506f3640SKyle Evans 
363506f3640SKyle Evans #ifndef _STANDALONE
364506f3640SKyle Evans #define	lfs_mkdir_impl(path)	(mkdir((path), \
365506f3640SKyle Evans     S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
366506f3640SKyle Evans     S_IROTH | S_IXOTH))
367506f3640SKyle Evans 
368506f3640SKyle Evans static int
lua_mkdir(lua_State * L)369506f3640SKyle Evans lua_mkdir(lua_State *L)
370506f3640SKyle Evans {
371506f3640SKyle Evans 	const char *path;
372506f3640SKyle Evans 	int error, serrno;
373506f3640SKyle Evans 
374506f3640SKyle Evans 	path = luaL_checkstring(L, 1);
375506f3640SKyle Evans 	if (path == NULL) {
376506f3640SKyle Evans 		lua_pushnil(L);
377506f3640SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
378506f3640SKyle Evans 		lua_pushinteger(L, EINVAL);
379506f3640SKyle Evans 		return 3;
380506f3640SKyle Evans 	}
381506f3640SKyle Evans 
382506f3640SKyle Evans 	error = lfs_mkdir_impl(path);
383506f3640SKyle Evans 	if (error == -1) {
384506f3640SKyle Evans 		/* Save it; unclear what other libc functions may be invoked */
385506f3640SKyle Evans 		serrno = errno;
386506f3640SKyle Evans 		lua_pushnil(L);
387506f3640SKyle Evans 		lua_pushfstring(L, strerror(serrno));
388506f3640SKyle Evans 		lua_pushinteger(L, serrno);
389506f3640SKyle Evans 		return 3;
390506f3640SKyle Evans 	}
391506f3640SKyle Evans 
392506f3640SKyle Evans 	lua_pushboolean(L, 1);
393506f3640SKyle Evans 	return 1;
394506f3640SKyle Evans }
395506f3640SKyle Evans 
396506f3640SKyle Evans static int
lua_rmdir(lua_State * L)397506f3640SKyle Evans lua_rmdir(lua_State *L)
398506f3640SKyle Evans {
399506f3640SKyle Evans 	const char *path;
400506f3640SKyle Evans 	int error, serrno;
401506f3640SKyle Evans 
402506f3640SKyle Evans 	path = luaL_checkstring(L, 1);
403506f3640SKyle Evans 	if (path == NULL) {
404506f3640SKyle Evans 		lua_pushnil(L);
405506f3640SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
406506f3640SKyle Evans 		lua_pushinteger(L, EINVAL);
407506f3640SKyle Evans 		return 3;
408506f3640SKyle Evans 	}
409506f3640SKyle Evans 
410506f3640SKyle Evans 	error = rmdir(path);
411506f3640SKyle Evans 	if (error == -1) {
412506f3640SKyle Evans 		/* Save it; unclear what other libc functions may be invoked */
413506f3640SKyle Evans 		serrno = errno;
414506f3640SKyle Evans 		lua_pushnil(L);
415506f3640SKyle Evans 		lua_pushfstring(L, strerror(serrno));
416506f3640SKyle Evans 		lua_pushinteger(L, serrno);
417506f3640SKyle Evans 		return 3;
418506f3640SKyle Evans 	}
419506f3640SKyle Evans 
420506f3640SKyle Evans 	lua_pushboolean(L, 1);
421506f3640SKyle Evans 	return 1;
422506f3640SKyle Evans }
423506f3640SKyle Evans #endif
424506f3640SKyle Evans 
425506f3640SKyle Evans #define REG_SIMPLE(n)	{ #n, lua_ ## n }
426506f3640SKyle Evans static const struct luaL_Reg fslib[] = {
427506f3640SKyle Evans 	REG_SIMPLE(attributes),
428506f3640SKyle Evans 	REG_SIMPLE(dir),
429506f3640SKyle Evans #ifndef _STANDALONE
430506f3640SKyle Evans 	REG_SIMPLE(mkdir),
431506f3640SKyle Evans 	REG_SIMPLE(rmdir),
432506f3640SKyle Evans #endif
433506f3640SKyle Evans 	{ NULL, NULL },
434506f3640SKyle Evans };
435506f3640SKyle Evans #undef REG_SIMPLE
436506f3640SKyle Evans 
437506f3640SKyle Evans int
luaopen_lfs(lua_State * L)438506f3640SKyle Evans luaopen_lfs(lua_State *L)
439506f3640SKyle Evans {
440506f3640SKyle Evans 	register_metatable(L);
441506f3640SKyle Evans 	luaL_newlib(L, fslib);
442*e25ee296SKyle Evans #ifdef _STANDALONE
443*e25ee296SKyle Evans 	/* Non-standard extension for loader, used with lfs.dir(). */
444*e25ee296SKyle Evans 	lua_pushinteger(L, DT_DIR);
445*e25ee296SKyle Evans 	lua_setfield(L, -2, "DT_DIR");
446*e25ee296SKyle Evans #endif
447506f3640SKyle Evans 	return 1;
448506f3640SKyle Evans }
449