Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/flua/modules/lfs.c
35071 views
1
/*-
2
* Copyright (c) 2018 Conrad Meyer <[email protected]>
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
#ifndef _STANDALONE
54
#include <sys/stat.h>
55
#include <dirent.h>
56
#include <errno.h>
57
#include <unistd.h>
58
#include <stdio.h>
59
#include <string.h>
60
#endif
61
62
#include <lua.h>
63
#include "lauxlib.h"
64
#include "lfs.h"
65
66
#ifdef _STANDALONE
67
#include "lstd.h"
68
#include "lutils.h"
69
#include "bootstrap.h"
70
#endif
71
72
#ifndef nitems
73
#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
74
#endif
75
76
/*
77
* The goal is to emulate a subset of the upstream Lua FileSystem library, as
78
* faithfully as possible in the boot environment. Only APIs that seem useful
79
* need to emulated.
80
*
81
* Example usage:
82
*
83
* for file in lfs.dir("/boot") do
84
* print("\t"..file)
85
* end
86
*
87
* Prints:
88
* .
89
* ..
90
* (etc.)
91
*
92
* The other available API is lfs.attributes(), which functions somewhat like
93
* stat(2) and returns a table of values. Example code:
94
*
95
* attrs, errormsg, errorcode = lfs.attributes("/boot")
96
* if attrs == nil then
97
* print(errormsg)
98
* return errorcode
99
* end
100
*
101
* for k, v in pairs(attrs) do
102
* print(k .. ":\t" .. v)
103
* end
104
* return 0
105
*
106
* Prints (on success):
107
* gid: 0
108
* change: 140737488342640
109
* mode: directory
110
* rdev: 0
111
* ino: 4199275
112
* dev: 140737488342544
113
* modification: 140737488342576
114
* size: 512
115
* access: 140737488342560
116
* permissions: 755
117
* nlink: 58283552
118
* uid: 1001
119
*/
120
121
#define DIR_METATABLE "directory iterator metatable"
122
123
static int
124
lua_dir_iter_pushtype(lua_State *L __unused, const struct dirent *ent __unused)
125
{
126
127
/*
128
* This is a non-standard extension to luafilesystem for loader's
129
* benefit. The extra stat() calls to determine the entry type can
130
* be quite expensive on some systems, so this speeds up enumeration of
131
* /boot greatly by providing the type up front.
132
*
133
* This extension is compatible enough with luafilesystem, in that we're
134
* just using an extra return value for the iterator.
135
*/
136
#ifdef _STANDALONE
137
lua_pushinteger(L, ent->d_type);
138
return 1;
139
#else
140
return 0;
141
#endif
142
}
143
144
static int
145
lua_dir_iter_next(lua_State *L)
146
{
147
struct dirent *entry;
148
DIR *dp, **dpp;
149
150
dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
151
dp = *dpp;
152
luaL_argcheck(L, dp != NULL, 1, "closed directory");
153
154
#ifdef _STANDALONE
155
entry = readdirfd(dp->fd);
156
#else
157
entry = readdir(dp);
158
#endif
159
if (entry == NULL) {
160
closedir(dp);
161
*dpp = NULL;
162
return 0;
163
}
164
165
lua_pushstring(L, entry->d_name);
166
return 1 + lua_dir_iter_pushtype(L, entry);
167
}
168
169
static int
170
lua_dir_iter_close(lua_State *L)
171
{
172
DIR *dp, **dpp;
173
174
dpp = (DIR **)lua_touserdata(L, 1);
175
dp = *dpp;
176
if (dp == NULL)
177
return 0;
178
179
closedir(dp);
180
*dpp = NULL;
181
return 0;
182
}
183
184
static int
185
lua_dir(lua_State *L)
186
{
187
const char *path;
188
DIR *dp;
189
190
if (lua_gettop(L) != 1) {
191
lua_pushnil(L);
192
return 1;
193
}
194
195
path = luaL_checkstring(L, 1);
196
dp = opendir(path);
197
if (dp == NULL) {
198
lua_pushnil(L);
199
return 1;
200
}
201
202
lua_pushcfunction(L, lua_dir_iter_next);
203
*(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
204
luaL_getmetatable(L, DIR_METATABLE);
205
lua_setmetatable(L, -2);
206
return 2;
207
}
208
209
static void
210
register_metatable(lua_State *L)
211
{
212
/*
213
* Create so-called metatable for iterator object returned by
214
* lfs.dir().
215
*/
216
luaL_newmetatable(L, DIR_METATABLE);
217
218
lua_newtable(L);
219
lua_pushcfunction(L, lua_dir_iter_next);
220
lua_setfield(L, -2, "next");
221
lua_pushcfunction(L, lua_dir_iter_close);
222
lua_setfield(L, -2, "close");
223
224
/* Magically associate anonymous method table with metatable. */
225
lua_setfield(L, -2, "__index");
226
/* Implement magic destructor method */
227
lua_pushcfunction(L, lua_dir_iter_close);
228
lua_setfield(L, -2, "__gc");
229
230
lua_pop(L, 1);
231
}
232
233
#define PUSH_INTEGER(lname, stname) \
234
static void \
235
push_st_ ## lname (lua_State *L, struct stat *sb) \
236
{ \
237
lua_pushinteger(L, (lua_Integer)sb->st_ ## stname); \
238
}
239
PUSH_INTEGER(dev, dev)
240
PUSH_INTEGER(ino, ino)
241
PUSH_INTEGER(nlink, nlink)
242
PUSH_INTEGER(uid, uid)
243
PUSH_INTEGER(gid, gid)
244
PUSH_INTEGER(rdev, rdev)
245
PUSH_INTEGER(access, atime)
246
PUSH_INTEGER(modification, mtime)
247
PUSH_INTEGER(change, ctime)
248
PUSH_INTEGER(size, size)
249
#undef PUSH_INTEGER
250
251
static void
252
push_st_mode(lua_State *L, struct stat *sb)
253
{
254
const char *mode_s;
255
mode_t mode;
256
257
mode = (sb->st_mode & S_IFMT);
258
if (S_ISREG(mode))
259
mode_s = "file";
260
else if (S_ISDIR(mode))
261
mode_s = "directory";
262
else if (S_ISLNK(mode))
263
mode_s = "link";
264
else if (S_ISSOCK(mode))
265
mode_s = "socket";
266
else if (S_ISFIFO(mode))
267
mode_s = "fifo";
268
else if (S_ISCHR(mode))
269
mode_s = "char device";
270
else if (S_ISBLK(mode))
271
mode_s = "block device";
272
else
273
mode_s = "other";
274
275
lua_pushstring(L, mode_s);
276
}
277
278
static void
279
push_st_permissions(lua_State *L, struct stat *sb)
280
{
281
char buf[20];
282
283
/*
284
* XXX
285
* Could actually format as "-rwxrwxrwx" -- do we care?
286
*/
287
snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
288
lua_pushstring(L, buf);
289
}
290
291
#define PUSH_ENTRY(n) { #n, push_st_ ## n }
292
struct stat_members {
293
const char *name;
294
void (*push)(lua_State *, struct stat *);
295
} members[] = {
296
PUSH_ENTRY(mode),
297
PUSH_ENTRY(dev),
298
PUSH_ENTRY(ino),
299
PUSH_ENTRY(nlink),
300
PUSH_ENTRY(uid),
301
PUSH_ENTRY(gid),
302
PUSH_ENTRY(rdev),
303
PUSH_ENTRY(access),
304
PUSH_ENTRY(modification),
305
PUSH_ENTRY(change),
306
PUSH_ENTRY(size),
307
PUSH_ENTRY(permissions),
308
};
309
#undef PUSH_ENTRY
310
311
static int
312
lua_attributes(lua_State *L)
313
{
314
struct stat sb;
315
const char *path, *member;
316
size_t i;
317
int rc;
318
319
path = luaL_checkstring(L, 1);
320
if (path == NULL) {
321
lua_pushnil(L);
322
lua_pushfstring(L, "cannot convert first argument to string");
323
lua_pushinteger(L, EINVAL);
324
return 3;
325
}
326
327
rc = stat(path, &sb);
328
if (rc != 0) {
329
lua_pushnil(L);
330
lua_pushfstring(L,
331
"cannot obtain information from file '%s': %s", path,
332
strerror(errno));
333
lua_pushinteger(L, errno);
334
return 3;
335
}
336
337
if (lua_isstring(L, 2)) {
338
member = lua_tostring(L, 2);
339
for (i = 0; i < nitems(members); i++) {
340
if (strcmp(members[i].name, member) != 0)
341
continue;
342
343
members[i].push(L, &sb);
344
return 1;
345
}
346
return luaL_error(L, "invalid attribute name '%s'", member);
347
}
348
349
/* Create or reuse existing table */
350
lua_settop(L, 2);
351
if (!lua_istable(L, 2))
352
lua_newtable(L);
353
354
/* Export all stat data to caller */
355
for (i = 0; i < nitems(members); i++) {
356
lua_pushstring(L, members[i].name);
357
members[i].push(L, &sb);
358
lua_rawset(L, -3);
359
}
360
return 1;
361
}
362
363
#ifndef _STANDALONE
364
#define lfs_mkdir_impl(path) (mkdir((path), \
365
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
366
S_IROTH | S_IXOTH))
367
368
static int
369
lua_mkdir(lua_State *L)
370
{
371
const char *path;
372
int error, serrno;
373
374
path = luaL_checkstring(L, 1);
375
if (path == NULL) {
376
lua_pushnil(L);
377
lua_pushfstring(L, "cannot convert first argument to string");
378
lua_pushinteger(L, EINVAL);
379
return 3;
380
}
381
382
error = lfs_mkdir_impl(path);
383
if (error == -1) {
384
/* Save it; unclear what other libc functions may be invoked */
385
serrno = errno;
386
lua_pushnil(L);
387
lua_pushfstring(L, strerror(serrno));
388
lua_pushinteger(L, serrno);
389
return 3;
390
}
391
392
lua_pushboolean(L, 1);
393
return 1;
394
}
395
396
static int
397
lua_rmdir(lua_State *L)
398
{
399
const char *path;
400
int error, serrno;
401
402
path = luaL_checkstring(L, 1);
403
if (path == NULL) {
404
lua_pushnil(L);
405
lua_pushfstring(L, "cannot convert first argument to string");
406
lua_pushinteger(L, EINVAL);
407
return 3;
408
}
409
410
error = rmdir(path);
411
if (error == -1) {
412
/* Save it; unclear what other libc functions may be invoked */
413
serrno = errno;
414
lua_pushnil(L);
415
lua_pushfstring(L, strerror(serrno));
416
lua_pushinteger(L, serrno);
417
return 3;
418
}
419
420
lua_pushboolean(L, 1);
421
return 1;
422
}
423
#endif
424
425
#define REG_SIMPLE(n) { #n, lua_ ## n }
426
static const struct luaL_Reg fslib[] = {
427
REG_SIMPLE(attributes),
428
REG_SIMPLE(dir),
429
#ifndef _STANDALONE
430
REG_SIMPLE(mkdir),
431
REG_SIMPLE(rmdir),
432
#endif
433
{ NULL, NULL },
434
};
435
#undef REG_SIMPLE
436
437
int
438
luaopen_lfs(lua_State *L)
439
{
440
register_metatable(L);
441
luaL_newlib(L, fslib);
442
#ifdef _STANDALONE
443
/* Non-standard extension for loader, used with lfs.dir(). */
444
lua_pushinteger(L, DT_DIR);
445
lua_setfield(L, -2, "DT_DIR");
446
#endif
447
return 1;
448
}
449
450