lfs.cc (26661B)
1 /* 2 ** LuaFileSystem 3 ** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) 4 ** 5 ** File system manipulation library. 6 ** This library offers these functions: 7 ** lfs.attributes (filepath [, attributename | attributetable]) 8 ** lfs.chdir (path) 9 ** lfs.currentdir () 10 ** lfs.dir (path) 11 ** lfs.link (old, new[, symlink]) 12 ** lfs.lock (fh, mode) 13 ** lfs.lock_dir (path) 14 ** lfs.mkdir (path) 15 ** lfs.rmdir (path) 16 ** lfs.setmode (filepath, mode) 17 ** lfs.symlinkattributes (filepath [, attributename]) 18 ** lfs.touch (filepath [, atime [, mtime]]) 19 ** lfs.unlock (fh) 20 */ 21 22 #ifndef LFS_DO_NOT_USE_LARGE_FILE 23 #ifndef _WIN32 24 #ifndef _AIX 25 #define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ 26 #else 27 #define _LARGE_FILES 1 /* AIX */ 28 #endif 29 #endif 30 #endif 31 32 #ifndef LFS_DO_NOT_USE_LARGE_FILE 33 #define _LARGEFILE64_SOURCE 34 #endif 35 36 #include <errno.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <time.h> 41 #include <sys/stat.h> 42 43 #ifdef _WIN32 44 #include <direct.h> 45 #include <windows.h> 46 #include <io.h> 47 #include <sys/locking.h> 48 #ifdef __BORLANDC__ 49 #include <utime.h> 50 #else 51 #include <sys/utime.h> 52 #endif 53 #include <fcntl.h> 54 /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ 55 #define LFS_MAXPATHLEN MAX_PATH 56 #else 57 #include <unistd.h> 58 #include <dirent.h> 59 #include <fcntl.h> 60 #include <sys/types.h> 61 #include <utime.h> 62 #include <sys/param.h> /* for MAXPATHLEN */ 63 #define LFS_MAXPATHLEN MAXPATHLEN 64 #endif 65 66 #include <lua.h> 67 #include <lauxlib.h> 68 #include <lualib.h> 69 70 #include "lfs.h" 71 72 #define LFS_VERSION "1.7.0" 73 #define LFS_LIBNAME "lfs" 74 75 #if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ 76 77 #ifndef luaL_optlong 78 #define luaL_optlong luaL_optinteger 79 #endif 80 81 #endif 82 83 #if LUA_VERSION_NUM >= 502 84 # define new_lib(L, l) (luaL_newlib(L, l)) 85 #else 86 # define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) 87 #endif 88 89 /* Define 'strerror' for systems that do not implement it */ 90 #ifdef NO_STRERROR 91 #define strerror(_) "System unable to describe the error" 92 #endif 93 94 #define DIR_METATABLE "directory metatable" 95 typedef struct dir_data { 96 int closed; 97 #ifdef _WIN32 98 intptr_t hFile; 99 char pattern[MAX_PATH+1]; 100 #else 101 DIR *dir; 102 #endif 103 } dir_data; 104 105 #define LOCK_METATABLE "lock metatable" 106 107 #ifdef _WIN32 108 #ifdef __BORLANDC__ 109 #define lfs_setmode(file, m) (setmode(_fileno(file), m)) 110 #define STAT_STRUCT struct stati64 111 #else 112 #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) 113 #define STAT_STRUCT struct _stati64 114 #endif 115 #define STAT_FUNC _stati64 116 #define LSTAT_FUNC STAT_FUNC 117 #else 118 #define _O_TEXT 0 119 #define _O_BINARY 0 120 #define lfs_setmode(file, m) ((void)file, (void)m, 0) 121 #define STAT_STRUCT struct stat 122 #define STAT_FUNC stat 123 #define LSTAT_FUNC lstat 124 #endif 125 126 #ifdef _WIN32 127 #define lfs_mkdir _mkdir 128 #else 129 #define lfs_mkdir(path) (mkdir((path), \ 130 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) 131 #endif 132 133 /* 134 ** Utility functions 135 */ 136 static int pusherror(lua_State *L, const char *info) 137 { 138 lua_pushnil(L); 139 if (info==NULL) 140 lua_pushstring(L, strerror(errno)); 141 else 142 lua_pushfstring(L, "%s: %s", info, strerror(errno)); 143 lua_pushinteger(L, errno); 144 return 3; 145 } 146 147 static int pushresult(lua_State *L, int res, const char *info) { 148 if (res == -1) { 149 return pusherror(L, info); 150 } else { 151 lua_pushboolean(L, 1); 152 return 1; 153 } 154 } 155 156 157 /* 158 ** This function changes the working (current) directory 159 */ 160 static int change_dir (lua_State *L) { 161 const char *path = luaL_checkstring(L, 1); 162 if (chdir(path)) { 163 lua_pushnil (L); 164 lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", 165 path, chdir_error); 166 return 2; 167 } else { 168 lua_pushboolean (L, 1); 169 return 1; 170 } 171 } 172 173 /* 174 ** This function returns the current directory 175 ** If unable to get the current directory, it returns nil 176 ** and a string describing the error 177 */ 178 static int get_dir (lua_State *L) { 179 #ifdef NO_GETCWD 180 lua_pushnil(L); 181 lua_pushstring(L, "Function 'getcwd' not provided by system"); 182 return 2; 183 #else 184 char *path = NULL; 185 /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ 186 size_t size = LFS_MAXPATHLEN; /* initial buffer size */ 187 int result; 188 while (1) { 189 path = realloc(path, size); 190 if (!path) /* failed to allocate */ 191 return pusherror(L, "get_dir realloc() failed"); 192 if (getcwd(path, size) != NULL) { 193 /* success, push the path to the Lua stack */ 194 lua_pushstring(L, path); 195 result = 1; 196 break; 197 } 198 if (errno != ERANGE) { /* unexpected error */ 199 result = pusherror(L, "get_dir getcwd() failed"); 200 break; 201 } 202 /* ERANGE = insufficient buffer capacity, double size and retry */ 203 size *= 2; 204 } 205 free(path); 206 return result; 207 #endif 208 } 209 210 /* 211 ** Check if the given element on the stack is a file and returns it. 212 */ 213 static FILE *check_file (lua_State *L, int idx, const char *funcname) { 214 #if LUA_VERSION_NUM == 501 215 FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); 216 if (*fh == NULL) { 217 luaL_error (L, "%s: closed file", funcname); 218 return 0; 219 } else 220 return *fh; 221 #elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 222 luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); 223 if (fh->closef == 0 || fh->f == NULL) { 224 luaL_error (L, "%s: closed file", funcname); 225 return 0; 226 } else 227 return fh->f; 228 #else 229 #error unsupported Lua version 230 #endif 231 } 232 233 234 /* 235 ** 236 */ 237 static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { 238 int code; 239 #ifdef _WIN32 240 /* lkmode valid values are: 241 LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. 242 LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. 243 LK_NBRLCK Same as _LK_NBLCK. 244 LK_RLCK Same as _LK_LOCK. 245 LK_UNLCK Unlocks the specified bytes, which must have been previously locked. 246 247 Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. 248 249 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp 250 */ 251 int lkmode; 252 switch (*mode) { 253 case 'r': lkmode = LK_NBLCK; break; 254 case 'w': lkmode = LK_NBLCK; break; 255 case 'u': lkmode = LK_UNLCK; break; 256 default : return luaL_error (L, "%s: invalid mode", funcname); 257 } 258 if (!len) { 259 fseek (fh, 0L, SEEK_END); 260 len = ftell (fh); 261 } 262 fseek (fh, start, SEEK_SET); 263 #ifdef __BORLANDC__ 264 code = locking (fileno(fh), lkmode, len); 265 #else 266 code = _locking (fileno(fh), lkmode, len); 267 #endif 268 #else 269 struct flock f; 270 switch (*mode) { 271 case 'w': f.l_type = F_WRLCK; break; 272 case 'r': f.l_type = F_RDLCK; break; 273 case 'u': f.l_type = F_UNLCK; break; 274 default : return luaL_error (L, "%s: invalid mode", funcname); 275 } 276 f.l_whence = SEEK_SET; 277 f.l_start = (off_t)start; 278 f.l_len = (off_t)len; 279 code = fcntl (fileno(fh), F_SETLK, &f); 280 #endif 281 return (code != -1); 282 } 283 284 #ifdef _WIN32 285 typedef struct lfs_Lock { 286 HANDLE fd; 287 } lfs_Lock; 288 static int lfs_lock_dir(lua_State *L) { 289 size_t pathl; HANDLE fd; 290 lfs_Lock *lock; 291 char *ln; 292 const char *lockfile = "/lockfile.lfs"; 293 const char *path = luaL_checklstring(L, 1, &pathl); 294 ln = (char*)malloc(pathl + strlen(lockfile) + 1); 295 if(!ln) { 296 lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; 297 } 298 strcpy(ln, path); strcat(ln, lockfile); 299 if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, 300 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { 301 int en = GetLastError(); 302 free(ln); lua_pushnil(L); 303 if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) 304 lua_pushstring(L, "File exists"); 305 else 306 lua_pushstring(L, strerror(en)); 307 return 2; 308 } 309 free(ln); 310 lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); 311 lock->fd = fd; 312 luaL_getmetatable (L, LOCK_METATABLE); 313 lua_setmetatable (L, -2); 314 return 1; 315 } 316 static int lfs_unlock_dir(lua_State *L) { 317 lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); 318 if(lock->fd != INVALID_HANDLE_VALUE) { 319 CloseHandle(lock->fd); 320 lock->fd=INVALID_HANDLE_VALUE; 321 } 322 return 0; 323 } 324 #else 325 typedef struct lfs_Lock { 326 char *ln; 327 } lfs_Lock; 328 static int lfs_lock_dir(lua_State *L) { 329 lfs_Lock *lock; 330 size_t pathl; 331 char *ln; 332 const char *lockfile = "/lockfile.lfs"; 333 const char *path = luaL_checklstring(L, 1, &pathl); 334 lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); 335 ln = (char*)malloc(pathl + strlen(lockfile) + 1); 336 if(!ln) { 337 lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; 338 } 339 strcpy(ln, path); strcat(ln, lockfile); 340 if(symlink("lock", ln) == -1) { 341 free(ln); lua_pushnil(L); 342 lua_pushstring(L, strerror(errno)); return 2; 343 } 344 lock->ln = ln; 345 luaL_getmetatable (L, LOCK_METATABLE); 346 lua_setmetatable (L, -2); 347 return 1; 348 } 349 static int lfs_unlock_dir(lua_State *L) { 350 lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); 351 if(lock->ln) { 352 unlink(lock->ln); 353 free(lock->ln); 354 lock->ln = NULL; 355 } 356 return 0; 357 } 358 #endif 359 360 static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { 361 static const int mode[] = {_O_BINARY, _O_TEXT}; 362 static const char *const modenames[] = {"binary", "text", NULL}; 363 int op = luaL_checkoption(L, arg, NULL, modenames); 364 int res = lfs_setmode(f, mode[op]); 365 if (res != -1) { 366 int i; 367 lua_pushboolean(L, 1); 368 for (i = 0; modenames[i] != NULL; i++) { 369 if (mode[i] == res) { 370 lua_pushstring(L, modenames[i]); 371 return 2; 372 } 373 } 374 lua_pushnil(L); 375 return 2; 376 } else { 377 return pusherror(L, NULL); 378 } 379 } 380 381 static int lfs_f_setmode(lua_State *L) { 382 return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); 383 } 384 385 /* 386 ** Locks a file. 387 ** @param #1 File handle. 388 ** @param #2 String with lock mode ('w'rite, 'r'ead). 389 ** @param #3 Number with start position (optional). 390 ** @param #4 Number with length (optional). 391 */ 392 static int file_lock (lua_State *L) { 393 FILE *fh = check_file (L, 1, "lock"); 394 const char *mode = luaL_checkstring (L, 2); 395 const long start = (long) luaL_optinteger (L, 3, 0); 396 long len = (long) luaL_optinteger (L, 4, 0); 397 if (_file_lock (L, fh, mode, start, len, "lock")) { 398 lua_pushboolean (L, 1); 399 return 1; 400 } else { 401 lua_pushnil (L); 402 lua_pushfstring (L, "%s", strerror(errno)); 403 return 2; 404 } 405 } 406 407 408 /* 409 ** Unlocks a file. 410 ** @param #1 File handle. 411 ** @param #2 Number with start position (optional). 412 ** @param #3 Number with length (optional). 413 */ 414 static int file_unlock (lua_State *L) { 415 FILE *fh = check_file (L, 1, "unlock"); 416 const long start = (long) luaL_optinteger (L, 2, 0); 417 long len = (long) luaL_optinteger (L, 3, 0); 418 if (_file_lock (L, fh, "u", start, len, "unlock")) { 419 lua_pushboolean (L, 1); 420 return 1; 421 } else { 422 lua_pushnil (L); 423 lua_pushfstring (L, "%s", strerror(errno)); 424 return 2; 425 } 426 } 427 428 429 /* 430 ** Creates a link. 431 ** @param #1 Object to link to. 432 ** @param #2 Name of link. 433 ** @param #3 True if link is symbolic (optional). 434 */ 435 static int make_link (lua_State *L) { 436 #ifndef _WIN32 437 const char *oldpath = luaL_checkstring(L, 1); 438 const char *newpath = luaL_checkstring(L, 2); 439 int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath); 440 if (res == -1) { 441 return pusherror(L, NULL); 442 } else { 443 lua_pushinteger(L, 0); 444 return 1; 445 } 446 #else 447 errno = ENOSYS; /* = "Function not implemented" */ 448 return pushresult(L, -1, "make_link is not supported on Windows"); 449 #endif 450 } 451 452 453 /* 454 ** Creates a directory. 455 ** @param #1 Directory path. 456 */ 457 static int make_dir (lua_State *L) { 458 const char *path = luaL_checkstring(L, 1); 459 return pushresult(L, lfs_mkdir(path), NULL); 460 } 461 462 463 /* 464 ** Removes a directory. 465 ** @param #1 Directory path. 466 */ 467 static int remove_dir (lua_State *L) { 468 const char *path = luaL_checkstring(L, 1); 469 return pushresult(L, rmdir(path), NULL); 470 } 471 472 473 /* 474 ** Directory iterator 475 */ 476 static int dir_iter (lua_State *L) { 477 #ifdef _WIN32 478 struct _finddata_t c_file; 479 #else 480 struct dirent *entry; 481 #endif 482 dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); 483 luaL_argcheck (L, d->closed == 0, 1, "closed directory"); 484 #ifdef _WIN32 485 if (d->hFile == 0L) { /* first entry */ 486 if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { 487 lua_pushnil (L); 488 lua_pushstring (L, strerror (errno)); 489 d->closed = 1; 490 return 2; 491 } else { 492 lua_pushstring (L, c_file.name); 493 return 1; 494 } 495 } else { /* next entry */ 496 if (_findnext (d->hFile, &c_file) == -1L) { 497 /* no more entries => close directory */ 498 _findclose (d->hFile); 499 d->closed = 1; 500 return 0; 501 } else { 502 lua_pushstring (L, c_file.name); 503 return 1; 504 } 505 } 506 #else 507 if ((entry = readdir (d->dir)) != NULL) { 508 lua_pushstring (L, entry->d_name); 509 return 1; 510 } else { 511 /* no more entries => close directory */ 512 closedir (d->dir); 513 d->closed = 1; 514 return 0; 515 } 516 #endif 517 } 518 519 520 /* 521 ** Closes directory iterators 522 */ 523 static int dir_close (lua_State *L) { 524 dir_data *d = (dir_data *)lua_touserdata (L, 1); 525 #ifdef _WIN32 526 if (!d->closed && d->hFile) { 527 _findclose (d->hFile); 528 } 529 #else 530 if (!d->closed && d->dir) { 531 closedir (d->dir); 532 } 533 #endif 534 d->closed = 1; 535 return 0; 536 } 537 538 539 /* 540 ** Factory of directory iterators 541 */ 542 static int dir_iter_factory (lua_State *L) { 543 const char *path = luaL_checkstring (L, 1); 544 dir_data *d; 545 lua_pushcfunction (L, dir_iter); 546 d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); 547 luaL_getmetatable (L, DIR_METATABLE); 548 lua_setmetatable (L, -2); 549 d->closed = 0; 550 #ifdef _WIN32 551 d->hFile = 0L; 552 if (strlen(path) > MAX_PATH-2) 553 luaL_error (L, "path too long: %s", path); 554 else 555 sprintf (d->pattern, "%s/*", path); 556 #else 557 d->dir = opendir (path); 558 if (d->dir == NULL) 559 luaL_error (L, "cannot open %s: %s", path, strerror (errno)); 560 #endif 561 return 2; 562 } 563 564 565 /* 566 ** Creates directory metatable. 567 */ 568 static int dir_create_meta (lua_State *L) { 569 luaL_newmetatable (L, DIR_METATABLE); 570 571 /* Method table */ 572 lua_newtable(L); 573 lua_pushcfunction (L, dir_iter); 574 lua_setfield(L, -2, "next"); 575 lua_pushcfunction (L, dir_close); 576 lua_setfield(L, -2, "close"); 577 578 /* Metamethods */ 579 lua_setfield(L, -2, "__index"); 580 lua_pushcfunction (L, dir_close); 581 lua_setfield (L, -2, "__gc"); 582 return 1; 583 } 584 585 586 /* 587 ** Creates lock metatable. 588 */ 589 static int lock_create_meta (lua_State *L) { 590 luaL_newmetatable (L, LOCK_METATABLE); 591 592 /* Method table */ 593 lua_newtable(L); 594 lua_pushcfunction(L, lfs_unlock_dir); 595 lua_setfield(L, -2, "free"); 596 597 /* Metamethods */ 598 lua_setfield(L, -2, "__index"); 599 lua_pushcfunction(L, lfs_unlock_dir); 600 lua_setfield(L, -2, "__gc"); 601 return 1; 602 } 603 604 605 #ifdef _WIN32 606 #ifndef S_ISDIR 607 #define S_ISDIR(mode) (mode&_S_IFDIR) 608 #endif 609 #ifndef S_ISREG 610 #define S_ISREG(mode) (mode&_S_IFREG) 611 #endif 612 #ifndef S_ISLNK 613 #define S_ISLNK(mode) (0) 614 #endif 615 #ifndef S_ISSOCK 616 #define S_ISSOCK(mode) (0) 617 #endif 618 #ifndef S_ISFIFO 619 #define S_ISFIFO(mode) (0) 620 #endif 621 #ifndef S_ISCHR 622 #define S_ISCHR(mode) (mode&_S_IFCHR) 623 #endif 624 #ifndef S_ISBLK 625 #define S_ISBLK(mode) (0) 626 #endif 627 #endif 628 /* 629 ** Convert the inode protection mode to a string. 630 */ 631 #ifdef _WIN32 632 static const char *mode2string (unsigned short mode) { 633 #else 634 static const char *mode2string (mode_t mode) { 635 #endif 636 if ( S_ISREG(mode) ) 637 return "file"; 638 else if ( S_ISDIR(mode) ) 639 return "directory"; 640 else if ( S_ISLNK(mode) ) 641 return "link"; 642 else if ( S_ISSOCK(mode) ) 643 return "socket"; 644 else if ( S_ISFIFO(mode) ) 645 return "named pipe"; 646 else if ( S_ISCHR(mode) ) 647 return "char device"; 648 else if ( S_ISBLK(mode) ) 649 return "block device"; 650 else 651 return "other"; 652 } 653 654 655 /* 656 ** Set access time and modification values for a file. 657 ** @param #1 File path. 658 ** @param #2 Access time in seconds, current time is used if missing. 659 ** @param #3 Modification time in seconds, access time is used if missing. 660 */ 661 static int file_utime (lua_State *L) { 662 const char *file = luaL_checkstring(L, 1); 663 struct utimbuf utb, *buf; 664 665 if (lua_gettop (L) == 1) /* set to current date/time */ 666 buf = NULL; 667 else { 668 utb.actime = (time_t) luaL_optnumber(L, 2, 0); 669 utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime); 670 buf = &utb; 671 } 672 673 return pushresult(L, utime(file, buf), NULL); 674 } 675 676 677 /* inode protection mode */ 678 static void push_st_mode (lua_State *L, STAT_STRUCT *info) { 679 lua_pushstring (L, mode2string (info->st_mode)); 680 } 681 /* device inode resides on */ 682 static void push_st_dev (lua_State *L, STAT_STRUCT *info) { 683 lua_pushinteger (L, (lua_Integer) info->st_dev); 684 } 685 /* inode's number */ 686 static void push_st_ino (lua_State *L, STAT_STRUCT *info) { 687 lua_pushinteger (L, (lua_Integer) info->st_ino); 688 } 689 /* number of hard links to the file */ 690 static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { 691 lua_pushinteger (L, (lua_Integer)info->st_nlink); 692 } 693 /* user-id of owner */ 694 static void push_st_uid (lua_State *L, STAT_STRUCT *info) { 695 lua_pushinteger (L, (lua_Integer)info->st_uid); 696 } 697 /* group-id of owner */ 698 static void push_st_gid (lua_State *L, STAT_STRUCT *info) { 699 lua_pushinteger (L, (lua_Integer)info->st_gid); 700 } 701 /* device type, for special file inode */ 702 static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { 703 lua_pushinteger (L, (lua_Integer) info->st_rdev); 704 } 705 /* time of last access */ 706 static void push_st_atime (lua_State *L, STAT_STRUCT *info) { 707 lua_pushinteger (L, (lua_Integer) info->st_atime); 708 } 709 /* time of last data modification */ 710 static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { 711 lua_pushinteger (L, (lua_Integer) info->st_mtime); 712 } 713 /* time of last file status change */ 714 static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { 715 lua_pushinteger (L, (lua_Integer) info->st_ctime); 716 } 717 /* file size, in bytes */ 718 static void push_st_size (lua_State *L, STAT_STRUCT *info) { 719 lua_pushinteger (L, (lua_Integer)info->st_size); 720 } 721 #ifndef _WIN32 722 /* blocks allocated for file */ 723 static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { 724 lua_pushinteger (L, (lua_Integer)info->st_blocks); 725 } 726 /* optimal file system I/O blocksize */ 727 static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { 728 lua_pushinteger (L, (lua_Integer)info->st_blksize); 729 } 730 #endif 731 732 /* 733 ** Convert the inode protection mode to a permission list. 734 */ 735 736 #ifdef _WIN32 737 static const char *perm2string (unsigned short mode) { 738 static char perms[10] = "---------"; 739 int i; 740 for (i=0;i<9;i++) perms[i]='-'; 741 if (mode & _S_IREAD) 742 { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } 743 if (mode & _S_IWRITE) 744 { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } 745 if (mode & _S_IEXEC) 746 { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } 747 return perms; 748 } 749 #else 750 static const char *perm2string (mode_t mode) { 751 static char perms[10] = "---------"; 752 int i; 753 for (i=0;i<9;i++) perms[i]='-'; 754 if (mode & S_IRUSR) perms[0] = 'r'; 755 if (mode & S_IWUSR) perms[1] = 'w'; 756 if (mode & S_IXUSR) perms[2] = 'x'; 757 if (mode & S_IRGRP) perms[3] = 'r'; 758 if (mode & S_IWGRP) perms[4] = 'w'; 759 if (mode & S_IXGRP) perms[5] = 'x'; 760 if (mode & S_IROTH) perms[6] = 'r'; 761 if (mode & S_IWOTH) perms[7] = 'w'; 762 if (mode & S_IXOTH) perms[8] = 'x'; 763 return perms; 764 } 765 #endif 766 767 /* permssions string */ 768 static void push_st_perm (lua_State *L, STAT_STRUCT *info) { 769 lua_pushstring (L, perm2string (info->st_mode)); 770 } 771 772 typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); 773 774 struct _stat_members { 775 const char *name; 776 _push_function push; 777 }; 778 779 struct _stat_members members[] = { 780 { "mode", push_st_mode }, 781 { "dev", push_st_dev }, 782 { "ino", push_st_ino }, 783 { "nlink", push_st_nlink }, 784 { "uid", push_st_uid }, 785 { "gid", push_st_gid }, 786 { "rdev", push_st_rdev }, 787 { "access", push_st_atime }, 788 { "modification", push_st_mtime }, 789 { "change", push_st_ctime }, 790 { "size", push_st_size }, 791 { "permissions", push_st_perm }, 792 #ifndef _WIN32 793 { "blocks", push_st_blocks }, 794 { "blksize", push_st_blksize }, 795 #endif 796 { NULL, NULL } 797 }; 798 799 /* 800 ** Get file or symbolic link information 801 */ 802 static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { 803 STAT_STRUCT info; 804 const char *file = luaL_checkstring (L, 1); 805 int i; 806 807 if (st(file, &info)) { 808 lua_pushnil(L); 809 lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); 810 lua_pushinteger(L, errno); 811 return 3; 812 } 813 if (lua_isstring (L, 2)) { 814 const char *member = lua_tostring (L, 2); 815 for (i = 0; members[i].name; i++) { 816 if (strcmp(members[i].name, member) == 0) { 817 /* push member value and return */ 818 members[i].push (L, &info); 819 return 1; 820 } 821 } 822 /* member not found */ 823 return luaL_error(L, "invalid attribute name '%s'", member); 824 } 825 /* creates a table if none is given, removes extra arguments */ 826 lua_settop(L, 2); 827 if (!lua_istable (L, 2)) { 828 lua_newtable (L); 829 } 830 /* stores all members in table on top of the stack */ 831 for (i = 0; members[i].name; i++) { 832 lua_pushstring (L, members[i].name); 833 members[i].push (L, &info); 834 lua_rawset (L, -3); 835 } 836 return 1; 837 } 838 839 840 /* 841 ** Get file information using stat. 842 */ 843 static int file_info (lua_State *L) { 844 return _file_info_ (L, STAT_FUNC); 845 } 846 847 848 /* 849 ** Push the symlink target to the top of the stack. 850 ** Assumes the file name is at position 1 of the stack. 851 ** Returns 1 if successful (with the target on top of the stack), 852 ** 0 on failure (with stack unchanged, and errno set). 853 */ 854 static int push_link_target(lua_State *L) { 855 #ifdef _WIN32 856 errno = ENOSYS; 857 return 0; 858 #else 859 const char *file = luaL_checkstring(L, 1); 860 char *target = NULL; 861 int tsize, size = 256; /* size = initial buffer capacity */ 862 while (1) { 863 target = realloc(target, size); 864 if (!target) /* failed to allocate */ 865 return 0; 866 tsize = readlink(file, target, size); 867 if (tsize < 0) { /* a readlink() error occurred */ 868 free(target); 869 return 0; 870 } 871 if (tsize < size) 872 break; 873 /* possibly truncated readlink() result, double size and retry */ 874 size *= 2; 875 } 876 target[tsize] = '\0'; 877 lua_pushlstring(L, target, tsize); 878 free(target); 879 return 1; 880 #endif 881 } 882 883 /* 884 ** Get symbolic link information using lstat. 885 */ 886 static int link_info (lua_State *L) { 887 int ret; 888 if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { 889 int ok = push_link_target(L); 890 return ok ? 1 : pusherror(L, "could not obtain link target"); 891 } 892 ret = _file_info_ (L, LSTAT_FUNC); 893 if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { 894 int ok = push_link_target(L); 895 if (ok) { 896 lua_setfield(L, -2, "target"); 897 } 898 } 899 return ret; 900 } 901 902 903 /* 904 ** Assumes the table is on top of the stack. 905 */ 906 static void set_info (lua_State *L) { 907 lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); 908 lua_setfield(L, -2, "_COPYRIGHT"); 909 lua_pushliteral(L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); 910 lua_setfield(L, -2, "_DESCRIPTION"); 911 lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); 912 lua_setfield(L, -2, "_VERSION"); 913 } 914 915 916 static const struct luaL_Reg fslib[] = { 917 {"attributes", file_info}, 918 {"chdir", change_dir}, 919 {"currentdir", get_dir}, 920 {"dir", dir_iter_factory}, 921 {"link", make_link}, 922 {"lock", file_lock}, 923 {"mkdir", make_dir}, 924 {"rmdir", remove_dir}, 925 {"symlinkattributes", link_info}, 926 {"setmode", lfs_f_setmode}, 927 {"touch", file_utime}, 928 {"unlock", file_unlock}, 929 {"lock_dir", lfs_lock_dir}, 930 {NULL, NULL}, 931 }; 932 933 LFS_EXPORT int luaopen_lfs (lua_State *L) { 934 dir_create_meta (L); 935 lock_create_meta (L); 936 new_lib (L, fslib); 937 lua_pushvalue(L, -1); 938 lua_setglobal(L, LFS_LIBNAME); 939 set_info (L); 940 return 1; 941 }