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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt