file.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. // Module for interfacing with file system
  2. #include "module.h"
  3. #include "lauxlib.h"
  4. #include "lmem.h"
  5. #include "platform.h"
  6. #include "c_types.h"
  7. #include "vfs.h"
  8. #include "c_string.h"
  9. #include <alloca.h>
  10. #define FILE_READ_CHUNK 1024
  11. static int file_fd = 0;
  12. static int file_fd_ref = LUA_NOREF;
  13. static int rtc_cb_ref = LUA_NOREF;
  14. typedef struct _file_fd_ud {
  15. int fd;
  16. } file_fd_ud;
  17. static void table2tm( lua_State *L, vfs_time *tm )
  18. {
  19. int idx = lua_gettop( L );
  20. // extract items from table
  21. lua_getfield( L, idx, "year" );
  22. lua_getfield( L, idx, "mon" );
  23. lua_getfield( L, idx, "day" );
  24. lua_getfield( L, idx, "hour" );
  25. lua_getfield( L, idx, "min" );
  26. lua_getfield( L, idx, "sec" );
  27. tm->year = luaL_optint( L, ++idx, 2016 );
  28. tm->mon = luaL_optint( L, ++idx, 6 );
  29. tm->day = luaL_optint( L, ++idx, 21 );
  30. tm->hour = luaL_optint( L, ++idx, 0 );
  31. tm->min = luaL_optint( L, ++idx, 0 );
  32. tm->sec = luaL_optint( L, ++idx, 0 );
  33. // remove items from stack
  34. lua_pop( L, 6 );
  35. }
  36. static sint32_t file_rtc_cb( vfs_time *tm )
  37. {
  38. sint32_t res = VFS_RES_ERR;
  39. if (rtc_cb_ref != LUA_NOREF) {
  40. lua_State *L = lua_getstate();
  41. lua_rawgeti( L, LUA_REGISTRYINDEX, rtc_cb_ref );
  42. lua_call( L, 0, 1 );
  43. if (lua_type( L, lua_gettop( L ) ) == LUA_TTABLE) {
  44. table2tm( L, tm );
  45. res = VFS_RES_OK;
  46. }
  47. // pop item returned by callback
  48. lua_pop( L, 1 );
  49. }
  50. return res;
  51. }
  52. // Lua: on()
  53. static int file_on(lua_State *L)
  54. {
  55. enum events{
  56. ON_RTC = 0
  57. };
  58. const char *const eventnames[] = {"rtc", NULL};
  59. int event = luaL_checkoption(L, 1, "rtc", eventnames);
  60. switch (event) {
  61. case ON_RTC:
  62. luaL_unref(L, LUA_REGISTRYINDEX, rtc_cb_ref);
  63. if ((lua_type(L, 2) == LUA_TFUNCTION) ||
  64. (lua_type(L, 2) == LUA_TLIGHTFUNCTION)) {
  65. lua_pushvalue(L, 2); // copy argument (func) to the top of stack
  66. rtc_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  67. vfs_register_rtc_cb(file_rtc_cb);
  68. } else {
  69. rtc_cb_ref = LUA_NOREF;
  70. vfs_register_rtc_cb(NULL);
  71. }
  72. break;
  73. default:
  74. break;
  75. }
  76. return 0;
  77. }
  78. // Lua: close()
  79. static int file_close( lua_State* L )
  80. {
  81. int need_pop = FALSE;
  82. file_fd_ud *ud;
  83. if (lua_type( L, 1 ) != LUA_TUSERDATA) {
  84. // fall back to last opened file
  85. if (file_fd_ref != LUA_NOREF) {
  86. lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
  87. // top of stack is now default file descriptor
  88. ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
  89. lua_pop( L, 1 );
  90. } else {
  91. // no default file currently opened
  92. return 0;
  93. }
  94. } else {
  95. ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  96. }
  97. // unref default file descriptor
  98. luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
  99. file_fd_ref = LUA_NOREF;
  100. if(ud->fd){
  101. vfs_close(ud->fd);
  102. // mark as closed
  103. ud->fd = 0;
  104. }
  105. return 0;
  106. }
  107. static int file_obj_free( lua_State *L )
  108. {
  109. file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  110. if (ud->fd) {
  111. // close file if it's still open
  112. vfs_close(ud->fd);
  113. ud->fd = 0;
  114. }
  115. return 0;
  116. }
  117. // Lua: format()
  118. static int file_format( lua_State* L )
  119. {
  120. size_t len;
  121. file_close(L);
  122. if( !vfs_format() )
  123. {
  124. NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
  125. NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
  126. luaL_error(L, "Failed to format file system");
  127. }
  128. else{
  129. NODE_ERR( "format done.\n" );
  130. }
  131. return 0;
  132. }
  133. static int file_fscfg (lua_State *L)
  134. {
  135. uint32_t phys_addr, phys_size;
  136. vfs_fscfg("/FLASH", &phys_addr, &phys_size);
  137. lua_pushinteger (L, phys_addr);
  138. lua_pushinteger (L, phys_size);
  139. return 2;
  140. }
  141. // Lua: open(filename, mode)
  142. static int file_open( lua_State* L )
  143. {
  144. size_t len;
  145. // unref last file descriptor to allow gc'ing if not kept by user script
  146. luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
  147. file_fd_ref = LUA_NOREF;
  148. const char *fname = luaL_checklstring( L, 1, &len );
  149. const char *basename = vfs_basename( fname );
  150. luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
  151. const char *mode = luaL_optstring(L, 2, "r");
  152. file_fd = vfs_open(fname, mode);
  153. if(!file_fd){
  154. lua_pushnil(L);
  155. } else {
  156. file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
  157. ud->fd = file_fd;
  158. luaL_getmetatable( L, "file.obj" );
  159. lua_setmetatable( L, -2 );
  160. // store reference to opened file
  161. lua_pushvalue( L, -1 );
  162. file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
  163. }
  164. return 1;
  165. }
  166. // Lua: list()
  167. static int file_list( lua_State* L )
  168. {
  169. vfs_dir *dir;
  170. vfs_item *item;
  171. if (dir = vfs_opendir("")) {
  172. lua_newtable( L );
  173. while (item = vfs_readdir(dir)) {
  174. lua_pushinteger(L, vfs_item_size(item));
  175. lua_setfield(L, -2, vfs_item_name(item));
  176. vfs_closeitem(item);
  177. }
  178. vfs_closedir(dir);
  179. return 1;
  180. }
  181. return 0;
  182. }
  183. static int get_file_obj( lua_State *L, int *argpos )
  184. {
  185. if (lua_type( L, 1 ) == LUA_TUSERDATA) {
  186. file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
  187. *argpos = 2;
  188. return ud->fd;
  189. } else {
  190. *argpos = 1;
  191. return file_fd;
  192. }
  193. }
  194. #define GET_FILE_OBJ int argpos; \
  195. int fd = get_file_obj( L, &argpos );
  196. static int file_seek (lua_State *L)
  197. {
  198. GET_FILE_OBJ;
  199. static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
  200. static const char *const modenames[] = {"set", "cur", "end", NULL};
  201. if(!fd)
  202. return luaL_error(L, "open a file first");
  203. int op = luaL_checkoption(L, argpos, "cur", modenames);
  204. long offset = luaL_optlong(L, ++argpos, 0);
  205. op = vfs_lseek(fd, offset, mode[op]);
  206. if (op < 0)
  207. lua_pushnil(L); /* error */
  208. else
  209. lua_pushinteger(L, vfs_tell(fd));
  210. return 1;
  211. }
  212. // Lua: exists(filename)
  213. static int file_exists( lua_State* L )
  214. {
  215. size_t len;
  216. const char *fname = luaL_checklstring( L, 1, &len );
  217. const char *basename = vfs_basename( fname );
  218. luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
  219. vfs_item *stat = vfs_stat((char *)fname);
  220. lua_pushboolean(L, stat ? 1 : 0);
  221. if (stat) vfs_closeitem(stat);
  222. return 1;
  223. }
  224. // Lua: remove(filename)
  225. static int file_remove( lua_State* L )
  226. {
  227. size_t len;
  228. const char *fname = luaL_checklstring( L, 1, &len );
  229. const char *basename = vfs_basename( fname );
  230. luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
  231. vfs_remove((char *)fname);
  232. return 0;
  233. }
  234. // Lua: flush()
  235. static int file_flush( lua_State* L )
  236. {
  237. GET_FILE_OBJ;
  238. if(!fd)
  239. return luaL_error(L, "open a file first");
  240. if(vfs_flush(fd) == 0)
  241. lua_pushboolean(L, 1);
  242. else
  243. lua_pushnil(L);
  244. return 1;
  245. }
  246. // Lua: rename("oldname", "newname")
  247. static int file_rename( lua_State* L )
  248. {
  249. size_t len;
  250. const char *oldname = luaL_checklstring( L, 1, &len );
  251. const char *basename = vfs_basename( oldname );
  252. luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(oldname) == len, 1, "filename invalid");
  253. const char *newname = luaL_checklstring( L, 2, &len );
  254. basename = vfs_basename( newname );
  255. luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(newname) == len, 2, "filename invalid");
  256. if(0 <= vfs_rename( oldname, newname )){
  257. lua_pushboolean(L, 1);
  258. } else {
  259. lua_pushboolean(L, 0);
  260. }
  261. return 1;
  262. }
  263. // g_read()
  264. static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
  265. {
  266. static char *heap_mem = NULL;
  267. // free leftover memory
  268. if (heap_mem) {
  269. luaM_free(L, heap_mem);
  270. heap_mem = NULL;
  271. }
  272. if(n <= 0)
  273. n = FILE_READ_CHUNK;
  274. if(end_char < 0 || end_char >255)
  275. end_char = EOF;
  276. if(!fd)
  277. return luaL_error(L, "open a file first");
  278. char *p;
  279. int i;
  280. if (n > LUAL_BUFFERSIZE) {
  281. // get buffer from heap
  282. p = heap_mem = luaM_malloc(L, n);
  283. } else {
  284. // small chunks go onto the stack
  285. p = alloca(n);
  286. }
  287. n = vfs_read(fd, p, n);
  288. // bypass search if no end character provided
  289. if (n > 0 && end_char != EOF) {
  290. for (i = 0; i < n; ++i)
  291. if (p[i] == end_char)
  292. {
  293. ++i;
  294. break;
  295. }
  296. } else {
  297. i = n;
  298. }
  299. if (i == 0 || n == VFS_RES_ERR) {
  300. if (heap_mem) {
  301. luaM_free(L, heap_mem);
  302. heap_mem = NULL;
  303. }
  304. return 0;
  305. }
  306. vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
  307. lua_pushlstring(L, p, i);
  308. if (heap_mem) {
  309. luaM_free(L, heap_mem);
  310. heap_mem = NULL;
  311. }
  312. return 1;
  313. }
  314. // Lua: read()
  315. // file.read() will read all byte in file
  316. // file.read(10) will read 10 byte from file, or EOF is reached.
  317. // file.read('q') will read until 'q' or EOF is reached.
  318. static int file_read( lua_State* L )
  319. {
  320. unsigned need_len = FILE_READ_CHUNK;
  321. int16_t end_char = EOF;
  322. size_t el;
  323. GET_FILE_OBJ;
  324. if( lua_type( L, argpos ) == LUA_TNUMBER )
  325. {
  326. need_len = ( unsigned )luaL_checkinteger( L, argpos );
  327. }
  328. else if(lua_isstring(L, argpos))
  329. {
  330. const char *end = luaL_checklstring( L, argpos, &el );
  331. if(el!=1){
  332. return luaL_error( L, "wrong arg range" );
  333. }
  334. end_char = (int16_t)end[0];
  335. }
  336. return file_g_read(L, need_len, end_char, fd);
  337. }
  338. // Lua: readline()
  339. static int file_readline( lua_State* L )
  340. {
  341. GET_FILE_OBJ;
  342. return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd);
  343. }
  344. // Lua: write("string")
  345. static int file_write( lua_State* L )
  346. {
  347. GET_FILE_OBJ;
  348. if(!fd)
  349. return luaL_error(L, "open a file first");
  350. size_t l, rl;
  351. const char *s = luaL_checklstring(L, argpos, &l);
  352. rl = vfs_write(fd, s, l);
  353. if(rl==l)
  354. lua_pushboolean(L, 1);
  355. else
  356. lua_pushnil(L);
  357. return 1;
  358. }
  359. // Lua: writeline("string")
  360. static int file_writeline( lua_State* L )
  361. {
  362. GET_FILE_OBJ;
  363. if(!fd)
  364. return luaL_error(L, "open a file first");
  365. size_t l, rl;
  366. const char *s = luaL_checklstring(L, argpos, &l);
  367. rl = vfs_write(fd, s, l);
  368. if(rl==l){
  369. rl = vfs_write(fd, "\n", 1);
  370. if(rl==1)
  371. lua_pushboolean(L, 1);
  372. else
  373. lua_pushnil(L);
  374. }
  375. else{
  376. lua_pushnil(L);
  377. }
  378. return 1;
  379. }
  380. // Lua: fsinfo()
  381. static int file_fsinfo( lua_State* L )
  382. {
  383. u32_t total, used;
  384. if (vfs_fsinfo("", &total, &used)) {
  385. return luaL_error(L, "file system failed");
  386. }
  387. NODE_DBG("total: %d, used:%d\n", total, used);
  388. if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
  389. {
  390. return luaL_error(L, "file system error");
  391. }
  392. lua_pushinteger(L, total-used);
  393. lua_pushinteger(L, used);
  394. lua_pushinteger(L, total);
  395. return 3;
  396. }
  397. typedef struct {
  398. vfs_vol *vol;
  399. } volume_type;
  400. // Lua: vol = file.mount("/SD0")
  401. static int file_mount( lua_State *L )
  402. {
  403. const char *ldrv = luaL_checkstring( L, 1 );
  404. int num = luaL_optint( L, 2, -1 );
  405. volume_type *vol = (volume_type *)lua_newuserdata( L, sizeof( volume_type ) );
  406. if (vol->vol = vfs_mount( ldrv, num )) {
  407. /* set its metatable */
  408. luaL_getmetatable(L, "vfs.vol");
  409. lua_setmetatable(L, -2);
  410. return 1;
  411. } else {
  412. // remove created userdata
  413. lua_pop( L, 1 );
  414. return 0;
  415. }
  416. }
  417. // Lua: success = file.chdir("/SD0/")
  418. static int file_chdir( lua_State *L )
  419. {
  420. const char *path = luaL_checkstring( L, 1 );
  421. lua_pushboolean( L, 0 <= vfs_chdir( path ) );
  422. return 1;
  423. }
  424. static int file_vol_umount( lua_State *L )
  425. {
  426. volume_type *vol = luaL_checkudata( L, 1, "file.vol" );
  427. luaL_argcheck( L, vol, 1, "volume expected" );
  428. lua_pushboolean( L, 0 <= vfs_umount( vol->vol ) );
  429. // invalidate vfs descriptor, it has been free'd anyway
  430. vol->vol = NULL;
  431. return 1;
  432. }
  433. static const LUA_REG_TYPE file_obj_map[] =
  434. {
  435. { LSTRKEY( "close" ), LFUNCVAL( file_close ) },
  436. { LSTRKEY( "read" ), LFUNCVAL( file_read ) },
  437. { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
  438. { LSTRKEY( "write" ), LFUNCVAL( file_write ) },
  439. { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
  440. { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
  441. { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
  442. { LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
  443. { LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
  444. { LNILKEY, LNILVAL }
  445. };
  446. static const LUA_REG_TYPE file_vol_map[] =
  447. {
  448. { LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )},
  449. //{ LSTRKEY( "getfree" ), LFUNCVAL( file_vol_getfree )},
  450. //{ LSTRKEY( "getlabel" ), LFUNCVAL( file_vol_getlabel )},
  451. //{ LSTRKEY( "__gc" ), LFUNCVAL( file_vol_free ) },
  452. { LSTRKEY( "__index" ), LROVAL( file_vol_map ) },
  453. { LNILKEY, LNILVAL }
  454. };
  455. // Module function map
  456. static const LUA_REG_TYPE file_map[] = {
  457. { LSTRKEY( "list" ), LFUNCVAL( file_list ) },
  458. { LSTRKEY( "open" ), LFUNCVAL( file_open ) },
  459. { LSTRKEY( "close" ), LFUNCVAL( file_close ) },
  460. { LSTRKEY( "write" ), LFUNCVAL( file_write ) },
  461. { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
  462. { LSTRKEY( "read" ), LFUNCVAL( file_read ) },
  463. { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
  464. #ifdef BUILD_SPIFFS
  465. { LSTRKEY( "format" ), LFUNCVAL( file_format ) },
  466. { LSTRKEY( "fscfg" ), LFUNCVAL( file_fscfg ) },
  467. #endif
  468. { LSTRKEY( "remove" ), LFUNCVAL( file_remove ) },
  469. { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
  470. { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
  471. { LSTRKEY( "rename" ), LFUNCVAL( file_rename ) },
  472. { LSTRKEY( "exists" ), LFUNCVAL( file_exists ) },
  473. { LSTRKEY( "fsinfo" ), LFUNCVAL( file_fsinfo ) },
  474. { LSTRKEY( "on" ), LFUNCVAL( file_on ) },
  475. #ifdef BUILD_FATFS
  476. { LSTRKEY( "mount" ), LFUNCVAL( file_mount ) },
  477. { LSTRKEY( "chdir" ), LFUNCVAL( file_chdir ) },
  478. #endif
  479. { LNILKEY, LNILVAL }
  480. };
  481. int luaopen_file( lua_State *L ) {
  482. luaL_rometatable( L, "file.vol", (void *)file_vol_map );
  483. luaL_rometatable( L, "file.obj", (void *)file_obj_map );
  484. return 0;
  485. }
  486. NODEMCU_MODULE(FILE, "file", file_map, luaopen_file);