xref: /freebsd/libexec/flua/lfs/lfs.c (revision b11a5709ec2b61fefb03bfdd38e2f06d2c1107c1)
1*b11a5709SKyle Evans /*-
2*b11a5709SKyle Evans  * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
3*b11a5709SKyle Evans  * All rights reserved.
4*b11a5709SKyle Evans  *
5*b11a5709SKyle Evans  * Redistribution and use in source and binary forms, with or without
6*b11a5709SKyle Evans  * modification, are permitted provided that the following conditions
7*b11a5709SKyle Evans  * are met:
8*b11a5709SKyle Evans  * 1. Redistributions of source code must retain the above copyright
9*b11a5709SKyle Evans  *    notice, this list of conditions and the following disclaimer.
10*b11a5709SKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
11*b11a5709SKyle Evans  *    notice, this list of conditions and the following disclaimer in the
12*b11a5709SKyle Evans  *    documentation and/or other materials provided with the distribution.
13*b11a5709SKyle Evans  *
14*b11a5709SKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*b11a5709SKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*b11a5709SKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*b11a5709SKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*b11a5709SKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*b11a5709SKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*b11a5709SKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*b11a5709SKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*b11a5709SKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*b11a5709SKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*b11a5709SKyle Evans  * SUCH DAMAGE.
25*b11a5709SKyle Evans  *
26*b11a5709SKyle Evans  * Portions derived from https://github.com/keplerproject/luafilesystem under
27*b11a5709SKyle Evans  * the terms of the MIT license:
28*b11a5709SKyle Evans  *
29*b11a5709SKyle Evans  * Copyright (c) 2003-2014 Kepler Project.
30*b11a5709SKyle Evans  *
31*b11a5709SKyle Evans  * Permission is hereby granted, free of charge, to any person
32*b11a5709SKyle Evans  * obtaining a copy of this software and associated documentation
33*b11a5709SKyle Evans  * files (the "Software"), to deal in the Software without
34*b11a5709SKyle Evans  * restriction, including without limitation the rights to use, copy,
35*b11a5709SKyle Evans  * modify, merge, publish, distribute, sublicense, and/or sell copies
36*b11a5709SKyle Evans  * of the Software, and to permit persons to whom the Software is
37*b11a5709SKyle Evans  * furnished to do so, subject to the following conditions:
38*b11a5709SKyle Evans  *
39*b11a5709SKyle Evans  * The above copyright notice and this permission notice shall be
40*b11a5709SKyle Evans  * included in all copies or substantial portions of the Software.
41*b11a5709SKyle Evans  *
42*b11a5709SKyle Evans  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43*b11a5709SKyle Evans  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44*b11a5709SKyle Evans  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45*b11a5709SKyle Evans  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
46*b11a5709SKyle Evans  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
47*b11a5709SKyle Evans  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
48*b11a5709SKyle Evans  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49*b11a5709SKyle Evans  * SOFTWARE.
50*b11a5709SKyle Evans  */
51*b11a5709SKyle Evans 
52*b11a5709SKyle Evans #include <sys/cdefs.h>
53*b11a5709SKyle Evans #ifndef _STANDALONE
54*b11a5709SKyle Evans #include <sys/stat.h>
55*b11a5709SKyle Evans #include <dirent.h>
56*b11a5709SKyle Evans #include <errno.h>
57*b11a5709SKyle Evans #include <unistd.h>
58*b11a5709SKyle Evans #include <stdio.h>
59*b11a5709SKyle Evans #include <string.h>
60*b11a5709SKyle Evans #endif
61*b11a5709SKyle Evans 
62*b11a5709SKyle Evans #include <lua.h>
63*b11a5709SKyle Evans #include "lauxlib.h"
64*b11a5709SKyle Evans #include "lfs.h"
65*b11a5709SKyle Evans 
66*b11a5709SKyle Evans #ifdef _STANDALONE
67*b11a5709SKyle Evans #include "lstd.h"
68*b11a5709SKyle Evans #include "lutils.h"
69*b11a5709SKyle Evans #endif
70*b11a5709SKyle Evans 
71*b11a5709SKyle Evans #include "bootstrap.h"
72*b11a5709SKyle Evans 
73*b11a5709SKyle Evans #ifndef nitems
74*b11a5709SKyle Evans #define	nitems(x)	(sizeof((x)) / sizeof((x)[0]))
75*b11a5709SKyle Evans #endif
76*b11a5709SKyle Evans 
77*b11a5709SKyle Evans /*
78*b11a5709SKyle Evans  * The goal is to emulate a subset of the upstream Lua FileSystem library, as
79*b11a5709SKyle Evans  * faithfully as possible in the boot environment.  Only APIs that seem useful
80*b11a5709SKyle Evans  * need to emulated.
81*b11a5709SKyle Evans  *
82*b11a5709SKyle Evans  * Example usage:
83*b11a5709SKyle Evans  *
84*b11a5709SKyle Evans  *     for file in lfs.dir("/boot") do
85*b11a5709SKyle Evans  *         print("\t"..file)
86*b11a5709SKyle Evans  *     end
87*b11a5709SKyle Evans  *
88*b11a5709SKyle Evans  * Prints:
89*b11a5709SKyle Evans  *     .
90*b11a5709SKyle Evans  *     ..
91*b11a5709SKyle Evans  * (etc.)
92*b11a5709SKyle Evans  *
93*b11a5709SKyle Evans  * The other available API is lfs.attributes(), which functions somewhat like
94*b11a5709SKyle Evans  * stat(2) and returns a table of values.  Example code:
95*b11a5709SKyle Evans  *
96*b11a5709SKyle Evans  *     attrs, errormsg, errorcode = lfs.attributes("/boot")
97*b11a5709SKyle Evans  *     if attrs == nil then
98*b11a5709SKyle Evans  *         print(errormsg)
99*b11a5709SKyle Evans  *         return errorcode
100*b11a5709SKyle Evans  *     end
101*b11a5709SKyle Evans  *
102*b11a5709SKyle Evans  *     for k, v in pairs(attrs) do
103*b11a5709SKyle Evans  *         print(k .. ":\t" .. v)
104*b11a5709SKyle Evans  *     end
105*b11a5709SKyle Evans  *     return 0
106*b11a5709SKyle Evans  *
107*b11a5709SKyle Evans  * Prints (on success):
108*b11a5709SKyle Evans  *     gid:    0
109*b11a5709SKyle Evans  *     change: 140737488342640
110*b11a5709SKyle Evans  *     mode:   directory
111*b11a5709SKyle Evans  *     rdev:   0
112*b11a5709SKyle Evans  *     ino:    4199275
113*b11a5709SKyle Evans  *     dev:    140737488342544
114*b11a5709SKyle Evans  *     modification:   140737488342576
115*b11a5709SKyle Evans  *     size:   512
116*b11a5709SKyle Evans  *     access: 140737488342560
117*b11a5709SKyle Evans  *     permissions:    755
118*b11a5709SKyle Evans  *     nlink:  58283552
119*b11a5709SKyle Evans  *     uid:    1001
120*b11a5709SKyle Evans  */
121*b11a5709SKyle Evans 
122*b11a5709SKyle Evans #define DIR_METATABLE "directory iterator metatable"
123*b11a5709SKyle Evans 
124*b11a5709SKyle Evans static int
lua_dir_iter_pushtype(lua_State * L __unused,const struct dirent * ent __unused)125*b11a5709SKyle Evans lua_dir_iter_pushtype(lua_State *L __unused, const struct dirent *ent __unused)
126*b11a5709SKyle Evans {
127*b11a5709SKyle Evans 
128*b11a5709SKyle Evans 	/*
129*b11a5709SKyle Evans 	 * This is a non-standard extension to luafilesystem for loader's
130*b11a5709SKyle Evans 	 * benefit.  The extra stat() calls to determine the entry type can
131*b11a5709SKyle Evans 	 * be quite expensive on some systems, so this speeds up enumeration of
132*b11a5709SKyle Evans 	 * /boot greatly by providing the type up front.
133*b11a5709SKyle Evans 	 *
134*b11a5709SKyle Evans 	 * This extension is compatible enough with luafilesystem, in that we're
135*b11a5709SKyle Evans 	 * just using an extra return value for the iterator.
136*b11a5709SKyle Evans 	 */
137*b11a5709SKyle Evans #ifdef _STANDALONE
138*b11a5709SKyle Evans 	lua_pushinteger(L, ent->d_type);
139*b11a5709SKyle Evans 	return 1;
140*b11a5709SKyle Evans #else
141*b11a5709SKyle Evans 	return 0;
142*b11a5709SKyle Evans #endif
143*b11a5709SKyle Evans }
144*b11a5709SKyle Evans 
145*b11a5709SKyle Evans static int
lua_dir_iter_next(lua_State * L)146*b11a5709SKyle Evans lua_dir_iter_next(lua_State *L)
147*b11a5709SKyle Evans {
148*b11a5709SKyle Evans 	struct dirent *entry;
149*b11a5709SKyle Evans 	DIR *dp, **dpp;
150*b11a5709SKyle Evans 
151*b11a5709SKyle Evans 	dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
152*b11a5709SKyle Evans 	dp = *dpp;
153*b11a5709SKyle Evans 	luaL_argcheck(L, dp != NULL, 1, "closed directory");
154*b11a5709SKyle Evans 
155*b11a5709SKyle Evans #ifdef _STANDALONE
156*b11a5709SKyle Evans 	entry = readdirfd(dp->fd);
157*b11a5709SKyle Evans #else
158*b11a5709SKyle Evans 	entry = readdir(dp);
159*b11a5709SKyle Evans #endif
160*b11a5709SKyle Evans 	if (entry == NULL) {
161*b11a5709SKyle Evans 		closedir(dp);
162*b11a5709SKyle Evans 		*dpp = NULL;
163*b11a5709SKyle Evans 		return 0;
164*b11a5709SKyle Evans 	}
165*b11a5709SKyle Evans 
166*b11a5709SKyle Evans 	lua_pushstring(L, entry->d_name);
167*b11a5709SKyle Evans 	return 1 + lua_dir_iter_pushtype(L, entry);
168*b11a5709SKyle Evans }
169*b11a5709SKyle Evans 
170*b11a5709SKyle Evans static int
lua_dir_iter_close(lua_State * L)171*b11a5709SKyle Evans lua_dir_iter_close(lua_State *L)
172*b11a5709SKyle Evans {
173*b11a5709SKyle Evans 	DIR *dp, **dpp;
174*b11a5709SKyle Evans 
175*b11a5709SKyle Evans 	dpp = (DIR **)lua_touserdata(L, 1);
176*b11a5709SKyle Evans 	dp = *dpp;
177*b11a5709SKyle Evans 	if (dp == NULL)
178*b11a5709SKyle Evans 		return 0;
179*b11a5709SKyle Evans 
180*b11a5709SKyle Evans 	closedir(dp);
181*b11a5709SKyle Evans 	*dpp = NULL;
182*b11a5709SKyle Evans 	return 0;
183*b11a5709SKyle Evans }
184*b11a5709SKyle Evans 
185*b11a5709SKyle Evans static int
lua_dir(lua_State * L)186*b11a5709SKyle Evans lua_dir(lua_State *L)
187*b11a5709SKyle Evans {
188*b11a5709SKyle Evans 	const char *path;
189*b11a5709SKyle Evans 	DIR *dp;
190*b11a5709SKyle Evans 
191*b11a5709SKyle Evans 	if (lua_gettop(L) != 1) {
192*b11a5709SKyle Evans 		lua_pushnil(L);
193*b11a5709SKyle Evans 		return 1;
194*b11a5709SKyle Evans 	}
195*b11a5709SKyle Evans 
196*b11a5709SKyle Evans 	path = luaL_checkstring(L, 1);
197*b11a5709SKyle Evans 	dp = opendir(path);
198*b11a5709SKyle Evans 	if (dp == NULL) {
199*b11a5709SKyle Evans 		lua_pushnil(L);
200*b11a5709SKyle Evans 		return 1;
201*b11a5709SKyle Evans 	}
202*b11a5709SKyle Evans 
203*b11a5709SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_next);
204*b11a5709SKyle Evans 	*(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
205*b11a5709SKyle Evans 	luaL_getmetatable(L, DIR_METATABLE);
206*b11a5709SKyle Evans 	lua_setmetatable(L, -2);
207*b11a5709SKyle Evans 	return 2;
208*b11a5709SKyle Evans }
209*b11a5709SKyle Evans 
210*b11a5709SKyle Evans static void
register_metatable(lua_State * L)211*b11a5709SKyle Evans register_metatable(lua_State *L)
212*b11a5709SKyle Evans {
213*b11a5709SKyle Evans 	/*
214*b11a5709SKyle Evans 	 * Create so-called metatable for iterator object returned by
215*b11a5709SKyle Evans 	 * lfs.dir().
216*b11a5709SKyle Evans 	 */
217*b11a5709SKyle Evans 	luaL_newmetatable(L, DIR_METATABLE);
218*b11a5709SKyle Evans 
219*b11a5709SKyle Evans 	lua_newtable(L);
220*b11a5709SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_next);
221*b11a5709SKyle Evans 	lua_setfield(L, -2, "next");
222*b11a5709SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_close);
223*b11a5709SKyle Evans 	lua_setfield(L, -2, "close");
224*b11a5709SKyle Evans 
225*b11a5709SKyle Evans 	/* Magically associate anonymous method table with metatable. */
226*b11a5709SKyle Evans 	lua_setfield(L, -2, "__index");
227*b11a5709SKyle Evans 	/* Implement magic destructor method */
228*b11a5709SKyle Evans 	lua_pushcfunction(L, lua_dir_iter_close);
229*b11a5709SKyle Evans 	lua_setfield(L, -2, "__gc");
230*b11a5709SKyle Evans 
231*b11a5709SKyle Evans 	lua_pop(L, 1);
232*b11a5709SKyle Evans }
233*b11a5709SKyle Evans 
234*b11a5709SKyle Evans #define PUSH_INTEGER(lname, stname)				\
235*b11a5709SKyle Evans static void							\
236*b11a5709SKyle Evans push_st_ ## lname (lua_State *L, struct stat *sb)		\
237*b11a5709SKyle Evans {								\
238*b11a5709SKyle Evans 	lua_pushinteger(L, (lua_Integer)sb->st_ ## stname);	\
239*b11a5709SKyle Evans }
PUSH_INTEGER(dev,dev)240*b11a5709SKyle Evans PUSH_INTEGER(dev, dev)
241*b11a5709SKyle Evans PUSH_INTEGER(ino, ino)
242*b11a5709SKyle Evans PUSH_INTEGER(nlink, nlink)
243*b11a5709SKyle Evans PUSH_INTEGER(uid, uid)
244*b11a5709SKyle Evans PUSH_INTEGER(gid, gid)
245*b11a5709SKyle Evans PUSH_INTEGER(rdev, rdev)
246*b11a5709SKyle Evans PUSH_INTEGER(access, atime)
247*b11a5709SKyle Evans PUSH_INTEGER(modification, mtime)
248*b11a5709SKyle Evans PUSH_INTEGER(change, ctime)
249*b11a5709SKyle Evans PUSH_INTEGER(size, size)
250*b11a5709SKyle Evans #undef PUSH_INTEGER
251*b11a5709SKyle Evans 
252*b11a5709SKyle Evans static void
253*b11a5709SKyle Evans push_st_mode(lua_State *L, struct stat *sb)
254*b11a5709SKyle Evans {
255*b11a5709SKyle Evans 	const char *mode_s;
256*b11a5709SKyle Evans 	mode_t mode;
257*b11a5709SKyle Evans 
258*b11a5709SKyle Evans 	mode = (sb->st_mode & S_IFMT);
259*b11a5709SKyle Evans 	if (S_ISREG(mode))
260*b11a5709SKyle Evans 		mode_s = "file";
261*b11a5709SKyle Evans 	else if (S_ISDIR(mode))
262*b11a5709SKyle Evans 		mode_s = "directory";
263*b11a5709SKyle Evans 	else if (S_ISLNK(mode))
264*b11a5709SKyle Evans 		mode_s = "link";
265*b11a5709SKyle Evans 	else if (S_ISSOCK(mode))
266*b11a5709SKyle Evans 		mode_s = "socket";
267*b11a5709SKyle Evans 	else if (S_ISFIFO(mode))
268*b11a5709SKyle Evans 		mode_s = "fifo";
269*b11a5709SKyle Evans 	else if (S_ISCHR(mode))
270*b11a5709SKyle Evans 		mode_s = "char device";
271*b11a5709SKyle Evans 	else if (S_ISBLK(mode))
272*b11a5709SKyle Evans 		mode_s = "block device";
273*b11a5709SKyle Evans 	else
274*b11a5709SKyle Evans 		mode_s = "other";
275*b11a5709SKyle Evans 
276*b11a5709SKyle Evans 	lua_pushstring(L, mode_s);
277*b11a5709SKyle Evans }
278*b11a5709SKyle Evans 
279*b11a5709SKyle Evans static void
push_st_permissions(lua_State * L,struct stat * sb)280*b11a5709SKyle Evans push_st_permissions(lua_State *L, struct stat *sb)
281*b11a5709SKyle Evans {
282*b11a5709SKyle Evans 	char buf[20];
283*b11a5709SKyle Evans 
284*b11a5709SKyle Evans 	/*
285*b11a5709SKyle Evans 	 * XXX
286*b11a5709SKyle Evans 	 * Could actually format as "-rwxrwxrwx" -- do we care?
287*b11a5709SKyle Evans 	 */
288*b11a5709SKyle Evans 	snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
289*b11a5709SKyle Evans 	lua_pushstring(L, buf);
290*b11a5709SKyle Evans }
291*b11a5709SKyle Evans 
292*b11a5709SKyle Evans #define PUSH_ENTRY(n)	{ #n, push_st_ ## n }
293*b11a5709SKyle Evans struct stat_members {
294*b11a5709SKyle Evans 	const char *name;
295*b11a5709SKyle Evans 	void (*push)(lua_State *, struct stat *);
296*b11a5709SKyle Evans } members[] = {
297*b11a5709SKyle Evans 	PUSH_ENTRY(mode),
298*b11a5709SKyle Evans 	PUSH_ENTRY(dev),
299*b11a5709SKyle Evans 	PUSH_ENTRY(ino),
300*b11a5709SKyle Evans 	PUSH_ENTRY(nlink),
301*b11a5709SKyle Evans 	PUSH_ENTRY(uid),
302*b11a5709SKyle Evans 	PUSH_ENTRY(gid),
303*b11a5709SKyle Evans 	PUSH_ENTRY(rdev),
304*b11a5709SKyle Evans 	PUSH_ENTRY(access),
305*b11a5709SKyle Evans 	PUSH_ENTRY(modification),
306*b11a5709SKyle Evans 	PUSH_ENTRY(change),
307*b11a5709SKyle Evans 	PUSH_ENTRY(size),
308*b11a5709SKyle Evans 	PUSH_ENTRY(permissions),
309*b11a5709SKyle Evans };
310*b11a5709SKyle Evans #undef PUSH_ENTRY
311*b11a5709SKyle Evans 
312*b11a5709SKyle Evans static int
lua_attributes(lua_State * L)313*b11a5709SKyle Evans lua_attributes(lua_State *L)
314*b11a5709SKyle Evans {
315*b11a5709SKyle Evans 	struct stat sb;
316*b11a5709SKyle Evans 	const char *path, *member;
317*b11a5709SKyle Evans 	size_t i;
318*b11a5709SKyle Evans 	int rc;
319*b11a5709SKyle Evans 
320*b11a5709SKyle Evans 	path = luaL_checkstring(L, 1);
321*b11a5709SKyle Evans 	if (path == NULL) {
322*b11a5709SKyle Evans 		lua_pushnil(L);
323*b11a5709SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
324*b11a5709SKyle Evans 		lua_pushinteger(L, EINVAL);
325*b11a5709SKyle Evans 		return 3;
326*b11a5709SKyle Evans 	}
327*b11a5709SKyle Evans 
328*b11a5709SKyle Evans 	rc = stat(path, &sb);
329*b11a5709SKyle Evans 	if (rc != 0) {
330*b11a5709SKyle Evans 		lua_pushnil(L);
331*b11a5709SKyle Evans 		lua_pushfstring(L,
332*b11a5709SKyle Evans 		    "cannot obtain information from file '%s': %s", path,
333*b11a5709SKyle Evans 		    strerror(errno));
334*b11a5709SKyle Evans 		lua_pushinteger(L, errno);
335*b11a5709SKyle Evans 		return 3;
336*b11a5709SKyle Evans 	}
337*b11a5709SKyle Evans 
338*b11a5709SKyle Evans 	if (lua_isstring(L, 2)) {
339*b11a5709SKyle Evans 		member = lua_tostring(L, 2);
340*b11a5709SKyle Evans 		for (i = 0; i < nitems(members); i++) {
341*b11a5709SKyle Evans 			if (strcmp(members[i].name, member) != 0)
342*b11a5709SKyle Evans 				continue;
343*b11a5709SKyle Evans 
344*b11a5709SKyle Evans 			members[i].push(L, &sb);
345*b11a5709SKyle Evans 			return 1;
346*b11a5709SKyle Evans 		}
347*b11a5709SKyle Evans 		return luaL_error(L, "invalid attribute name '%s'", member);
348*b11a5709SKyle Evans 	}
349*b11a5709SKyle Evans 
350*b11a5709SKyle Evans 	/* Create or reuse existing table */
351*b11a5709SKyle Evans 	lua_settop(L, 2);
352*b11a5709SKyle Evans 	if (!lua_istable(L, 2))
353*b11a5709SKyle Evans 		lua_newtable(L);
354*b11a5709SKyle Evans 
355*b11a5709SKyle Evans 	/* Export all stat data to caller */
356*b11a5709SKyle Evans 	for (i = 0; i < nitems(members); i++) {
357*b11a5709SKyle Evans 		lua_pushstring(L, members[i].name);
358*b11a5709SKyle Evans 		members[i].push(L, &sb);
359*b11a5709SKyle Evans 		lua_rawset(L, -3);
360*b11a5709SKyle Evans 	}
361*b11a5709SKyle Evans 	return 1;
362*b11a5709SKyle Evans }
363*b11a5709SKyle Evans 
364*b11a5709SKyle Evans #ifndef _STANDALONE
365*b11a5709SKyle Evans #define	lfs_mkdir_impl(path)	(mkdir((path), \
366*b11a5709SKyle Evans     S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
367*b11a5709SKyle Evans     S_IROTH | S_IXOTH))
368*b11a5709SKyle Evans 
369*b11a5709SKyle Evans static int
lua_mkdir(lua_State * L)370*b11a5709SKyle Evans lua_mkdir(lua_State *L)
371*b11a5709SKyle Evans {
372*b11a5709SKyle Evans 	const char *path;
373*b11a5709SKyle Evans 	int error, serrno;
374*b11a5709SKyle Evans 
375*b11a5709SKyle Evans 	path = luaL_checkstring(L, 1);
376*b11a5709SKyle Evans 	if (path == NULL) {
377*b11a5709SKyle Evans 		lua_pushnil(L);
378*b11a5709SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
379*b11a5709SKyle Evans 		lua_pushinteger(L, EINVAL);
380*b11a5709SKyle Evans 		return 3;
381*b11a5709SKyle Evans 	}
382*b11a5709SKyle Evans 
383*b11a5709SKyle Evans 	error = lfs_mkdir_impl(path);
384*b11a5709SKyle Evans 	if (error == -1) {
385*b11a5709SKyle Evans 		/* Save it; unclear what other libc functions may be invoked */
386*b11a5709SKyle Evans 		serrno = errno;
387*b11a5709SKyle Evans 		lua_pushnil(L);
388*b11a5709SKyle Evans 		lua_pushfstring(L, strerror(serrno));
389*b11a5709SKyle Evans 		lua_pushinteger(L, serrno);
390*b11a5709SKyle Evans 		return 3;
391*b11a5709SKyle Evans 	}
392*b11a5709SKyle Evans 
393*b11a5709SKyle Evans 	lua_pushboolean(L, 1);
394*b11a5709SKyle Evans 	return 1;
395*b11a5709SKyle Evans }
396*b11a5709SKyle Evans 
397*b11a5709SKyle Evans static int
lua_rmdir(lua_State * L)398*b11a5709SKyle Evans lua_rmdir(lua_State *L)
399*b11a5709SKyle Evans {
400*b11a5709SKyle Evans 	const char *path;
401*b11a5709SKyle Evans 	int error, serrno;
402*b11a5709SKyle Evans 
403*b11a5709SKyle Evans 	path = luaL_checkstring(L, 1);
404*b11a5709SKyle Evans 	if (path == NULL) {
405*b11a5709SKyle Evans 		lua_pushnil(L);
406*b11a5709SKyle Evans 		lua_pushfstring(L, "cannot convert first argument to string");
407*b11a5709SKyle Evans 		lua_pushinteger(L, EINVAL);
408*b11a5709SKyle Evans 		return 3;
409*b11a5709SKyle Evans 	}
410*b11a5709SKyle Evans 
411*b11a5709SKyle Evans 	error = rmdir(path);
412*b11a5709SKyle Evans 	if (error == -1) {
413*b11a5709SKyle Evans 		/* Save it; unclear what other libc functions may be invoked */
414*b11a5709SKyle Evans 		serrno = errno;
415*b11a5709SKyle Evans 		lua_pushnil(L);
416*b11a5709SKyle Evans 		lua_pushfstring(L, strerror(serrno));
417*b11a5709SKyle Evans 		lua_pushinteger(L, serrno);
418*b11a5709SKyle Evans 		return 3;
419*b11a5709SKyle Evans 	}
420*b11a5709SKyle Evans 
421*b11a5709SKyle Evans 	lua_pushboolean(L, 1);
422*b11a5709SKyle Evans 	return 1;
423*b11a5709SKyle Evans }
424*b11a5709SKyle Evans #endif
425*b11a5709SKyle Evans 
426*b11a5709SKyle Evans #define REG_SIMPLE(n)	{ #n, lua_ ## n }
427*b11a5709SKyle Evans static const struct luaL_Reg fslib[] = {
428*b11a5709SKyle Evans 	REG_SIMPLE(attributes),
429*b11a5709SKyle Evans 	REG_SIMPLE(dir),
430*b11a5709SKyle Evans #ifndef _STANDALONE
431*b11a5709SKyle Evans 	REG_SIMPLE(mkdir),
432*b11a5709SKyle Evans 	REG_SIMPLE(rmdir),
433*b11a5709SKyle Evans #endif
434*b11a5709SKyle Evans 	{ NULL, NULL },
435*b11a5709SKyle Evans };
436*b11a5709SKyle Evans #undef REG_SIMPLE
437*b11a5709SKyle Evans 
438*b11a5709SKyle Evans int
luaopen_lfs(lua_State * L)439*b11a5709SKyle Evans luaopen_lfs(lua_State *L)
440*b11a5709SKyle Evans {
441*b11a5709SKyle Evans 	register_metatable(L);
442*b11a5709SKyle Evans 	luaL_newlib(L, fslib);
443*b11a5709SKyle Evans #ifdef _STANDALONE
444*b11a5709SKyle Evans 	/* Non-standard extension for loader, used with lfs.dir(). */
445*b11a5709SKyle Evans 	lua_pushinteger(L, DT_DIR);
446*b11a5709SKyle Evans 	lua_setfield(L, -2, "DT_DIR");
447*b11a5709SKyle Evans #endif
448*b11a5709SKyle Evans 	return 1;
449*b11a5709SKyle Evans }
450*b11a5709SKyle Evans 
451*b11a5709SKyle Evans #ifndef _STANDALONE
452*b11a5709SKyle Evans FLUA_MODULE(lfs);
453*b11a5709SKyle Evans #endif
454