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