1329500Scem/*- 2329500Scem * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org> 3329500Scem * All rights reserved. 4329500Scem * 5329500Scem * Redistribution and use in source and binary forms, with or without 6329500Scem * modification, are permitted provided that the following conditions 7329500Scem * are met: 8329500Scem * 1. Redistributions of source code must retain the above copyright 9329500Scem * notice, this list of conditions and the following disclaimer. 10329500Scem * 2. Redistributions in binary form must reproduce the above copyright 11329500Scem * notice, this list of conditions and the following disclaimer in the 12329500Scem * documentation and/or other materials provided with the distribution. 13329500Scem * 14329500Scem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15329500Scem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16329500Scem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17329500Scem * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18329500Scem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19329500Scem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20329500Scem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21329500Scem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22329500Scem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23329500Scem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24329500Scem * SUCH DAMAGE. 25329500Scem * 26329500Scem * Portions derived from https://github.com/keplerproject/luafilesystem under 27329500Scem * the terms of the MIT license: 28329500Scem * 29329500Scem * Copyright (c) 2003-2014 Kepler Project. 30329500Scem * 31329500Scem * Permission is hereby granted, free of charge, to any person 32329500Scem * obtaining a copy of this software and associated documentation 33329500Scem * files (the "Software"), to deal in the Software without 34329500Scem * restriction, including without limitation the rights to use, copy, 35329500Scem * modify, merge, publish, distribute, sublicense, and/or sell copies 36329500Scem * of the Software, and to permit persons to whom the Software is 37329500Scem * furnished to do so, subject to the following conditions: 38329500Scem * 39329500Scem * The above copyright notice and this permission notice shall be 40329500Scem * included in all copies or substantial portions of the Software. 41329500Scem * 42329500Scem * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 43329500Scem * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 44329500Scem * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 45329500Scem * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 46329500Scem * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 47329500Scem * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48329500Scem * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49329500Scem * SOFTWARE. 50329500Scem */ 51329500Scem 52329500Scem#include <sys/cdefs.h> 53329500Scem__FBSDID("$FreeBSD: stable/11/libexec/flua/modules/lfs.c 354833 2019-11-18 23:21:13Z kevans $"); 54329500Scem 55354833Skevans#ifndef _STANDALONE 56354833Skevans#include <sys/stat.h> 57354833Skevans#include <dirent.h> 58354833Skevans#include <errno.h> 59354833Skevans#include <unistd.h> 60354833Skevans#include <stdio.h> 61354833Skevans#include <string.h> 62354833Skevans#endif 63354833Skevans 64329500Scem#include <lua.h> 65329500Scem#include "lauxlib.h" 66329500Scem#include "lfs.h" 67354833Skevans 68354833Skevans#ifdef _STANDALONE 69329500Scem#include "lstd.h" 70329500Scem#include "lutils.h" 71329500Scem#include "bootstrap.h" 72354833Skevans#endif 73329500Scem 74329500Scem#ifndef nitems 75329500Scem#define nitems(x) (sizeof((x)) / sizeof((x)[0])) 76329500Scem#endif 77329500Scem 78329500Scem/* 79329500Scem * The goal is to emulate a subset of the upstream Lua FileSystem library, as 80329500Scem * faithfully as possible in the boot environment. Only APIs that seem useful 81329500Scem * need to emulated. 82329500Scem * 83329500Scem * Example usage: 84329500Scem * 85329500Scem * for file in lfs.dir("/boot") do 86329500Scem * print("\t"..file) 87329500Scem * end 88329500Scem * 89329500Scem * Prints: 90329500Scem * . 91329500Scem * .. 92329500Scem * (etc.) 93329500Scem * 94329500Scem * The other available API is lfs.attributes(), which functions somewhat like 95329649Scem * stat(2) and returns a table of values. Example code: 96329500Scem * 97329649Scem * attrs, errormsg, errorcode = lfs.attributes("/boot") 98329649Scem * if attrs == nil then 99329649Scem * print(errormsg) 100329649Scem * return errorcode 101329649Scem * end 102329649Scem * 103329649Scem * for k, v in pairs(attrs) do 104329500Scem * print(k .. ":\t" .. v) 105329500Scem * end 106329649Scem * return 0 107329500Scem * 108329649Scem * Prints (on success): 109329500Scem * gid: 0 110329500Scem * change: 140737488342640 111329500Scem * mode: directory 112329500Scem * rdev: 0 113329500Scem * ino: 4199275 114329500Scem * dev: 140737488342544 115329500Scem * modification: 140737488342576 116329500Scem * size: 512 117329500Scem * access: 140737488342560 118329500Scem * permissions: 755 119329500Scem * nlink: 58283552 120329500Scem * uid: 1001 121329500Scem */ 122329500Scem 123329500Scem#define DIR_METATABLE "directory iterator metatable" 124329500Scem 125329500Scemstatic int 126329500Scemlua_dir_iter_next(lua_State *L) 127329500Scem{ 128329500Scem struct dirent *entry; 129329500Scem DIR *dp, **dpp; 130329500Scem 131329500Scem dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE); 132329500Scem dp = *dpp; 133329500Scem luaL_argcheck(L, dp != NULL, 1, "closed directory"); 134329500Scem 135354833Skevans#ifdef _STANDALONE 136329500Scem entry = readdirfd(dp->fd); 137354833Skevans#else 138354833Skevans entry = readdir(dp); 139354833Skevans#endif 140329500Scem if (entry == NULL) { 141329500Scem closedir(dp); 142329500Scem *dpp = NULL; 143329500Scem return 0; 144329500Scem } 145329500Scem 146329500Scem lua_pushstring(L, entry->d_name); 147329500Scem return 1; 148329500Scem} 149329500Scem 150329500Scemstatic int 151329500Scemlua_dir_iter_close(lua_State *L) 152329500Scem{ 153329500Scem DIR *dp, **dpp; 154329500Scem 155329500Scem dpp = (DIR **)lua_touserdata(L, 1); 156329500Scem dp = *dpp; 157329500Scem if (dp == NULL) 158329500Scem return 0; 159329500Scem 160329500Scem closedir(dp); 161329500Scem *dpp = NULL; 162329500Scem return 0; 163329500Scem} 164329500Scem 165329500Scemstatic int 166329500Scemlua_dir(lua_State *L) 167329500Scem{ 168329500Scem const char *path; 169329500Scem DIR *dp; 170329500Scem 171329500Scem if (lua_gettop(L) != 1) { 172329500Scem lua_pushnil(L); 173329500Scem return 1; 174329500Scem } 175329500Scem 176329500Scem path = luaL_checkstring(L, 1); 177329500Scem dp = opendir(path); 178329500Scem if (dp == NULL) { 179329500Scem lua_pushnil(L); 180329500Scem return 1; 181329500Scem } 182329500Scem 183329500Scem lua_pushcfunction(L, lua_dir_iter_next); 184329500Scem *(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp; 185329500Scem luaL_getmetatable(L, DIR_METATABLE); 186329500Scem lua_setmetatable(L, -2); 187329500Scem return 2; 188329500Scem} 189329500Scem 190329500Scemstatic void 191329500Scemregister_metatable(lua_State *L) 192329500Scem{ 193329500Scem /* 194329500Scem * Create so-called metatable for iterator object returned by 195329500Scem * lfs.dir(). 196329500Scem */ 197329500Scem luaL_newmetatable(L, DIR_METATABLE); 198329500Scem 199329500Scem lua_newtable(L); 200329500Scem lua_pushcfunction(L, lua_dir_iter_next); 201329500Scem lua_setfield(L, -2, "next"); 202329500Scem lua_pushcfunction(L, lua_dir_iter_close); 203329500Scem lua_setfield(L, -2, "close"); 204329500Scem 205329500Scem /* Magically associate anonymous method table with metatable. */ 206329500Scem lua_setfield(L, -2, "__index"); 207329500Scem /* Implement magic destructor method */ 208329500Scem lua_pushcfunction(L, lua_dir_iter_close); 209329500Scem lua_setfield(L, -2, "__gc"); 210329500Scem 211329500Scem lua_pop(L, 1); 212329500Scem} 213329500Scem 214329500Scem#define PUSH_INTEGER(lname, stname) \ 215329500Scemstatic void \ 216329500Scempush_st_ ## lname (lua_State *L, struct stat *sb) \ 217329500Scem{ \ 218329500Scem lua_pushinteger(L, (lua_Integer)sb->st_ ## stname); \ 219329500Scem} 220329500ScemPUSH_INTEGER(dev, dev) 221329500ScemPUSH_INTEGER(ino, ino) 222329500ScemPUSH_INTEGER(nlink, nlink) 223329500ScemPUSH_INTEGER(uid, uid) 224329500ScemPUSH_INTEGER(gid, gid) 225329500ScemPUSH_INTEGER(rdev, rdev) 226329500ScemPUSH_INTEGER(access, atime) 227329500ScemPUSH_INTEGER(modification, mtime) 228329500ScemPUSH_INTEGER(change, ctime) 229329500ScemPUSH_INTEGER(size, size) 230329500Scem#undef PUSH_INTEGER 231329500Scem 232329500Scemstatic void 233329500Scempush_st_mode(lua_State *L, struct stat *sb) 234329500Scem{ 235329500Scem const char *mode_s; 236329500Scem mode_t mode; 237329500Scem 238329500Scem mode = (sb->st_mode & S_IFMT); 239329500Scem if (S_ISREG(mode)) 240329500Scem mode_s = "file"; 241329500Scem else if (S_ISDIR(mode)) 242329500Scem mode_s = "directory"; 243329500Scem else if (S_ISLNK(mode)) 244329500Scem mode_s = "link"; 245329500Scem else if (S_ISSOCK(mode)) 246329500Scem mode_s = "socket"; 247329500Scem else if (S_ISFIFO(mode)) 248329500Scem mode_s = "fifo"; 249329500Scem else if (S_ISCHR(mode)) 250329500Scem mode_s = "char device"; 251329500Scem else if (S_ISBLK(mode)) 252329500Scem mode_s = "block device"; 253329500Scem else 254329500Scem mode_s = "other"; 255329500Scem 256329500Scem lua_pushstring(L, mode_s); 257329500Scem} 258329500Scem 259329500Scemstatic void 260329500Scempush_st_permissions(lua_State *L, struct stat *sb) 261329500Scem{ 262329500Scem char buf[20]; 263329500Scem 264329500Scem /* 265329500Scem * XXX 266329500Scem * Could actually format as "-rwxrwxrwx" -- do we care? 267329500Scem */ 268329500Scem snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT); 269329500Scem lua_pushstring(L, buf); 270329500Scem} 271329500Scem 272329500Scem#define PUSH_ENTRY(n) { #n, push_st_ ## n } 273329500Scemstruct stat_members { 274329500Scem const char *name; 275329500Scem void (*push)(lua_State *, struct stat *); 276329500Scem} members[] = { 277329500Scem PUSH_ENTRY(mode), 278329500Scem PUSH_ENTRY(dev), 279329500Scem PUSH_ENTRY(ino), 280329500Scem PUSH_ENTRY(nlink), 281329500Scem PUSH_ENTRY(uid), 282329500Scem PUSH_ENTRY(gid), 283329500Scem PUSH_ENTRY(rdev), 284329500Scem PUSH_ENTRY(access), 285329500Scem PUSH_ENTRY(modification), 286329500Scem PUSH_ENTRY(change), 287329500Scem PUSH_ENTRY(size), 288329500Scem PUSH_ENTRY(permissions), 289329500Scem}; 290329500Scem#undef PUSH_ENTRY 291329500Scem 292329500Scemstatic int 293329500Scemlua_attributes(lua_State *L) 294329500Scem{ 295329500Scem struct stat sb; 296329500Scem const char *path, *member; 297329500Scem size_t i; 298329500Scem int rc; 299329500Scem 300329500Scem path = luaL_checkstring(L, 1); 301329500Scem if (path == NULL) { 302329500Scem lua_pushnil(L); 303329649Scem lua_pushfstring(L, "cannot convert first argument to string"); 304329649Scem lua_pushinteger(L, EINVAL); 305329649Scem return 3; 306329500Scem } 307329500Scem 308329500Scem rc = stat(path, &sb); 309329500Scem if (rc != 0) { 310329500Scem lua_pushnil(L); 311329500Scem lua_pushfstring(L, 312329500Scem "cannot obtain information from file '%s': %s", path, 313329500Scem strerror(errno)); 314329500Scem lua_pushinteger(L, errno); 315329500Scem return 3; 316329500Scem } 317329500Scem 318329500Scem if (lua_isstring(L, 2)) { 319329500Scem member = lua_tostring(L, 2); 320329500Scem for (i = 0; i < nitems(members); i++) { 321329500Scem if (strcmp(members[i].name, member) != 0) 322329500Scem continue; 323329500Scem 324329500Scem members[i].push(L, &sb); 325329500Scem return 1; 326329500Scem } 327329500Scem return luaL_error(L, "invalid attribute name '%s'", member); 328329500Scem } 329329500Scem 330329500Scem /* Create or reuse existing table */ 331329500Scem lua_settop(L, 2); 332329500Scem if (!lua_istable(L, 2)) 333329500Scem lua_newtable(L); 334329500Scem 335329500Scem /* Export all stat data to caller */ 336329500Scem for (i = 0; i < nitems(members); i++) { 337329500Scem lua_pushstring(L, members[i].name); 338329500Scem members[i].push(L, &sb); 339329500Scem lua_rawset(L, -3); 340329500Scem } 341329500Scem return 1; 342329500Scem} 343329500Scem 344354833Skevans#ifndef _STANDALONE 345354833Skevans#define lfs_mkdir_impl(path) (mkdir((path), \ 346354833Skevans S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \ 347354833Skevans S_IROTH | S_IXOTH)) 348354833Skevans 349354833Skevansstatic int 350354833Skevanslua_mkdir(lua_State *L) 351354833Skevans{ 352354833Skevans const char *path; 353354833Skevans int error, serrno; 354354833Skevans 355354833Skevans path = luaL_checkstring(L, 1); 356354833Skevans if (path == NULL) { 357354833Skevans lua_pushnil(L); 358354833Skevans lua_pushfstring(L, "cannot convert first argument to string"); 359354833Skevans lua_pushinteger(L, EINVAL); 360354833Skevans return 3; 361354833Skevans } 362354833Skevans 363354833Skevans error = lfs_mkdir_impl(path); 364354833Skevans if (error == -1) { 365354833Skevans /* Save it; unclear what other libc functions may be invoked */ 366354833Skevans serrno = errno; 367354833Skevans lua_pushnil(L); 368354833Skevans lua_pushfstring(L, strerror(serrno)); 369354833Skevans lua_pushinteger(L, serrno); 370354833Skevans return 3; 371354833Skevans } 372354833Skevans 373354833Skevans lua_pushboolean(L, 1); 374354833Skevans return 1; 375354833Skevans} 376354833Skevans 377354833Skevansstatic int 378354833Skevanslua_rmdir(lua_State *L) 379354833Skevans{ 380354833Skevans const char *path; 381354833Skevans int error, serrno; 382354833Skevans 383354833Skevans path = luaL_checkstring(L, 1); 384354833Skevans if (path == NULL) { 385354833Skevans lua_pushnil(L); 386354833Skevans lua_pushfstring(L, "cannot convert first argument to string"); 387354833Skevans lua_pushinteger(L, EINVAL); 388354833Skevans return 3; 389354833Skevans } 390354833Skevans 391354833Skevans error = rmdir(path); 392354833Skevans if (error == -1) { 393354833Skevans /* Save it; unclear what other libc functions may be invoked */ 394354833Skevans serrno = errno; 395354833Skevans lua_pushnil(L); 396354833Skevans lua_pushfstring(L, strerror(serrno)); 397354833Skevans lua_pushinteger(L, serrno); 398354833Skevans return 3; 399354833Skevans } 400354833Skevans 401354833Skevans lua_pushboolean(L, 1); 402354833Skevans return 1; 403354833Skevans} 404354833Skevans#endif 405354833Skevans 406329500Scem#define REG_SIMPLE(n) { #n, lua_ ## n } 407329500Scemstatic const struct luaL_Reg fslib[] = { 408329500Scem REG_SIMPLE(attributes), 409329500Scem REG_SIMPLE(dir), 410354833Skevans#ifndef _STANDALONE 411354833Skevans REG_SIMPLE(mkdir), 412354833Skevans REG_SIMPLE(rmdir), 413354833Skevans#endif 414329500Scem { NULL, NULL }, 415329500Scem}; 416329500Scem#undef REG_SIMPLE 417329500Scem 418329500Scemint 419329500Scemluaopen_lfs(lua_State *L) 420329500Scem{ 421329500Scem register_metatable(L); 422329500Scem luaL_newlib(L, fslib); 423329500Scem return 1; 424329500Scem} 425