• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/fs.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <stdio.h>
 18 #include <errno.h>
 19 #include <string.h>
 20 #include <dirent.h>
 21 #include <unistd.h>
 22 #include <sys/stat.h>
 23 #include <sys/types.h>
 24 #include <grp.h>
 25 #include <pwd.h>
 26 #include <glob.h>
 27 #include <fnmatch.h>
 28 #include <limits.h>
 29 #include <fcntl.h>
 30 
 31 #ifndef __APPLE__
 32 #include <sys/sysmacros.h> /* major(), minor() */
 33 #endif
 34 
 35 #include "ucode/module.h"
 36 
 37 #define err_return(err) do { last_error = err; return NULL; } while(0)
 38 
 39 //static const uc_ops *ops;
 40 static uc_resource_type_t *file_type, *proc_type, *dir_type;
 41 
 42 static int last_error = 0;
 43 
 44 static uc_value_t *
 45 uc_fs_error(uc_vm_t *vm, size_t nargs)
 46 {
 47         uc_value_t *errmsg;
 48 
 49         if (last_error == 0)
 50                 return NULL;
 51 
 52         errmsg = ucv_string_new(strerror(last_error));
 53         last_error = 0;
 54 
 55         return errmsg;
 56 }
 57 
 58 static uc_value_t *
 59 uc_fs_read_common(uc_vm_t *vm, size_t nargs, const char *type)
 60 {
 61         uc_value_t *limit = uc_fn_arg(0);
 62         uc_value_t *rv = NULL;
 63         char buf[128], *p = NULL, *tmp;
 64         size_t rlen, len = 0;
 65         const char *lstr;
 66         int64_t lsize;
 67         ssize_t llen;
 68 
 69         FILE **fp = uc_fn_this(type);
 70 
 71         if (!fp || !*fp)
 72                 err_return(EBADF);
 73 
 74         if (ucv_type(limit) == UC_STRING) {
 75                 lstr = ucv_string_get(limit);
 76                 llen = ucv_string_length(limit);
 77 
 78                 if (llen == 4 && !strcmp(lstr, "line")) {
 79                         llen = getline(&p, &rlen, *fp);
 80 
 81                         if (llen == -1)
 82                                 err_return(errno);
 83 
 84                         len = (size_t)llen;
 85                 }
 86                 else if (llen == 3 && !strcmp(lstr, "all")) {
 87                         while (true) {
 88                                 rlen = fread(buf, 1, sizeof(buf), *fp);
 89 
 90                                 tmp = realloc(p, len + rlen);
 91 
 92                                 if (!tmp) {
 93                                         free(p);
 94                                         err_return(ENOMEM);
 95                                 }
 96 
 97                                 memcpy(tmp + len, buf, rlen);
 98 
 99                                 p = tmp;
100                                 len += rlen;
101 
102                                 if (rlen == 0)
103                                         break;
104                         }
105                 }
106                 else if (llen == 1) {
107                         llen = getdelim(&p, &rlen, *lstr, *fp);
108 
109                         if (llen == -1)
110                                 err_return(errno);
111 
112                         len = (size_t)llen;
113                 }
114                 else {
115                         return NULL;
116                 }
117         }
118         else if (ucv_type(limit) == UC_INTEGER) {
119                 lsize = ucv_int64_get(limit);
120 
121                 if (lsize <= 0)
122                         return NULL;
123 
124                 p = calloc(1, lsize);
125 
126                 if (!p)
127                         err_return(ENOMEM);
128 
129                 len = fread(p, 1, lsize, *fp);
130 
131                 if (ferror(*fp)) {
132                         free(p);
133                         err_return(errno);
134                 }
135         }
136         else {
137                 err_return(EINVAL);
138         }
139 
140         rv = ucv_string_new_length(p, len);
141         free(p);
142 
143         return rv;
144 }
145 
146 static uc_value_t *
147 uc_fs_write_common(uc_vm_t *vm, size_t nargs, const char *type)
148 {
149         uc_value_t *data = uc_fn_arg(0);
150         size_t len, wsize;
151         char *str;
152 
153         FILE **fp = uc_fn_this(type);
154 
155         if (!fp || !*fp)
156                 err_return(EBADF);
157 
158         if (ucv_type(data) == UC_STRING) {
159                 len = ucv_string_length(data);
160                 wsize = fwrite(ucv_string_get(data), 1, len, *fp);
161         }
162         else {
163                 str = ucv_to_jsonstring(vm, data);
164                 len = str ? strlen(str) : 0;
165                 wsize = fwrite(str, 1, len, *fp);
166                 free(str);
167         }
168 
169         if (wsize < len && ferror(*fp))
170                 err_return(errno);
171 
172         return ucv_int64_new(wsize);
173 }
174 
175 static uc_value_t *
176 uc_fs_flush_common(uc_vm_t *vm, size_t nargs, const char *type)
177 {
178         FILE **fp = uc_fn_this(type);
179 
180         if (!fp || !*fp)
181                 err_return(EBADF);
182 
183         if (fflush(*fp) != EOF)
184                 err_return(errno);
185 
186         return ucv_boolean_new(true);
187 }
188 
189 static uc_value_t *
190 uc_fs_fileno_common(uc_vm_t *vm, size_t nargs, const char *type)
191 {
192         int fd;
193 
194         FILE **fp = uc_fn_this(type);
195 
196         if (!fp || !*fp)
197                 err_return(EBADF);
198 
199         fd = fileno(*fp);
200 
201         if (fd == -1)
202                 err_return(errno);
203 
204         return ucv_int64_new(fd);
205 }
206 
207 
208 static uc_value_t *
209 uc_fs_pclose(uc_vm_t *vm, size_t nargs)
210 {
211         FILE **fp = uc_fn_this("fs.proc");
212         int rc;
213 
214         if (!fp || !*fp)
215                 err_return(EBADF);
216 
217         rc = pclose(*fp);
218         *fp = NULL;
219 
220         if (rc == -1)
221                 err_return(errno);
222 
223         if (WIFEXITED(rc))
224                 return ucv_int64_new(WEXITSTATUS(rc));
225 
226         if (WIFSIGNALED(rc))
227                 return ucv_int64_new(-WTERMSIG(rc));
228 
229         return ucv_int64_new(0);
230 }
231 
232 static uc_value_t *
233 uc_fs_pread(uc_vm_t *vm, size_t nargs)
234 {
235         return uc_fs_read_common(vm, nargs, "fs.proc");
236 }
237 
238 static uc_value_t *
239 uc_fs_pwrite(uc_vm_t *vm, size_t nargs)
240 {
241         return uc_fs_write_common(vm, nargs, "fs.proc");
242 }
243 
244 static uc_value_t *
245 uc_fs_pflush(uc_vm_t *vm, size_t nargs)
246 {
247         return uc_fs_flush_common(vm, nargs, "fs.proc");
248 }
249 
250 static uc_value_t *
251 uc_fs_pfileno(uc_vm_t *vm, size_t nargs)
252 {
253         return uc_fs_fileno_common(vm, nargs, "fs.proc");
254 }
255 
256 static uc_value_t *
257 uc_fs_popen(uc_vm_t *vm, size_t nargs)
258 {
259         uc_value_t *comm = uc_fn_arg(0);
260         uc_value_t *mode = uc_fn_arg(1);
261         FILE *fp;
262 
263         if (ucv_type(comm) != UC_STRING)
264                 err_return(EINVAL);
265 
266         fp = popen(ucv_string_get(comm),
267                 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r");
268 
269         if (!fp)
270                 err_return(errno);
271 
272         return uc_resource_new(proc_type, fp);
273 }
274 
275 
276 static uc_value_t *
277 uc_fs_close(uc_vm_t *vm, size_t nargs)
278 {
279         FILE **fp = uc_fn_this("fs.file");
280 
281         if (!fp || !*fp)
282                 err_return(EBADF);
283 
284         fclose(*fp);
285         *fp = NULL;
286 
287         return ucv_boolean_new(true);
288 }
289 
290 static uc_value_t *
291 uc_fs_read(uc_vm_t *vm, size_t nargs)
292 {
293         return uc_fs_read_common(vm, nargs, "fs.file");
294 }
295 
296 static uc_value_t *
297 uc_fs_write(uc_vm_t *vm, size_t nargs)
298 {
299         return uc_fs_write_common(vm, nargs, "fs.file");
300 }
301 
302 static uc_value_t *
303 uc_fs_seek(uc_vm_t *vm, size_t nargs)
304 {
305         uc_value_t *ofs = uc_fn_arg(0);
306         uc_value_t *how = uc_fn_arg(1);
307         int whence, res;
308         long offset;
309 
310         FILE **fp = uc_fn_this("fs.file");
311 
312         if (!fp || !*fp)
313                 err_return(EBADF);
314 
315         if (!ofs)
316                 offset = 0;
317         else if (ucv_type(ofs) != UC_INTEGER)
318                 err_return(EINVAL);
319         else
320                 offset = (long)ucv_int64_get(ofs);
321 
322         if (!how)
323                 whence = 0;
324         else if (ucv_type(how) != UC_INTEGER)
325                 err_return(EINVAL);
326         else
327                 whence = (int)ucv_int64_get(how);
328 
329         res = fseek(*fp, offset, whence);
330 
331         if (res < 0)
332                 err_return(errno);
333 
334         return ucv_boolean_new(true);
335 }
336 
337 static uc_value_t *
338 uc_fs_tell(uc_vm_t *vm, size_t nargs)
339 {
340         long offset;
341 
342         FILE **fp = uc_fn_this("fs.file");
343 
344         if (!fp || !*fp)
345                 err_return(EBADF);
346 
347         offset = ftell(*fp);
348 
349         if (offset < 0)
350                 err_return(errno);
351 
352         return ucv_int64_new(offset);
353 }
354 
355 static uc_value_t *
356 uc_fs_isatty(uc_vm_t *vm, size_t nargs)
357 {
358         FILE **fp = uc_fn_this("fs.file");
359         int fd;
360 
361         if (!fp || !*fp)
362                 err_return(EBADF);
363 
364         fd = fileno(*fp);
365 
366         if (fd == -1)
367                 err_return(errno);
368 
369         return ucv_boolean_new(isatty(fd));
370 }
371 
372 static uc_value_t *
373 uc_fs_flush(uc_vm_t *vm, size_t nargs)
374 {
375         return uc_fs_flush_common(vm, nargs, "fs.file");
376 }
377 
378 static uc_value_t *
379 uc_fs_fileno(uc_vm_t *vm, size_t nargs)
380 {
381         return uc_fs_fileno_common(vm, nargs, "fs.file");
382 }
383 
384 static uc_value_t *
385 uc_fs_open(uc_vm_t *vm, size_t nargs)
386 {
387         int open_mode, open_flags, fd, i;
388         uc_value_t *path = uc_fn_arg(0);
389         uc_value_t *mode = uc_fn_arg(1);
390         uc_value_t *perm = uc_fn_arg(2);
391         mode_t open_perm = 0666;
392         FILE *fp;
393         char *m;
394 
395         if (ucv_type(path) != UC_STRING)
396                 err_return(EINVAL);
397 
398         m = (ucv_type(mode) == UC_STRING) ? ucv_string_get(mode) : "r";
399 
400         switch (*m) {
401         case 'r':
402                 open_mode = O_RDONLY;
403                 open_flags = 0;
404                 break;
405 
406         case 'w':
407                 open_mode = O_WRONLY;
408                 open_flags = O_CREAT | O_TRUNC;
409                 break;
410 
411         case 'a':
412                 open_mode = O_WRONLY;
413                 open_flags = O_CREAT | O_APPEND;
414                 break;
415 
416         default:
417                 err_return(EINVAL);
418         }
419 
420         for (i = 1; m[i]; i++) {
421                 switch (m[i]) {
422                 case '+': open_mode = O_RDWR;      break;
423                 case 'x': open_flags |= O_EXCL;    break;
424                 case 'e': open_flags |= O_CLOEXEC; break;
425                 }
426         }
427 
428         if (perm) {
429                 if (ucv_type(perm) != UC_INTEGER)
430                         err_return(EINVAL);
431 
432                 open_perm = ucv_int64_get(perm);
433         }
434 
435 #ifdef O_LARGEFILE
436         open_flags |= open_mode | O_LARGEFILE;
437 #else
438         open_flags |= open_mode;
439 #endif
440 
441         fd = open(ucv_string_get(path), open_flags, open_perm);
442 
443         if (fd < 0)
444                 return NULL;
445 
446         fp = fdopen(fd, m);
447 
448         if (!fp) {
449                 i = errno;
450                 close(fd);
451                 err_return(i);
452         }
453 
454         return uc_resource_new(file_type, fp);
455 }
456 
457 static uc_value_t *
458 uc_fs_fdopen(uc_vm_t *vm, size_t nargs)
459 {
460         uc_value_t *fdno = uc_fn_arg(0);
461         uc_value_t *mode = uc_fn_arg(1);
462         int64_t n;
463         FILE *fp;
464 
465         if (ucv_type(fdno) != UC_INTEGER)
466                 err_return(EINVAL);
467 
468         n = ucv_int64_get(fdno);
469 
470         if (n < 0 || n > INT_MAX)
471                 err_return(EBADF);
472 
473         fp = fdopen((int)n,
474                 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r");
475 
476         if (!fp)
477                 err_return(errno);
478 
479         return uc_resource_new(file_type, fp);
480 }
481 
482 
483 static uc_value_t *
484 uc_fs_readdir(uc_vm_t *vm, size_t nargs)
485 {
486         DIR **dp = uc_fn_this("fs.dir");
487         struct dirent *e;
488 
489         if (!dp || !*dp)
490                 err_return(EINVAL);
491 
492         errno = 0;
493         e = readdir(*dp);
494 
495         if (!e)
496                 err_return(errno);
497 
498         return ucv_string_new(e->d_name);
499 }
500 
501 static uc_value_t *
502 uc_fs_telldir(uc_vm_t *vm, size_t nargs)
503 {
504         DIR **dp = uc_fn_this("fs.dir");
505         long position;
506 
507         if (!dp || !*dp)
508                 err_return(EBADF);
509 
510         position = telldir(*dp);
511 
512         if (position == -1)
513                 err_return(errno);
514 
515         return ucv_int64_new((int64_t)position);
516 }
517 
518 static uc_value_t *
519 uc_fs_seekdir(uc_vm_t *vm, size_t nargs)
520 {
521         uc_value_t *ofs = uc_fn_arg(0);
522         DIR **dp = uc_fn_this("fs.dir");
523         long position;
524 
525         if (ucv_type(ofs) != UC_INTEGER)
526                 err_return(EINVAL);
527 
528         if (!dp || !*dp)
529                 err_return(EBADF);
530 
531         position = (long)ucv_int64_get(ofs);
532 
533         seekdir(*dp, position);
534 
535         return ucv_boolean_new(true);
536 }
537 
538 static uc_value_t *
539 uc_fs_closedir(uc_vm_t *vm, size_t nargs)
540 {
541         DIR **dp = uc_fn_this("fs.dir");
542 
543         if (!dp || !*dp)
544                 err_return(EBADF);
545 
546         closedir(*dp);
547         *dp = NULL;
548 
549         return ucv_boolean_new(true);
550 }
551 
552 static uc_value_t *
553 uc_fs_opendir(uc_vm_t *vm, size_t nargs)
554 {
555         uc_value_t *path = uc_fn_arg(0);
556         DIR *dp;
557 
558         if (ucv_type(path) != UC_STRING)
559                 err_return(EINVAL);
560 
561         dp = opendir(ucv_string_get(path));
562 
563         if (!dp)
564                 err_return(errno);
565 
566         return uc_resource_new(dir_type, dp);
567 }
568 
569 static uc_value_t *
570 uc_fs_readlink(uc_vm_t *vm, size_t nargs)
571 {
572         uc_value_t *path = uc_fn_arg(0);
573         uc_value_t *res;
574         ssize_t buflen = 0, rv;
575         char *buf = NULL, *tmp;
576 
577         if (ucv_type(path) != UC_STRING)
578                 err_return(EINVAL);
579 
580         do {
581                 buflen += 128;
582                 tmp = realloc(buf, buflen);
583 
584                 if (!tmp) {
585                         free(buf);
586                         err_return(ENOMEM);
587                 }
588 
589                 buf = tmp;
590                 rv = readlink(ucv_string_get(path), buf, buflen);
591 
592                 if (rv == -1) {
593                         free(buf);
594                         err_return(errno);
595                 }
596 
597                 if (rv < buflen)
598                         break;
599         }
600         while (true);
601 
602         res = ucv_string_new_length(buf, rv);
603 
604         free(buf);
605 
606         return res;
607 }
608 
609 static uc_value_t *
610 uc_fs_stat_common(uc_vm_t *vm, size_t nargs, bool use_lstat)
611 {
612         uc_value_t *path = uc_fn_arg(0);
613         uc_value_t *res, *o;
614         struct stat st;
615         int rv;
616 
617         if (ucv_type(path) != UC_STRING)
618                 err_return(EINVAL);
619 
620         rv = (use_lstat ? lstat : stat)(ucv_string_get(path), &st);
621 
622         if (rv == -1)
623                 err_return(errno);
624 
625         res = ucv_object_new(vm);
626 
627         if (!res)
628                 err_return(ENOMEM);
629 
630         o = ucv_object_new(vm);
631 
632         if (o) {
633                 ucv_object_add(o, "major", ucv_int64_new(major(st.st_dev)));
634                 ucv_object_add(o, "minor", ucv_int64_new(minor(st.st_dev)));
635 
636                 ucv_object_add(res, "dev", o);
637         }
638 
639         o = ucv_object_new(vm);
640 
641         if (o) {
642                 ucv_object_add(o, "setuid", ucv_boolean_new(st.st_mode & S_ISUID));
643                 ucv_object_add(o, "setgid", ucv_boolean_new(st.st_mode & S_ISGID));
644                 ucv_object_add(o, "sticky", ucv_boolean_new(st.st_mode & S_ISVTX));
645 
646                 ucv_object_add(o, "user_read", ucv_boolean_new(st.st_mode & S_IRUSR));
647                 ucv_object_add(o, "user_write", ucv_boolean_new(st.st_mode & S_IWUSR));
648                 ucv_object_add(o, "user_exec", ucv_boolean_new(st.st_mode & S_IXUSR));
649 
650                 ucv_object_add(o, "group_read", ucv_boolean_new(st.st_mode & S_IRGRP));
651                 ucv_object_add(o, "group_write", ucv_boolean_new(st.st_mode & S_IWGRP));
652                 ucv_object_add(o, "group_exec", ucv_boolean_new(st.st_mode & S_IXGRP));
653 
654                 ucv_object_add(o, "other_read", ucv_boolean_new(st.st_mode & S_IROTH));
655                 ucv_object_add(o, "other_write", ucv_boolean_new(st.st_mode & S_IWOTH));
656                 ucv_object_add(o, "other_exec", ucv_boolean_new(st.st_mode & S_IXOTH));
657 
658                 ucv_object_add(res, "perm", o);
659         }
660 
661         ucv_object_add(res, "inode", ucv_int64_new((int64_t)st.st_ino));
662         ucv_object_add(res, "mode", ucv_int64_new((int64_t)st.st_mode & ~S_IFMT));
663         ucv_object_add(res, "nlink", ucv_int64_new((int64_t)st.st_nlink));
664         ucv_object_add(res, "uid", ucv_int64_new((int64_t)st.st_uid));
665         ucv_object_add(res, "gid", ucv_int64_new((int64_t)st.st_gid));
666         ucv_object_add(res, "size", ucv_int64_new((int64_t)st.st_size));
667         ucv_object_add(res, "blksize", ucv_int64_new((int64_t)st.st_blksize));
668         ucv_object_add(res, "blocks", ucv_int64_new((int64_t)st.st_blocks));
669         ucv_object_add(res, "atime", ucv_int64_new((int64_t)st.st_atime));
670         ucv_object_add(res, "mtime", ucv_int64_new((int64_t)st.st_mtime));
671         ucv_object_add(res, "ctime", ucv_int64_new((int64_t)st.st_ctime));
672 
673         if (S_ISREG(st.st_mode))
674                 ucv_object_add(res, "type", ucv_string_new("file"));
675         else if (S_ISDIR(st.st_mode))
676                 ucv_object_add(res, "type", ucv_string_new("directory"));
677         else if (S_ISCHR(st.st_mode))
678                 ucv_object_add(res, "type", ucv_string_new("char"));
679         else if (S_ISBLK(st.st_mode))
680                 ucv_object_add(res, "type", ucv_string_new("block"));
681         else if (S_ISFIFO(st.st_mode))
682                 ucv_object_add(res, "type", ucv_string_new("fifo"));
683         else if (S_ISLNK(st.st_mode))
684                 ucv_object_add(res, "type", ucv_string_new("link"));
685         else if (S_ISSOCK(st.st_mode))
686                 ucv_object_add(res, "type", ucv_string_new("socket"));
687         else
688                 ucv_object_add(res, "type", ucv_string_new("unknown"));
689 
690         return res;
691 }
692 
693 static uc_value_t *
694 uc_fs_stat(uc_vm_t *vm, size_t nargs)
695 {
696         return uc_fs_stat_common(vm, nargs, false);
697 }
698 
699 static uc_value_t *
700 uc_fs_lstat(uc_vm_t *vm, size_t nargs)
701 {
702         return uc_fs_stat_common(vm, nargs, true);
703 }
704 
705 static uc_value_t *
706 uc_fs_mkdir(uc_vm_t *vm, size_t nargs)
707 {
708         uc_value_t *path = uc_fn_arg(0);
709         uc_value_t *mode = uc_fn_arg(1);
710 
711         if (ucv_type(path) != UC_STRING ||
712             (mode && ucv_type(mode) != UC_INTEGER))
713                 err_return(EINVAL);
714 
715         if (mkdir(ucv_string_get(path), (mode_t)(mode ? ucv_int64_get(mode) : 0777)) == -1)
716                 err_return(errno);
717 
718         return ucv_boolean_new(true);
719 }
720 
721 static uc_value_t *
722 uc_fs_rmdir(uc_vm_t *vm, size_t nargs)
723 {
724         uc_value_t *path = uc_fn_arg(0);
725 
726         if (ucv_type(path) != UC_STRING)
727                 err_return(EINVAL);
728 
729         if (rmdir(ucv_string_get(path)) == -1)
730                 err_return(errno);
731 
732         return ucv_boolean_new(true);
733 }
734 
735 static uc_value_t *
736 uc_fs_symlink(uc_vm_t *vm, size_t nargs)
737 {
738         uc_value_t *dest = uc_fn_arg(0);
739         uc_value_t *path = uc_fn_arg(1);
740 
741         if (ucv_type(dest) != UC_STRING ||
742             ucv_type(path) != UC_STRING)
743                 err_return(EINVAL);
744 
745         if (symlink(ucv_string_get(dest), ucv_string_get(path)) == -1)
746                 err_return(errno);
747 
748         return ucv_boolean_new(true);
749 }
750 
751 static uc_value_t *
752 uc_fs_unlink(uc_vm_t *vm, size_t nargs)
753 {
754         uc_value_t *path = uc_fn_arg(0);
755 
756         if (ucv_type(path) != UC_STRING)
757                 err_return(EINVAL);
758 
759         if (unlink(ucv_string_get(path)) == -1)
760                 err_return(errno);
761 
762         return ucv_boolean_new(true);
763 }
764 
765 static uc_value_t *
766 uc_fs_getcwd(uc_vm_t *vm, size_t nargs)
767 {
768         uc_value_t *res;
769         char *buf = NULL, *tmp;
770         size_t buflen = 0;
771 
772         do {
773                 buflen += 128;
774                 tmp = realloc(buf, buflen);
775 
776                 if (!tmp) {
777                         free(buf);
778                         err_return(ENOMEM);
779                 }
780 
781                 buf = tmp;
782 
783                 if (getcwd(buf, buflen) != NULL)
784                         break;
785 
786                 if (errno == ERANGE)
787                         continue;
788 
789                 free(buf);
790                 err_return(errno);
791         }
792         while (true);
793 
794         res = ucv_string_new(buf);
795 
796         free(buf);
797 
798         return res;
799 }
800 
801 static uc_value_t *
802 uc_fs_chdir(uc_vm_t *vm, size_t nargs)
803 {
804         uc_value_t *path = uc_fn_arg(0);
805 
806         if (ucv_type(path) != UC_STRING)
807                 err_return(EINVAL);
808 
809         if (chdir(ucv_string_get(path)) == -1)
810                 err_return(errno);
811 
812         return ucv_boolean_new(true);
813 }
814 
815 static uc_value_t *
816 uc_fs_chmod(uc_vm_t *vm, size_t nargs)
817 {
818         uc_value_t *path = uc_fn_arg(0);
819         uc_value_t *mode = uc_fn_arg(1);
820 
821         if (ucv_type(path) != UC_STRING ||
822             ucv_type(mode) != UC_INTEGER)
823                 err_return(EINVAL);
824 
825         if (chmod(ucv_string_get(path), (mode_t)ucv_int64_get(mode)) == -1)
826                 err_return(errno);
827 
828         return ucv_boolean_new(true);
829 }
830 
831 static bool
832 uc_fs_resolve_user(uc_value_t *v, uid_t *uid)
833 {
834         struct passwd *pw = NULL;
835         int64_t n;
836         char *s;
837 
838         *uid = (uid_t)-1;
839 
840         switch (ucv_type(v)) {
841         case UC_INTEGER:
842                 n = ucv_int64_get(v);
843 
844                 if (n < -1) {
845                         errno = ERANGE;
846 
847                         return false;
848                 }
849 
850                 *uid = (uid_t)n;
851 
852                 return true;
853 
854         case UC_STRING:
855                 s = ucv_string_get(v);
856                 pw = getpwnam(s);
857 
858                 if (!pw) {
859                         errno = ENOENT;
860 
861                         return false;
862                 }
863 
864                 *uid = pw->pw_uid;
865 
866                 return true;
867 
868         case UC_NULL:
869                 return true;
870 
871         default:
872                 errno = EINVAL;
873 
874                 return false;
875         }
876 }
877 
878 static bool
879 uc_fs_resolve_group(uc_value_t *v, gid_t *gid)
880 {
881         struct group *gr = NULL;
882         int64_t n;
883         char *s;
884 
885         *gid = (gid_t)-1;
886 
887         switch (ucv_type(v)) {
888         case UC_INTEGER:
889                 n = ucv_int64_get(v);
890 
891                 if (n < -1) {
892                         errno = ERANGE;
893 
894                         return false;
895                 }
896 
897                 *gid = (gid_t)n;
898 
899                 return true;
900 
901         case UC_STRING:
902                 s = ucv_string_get(v);
903                 gr = getgrnam(s);
904 
905                 if (!gr) {
906                         errno = ENOENT;
907 
908                         return false;
909                 }
910 
911                 *gid = gr->gr_gid;
912 
913                 return true;
914 
915         case UC_NULL:
916                 return true;
917 
918         default:
919                 errno = EINVAL;
920 
921                 return false;
922         }
923 }
924 
925 static uc_value_t *
926 uc_fs_chown(uc_vm_t *vm, size_t nargs)
927 {
928         uc_value_t *path = uc_fn_arg(0);
929         uc_value_t *user = uc_fn_arg(1);
930         uc_value_t *group = uc_fn_arg(2);
931         uid_t uid;
932         gid_t gid;
933 
934         if (ucv_type(path) != UC_STRING)
935             err_return(EINVAL);
936 
937         if (!uc_fs_resolve_user(user, &uid) ||
938             !uc_fs_resolve_group(group, &gid))
939                 err_return(errno);
940 
941         if (chown(ucv_string_get(path), uid, gid) == -1)
942                 err_return(errno);
943 
944         return ucv_boolean_new(true);
945 }
946 
947 static uc_value_t *
948 uc_fs_rename(uc_vm_t *vm, size_t nargs)
949 {
950         uc_value_t *oldpath = uc_fn_arg(0);
951         uc_value_t *newpath = uc_fn_arg(1);
952 
953         if (ucv_type(oldpath) != UC_STRING ||
954             ucv_type(newpath) != UC_STRING)
955                 err_return(EINVAL);
956 
957         if (rename(ucv_string_get(oldpath), ucv_string_get(newpath)))
958                 err_return(errno);
959 
960         return ucv_boolean_new(true);
961 }
962 
963 static uc_value_t *
964 uc_fs_glob(uc_vm_t *vm, size_t nargs)
965 {
966         uc_value_t *pat, *arr;
967         glob_t gl = { 0 };
968         size_t i;
969 
970         for (i = 0; i < nargs; i++) {
971                 pat = uc_fn_arg(i);
972 
973                 if (ucv_type(pat) != UC_STRING) {
974                         globfree(&gl);
975                         err_return(EINVAL);
976                 }
977 
978                 glob(ucv_string_get(pat), i ? GLOB_APPEND : 0, NULL, &gl);
979         }
980 
981         arr = ucv_array_new(vm);
982 
983         for (i = 0; i < gl.gl_pathc; i++)
984                 ucv_array_push(arr, ucv_string_new(gl.gl_pathv[i]));
985 
986         globfree(&gl);
987 
988         return arr;
989 }
990 
991 static uc_value_t *
992 uc_fs_dirname(uc_vm_t *vm, size_t nargs)
993 {
994         uc_value_t *path = uc_fn_arg(0);
995         size_t i;
996         char *s;
997 
998         if (ucv_type(path) != UC_STRING)
999                 err_return(EINVAL);
1000 
1001         i = ucv_string_length(path);
1002         s = ucv_string_get(path);
1003 
1004         if (i == 0)
1005                 return ucv_string_new(".");
1006 
1007         for (i--; s[i] == '/'; i--)
1008                 if (i == 0)
1009                         return ucv_string_new("/");
1010 
1011         for (; s[i] != '/'; i--)
1012                 if (i == 0)
1013                         return ucv_string_new(".");
1014 
1015         for (; s[i] == '/'; i--)
1016                 if (i == 0)
1017                         return ucv_string_new("/");
1018 
1019         return ucv_string_new_length(s, i + 1);
1020 }
1021 
1022 static uc_value_t *
1023 uc_fs_basename(uc_vm_t *vm, size_t nargs)
1024 {
1025         uc_value_t *path = uc_fn_arg(0);
1026         size_t i, len, skip;
1027         char *s;
1028 
1029         if (ucv_type(path) != UC_STRING)
1030                 err_return(EINVAL);
1031 
1032         len = ucv_string_length(path);
1033         s = ucv_string_get(path);
1034 
1035         if (len == 0)
1036                 return ucv_string_new(".");
1037 
1038         for (i = len - 1, skip = 0; i > 0 && s[i] == '/'; i--, skip++)
1039                 ;
1040 
1041         for (; i > 0 && s[i - 1] != '/'; i--)
1042                 ;
1043 
1044         return ucv_string_new_length(s + i, len - i - skip);
1045 }
1046 
1047 static int
1048 uc_fs_lsdir_sort_fn(const void *k1, const void *k2)
1049 {
1050         uc_value_t * const *v1 = k1;
1051         uc_value_t * const *v2 = k2;
1052 
1053         return strcmp(ucv_string_get(*v1), ucv_string_get(*v2));
1054 }
1055 
1056 static uc_value_t *
1057 uc_fs_lsdir(uc_vm_t *vm, size_t nargs)
1058 {
1059         uc_value_t *path = uc_fn_arg(0);
1060         uc_value_t *pat = uc_fn_arg(1);
1061         uc_value_t *res = NULL;
1062         uc_regexp_t *reg;
1063         struct dirent *e;
1064         DIR *d;
1065 
1066         if (ucv_type(path) != UC_STRING)
1067                 err_return(EINVAL);
1068 
1069         switch (ucv_type(pat)) {
1070         case UC_NULL:
1071         case UC_STRING:
1072         case UC_REGEXP:
1073                 break;
1074 
1075         default:
1076                 err_return(EINVAL);
1077         }
1078 
1079         d = opendir(ucv_string_get(path));
1080 
1081         if (!d)
1082                 err_return(errno);
1083 
1084         res = ucv_array_new(vm);
1085 
1086         while ((e = readdir(d)) != NULL) {
1087                 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
1088                         continue;
1089 
1090                 if (ucv_type(pat) == UC_REGEXP) {
1091                         reg = (uc_regexp_t *)pat;
1092 
1093                         if (regexec(&reg->regexp, e->d_name, 0, NULL, 0) == REG_NOMATCH)
1094                                 continue;
1095                 }
1096                 else if (ucv_type(pat) == UC_STRING) {
1097                         if (fnmatch(ucv_string_get(pat), e->d_name, 0) == FNM_NOMATCH)
1098                                 continue;
1099                 }
1100 
1101                 ucv_array_push(res, ucv_string_new(e->d_name));
1102         }
1103 
1104         closedir(d);
1105 
1106         ucv_array_sort(res, uc_fs_lsdir_sort_fn);
1107 
1108         return res;
1109 }
1110 
1111 static uc_value_t *
1112 uc_fs_mkstemp(uc_vm_t *vm, size_t nargs)
1113 {
1114         uc_value_t *template = uc_fn_arg(0);
1115         bool ends_with_template = false;
1116         char *path, *t;
1117         FILE *fp;
1118         size_t l;
1119         int fd;
1120 
1121         if (template && ucv_type(template) != UC_STRING)
1122                 err_return(EINVAL);
1123 
1124         t = ucv_string_get(template);
1125         l = ucv_string_length(template);
1126 
1127         ends_with_template = (l >= 6 && strcmp(&t[l - 6], "XXXXXX") == 0);
1128 
1129         if (t && strchr(t, '/')) {
1130                 if (ends_with_template)
1131                         xasprintf(&path, "%s", t);
1132                 else
1133                         xasprintf(&path, "%s.XXXXXX", t);
1134         }
1135         else if (t) {
1136                 if (ends_with_template)
1137                         xasprintf(&path, "/tmp/%s", t);
1138                 else
1139                         xasprintf(&path, "/tmp/%s.XXXXXX", t);
1140         }
1141         else {
1142                 xasprintf(&path, "/tmp/XXXXXX");
1143         }
1144 
1145         do {
1146                 fd = mkstemp(path);
1147         }
1148         while (fd == -1 && errno == EINTR);
1149 
1150         if (fd == -1) {
1151                 free(path);
1152                 err_return(errno);
1153         }
1154 
1155         unlink(path);
1156         free(path);
1157 
1158         fp = fdopen(fd, "r+");
1159 
1160         if (!fp) {
1161                 close(fd);
1162                 err_return(errno);
1163         }
1164 
1165         return uc_resource_new(file_type, fp);
1166 }
1167 
1168 static uc_value_t *
1169 uc_fs_access(uc_vm_t *vm, size_t nargs)
1170 {
1171         uc_value_t *path = uc_fn_arg(0);
1172         uc_value_t *test = uc_fn_arg(1);
1173         int mode = F_OK;
1174         char *p;
1175 
1176         if (ucv_type(path) != UC_STRING)
1177                 err_return(EINVAL);
1178 
1179         if (test && ucv_type(test) != UC_STRING)
1180                 err_return(EINVAL);
1181 
1182         for (p = ucv_string_get(test); p && *p; p++) {
1183                 switch (*p) {
1184                 case 'r':
1185                         mode |= R_OK;
1186                         break;
1187 
1188                 case 'w':
1189                         mode |= W_OK;
1190                         break;
1191 
1192                 case 'x':
1193                         mode |= X_OK;
1194                         break;
1195 
1196                 case 'f':
1197                         mode |= F_OK;
1198                         break;
1199 
1200                 default:
1201                         err_return(EINVAL);
1202                 }
1203         }
1204 
1205         if (access(ucv_string_get(path), mode) == -1)
1206                 err_return(errno);
1207 
1208         return ucv_boolean_new(true);
1209 }
1210 
1211 static uc_value_t *
1212 uc_fs_readfile(uc_vm_t *vm, size_t nargs)
1213 {
1214         uc_value_t *path = uc_fn_arg(0);
1215         uc_value_t *size = uc_fn_arg(1);
1216         uc_value_t *res = NULL;
1217         uc_stringbuf_t *buf;
1218         ssize_t limit = -1;
1219         size_t rlen, blen;
1220         FILE *fp;
1221 
1222         if (ucv_type(path) != UC_STRING)
1223                 err_return(EINVAL);
1224 
1225         if (size) {
1226                 if (ucv_type(size) != UC_INTEGER)
1227                         err_return(EINVAL);
1228 
1229                 limit = ucv_int64_get(size);
1230         }
1231 
1232         fp = fopen(ucv_string_get(path), "r");
1233 
1234         if (!fp)
1235                 err_return(errno);
1236 
1237         buf = ucv_stringbuf_new();
1238 
1239         if (limit > -1 && limit < BUFSIZ)
1240                 setvbuf(fp, NULL, _IONBF, 0);
1241 
1242         while (limit != 0) {
1243                 blen = 1024;
1244 
1245                 if (limit > 0 && blen > (size_t)limit)
1246                         blen = (size_t)limit;
1247 
1248                 printbuf_memset(buf, printbuf_length(buf) + blen - 1, 0, 1);
1249 
1250                 buf->bpos -= blen;
1251                 rlen = fread(buf->buf + buf->bpos, 1, blen, fp);
1252                 buf->bpos += rlen;
1253 
1254                 if (rlen < blen)
1255                         break;
1256 
1257                 if (limit > 0)
1258                         limit -= rlen;
1259         }
1260 
1261         if (ferror(fp)) {
1262                 fclose(fp);
1263                 printbuf_free(buf);
1264                 err_return(errno);
1265         }
1266 
1267         fclose(fp);
1268 
1269         /* add sentinel null byte but don't count it towards the string length */
1270         printbuf_memappend_fast(buf, "\0", 1);
1271         res = ucv_stringbuf_finish(buf);
1272         ((uc_string_t *)res)->length--;
1273 
1274         return res;
1275 }
1276 
1277 static uc_value_t *
1278 uc_fs_writefile(uc_vm_t *vm, size_t nargs)
1279 {
1280         uc_value_t *path = uc_fn_arg(0);
1281         uc_value_t *data = uc_fn_arg(1);
1282         uc_value_t *size = uc_fn_arg(2);
1283         uc_stringbuf_t *buf = NULL;
1284         ssize_t limit = -1;
1285         size_t wlen = 0;
1286         int err = 0;
1287         FILE *fp;
1288 
1289         if (ucv_type(path) != UC_STRING)
1290                 err_return(EINVAL);
1291 
1292         if (size) {
1293                 if (ucv_type(size) != UC_INTEGER)
1294                         err_return(EINVAL);
1295 
1296                 limit = ucv_int64_get(size);
1297         }
1298 
1299         fp = fopen(ucv_string_get(path), "w");
1300 
1301         if (!fp)
1302                 err_return(errno);
1303 
1304         if (data && ucv_type(data) != UC_STRING) {
1305                 buf = xprintbuf_new();
1306                 ucv_to_stringbuf_formatted(vm, buf, data, 0, '\0', 0);
1307 
1308                 if (limit < 0 || limit > printbuf_length(buf))
1309                         limit = printbuf_length(buf);
1310 
1311                 wlen = fwrite(buf->buf, 1, limit, fp);
1312 
1313                 if (wlen < (size_t)limit)
1314                         err = errno;
1315 
1316                 printbuf_free(buf);
1317         }
1318         else if (data) {
1319                 if (limit < 0 || (size_t)limit > ucv_string_length(data))
1320                         limit = ucv_string_length(data);
1321 
1322                 wlen = fwrite(ucv_string_get(data), 1, limit, fp);
1323 
1324                 if (wlen < (size_t)limit)
1325                         err = errno;
1326         }
1327 
1328         fclose(fp);
1329 
1330         if (err)
1331                 err_return(err);
1332 
1333         return ucv_uint64_new(wlen);
1334 }
1335 
1336 static uc_value_t *
1337 uc_fs_realpath(uc_vm_t *vm, size_t nargs)
1338 {
1339         uc_value_t *path = uc_fn_arg(0), *rv;
1340         char *resolved;
1341 
1342         if (ucv_type(path) != UC_STRING)
1343                 err_return(EINVAL);
1344 
1345         resolved = realpath(ucv_string_get(path), NULL);
1346 
1347         if (!resolved)
1348                 err_return(errno);
1349 
1350         rv = ucv_string_new(resolved);
1351 
1352         free(resolved);
1353 
1354         return rv;
1355 }
1356 
1357 static uc_value_t *
1358 uc_fs_pipe(uc_vm_t *vm, size_t nargs)
1359 {
1360         int pfds[2], err;
1361         FILE *rfp, *wfp;
1362         uc_value_t *rv;
1363 
1364         if (pipe(pfds) == -1)
1365                 err_return(errno);
1366 
1367         rfp = fdopen(pfds[0], "r");
1368 
1369         if (!rfp) {
1370                 err = errno;
1371                 close(pfds[0]);
1372                 close(pfds[1]);
1373                 err_return(err);
1374         }
1375 
1376         wfp = fdopen(pfds[1], "w");
1377 
1378         if (!wfp) {
1379                 err = errno;
1380                 fclose(rfp);
1381                 close(pfds[1]);
1382                 err_return(err);
1383         }
1384 
1385         rv = ucv_array_new_length(vm, 2);
1386 
1387         ucv_array_push(rv, uc_resource_new(file_type, rfp));
1388         ucv_array_push(rv, uc_resource_new(file_type, wfp));
1389 
1390         return rv;
1391 }
1392 
1393 
1394 static const uc_function_list_t proc_fns[] = {
1395         { "read",               uc_fs_pread },
1396         { "write",              uc_fs_pwrite },
1397         { "close",              uc_fs_pclose },
1398         { "flush",              uc_fs_pflush },
1399         { "fileno",             uc_fs_pfileno },
1400         { "error",              uc_fs_error },
1401 };
1402 
1403 static const uc_function_list_t file_fns[] = {
1404         { "read",               uc_fs_read },
1405         { "write",              uc_fs_write },
1406         { "seek",               uc_fs_seek },
1407         { "tell",               uc_fs_tell },
1408         { "close",              uc_fs_close },
1409         { "flush",              uc_fs_flush },
1410         { "fileno",             uc_fs_fileno },
1411         { "error",              uc_fs_error },
1412         { "isatty",             uc_fs_isatty },
1413 };
1414 
1415 static const uc_function_list_t dir_fns[] = {
1416         { "read",               uc_fs_readdir },
1417         { "seek",               uc_fs_seekdir },
1418         { "tell",               uc_fs_telldir },
1419         { "close",              uc_fs_closedir },
1420         { "error",              uc_fs_error },
1421 };
1422 
1423 static const uc_function_list_t global_fns[] = {
1424         { "error",              uc_fs_error },
1425         { "open",               uc_fs_open },
1426         { "fdopen",             uc_fs_fdopen },
1427         { "opendir",    uc_fs_opendir },
1428         { "popen",              uc_fs_popen },
1429         { "readlink",   uc_fs_readlink },
1430         { "stat",               uc_fs_stat },
1431         { "lstat",              uc_fs_lstat },
1432         { "mkdir",              uc_fs_mkdir },
1433         { "rmdir",              uc_fs_rmdir },
1434         { "symlink",    uc_fs_symlink },
1435         { "unlink",             uc_fs_unlink },
1436         { "getcwd",             uc_fs_getcwd },
1437         { "chdir",              uc_fs_chdir },
1438         { "chmod",              uc_fs_chmod },
1439         { "chown",              uc_fs_chown },
1440         { "rename",             uc_fs_rename },
1441         { "glob",               uc_fs_glob },
1442         { "dirname",    uc_fs_dirname },
1443         { "basename",   uc_fs_basename },
1444         { "lsdir",              uc_fs_lsdir },
1445         { "mkstemp",    uc_fs_mkstemp },
1446         { "access",             uc_fs_access },
1447         { "readfile",   uc_fs_readfile },
1448         { "writefile",  uc_fs_writefile },
1449         { "realpath",   uc_fs_realpath },
1450         { "pipe",               uc_fs_pipe },
1451 };
1452 
1453 
1454 static void close_proc(void *ud)
1455 {
1456         FILE *fp = ud;
1457 
1458         if (fp)
1459                 pclose(fp);
1460 }
1461 
1462 static void close_file(void *ud)
1463 {
1464         FILE *fp = ud;
1465         int n;
1466 
1467         n = fp ? fileno(fp) : -1;
1468 
1469         if (n > 2)
1470                 fclose(fp);
1471 }
1472 
1473 static void close_dir(void *ud)
1474 {
1475         DIR *dp = ud;
1476 
1477         if (dp)
1478                 closedir(dp);
1479 }
1480 
1481 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1482 {
1483         uc_function_list_register(scope, global_fns);
1484 
1485         proc_type = uc_type_declare(vm, "fs.proc", proc_fns, close_proc);
1486         file_type = uc_type_declare(vm, "fs.file", file_fns, close_file);
1487         dir_type = uc_type_declare(vm, "fs.dir", dir_fns, close_dir);
1488 
1489         ucv_object_add(scope, "stdin", uc_resource_new(file_type, stdin));
1490         ucv_object_add(scope, "stdout", uc_resource_new(file_type, stdout));
1491         ucv_object_add(scope, "stderr", uc_resource_new(file_type, stderr));
1492 }
1493 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt