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