• 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_flush(uc_vm_t *vm, size_t nargs)
357 {
358         return uc_fs_flush_common(vm, nargs, "fs.file");
359 }
360 
361 static uc_value_t *
362 uc_fs_fileno(uc_vm_t *vm, size_t nargs)
363 {
364         return uc_fs_fileno_common(vm, nargs, "fs.file");
365 }
366 
367 static uc_value_t *
368 uc_fs_open(uc_vm_t *vm, size_t nargs)
369 {
370         int open_mode, open_flags, fd, i;
371         uc_value_t *path = uc_fn_arg(0);
372         uc_value_t *mode = uc_fn_arg(1);
373         uc_value_t *perm = uc_fn_arg(2);
374         mode_t open_perm = 0666;
375         FILE *fp;
376         char *m;
377 
378         if (ucv_type(path) != UC_STRING)
379                 err_return(EINVAL);
380 
381         m = (ucv_type(mode) == UC_STRING) ? ucv_string_get(mode) : "r";
382 
383         switch (*m) {
384         case 'r':
385                 open_mode = O_RDONLY;
386                 open_flags = 0;
387                 break;
388 
389         case 'w':
390                 open_mode = O_WRONLY;
391                 open_flags = O_CREAT | O_TRUNC;
392                 break;
393 
394         case 'a':
395                 open_mode = O_WRONLY;
396                 open_flags = O_CREAT | O_APPEND;
397                 break;
398 
399         default:
400                 err_return(EINVAL);
401         }
402 
403         for (i = 1; m[i]; i++) {
404                 switch (m[i]) {
405                 case '+': open_mode = O_RDWR;      break;
406                 case 'x': open_flags |= O_EXCL;    break;
407                 case 'e': open_flags |= O_CLOEXEC; break;
408                 }
409         }
410 
411         if (perm) {
412                 if (ucv_type(perm) != UC_INTEGER)
413                         err_return(EINVAL);
414 
415                 open_perm = ucv_int64_get(perm);
416         }
417 
418 #ifdef O_LARGEFILE
419         open_flags |= open_mode | O_LARGEFILE;
420 #else
421         open_flags |= open_mode;
422 #endif
423 
424         fd = open(ucv_string_get(path), open_flags, open_perm);
425 
426         if (fd < 0)
427                 return NULL;
428 
429         fp = fdopen(fd, m);
430 
431         if (!fp) {
432                 i = errno;
433                 close(fd);
434                 err_return(i);
435         }
436 
437         return uc_resource_new(file_type, fp);
438 }
439 
440 static uc_value_t *
441 uc_fs_fdopen(uc_vm_t *vm, size_t nargs)
442 {
443         uc_value_t *fdno = uc_fn_arg(0);
444         uc_value_t *mode = uc_fn_arg(1);
445         int64_t n;
446         FILE *fp;
447 
448         if (ucv_type(fdno) != UC_INTEGER)
449                 err_return(EINVAL);
450 
451         n = ucv_int64_get(fdno);
452 
453         if (n < 0 || n > INT_MAX)
454                 err_return(EBADF);
455 
456         fp = fdopen((int)n,
457                 ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r");
458 
459         if (!fp)
460                 err_return(errno);
461 
462         return uc_resource_new(file_type, fp);
463 }
464 
465 
466 static uc_value_t *
467 uc_fs_readdir(uc_vm_t *vm, size_t nargs)
468 {
469         DIR **dp = uc_fn_this("fs.dir");
470         struct dirent *e;
471 
472         if (!dp || !*dp)
473                 err_return(EINVAL);
474 
475         errno = 0;
476         e = readdir(*dp);
477 
478         if (!e)
479                 err_return(errno);
480 
481         return ucv_string_new(e->d_name);
482 }
483 
484 static uc_value_t *
485 uc_fs_telldir(uc_vm_t *vm, size_t nargs)
486 {
487         DIR **dp = uc_fn_this("fs.dir");
488         long position;
489 
490         if (!dp || !*dp)
491                 err_return(EBADF);
492 
493         position = telldir(*dp);
494 
495         if (position == -1)
496                 err_return(errno);
497 
498         return ucv_int64_new((int64_t)position);
499 }
500 
501 static uc_value_t *
502 uc_fs_seekdir(uc_vm_t *vm, size_t nargs)
503 {
504         uc_value_t *ofs = uc_fn_arg(0);
505         DIR **dp = uc_fn_this("fs.dir");
506         long position;
507 
508         if (ucv_type(ofs) != UC_INTEGER)
509                 err_return(EINVAL);
510 
511         if (!dp || !*dp)
512                 err_return(EBADF);
513 
514         position = (long)ucv_int64_get(ofs);
515 
516         seekdir(*dp, position);
517 
518         return ucv_boolean_new(true);
519 }
520 
521 static uc_value_t *
522 uc_fs_closedir(uc_vm_t *vm, size_t nargs)
523 {
524         DIR **dp = uc_fn_this("fs.dir");
525 
526         if (!dp || !*dp)
527                 err_return(EBADF);
528 
529         closedir(*dp);
530         *dp = NULL;
531 
532         return ucv_boolean_new(true);
533 }
534 
535 static uc_value_t *
536 uc_fs_opendir(uc_vm_t *vm, size_t nargs)
537 {
538         uc_value_t *path = uc_fn_arg(0);
539         DIR *dp;
540 
541         if (ucv_type(path) != UC_STRING)
542                 err_return(EINVAL);
543 
544         dp = opendir(ucv_string_get(path));
545 
546         if (!dp)
547                 err_return(errno);
548 
549         return uc_resource_new(dir_type, dp);
550 }
551 
552 static uc_value_t *
553 uc_fs_readlink(uc_vm_t *vm, size_t nargs)
554 {
555         uc_value_t *path = uc_fn_arg(0);
556         uc_value_t *res;
557         ssize_t buflen = 0, rv;
558         char *buf = NULL, *tmp;
559 
560         if (ucv_type(path) != UC_STRING)
561                 err_return(EINVAL);
562 
563         do {
564                 buflen += 128;
565                 tmp = realloc(buf, buflen);
566 
567                 if (!tmp) {
568                         free(buf);
569                         err_return(ENOMEM);
570                 }
571 
572                 buf = tmp;
573                 rv = readlink(ucv_string_get(path), buf, buflen);
574 
575                 if (rv == -1) {
576                         free(buf);
577                         err_return(errno);
578                 }
579 
580                 if (rv < buflen)
581                         break;
582         }
583         while (true);
584 
585         res = ucv_string_new_length(buf, rv);
586 
587         free(buf);
588 
589         return res;
590 }
591 
592 static uc_value_t *
593 uc_fs_stat_common(uc_vm_t *vm, size_t nargs, bool use_lstat)
594 {
595         uc_value_t *path = uc_fn_arg(0);
596         uc_value_t *res, *o;
597         struct stat st;
598         int rv;
599 
600         if (ucv_type(path) != UC_STRING)
601                 err_return(EINVAL);
602 
603         rv = (use_lstat ? lstat : stat)(ucv_string_get(path), &st);
604 
605         if (rv == -1)
606                 err_return(errno);
607 
608         res = ucv_object_new(vm);
609 
610         if (!res)
611                 err_return(ENOMEM);
612 
613         o = ucv_object_new(vm);
614 
615         if (o) {
616                 ucv_object_add(o, "major", ucv_int64_new(major(st.st_dev)));
617                 ucv_object_add(o, "minor", ucv_int64_new(minor(st.st_dev)));
618 
619                 ucv_object_add(res, "dev", o);
620         }
621 
622         o = ucv_object_new(vm);
623 
624         if (o) {
625                 ucv_object_add(o, "setuid", ucv_boolean_new(st.st_mode & S_ISUID));
626                 ucv_object_add(o, "setgid", ucv_boolean_new(st.st_mode & S_ISGID));
627                 ucv_object_add(o, "sticky", ucv_boolean_new(st.st_mode & S_ISVTX));
628 
629                 ucv_object_add(o, "user_read", ucv_boolean_new(st.st_mode & S_IRUSR));
630                 ucv_object_add(o, "user_write", ucv_boolean_new(st.st_mode & S_IWUSR));
631                 ucv_object_add(o, "user_exec", ucv_boolean_new(st.st_mode & S_IXUSR));
632 
633                 ucv_object_add(o, "group_read", ucv_boolean_new(st.st_mode & S_IRGRP));
634                 ucv_object_add(o, "group_write", ucv_boolean_new(st.st_mode & S_IWGRP));
635                 ucv_object_add(o, "group_exec", ucv_boolean_new(st.st_mode & S_IXGRP));
636 
637                 ucv_object_add(o, "other_read", ucv_boolean_new(st.st_mode & S_IROTH));
638                 ucv_object_add(o, "other_write", ucv_boolean_new(st.st_mode & S_IWOTH));
639                 ucv_object_add(o, "other_exec", ucv_boolean_new(st.st_mode & S_IXOTH));
640 
641                 ucv_object_add(res, "perm", o);
642         }
643 
644         ucv_object_add(res, "inode", ucv_int64_new((int64_t)st.st_ino));
645         ucv_object_add(res, "mode", ucv_int64_new((int64_t)st.st_mode & ~S_IFMT));
646         ucv_object_add(res, "nlink", ucv_int64_new((int64_t)st.st_nlink));
647         ucv_object_add(res, "uid", ucv_int64_new((int64_t)st.st_uid));
648         ucv_object_add(res, "gid", ucv_int64_new((int64_t)st.st_gid));
649         ucv_object_add(res, "size", ucv_int64_new((int64_t)st.st_size));
650         ucv_object_add(res, "blksize", ucv_int64_new((int64_t)st.st_blksize));
651         ucv_object_add(res, "blocks", ucv_int64_new((int64_t)st.st_blocks));
652         ucv_object_add(res, "atime", ucv_int64_new((int64_t)st.st_atime));
653         ucv_object_add(res, "mtime", ucv_int64_new((int64_t)st.st_mtime));
654         ucv_object_add(res, "ctime", ucv_int64_new((int64_t)st.st_ctime));
655 
656         if (S_ISREG(st.st_mode))
657                 ucv_object_add(res, "type", ucv_string_new("file"));
658         else if (S_ISDIR(st.st_mode))
659                 ucv_object_add(res, "type", ucv_string_new("directory"));
660         else if (S_ISCHR(st.st_mode))
661                 ucv_object_add(res, "type", ucv_string_new("char"));
662         else if (S_ISBLK(st.st_mode))
663                 ucv_object_add(res, "type", ucv_string_new("block"));
664         else if (S_ISFIFO(st.st_mode))
665                 ucv_object_add(res, "type", ucv_string_new("fifo"));
666         else if (S_ISLNK(st.st_mode))
667                 ucv_object_add(res, "type", ucv_string_new("link"));
668         else if (S_ISSOCK(st.st_mode))
669                 ucv_object_add(res, "type", ucv_string_new("socket"));
670         else
671                 ucv_object_add(res, "type", ucv_string_new("unknown"));
672 
673         return res;
674 }
675 
676 static uc_value_t *
677 uc_fs_stat(uc_vm_t *vm, size_t nargs)
678 {
679         return uc_fs_stat_common(vm, nargs, false);
680 }
681 
682 static uc_value_t *
683 uc_fs_lstat(uc_vm_t *vm, size_t nargs)
684 {
685         return uc_fs_stat_common(vm, nargs, true);
686 }
687 
688 static uc_value_t *
689 uc_fs_mkdir(uc_vm_t *vm, size_t nargs)
690 {
691         uc_value_t *path = uc_fn_arg(0);
692         uc_value_t *mode = uc_fn_arg(1);
693 
694         if (ucv_type(path) != UC_STRING ||
695             (mode && ucv_type(mode) != UC_INTEGER))
696                 err_return(EINVAL);
697 
698         if (mkdir(ucv_string_get(path), (mode_t)(mode ? ucv_int64_get(mode) : 0777)) == -1)
699                 err_return(errno);
700 
701         return ucv_boolean_new(true);
702 }
703 
704 static uc_value_t *
705 uc_fs_rmdir(uc_vm_t *vm, size_t nargs)
706 {
707         uc_value_t *path = uc_fn_arg(0);
708 
709         if (ucv_type(path) != UC_STRING)
710                 err_return(EINVAL);
711 
712         if (rmdir(ucv_string_get(path)) == -1)
713                 err_return(errno);
714 
715         return ucv_boolean_new(true);
716 }
717 
718 static uc_value_t *
719 uc_fs_symlink(uc_vm_t *vm, size_t nargs)
720 {
721         uc_value_t *dest = uc_fn_arg(0);
722         uc_value_t *path = uc_fn_arg(1);
723 
724         if (ucv_type(dest) != UC_STRING ||
725             ucv_type(path) != UC_STRING)
726                 err_return(EINVAL);
727 
728         if (symlink(ucv_string_get(dest), ucv_string_get(path)) == -1)
729                 err_return(errno);
730 
731         return ucv_boolean_new(true);
732 }
733 
734 static uc_value_t *
735 uc_fs_unlink(uc_vm_t *vm, size_t nargs)
736 {
737         uc_value_t *path = uc_fn_arg(0);
738 
739         if (ucv_type(path) != UC_STRING)
740                 err_return(EINVAL);
741 
742         if (unlink(ucv_string_get(path)) == -1)
743                 err_return(errno);
744 
745         return ucv_boolean_new(true);
746 }
747 
748 static uc_value_t *
749 uc_fs_getcwd(uc_vm_t *vm, size_t nargs)
750 {
751         uc_value_t *res;
752         char *buf = NULL, *tmp;
753         size_t buflen = 0;
754 
755         do {
756                 buflen += 128;
757                 tmp = realloc(buf, buflen);
758 
759                 if (!tmp) {
760                         free(buf);
761                         err_return(ENOMEM);
762                 }
763 
764                 buf = tmp;
765 
766                 if (getcwd(buf, buflen) != NULL)
767                         break;
768 
769                 if (errno == ERANGE)
770                         continue;
771 
772                 free(buf);
773                 err_return(errno);
774         }
775         while (true);
776 
777         res = ucv_string_new(buf);
778 
779         free(buf);
780 
781         return res;
782 }
783 
784 static uc_value_t *
785 uc_fs_chdir(uc_vm_t *vm, size_t nargs)
786 {
787         uc_value_t *path = uc_fn_arg(0);
788 
789         if (ucv_type(path) != UC_STRING)
790                 err_return(EINVAL);
791 
792         if (chdir(ucv_string_get(path)) == -1)
793                 err_return(errno);
794 
795         return ucv_boolean_new(true);
796 }
797 
798 static uc_value_t *
799 uc_fs_chmod(uc_vm_t *vm, size_t nargs)
800 {
801         uc_value_t *path = uc_fn_arg(0);
802         uc_value_t *mode = uc_fn_arg(1);
803 
804         if (ucv_type(path) != UC_STRING ||
805             ucv_type(mode) != UC_INTEGER)
806                 err_return(EINVAL);
807 
808         if (chmod(ucv_string_get(path), (mode_t)ucv_int64_get(mode)) == -1)
809                 err_return(errno);
810 
811         return ucv_boolean_new(true);
812 }
813 
814 static bool
815 uc_fs_resolve_user(uc_value_t *v, uid_t *uid)
816 {
817         struct passwd *pw = NULL;
818         int64_t n;
819         char *s;
820 
821         *uid = (uid_t)-1;
822 
823         switch (ucv_type(v)) {
824         case UC_INTEGER:
825                 n = ucv_int64_get(v);
826 
827                 if (n < -1) {
828                         errno = ERANGE;
829 
830                         return false;
831                 }
832 
833                 *uid = (uid_t)n;
834 
835                 return true;
836 
837         case UC_STRING:
838                 s = ucv_string_get(v);
839                 pw = getpwnam(s);
840 
841                 if (!pw) {
842                         errno = ENOENT;
843 
844                         return false;
845                 }
846 
847                 *uid = pw->pw_uid;
848 
849                 return true;
850 
851         case UC_NULL:
852                 return true;
853 
854         default:
855                 errno = EINVAL;
856 
857                 return false;
858         }
859 }
860 
861 static bool
862 uc_fs_resolve_group(uc_value_t *v, gid_t *gid)
863 {
864         struct group *gr = NULL;
865         int64_t n;
866         char *s;
867 
868         *gid = (gid_t)-1;
869 
870         switch (ucv_type(v)) {
871         case UC_INTEGER:
872                 n = ucv_int64_get(v);
873 
874                 if (n < -1) {
875                         errno = ERANGE;
876 
877                         return false;
878                 }
879 
880                 *gid = (gid_t)n;
881 
882                 return true;
883 
884         case UC_STRING:
885                 s = ucv_string_get(v);
886                 gr = getgrnam(s);
887 
888                 if (!gr) {
889                         errno = ENOENT;
890 
891                         return false;
892                 }
893 
894                 *gid = gr->gr_gid;
895 
896                 return true;
897 
898         case UC_NULL:
899                 return true;
900 
901         default:
902                 errno = EINVAL;
903 
904                 return false;
905         }
906 }
907 
908 static uc_value_t *
909 uc_fs_chown(uc_vm_t *vm, size_t nargs)
910 {
911         uc_value_t *path = uc_fn_arg(0);
912         uc_value_t *user = uc_fn_arg(1);
913         uc_value_t *group = uc_fn_arg(2);
914         uid_t uid;
915         gid_t gid;
916 
917         if (ucv_type(path) != UC_STRING)
918             err_return(EINVAL);
919 
920         if (!uc_fs_resolve_user(user, &uid) ||
921             !uc_fs_resolve_group(group, &gid))
922                 err_return(errno);
923 
924         if (chown(ucv_string_get(path), uid, gid) == -1)
925                 err_return(errno);
926 
927         return ucv_boolean_new(true);
928 }
929 
930 static uc_value_t *
931 uc_fs_rename(uc_vm_t *vm, size_t nargs)
932 {
933         uc_value_t *oldpath = uc_fn_arg(0);
934         uc_value_t *newpath = uc_fn_arg(1);
935 
936         if (ucv_type(oldpath) != UC_STRING ||
937             ucv_type(newpath) != UC_STRING)
938                 err_return(EINVAL);
939 
940         if (rename(ucv_string_get(oldpath), ucv_string_get(newpath)))
941                 err_return(errno);
942 
943         return ucv_boolean_new(true);
944 }
945 
946 static uc_value_t *
947 uc_fs_glob(uc_vm_t *vm, size_t nargs)
948 {
949         uc_value_t *pat, *arr;
950         glob_t gl = { 0 };
951         size_t i;
952 
953         for (i = 0; i < nargs; i++) {
954                 pat = uc_fn_arg(i);
955 
956                 if (ucv_type(pat) != UC_STRING) {
957                         globfree(&gl);
958                         err_return(EINVAL);
959                 }
960 
961                 glob(ucv_string_get(pat), i ? GLOB_APPEND : 0, NULL, &gl);
962         }
963 
964         arr = ucv_array_new(vm);
965 
966         for (i = 0; i < gl.gl_pathc; i++)
967                 ucv_array_push(arr, ucv_string_new(gl.gl_pathv[i]));
968 
969         globfree(&gl);
970 
971         return arr;
972 }
973 
974 static uc_value_t *
975 uc_fs_dirname(uc_vm_t *vm, size_t nargs)
976 {
977         uc_value_t *path = uc_fn_arg(0);
978         size_t i;
979         char *s;
980 
981         if (ucv_type(path) != UC_STRING)
982                 err_return(EINVAL);
983 
984         i = ucv_string_length(path);
985         s = ucv_string_get(path);
986 
987         if (i == 0)
988                 return ucv_string_new(".");
989 
990         for (i--; s[i] == '/'; i--)
991                 if (i == 0)
992                         return ucv_string_new("/");
993 
994         for (; s[i] != '/'; i--)
995                 if (i == 0)
996                         return ucv_string_new(".");
997 
998         for (; s[i] == '/'; i--)
999                 if (i == 0)
1000                         return ucv_string_new("/");
1001 
1002         return ucv_string_new_length(s, i + 1);
1003 }
1004 
1005 static uc_value_t *
1006 uc_fs_basename(uc_vm_t *vm, size_t nargs)
1007 {
1008         uc_value_t *path = uc_fn_arg(0);
1009         size_t i, len, skip;
1010         char *s;
1011 
1012         if (ucv_type(path) != UC_STRING)
1013                 err_return(EINVAL);
1014 
1015         len = ucv_string_length(path);
1016         s = ucv_string_get(path);
1017 
1018         if (len == 0)
1019                 return ucv_string_new(".");
1020 
1021         for (i = len - 1, skip = 0; i > 0 && s[i] == '/'; i--, skip++)
1022                 ;
1023 
1024         for (; i > 0 && s[i - 1] != '/'; i--)
1025                 ;
1026 
1027         return ucv_string_new_length(s + i, len - i - skip);
1028 }
1029 
1030 static int
1031 uc_fs_lsdir_sort_fn(const void *k1, const void *k2)
1032 {
1033         uc_value_t * const *v1 = k1;
1034         uc_value_t * const *v2 = k2;
1035 
1036         return strcmp(ucv_string_get(*v1), ucv_string_get(*v2));
1037 }
1038 
1039 static uc_value_t *
1040 uc_fs_lsdir(uc_vm_t *vm, size_t nargs)
1041 {
1042         uc_value_t *path = uc_fn_arg(0);
1043         uc_value_t *pat = uc_fn_arg(1);
1044         uc_value_t *res = NULL;
1045         uc_regexp_t *reg;
1046         struct dirent *e;
1047         DIR *d;
1048 
1049         if (ucv_type(path) != UC_STRING)
1050                 err_return(EINVAL);
1051 
1052         switch (ucv_type(pat)) {
1053         case UC_NULL:
1054         case UC_STRING:
1055         case UC_REGEXP:
1056                 break;
1057 
1058         default:
1059                 err_return(EINVAL);
1060         }
1061 
1062         d = opendir(ucv_string_get(path));
1063 
1064         if (!d)
1065                 err_return(errno);
1066 
1067         res = ucv_array_new(vm);
1068 
1069         while ((e = readdir(d)) != NULL) {
1070                 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
1071                         continue;
1072 
1073                 if (ucv_type(pat) == UC_REGEXP) {
1074                         reg = (uc_regexp_t *)pat;
1075 
1076                         if (regexec(&reg->regexp, e->d_name, 0, NULL, 0) == REG_NOMATCH)
1077                                 continue;
1078                 }
1079                 else if (ucv_type(pat) == UC_STRING) {
1080                         if (fnmatch(ucv_string_get(pat), e->d_name, 0) == FNM_NOMATCH)
1081                                 continue;
1082                 }
1083 
1084                 ucv_array_push(res, ucv_string_new(e->d_name));
1085         }
1086 
1087         closedir(d);
1088 
1089         ucv_array_sort(res, uc_fs_lsdir_sort_fn);
1090 
1091         return res;
1092 }
1093 
1094 static uc_value_t *
1095 uc_fs_mkstemp(uc_vm_t *vm, size_t nargs)
1096 {
1097         uc_value_t *template = uc_fn_arg(0);
1098         bool ends_with_template = false;
1099         char *path, *t;
1100         FILE *fp;
1101         size_t l;
1102         int fd;
1103 
1104         if (template && ucv_type(template) != UC_STRING)
1105                 err_return(EINVAL);
1106 
1107         t = ucv_string_get(template);
1108         l = ucv_string_length(template);
1109 
1110         ends_with_template = (l >= 6 && strcmp(&t[l - 6], "XXXXXX") == 0);
1111 
1112         if (t && strchr(t, '/')) {
1113                 if (ends_with_template)
1114                         xasprintf(&path, "%s", t);
1115                 else
1116                         xasprintf(&path, "%s.XXXXXX", t);
1117         }
1118         else if (t) {
1119                 if (ends_with_template)
1120                         xasprintf(&path, "/tmp/%s", t);
1121                 else
1122                         xasprintf(&path, "/tmp/%s.XXXXXX", t);
1123         }
1124         else {
1125                 xasprintf(&path, "/tmp/XXXXXX");
1126         }
1127 
1128         do {
1129                 fd = mkstemp(path);
1130         }
1131         while (fd == -1 && errno == EINTR);
1132 
1133         if (fd == -1) {
1134                 free(path);
1135                 err_return(errno);
1136         }
1137 
1138         unlink(path);
1139         free(path);
1140 
1141         fp = fdopen(fd, "r+");
1142 
1143         if (!fp) {
1144                 close(fd);
1145                 err_return(errno);
1146         }
1147 
1148         return uc_resource_new(file_type, fp);
1149 }
1150 
1151 static uc_value_t *
1152 uc_fs_access(uc_vm_t *vm, size_t nargs)
1153 {
1154         uc_value_t *path = uc_fn_arg(0);
1155         uc_value_t *test = uc_fn_arg(1);
1156         int mode = F_OK;
1157         char *p;
1158 
1159         if (ucv_type(path) != UC_STRING)
1160                 err_return(EINVAL);
1161 
1162         if (test && ucv_type(test) != UC_STRING)
1163                 err_return(EINVAL);
1164 
1165         for (p = ucv_string_get(test); p && *p; p++) {
1166                 switch (*p) {
1167                 case 'r':
1168                         mode |= R_OK;
1169                         break;
1170 
1171                 case 'w':
1172                         mode |= W_OK;
1173                         break;
1174 
1175                 case 'x':
1176                         mode |= X_OK;
1177                         break;
1178 
1179                 case 'f':
1180                         mode |= F_OK;
1181                         break;
1182 
1183                 default:
1184                         err_return(EINVAL);
1185                 }
1186         }
1187 
1188         if (access(ucv_string_get(path), mode) == -1)
1189                 err_return(errno);
1190 
1191         return ucv_boolean_new(true);
1192 }
1193 
1194 static uc_value_t *
1195 uc_fs_readfile(uc_vm_t *vm, size_t nargs)
1196 {
1197         uc_value_t *path = uc_fn_arg(0);
1198         uc_value_t *size = uc_fn_arg(1);
1199         uc_value_t *res = NULL;
1200         uc_stringbuf_t *buf;
1201         ssize_t limit = -1;
1202         size_t rlen, blen;
1203         FILE *fp;
1204 
1205         if (ucv_type(path) != UC_STRING)
1206                 err_return(EINVAL);
1207 
1208         if (size) {
1209                 if (ucv_type(size) != UC_INTEGER)
1210                         err_return(EINVAL);
1211 
1212                 limit = ucv_int64_get(size);
1213         }
1214 
1215         fp = fopen(ucv_string_get(path), "r");
1216 
1217         if (!fp)
1218                 err_return(errno);
1219 
1220         buf = ucv_stringbuf_new();
1221 
1222         if (limit > -1 && limit < BUFSIZ)
1223                 setvbuf(fp, NULL, _IONBF, 0);
1224 
1225         while (limit != 0) {
1226                 blen = 1024;
1227 
1228                 if (limit > 0 && blen > (size_t)limit)
1229                         blen = (size_t)limit;
1230 
1231                 printbuf_memset(buf, printbuf_length(buf) + blen - 1, 0, 1);
1232 
1233                 buf->bpos -= blen;
1234                 rlen = fread(buf->buf + buf->bpos, 1, blen, fp);
1235                 buf->bpos += rlen;
1236 
1237                 if (rlen < blen)
1238                         break;
1239 
1240                 if (limit > 0)
1241                         limit -= rlen;
1242         }
1243 
1244         if (ferror(fp)) {
1245                 fclose(fp);
1246                 printbuf_free(buf);
1247                 err_return(errno);
1248         }
1249 
1250         fclose(fp);
1251 
1252         /* add sentinel null byte but don't count it towards the string length */
1253         printbuf_memappend_fast(buf, "\0", 1);
1254         res = ucv_stringbuf_finish(buf);
1255         ((uc_string_t *)res)->length--;
1256 
1257         return res;
1258 }
1259 
1260 static uc_value_t *
1261 uc_fs_writefile(uc_vm_t *vm, size_t nargs)
1262 {
1263         uc_value_t *path = uc_fn_arg(0);
1264         uc_value_t *data = uc_fn_arg(1);
1265         uc_value_t *size = uc_fn_arg(2);
1266         uc_stringbuf_t *buf = NULL;
1267         ssize_t limit = -1;
1268         size_t wlen = 0;
1269         int err = 0;
1270         FILE *fp;
1271 
1272         if (ucv_type(path) != UC_STRING)
1273                 err_return(EINVAL);
1274 
1275         if (size) {
1276                 if (ucv_type(size) != UC_INTEGER)
1277                         err_return(EINVAL);
1278 
1279                 limit = ucv_int64_get(size);
1280         }
1281 
1282         fp = fopen(ucv_string_get(path), "w");
1283 
1284         if (!fp)
1285                 err_return(errno);
1286 
1287         if (data && ucv_type(data) != UC_STRING) {
1288                 buf = xprintbuf_new();
1289                 ucv_to_stringbuf_formatted(vm, buf, data, 0, '\0', 0);
1290 
1291                 if (limit < 0 || limit > printbuf_length(buf))
1292                         limit = printbuf_length(buf);
1293 
1294                 wlen = fwrite(buf->buf, 1, limit, fp);
1295 
1296                 if (wlen < (size_t)limit)
1297                         err = errno;
1298 
1299                 printbuf_free(buf);
1300         }
1301         else if (data) {
1302                 if (limit < 0 || (size_t)limit > ucv_string_length(data))
1303                         limit = ucv_string_length(data);
1304 
1305                 wlen = fwrite(ucv_string_get(data), 1, limit, fp);
1306 
1307                 if (wlen < (size_t)limit)
1308                         err = errno;
1309         }
1310 
1311         fclose(fp);
1312 
1313         if (err)
1314                 err_return(err);
1315 
1316         return ucv_uint64_new(wlen);
1317 }
1318 
1319 static uc_value_t *
1320 uc_fs_realpath(uc_vm_t *vm, size_t nargs)
1321 {
1322         uc_value_t *path = uc_fn_arg(0), *rv;
1323         char *resolved;
1324 
1325         if (ucv_type(path) != UC_STRING)
1326                 err_return(EINVAL);
1327 
1328         resolved = realpath(ucv_string_get(path), NULL);
1329 
1330         if (!resolved)
1331                 err_return(errno);
1332 
1333         rv = ucv_string_new(resolved);
1334 
1335         free(resolved);
1336 
1337         return rv;
1338 }
1339 
1340 
1341 static const uc_function_list_t proc_fns[] = {
1342         { "read",               uc_fs_pread },
1343         { "write",              uc_fs_pwrite },
1344         { "close",              uc_fs_pclose },
1345         { "flush",              uc_fs_pflush },
1346         { "fileno",             uc_fs_pfileno },
1347         { "error",              uc_fs_error },
1348 };
1349 
1350 static const uc_function_list_t file_fns[] = {
1351         { "read",               uc_fs_read },
1352         { "write",              uc_fs_write },
1353         { "seek",               uc_fs_seek },
1354         { "tell",               uc_fs_tell },
1355         { "close",              uc_fs_close },
1356         { "flush",              uc_fs_flush },
1357         { "fileno",             uc_fs_fileno },
1358         { "error",              uc_fs_error },
1359 };
1360 
1361 static const uc_function_list_t dir_fns[] = {
1362         { "read",               uc_fs_readdir },
1363         { "seek",               uc_fs_seekdir },
1364         { "tell",               uc_fs_telldir },
1365         { "close",              uc_fs_closedir },
1366         { "error",              uc_fs_error },
1367 };
1368 
1369 static const uc_function_list_t global_fns[] = {
1370         { "error",              uc_fs_error },
1371         { "open",               uc_fs_open },
1372         { "fdopen",             uc_fs_fdopen },
1373         { "opendir",    uc_fs_opendir },
1374         { "popen",              uc_fs_popen },
1375         { "readlink",   uc_fs_readlink },
1376         { "stat",               uc_fs_stat },
1377         { "lstat",              uc_fs_lstat },
1378         { "mkdir",              uc_fs_mkdir },
1379         { "rmdir",              uc_fs_rmdir },
1380         { "symlink",    uc_fs_symlink },
1381         { "unlink",             uc_fs_unlink },
1382         { "getcwd",             uc_fs_getcwd },
1383         { "chdir",              uc_fs_chdir },
1384         { "chmod",              uc_fs_chmod },
1385         { "chown",              uc_fs_chown },
1386         { "rename",             uc_fs_rename },
1387         { "glob",               uc_fs_glob },
1388         { "dirname",    uc_fs_dirname },
1389         { "basename",   uc_fs_basename },
1390         { "lsdir",              uc_fs_lsdir },
1391         { "mkstemp",    uc_fs_mkstemp },
1392         { "access",             uc_fs_access },
1393         { "readfile",   uc_fs_readfile },
1394         { "writefile",  uc_fs_writefile },
1395         { "realpath",   uc_fs_realpath },
1396 };
1397 
1398 
1399 static void close_proc(void *ud)
1400 {
1401         FILE *fp = ud;
1402 
1403         if (fp)
1404                 pclose(fp);
1405 }
1406 
1407 static void close_file(void *ud)
1408 {
1409         FILE *fp = ud;
1410         int n;
1411 
1412         n = fp ? fileno(fp) : -1;
1413 
1414         if (n > 2)
1415                 fclose(fp);
1416 }
1417 
1418 static void close_dir(void *ud)
1419 {
1420         DIR *dp = ud;
1421 
1422         if (dp)
1423                 closedir(dp);
1424 }
1425 
1426 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1427 {
1428         uc_function_list_register(scope, global_fns);
1429 
1430         proc_type = uc_type_declare(vm, "fs.proc", proc_fns, close_proc);
1431         file_type = uc_type_declare(vm, "fs.file", file_fns, close_file);
1432         dir_type = uc_type_declare(vm, "fs.dir", dir_fns, close_dir);
1433 
1434         ucv_object_add(scope, "stdin", uc_resource_new(file_type, stdin));
1435         ucv_object_add(scope, "stdout", uc_resource_new(file_type, stdout));
1436         ucv_object_add(scope, "stderr", uc_resource_new(file_type, stderr));
1437 }
1438 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt