Rework LuCI build system
[project/luci.git] / libs / luci-lib-nixio / src / fs.c
1 /*
2  * nixio - Linux I/O library for lua
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "nixio.h"
20 #include <libgen.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <dirent.h>
29
30 /* Reads argument from given index and transforms it into a mode bitfield */
31 int nixio__check_mode(lua_State *L, int idx, int def) {
32         if (lua_isnoneornil(L, idx) && def > 0) {
33                 return def;
34         } else if (lua_isstring(L, idx) && lua_objlen(L, idx) == 9) {
35                 int mode = 0;
36                 const char *modestr = lua_tostring(L, idx);
37                 int i;
38                 for (i=0; i<9; i++) {
39                         if (i % 3 == 0) {                       /* read flags */
40                                 if (modestr[i] == 'r') {
41                                         mode |= 1 << (8 - i);
42                                 } else if (modestr[i] != '-') {
43                                         break;
44                                 }
45                         } else if (i % 3 == 1) {        /* write flags */
46                                 if (modestr[i] == 'w') {
47                                         mode |= 1 << (8 - i);
48                                 } else if (modestr[i] != '-') {
49                                         break;
50                                 }
51                         } else if (i == 2) {
52                                 if (modestr[i] == 'x') {
53                                         mode |= 00100;
54                                 } else if (modestr[i] == 's') {
55                                         mode |= 04100;
56                                 } else if (modestr[i] == 'S') {
57                                         mode |= 04000;
58                                 } else if (modestr[i] != '-') {
59                                         break;
60                                 }
61                         } else if (i == 5) {
62                                 if (modestr[i] == 'x') {
63                                         mode |= 00010;
64                                 } else if (modestr[i] == 's') {
65                                         mode |= 02010;
66                                 } else if (modestr[i] == 'S') {
67                                         mode |= 02000;
68                                 } else if (modestr[i] != '-') {
69                                         break;
70                                 }
71                         } else if (i == 8) {
72                                 if (modestr[i] == 'x') {
73                                         mode |= 00001;
74                                 } else if (modestr[i] == 't') {
75                                         mode |= 01001;
76                                 } else if (modestr[i] == 'T') {
77                                         mode |= 01000;
78                                 } else if (modestr[i] != '-') {
79                                         break;
80                                 }
81                         }
82                 }
83                 if (i == 9) {   /* successfully parsed */
84                         return mode;
85                 }
86         } else if (lua_isnumber(L, idx)) {
87                 int decmode = lua_tointeger(L, idx);
88                 int s = (decmode % 10000)       / 1000;
89                 int u = (decmode % 1000)        / 100;
90                 int g = (decmode % 100)         / 10;
91                 int o = (decmode % 10);
92
93                 if (s>=0 && s<=7 && u>=0 && u<=7 && g>=0 && g<=7 && o>=0 && o<=7) {
94                         return (s << 9) + (u << 6) + (g << 3) + o;
95                 }
96         }
97
98         return luaL_argerror(L, idx, "supported values: [0-7]?[0-7][0-7][0-7], "
99                                 "[-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]");
100 }
101
102 /* Transforms a mode into the modestring */
103 int nixio__mode_write(int mode, char *modestr) {
104         if (modestr) {
105                 modestr[0] = (mode & 00400) ? 'r' : '-';
106                 modestr[1] = (mode & 00200) ? 'w' : '-';
107                 modestr[2] = ((mode & 04100) == 04100) ? 's' :
108                         (mode & 04000) ? 'S' : (mode & 00100) ? 'x' : '-';
109                 modestr[3] = (mode & 00040) ? 'r' : '-';
110                 modestr[4] = (mode & 00020) ? 'w' : '-';
111                 modestr[5] = ((mode & 02010) == 02010) ? 's' :
112                         (mode & 02000) ? 'S' : (mode & 00010) ? 'x' : '-';
113                 modestr[6] = (mode & 00004) ? 'r' : '-';
114                 modestr[7] = (mode & 00002) ? 'w' : '-';
115                 modestr[8] = ((mode & 01001) == 01001) ? 't' :
116                         (mode & 01000) ? 'T' : (mode & 00001) ? 'x' : '-';
117         }
118
119         return (mode & 00007) + ((mode & 00070) >> 3) * 10 +
120                 ((mode & 00700) >> 6) * 100 + ((mode & 07000) >> 9) * 1000;
121 }
122
123 static int nixio_access(lua_State *L) {
124         const char *path = luaL_checkstring(L, 1);
125         int mode = F_OK;
126
127         for (const char *s = luaL_optstring(L, 2, "f"); *s; s++) {
128                 if (*s == 'r') {
129                         mode |= R_OK;
130                 } else if (*s == 'w') {
131                         mode |= W_OK;
132                 } else if (*s == 'x') {
133                         mode |= X_OK;
134                 } else if (*s != 'f') {
135                         return luaL_argerror(L, 2, "supported values: [frwx]");
136                 }
137         }
138
139         return nixio__pstatus(L, !access(path, mode));
140 }
141
142 static int nixio_basename(lua_State *L) {
143         const char *path = luaL_checkstring(L, 1);
144         char base[PATH_MAX];
145         base[PATH_MAX-1] = 0;
146
147         strncpy(base, path, PATH_MAX-1);
148         lua_pushstring(L, basename(base));
149         return 1;
150 }
151
152 static int nixio_dirname(lua_State *L) {
153         const char *path = luaL_checkstring(L, 1);
154         char base[PATH_MAX];
155         base[PATH_MAX-1] = 0;
156
157         strncpy(base, path, PATH_MAX-1);
158         lua_pushstring(L, dirname(base));
159         return 1;
160 }
161
162 static int nixio_realpath(lua_State *L) {
163         const char *path = luaL_checkstring(L, 1);
164         char real[PATH_MAX];
165
166         if (!realpath(path, real)) {
167                 return nixio__perror(L);
168         } else {
169                 lua_pushstring(L, real);
170                 return 1;
171         }
172 }
173
174 static int nixio_remove(lua_State *L) {
175         return nixio__pstatus(L, !remove(luaL_checkstring(L, 1)));
176 }
177
178 static int nixio_unlink(lua_State *L) {
179         return nixio__pstatus(L, !unlink(luaL_checkstring(L, 1)));
180 }
181
182 static int nixio_rename(lua_State *L) {
183         return nixio__pstatus(L,
184                         !rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
185 }
186
187 static int nixio_rmdir(lua_State *L) {
188         return nixio__pstatus(L, !rmdir(luaL_checkstring(L, 1)));
189 }
190
191 static int nixio_mkdir(lua_State *L) {
192         return nixio__pstatus(L,
193                         !mkdir(luaL_checkstring(L, 1), nixio__check_mode(L, 2, 0777)));
194 }
195
196 static int nixio_chmod(lua_State *L) {
197         return nixio__pstatus(L,
198                         !chmod(luaL_checkstring(L, 1), nixio__check_mode(L, 2, -1)));
199 }
200
201 static int nixio_dir__gc(lua_State *L) {
202         DIR **dirp = lua_touserdata(L, 1);
203         if (dirp && *dirp) {
204                 closedir(*dirp);
205                 *dirp = NULL;
206         }
207         return 0;
208 }
209
210 static int nixio_dir__iter(lua_State *L) {
211         DIR **dirp = lua_touserdata(L, lua_upvalueindex(1));
212         struct dirent *entry;
213         const char *n = NULL;
214
215         if (*dirp) {
216                 do {
217                         entry = readdir(*dirp);
218                         n = (entry) ? entry->d_name : NULL;
219                 } while(n && n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0)));
220         }
221
222         if (n) {
223                 lua_pushstring(L, n);
224         } else {
225                 if (*dirp) {
226                         closedir(*dirp);
227                         *dirp = NULL;
228                 }
229                 lua_pushnil(L);
230         }
231
232         return 1;
233 }
234
235 static int nixio_dir(lua_State *L) {
236         const char *path = luaL_optstring(L, 1, ".");
237         DIR **dirp = lua_newuserdata(L, sizeof(DIR *));
238
239         *dirp = opendir(path);
240         if (!*dirp) {
241                 return nixio__perror(L);
242         } else {
243                 luaL_getmetatable(L, NIXIO_DIR_META);
244                 lua_setmetatable(L, -2);
245                 lua_pushcclosure(L, nixio_dir__iter, 1);
246                 return 1;
247         }
248 }
249
250 static int nixio_link(lua_State *L) {
251         return nixio__pstatus(L,
252                         !link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
253 }
254
255 static int nixio_utimes(lua_State *L) {
256         const char *path = luaL_checkstring(L, 1);
257         if (lua_gettop(L) < 2 || (lua_isnoneornil(L, 2) && lua_isnoneornil(L, 3))) {
258                 return nixio__pstatus(L, !utimes(path, NULL));
259         } else {
260                 double atime = nixio__checknumber(L, 2);
261                 double mtime = nixio__optnumber(L, 3, atime);
262                 struct timeval times[2];
263
264                 times[0].tv_sec = atime;
265                 times[0].tv_usec = 0;
266                 times[1].tv_sec = mtime;
267                 times[1].tv_usec = 0;
268
269                 return nixio__pstatus(L, !utimes(path, times));
270         }
271 }
272
273 int nixio__push_stat(lua_State *L, nixio_stat_t *buf) {
274         lua_createtable(L, 0, 15);
275
276         lua_pushinteger(L, buf->st_dev);
277         lua_setfield(L, -2, "dev");
278
279         lua_pushinteger(L, buf->st_ino);
280         lua_setfield(L, -2, "ino");
281
282         if (S_ISREG(buf->st_mode)) {
283                 lua_pushliteral(L, "reg");
284         } else if (S_ISDIR(buf->st_mode)) {
285                 lua_pushliteral(L, "dir");
286         } else if (S_ISCHR(buf->st_mode)) {
287                 lua_pushliteral(L, "chr");
288         } else if (S_ISBLK(buf->st_mode)) {
289                 lua_pushliteral(L, "blk");
290         } else if (S_ISFIFO(buf->st_mode)) {
291                 lua_pushliteral(L, "fifo");
292         } else if (S_ISLNK(buf->st_mode)) {
293                 lua_pushliteral(L, "lnk");
294         } else if (S_ISSOCK(buf->st_mode)) {
295                 lua_pushliteral(L, "sock");
296         } else {
297                 lua_pushliteral(L, "unknown");
298         }
299         lua_setfield(L, -2, "type");
300
301         char modestr[9];
302         lua_pushinteger(L, nixio__mode_write(buf->st_mode, modestr));
303         lua_setfield(L, -2, "modedec");
304
305         lua_pushlstring(L, modestr, 9);
306         lua_setfield(L, -2, "modestr");
307
308         lua_pushinteger(L, buf->st_nlink);
309         lua_setfield(L, -2, "nlink");
310
311         lua_pushinteger(L, buf->st_uid);
312         lua_setfield(L, -2, "uid");
313
314         lua_pushinteger(L, buf->st_gid);
315         lua_setfield(L, -2, "gid");
316
317         lua_pushinteger(L, buf->st_rdev);
318         lua_setfield(L, -2, "rdev");
319
320         nixio__pushnumber(L, buf->st_size);
321         lua_setfield(L, -2, "size");
322
323         lua_pushinteger(L, buf->st_atime);
324         lua_setfield(L, -2, "atime");
325
326         lua_pushinteger(L, buf->st_mtime);
327         lua_setfield(L, -2, "mtime");
328
329         lua_pushinteger(L, buf->st_ctime);
330         lua_setfield(L, -2, "ctime");
331
332 #ifndef __WINNT__
333         lua_pushinteger(L, buf->st_blksize);
334         lua_setfield(L, -2, "blksize");
335
336         lua_pushinteger(L, buf->st_blocks);
337         lua_setfield(L, -2, "blocks");
338 #endif
339
340         return 1;
341 }
342
343 static int nixio_stat(lua_State *L) {
344         nixio_stat_t buf;
345         if (stat(luaL_checkstring(L, 1), &buf)) {
346                 return nixio__perror(L);
347         } else {
348                 nixio__push_stat(L, &buf);
349                 if (lua_isstring(L, 2)) {
350                         lua_getfield(L, -1, lua_tostring(L, 2));
351                 }
352                 return 1;
353         }
354 }
355
356 static int nixio_lstat(lua_State *L) {
357         nixio_stat_t buf;
358         if (stat(luaL_checkstring(L, 1), &buf)) {
359                 return nixio__perror(L);
360         } else {
361                 nixio__push_stat(L, &buf);
362                 if (lua_isstring(L, 2)) {
363                         lua_getfield(L, -1, lua_tostring(L, 2));
364                 }
365                 return 1;
366         }
367 }
368
369 #ifndef __WINNT__
370
371 static int nixio_chown(lua_State *L) {
372         return nixio__pstatus(L,
373                         !chown(
374                                         luaL_checkstring(L, 1),
375                                         lua_isnoneornil(L, 2) ? -1 : nixio__check_user(L, 2),
376                                         lua_isnoneornil(L, 3) ? -1 : nixio__check_group(L, 3)
377                         )
378         );
379 }
380
381 static int nixio_lchown(lua_State *L) {
382         return nixio__pstatus(L,
383                         !lchown(
384                                         luaL_checkstring(L, 1),
385                                         lua_isnoneornil(L, 2) ? -1 : nixio__check_user(L, 2),
386                                         lua_isnoneornil(L, 3) ? -1 : nixio__check_group(L, 3)
387                         )
388         );
389 }
390
391 static int nixio_mkfifo(lua_State *L) {
392         return nixio__pstatus(L,
393                         !mkfifo(luaL_checkstring(L, 1), nixio__check_mode(L, 2, -1)));
394 }
395
396 static int nixio_symlink(lua_State *L) {
397         return nixio__pstatus(L,
398                         !symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
399 }
400
401 static int nixio_readlink(lua_State *L) {
402         char dest[PATH_MAX];
403         ssize_t res = readlink(luaL_checkstring(L, 1), dest, sizeof(dest));
404         if (res < 0) {
405                 return nixio__perror(L);
406         } else {
407                 lua_pushlstring(L, dest, res);
408                 return 1;
409         }
410 }
411
412 #include <glob.h>
413
414 typedef struct {
415         glob_t gl;
416         size_t pos;
417         int     freed;
418 } nixio_glob_t;
419
420 static int nixio_glob__iter(lua_State *L) {
421         nixio_glob_t *globres = lua_touserdata(L, lua_upvalueindex(1));
422         if (!globres->freed && globres->pos < globres->gl.gl_pathc) {
423                 lua_pushstring(L, globres->gl.gl_pathv[(globres->pos)++]);
424         } else {
425                 if (!globres->freed) {
426                         globfree(&globres->gl);
427                         globres->freed = 1;
428                 }
429                 lua_pushnil(L);
430         }
431         return 1;
432 }
433
434 static int nixio_glob__gc(lua_State *L) {
435         nixio_glob_t *globres = lua_touserdata(L, 1);
436         if (globres && !globres->freed) {
437                 globres->freed = 1;
438                 globfree(&globres->gl);
439         }
440         return 0;
441 }
442
443 static int nixio_glob(lua_State *L) {
444          const char *pattern = luaL_optstring(L, 1, "*");
445          nixio_glob_t *globres = lua_newuserdata(L, sizeof(nixio_glob_t));
446          if (!globres) {
447                  return luaL_error(L, NIXIO_OOM);
448          }
449          globres->pos = 0;
450          globres->freed = 0;
451
452          int globstat = glob(pattern, 0, NULL, &globres->gl);
453          if (globstat == GLOB_NOMATCH) {
454                  lua_pushcfunction(L, nixio__nulliter);
455                  lua_pushinteger(L, 0);
456          } else if (globstat) {
457                  return nixio__perror(L);
458          } else {
459                  luaL_getmetatable(L, NIXIO_GLOB_META);
460                  lua_setmetatable(L, -2);
461                  lua_pushcclosure(L, nixio_glob__iter, 1);
462                  lua_pushinteger(L, globres->gl.gl_pathc);
463          }
464          return 2;
465 }
466
467 #include <sys/statvfs.h>
468
469 static int nixio__push_statvfs(lua_State *L, struct statvfs *buf) {
470         lua_createtable(L, 0, 12);
471
472         nixio__pushnumber(L, buf->f_bavail);
473         lua_setfield(L, -2, "bavail");
474
475         nixio__pushnumber(L, buf->f_bfree);
476         lua_setfield(L, -2, "bfree");
477
478         nixio__pushnumber(L, buf->f_blocks);
479         lua_setfield(L, -2, "blocks");
480
481         nixio__pushnumber(L, buf->f_bsize);
482         lua_setfield(L, -2, "bsize");
483
484         nixio__pushnumber(L, buf->f_frsize);
485         lua_setfield(L, -2, "frsize");
486
487         nixio__pushnumber(L, buf->f_favail);
488         lua_setfield(L, -2, "favail");
489
490         nixio__pushnumber(L, buf->f_ffree);
491         lua_setfield(L, -2, "ffree");
492
493         nixio__pushnumber(L, buf->f_files);
494         lua_setfield(L, -2, "files");
495
496         nixio__pushnumber(L, buf->f_flag);
497         lua_setfield(L, -2, "flag");
498
499         nixio__pushnumber(L, buf->f_fsid);
500         lua_setfield(L, -2, "fsid");
501
502         nixio__pushnumber(L, buf->f_namemax);
503         lua_setfield(L, -2, "namemax");
504
505         return 1;
506 }
507
508 static int nixio_statvfs(lua_State *L) {
509         struct statvfs buf;
510         if (statvfs(luaL_optstring(L, 1, "."), &buf)) {
511                 return nixio__perror(L);
512         } else {
513                 return nixio__push_statvfs(L, &buf);
514         }
515 }
516
517 #endif /* !__WINNT__ */
518
519
520
521 /* module table */
522 static const luaL_reg R[] = {
523 #ifndef __WINNT__
524         {"glob",                nixio_glob},
525         {"mkfifo",              nixio_mkfifo},
526         {"symlink",             nixio_symlink},
527         {"readlink",    nixio_readlink},
528         {"chown",               nixio_chown},
529         {"lchown",              nixio_lchown},
530         {"statvfs",             nixio_statvfs},
531 #endif
532         {"chmod",               nixio_chmod},
533         {"access",              nixio_access},
534         {"basename",    nixio_basename},
535         {"dir",                 nixio_dir},
536         {"dirname",             nixio_dirname},
537         {"realpath",    nixio_realpath},
538         {"mkdir",               nixio_mkdir},
539         {"rmdir",               nixio_rmdir},
540         {"link",                nixio_link},
541         {"unlink",              nixio_unlink},
542         {"utimes",              nixio_utimes},
543         {"rename",              nixio_rename},
544         {"remove",              nixio_remove},
545         {"stat",                nixio_stat},
546         {"lstat",               nixio_lstat},
547         {NULL,                  NULL}
548 };
549
550 void nixio_open_fs(lua_State *L) {
551         lua_newtable(L);
552         luaL_register(L, NULL, R);
553         lua_setfield(L, -2, "fs");
554
555         luaL_newmetatable(L, NIXIO_DIR_META);
556         lua_pushcfunction(L, nixio_dir__gc);
557         lua_setfield(L, -2, "__gc");
558         lua_pop(L, 1);
559
560 #ifndef __WINNT__
561         luaL_newmetatable(L, NIXIO_GLOB_META);
562         lua_pushcfunction(L, nixio_glob__gc);
563         lua_setfield(L, -2, "__gc");
564         lua_pop(L, 1);
565 #endif
566 }