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