1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2014-2018 Paul Sokolovsky
7  * Copyright (c) 2014-2018 Damien P. George
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <dirent.h>
36 #include <fcntl.h>
37 
38 #include "py/mpconfig.h"
39 #include "py/runtime.h"
40 #include "py/objtuple.h"
41 #include "py/objstr.h"
42 #include "py/mperrno.h"
43 #include "py/mphal.h"
44 #include "py/mpthread.h"
45 #include "extmod/vfs.h"
46 #include "extmod/misc.h"
47 #include "ulog/ulog.h"
48 #include "genhdr/mpversion.h"
49 #include "aos/kernel.h"
50 #include "dirent.h"
51 
52 #if MICROPY_VFS_POSIX
53 #include "vfs_posix.h"
54 #endif
55 
56 #define LOG_TAG "MOD_OS"
57 
58 extern const mp_obj_type_t mp_fat_vfs_type;
59 
60 STATIC const qstr os_uname_info_fields[] = {
61     MP_QSTR_sysname, MP_QSTR_nodename,
62     MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
63 };
64 STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM);
65 STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM);
66 STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING);
67 STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE);
68 STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME);
69 
70 STATIC MP_DEFINE_ATTRTUPLE(
71     os_uname_info_obj,
72     os_uname_info_fields,
73     5,
74     (mp_obj_t)&os_uname_info_sysname_obj,
75     (mp_obj_t)&os_uname_info_nodename_obj,
76     (mp_obj_t)&os_uname_info_release_obj,
77     (mp_obj_t)&os_uname_info_version_obj,
78     (mp_obj_t)&os_uname_info_machine_obj
79     );
80 
os_uname(void)81 STATIC mp_obj_t os_uname(void) {
82     return (mp_obj_t)&os_uname_info_obj;
83 }
84 STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname);
85 
os_urandom(mp_obj_t num)86 STATIC mp_obj_t os_urandom(mp_obj_t num) {
87     mp_int_t n = mp_obj_get_int(num);
88     vstr_t vstr;
89     vstr_init_len(&vstr, n);
90     uint32_t r = 0;
91     for (int i = 0; i < n; i++) {
92         if ((i & 3) == 0) {
93             r = aos_rand(); // returns 32-bit random number
94         }
95         vstr.buf[i] = r;
96         r >>= 8;
97     }
98     vstr.buf[n] = '\0';
99     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
100 }
101 STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom);
102 
up_one_level(char * s)103 static int up_one_level(char *s)
104 {
105     char *tail;
106 
107     if (!s)
108         return -1;
109 
110     tail = s + strlen(s) - 1;
111     if (*tail == '/')
112         tail--;
113 
114     while (*tail != '\0' && *tail != '/')
115         tail--;
116 
117     if (*tail == '\0') {
118         return -1;
119     } else {
120         *(tail + 1) = '\0';
121         return 0;
122     }
123 }
124 
py_get_realpath(const char * path,char * resolved_path,unsigned int len)125 char *py_get_realpath(const char *path, char *resolved_path, unsigned int len)
126 {
127     char *ret, *p = (char *)path, *r = resolved_path;
128 
129     if (!path || !r || len < 1)
130         return NULL;
131 
132     memset(r, 0, len);
133 
134     // deal with heading char
135     if (p[0] != '/') {
136         // relative path
137         ret = getcwd(r, len);
138         if (!ret)
139             return NULL;
140 
141         // add tailing '/' if no
142         if (r[strlen(r) - 1] != '/') {
143             r[strlen(r)] = '/';
144         }
145 
146         r += strlen(r);
147     } else {
148         // absolute path
149         r[0] = '/';
150         r++;
151     }
152 
153     // iterate to exclude '.', '..'. '/'
154     while (*p != '\0') {
155         while (*p == '/')
156             p++;
157         if (*p == '\0')
158             break;
159 
160         if (*p == '.') {
161             p++;
162             // end with '.'
163             if (*p == '\0')
164                 break;
165 
166             if (*p == '.') {
167                 // '..' or '../'
168                 if ((*(p + 1) != '/') && (*(p + 1) != '\0')) {
169                     printf("Invalid path %s\r\n", path);
170                     return NULL;
171                 } else {
172                     // '..' case
173                     p++;
174                     // if (*p == '/') {
175                     if (up_one_level(resolved_path) != 0) {
176                         printf("Failed to go up now. Invalid path %s\r\n", path);
177                         return NULL;
178                     }
179 
180                     r = resolved_path + strlen(resolved_path);
181                     // }
182 
183                     // end with '.'
184                     if (*p == '\0') {
185                         break;
186                     }
187                 }
188             } else {
189                 if ((*p != '/') && (*p != '\0')) {
190                     printf("Invalid path %s\r\n", path);
191                     return NULL;
192                 } else {
193                     // '.' case
194                     p++;
195                 }
196             }
197         }
198 
199         while (*p == '/')
200             p++;
201         if (*p == '\0')
202             break;
203 
204         // if another round of ./.., just continue
205         if (*p == '.')
206             continue;
207 
208         // path string may be found now, save to r
209         while ((*p != '/') && (*p != '\0'))
210             *r++ = *p++;
211 
212         // add taling '/' if necessary
213         if (*(r - 1) != '/') {
214             *r++ = '/';
215         }
216     }
217 
218     // exclude the tailing '/', just in case it is a file
219     if ((resolved_path[strlen(resolved_path) - 1] == '/') &&
220         (strlen(resolved_path) != 1)) {
221         resolved_path[strlen(resolved_path) - 1] = '\0';
222     }
223 
224     return resolved_path;
225 }
226 
mod_os_stat(mp_obj_t path_in)227 STATIC mp_obj_t mod_os_stat(mp_obj_t path_in) {
228     struct stat sb;
229     const char *path = mp_obj_str_get_str(path_in);
230     int res = stat(path, &sb);
231 
232     mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
233     t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
234     t->items[1] = mp_obj_new_int_from_uint(sb.st_ino);
235     t->items[2] = mp_obj_new_int_from_uint(sb.st_dev);
236     t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink);
237     t->items[4] = mp_obj_new_int_from_uint(sb.st_uid);
238     t->items[5] = mp_obj_new_int_from_uint(sb.st_gid);
239     t->items[6] = mp_obj_new_int_from_uint(sb.st_size);
240     t->items[7] = mp_obj_new_int_from_uint(sb.st_atime);
241     t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime);
242     t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime);
243     return MP_OBJ_FROM_PTR(t);
244 }
245 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_stat_obj, mod_os_stat);
246 
mod_os_statvfs(mp_obj_t path_in)247 STATIC mp_obj_t mod_os_statvfs(mp_obj_t path_in) {
248     struct statfs sb;
249     const char *path = mp_obj_str_get_str(path_in);
250     int res = statfs(path, &sb);
251 
252     mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(9, NULL));
253     t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_type);
254     t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
255     t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
256     t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
257     t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
258     t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
259     t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
260     t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.f_fsid);
261     t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.f_namelen);
262     return MP_OBJ_FROM_PTR(t);
263 }
264 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_statvfs_obj, mod_os_statvfs);
265 
266 
mod_os_remove(mp_obj_t path_in)267 STATIC mp_obj_t mod_os_remove(mp_obj_t path_in) {
268     const char *path = mp_obj_str_get_str(path_in);
269 
270     char abspath[256] = {0};
271     path = py_get_realpath(path, abspath, sizeof(abspath));
272 
273     MP_THREAD_GIL_EXIT();
274     int r = unlink(path);
275     MP_THREAD_GIL_ENTER();
276 
277     return MP_OBJ_NEW_SMALL_INT(r);
278 }
279 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_remove_obj, mod_os_remove);
280 
281 
py_mv(char * from,char * to)282 static int py_mv(char *from, char *to)
283 {
284     int fd_from = 0, fd_to = 0;
285     char buf[128] = {0};
286     int rlen = 0, wlen = 0, ret = -1, isdir = false;
287     struct stat s;
288 
289     char abspath_from[256] = {0}, abspath_to[256] = {0};
290 
291     from = py_get_realpath(from, abspath_from, sizeof(abspath_from));
292     to = py_get_realpath(to, abspath_to, sizeof(abspath_to));
293     if (!from || !to) {
294         LOGE(LOG_TAG, "Failed to get real path!\r\n");
295         return -1;
296     }
297 
298     if (!stat(to, &s)) {
299         if (S_ISDIR(s.st_mode)) {
300             char *p = strrchr(from, '/');
301 
302             if (!p)
303                 ret = asprintf(&to, "%s/%s", to, from);
304             else
305                 ret = asprintf(&to, "%s%s", to, p);
306             if (ret < 0) {
307                 LOGE(LOG_TAG, "asprintf failed\n");
308                 return -1;
309             }
310             isdir = true;
311         }
312     }
313 
314     ret = rename(from, to);
315     if (ret < 0 && errno != EXDEV) {
316         LOGE(LOG_TAG, "rename %s to %s failed - %s\n", from, to, strerror(errno));
317         return -1;
318     } else if (ret == 0) {
319         return 0;
320     }
321 
322     fd_from = open(from, O_RDONLY);
323     if (fd_from < 0) {
324         LOGE(LOG_TAG, "open %s failed - %s\n", from, strerror(errno));
325         return -1;
326     }
327 
328     fd_to = open(to, O_WRONLY | O_CREAT | O_TRUNC);
329     if (fd_to < 0) {
330         LOGE(LOG_TAG, "open %s failed - %s\n", to, strerror(errno));
331         goto close_from;
332     }
333 
334     while ((rlen = read(fd_from, buf, 128))) {
335         if (rlen < 0) {
336             LOGE(LOG_TAG, "read %s failed - %s\n", from, strerror(errno));
337             goto close_to;
338         }
339 
340         wlen = write(fd_to, buf, rlen);
341         if (wlen != rlen) {
342             LOGE(LOG_TAG, "write %s failed - %s\n", to, strerror(errno));
343             goto close_to;
344         }
345     }
346 
347     ret = unlink(from);
348     if (ret) {
349         LOGE(LOG_TAG, "unlink %s failed - %s\n", from, strerror(errno));
350         goto close_to;
351     }
352 
353     ret = 0;
354 close_to:
355     close(fd_to);
356 close_from:
357     close(fd_from);
358     if (isdir)
359         free(to);
360     return ret;
361 }
362 
mod_os_rename(mp_obj_t old_path_in,mp_obj_t new_path_in)363 STATIC mp_obj_t mod_os_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) {
364     const char *old_path = mp_obj_str_get_str(old_path_in);
365     const char *new_path = mp_obj_str_get_str(new_path_in);
366 
367     MP_THREAD_GIL_EXIT();
368     int r = rename(old_path, new_path);
369     MP_THREAD_GIL_ENTER();
370 
371     return MP_OBJ_NEW_SMALL_INT(r);
372 }
373 STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_os_rename_obj, mod_os_rename);
374 
py_rrmdir(const char * path)375 int py_rrmdir(const char *path)
376 {
377     struct stat s;
378     DIR *pdir = NULL;
379     struct dirent *entry = NULL;
380     int ret = -1;
381     char *dir, *p;
382 
383     if (!path)
384         return -EINVAL;
385 
386     dir = strdup(path);
387     p = dir + strlen(dir) - 1;
388     while ((*p == '/') && (p > dir)) {
389         *p = '\0';
390         p--;
391     }
392 
393     if (stat(dir, &s) || !S_ISDIR(s.st_mode)) {
394         // LOGE(LOG_TAG, "%s is neither existed nor a directory\n", dir);
395         goto out;
396     }
397 
398     pdir = opendir(dir);
399     if (!pdir) {
400         LOGE(LOG_TAG, "opendir %s failed - %s\n", dir, strerror(errno));
401         goto out;
402     }
403 
404     ret = 0;
405     while ((ret == 0) && (entry = readdir(pdir))) {
406         char fpath[128];
407 
408         snprintf(fpath, 128, "%s/%s", dir, entry->d_name);
409 
410         ret = stat(fpath, &s);
411         if (ret) {
412             LOGE(LOG_TAG, "stat %s failed\n", fpath);
413             break;
414         }
415 
416         if (!strcmp(entry->d_name, "."))
417             continue;
418         if (!strcmp(entry->d_name, ".."))
419             continue;
420 
421         if (S_ISDIR(s.st_mode))
422             ret = py_rrmdir(fpath);
423         else
424             ret = unlink(fpath);
425     }
426 
427     closedir(pdir);
428     if (ret == 0) {
429         ret = rmdir(dir);
430         if (ret)
431             LOGE(LOG_TAG, "rmdir %s failed\n", dir);
432     }
433 out:
434     free(dir);
435     return ret;
436 }
437 
438 
mod_os_rmdir(mp_obj_t path_in)439 STATIC mp_obj_t mod_os_rmdir(mp_obj_t path_in) {
440     const char *path = mp_obj_str_get_str(path_in);
441     char abspath[256] = {0};
442 
443     path = py_get_realpath(path, abspath, sizeof(abspath));
444 
445     MP_THREAD_GIL_EXIT();
446     int r = rrmdir(path);
447     MP_THREAD_GIL_ENTER();
448 
449     return MP_OBJ_NEW_SMALL_INT(r);
450 }
451 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_rmdir_obj, mod_os_rmdir);
452 
mod_os_system(mp_obj_t cmd_in)453 STATIC mp_obj_t mod_os_system(mp_obj_t cmd_in) {
454     const char *cmd = mp_obj_str_get_str(cmd_in);
455 
456     MP_THREAD_GIL_EXIT();
457     int r = system(cmd);
458     MP_THREAD_GIL_ENTER();
459 
460     return MP_OBJ_NEW_SMALL_INT(r);
461 }
462 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_system_obj, mod_os_system);
463 
mod_os_getenv(mp_obj_t var_in)464 STATIC mp_obj_t mod_os_getenv(mp_obj_t var_in) {
465     const char *s = getenv(mp_obj_str_get_str(var_in));
466     if (s == NULL) {
467         return mp_const_none;
468     }
469     return mp_obj_new_str(s, strlen(s));
470 }
471 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_obj, mod_os_getenv);
472 
mod_os_putenv(mp_obj_t key_in,mp_obj_t value_in)473 STATIC mp_obj_t mod_os_putenv(mp_obj_t key_in, mp_obj_t value_in) {
474     const char *key = mp_obj_str_get_str(key_in);
475     const char *value = mp_obj_str_get_str(value_in);
476 
477     int ret = setenv(key, value, 1);
478     if (ret == -1) {
479         mp_raise_OSError(errno);
480     }
481 
482     return MP_OBJ_NEW_SMALL_INT(ret);
483 }
484 MP_DEFINE_CONST_FUN_OBJ_2(mod_os_putenv_obj, mod_os_putenv);
485 
mod_os_unsetenv(mp_obj_t key_in)486 STATIC mp_obj_t mod_os_unsetenv(mp_obj_t key_in) {
487     const char *key = mp_obj_str_get_str(key_in);
488     int ret = unsetenv(key);
489     if (ret == -1) {
490         mp_raise_OSError(errno);
491     }
492     return MP_OBJ_NEW_SMALL_INT(ret);
493 }
494 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_unsetenv_obj, mod_os_unsetenv);
495 
py_mkdir_do(char * path,int flags)496 static int py_mkdir_do(char *path, int flags)
497 {
498     char *s = NULL;
499     char abspath[256] = {0};
500     #define MKDIR_FLAGS_PARENTS (1 << 0)
501     path = py_get_realpath(path, abspath, sizeof(abspath));
502     if (!path) {
503         LOGE(LOG_TAG, "Failed to get real path!\r\n");
504         return -1;
505     }
506 
507     /*
508      * All of operations must base on root directory
509      * As alios has not root dierctory, we can operate '/data' but not '/'
510      */
511 #define MOUNT_BASE_DIR "/"
512     if (strncmp(path, MOUNT_BASE_DIR, strlen(MOUNT_BASE_DIR))) {
513         LOGE(LOG_TAG, "make directory must base on %s\n", MOUNT_BASE_DIR);
514         return -1;
515     }
516 
517     if (path[0] == '.') {
518         if (path[1] == '\0')
519             return 0;
520         if (path[1] == '.' && path[2] == '\0')
521             return 0;
522     }
523 
524     if (flags & MKDIR_FLAGS_PARENTS)
525         s = path + strlen(MOUNT_BASE_DIR);
526     while (1) {
527         struct stat st;
528 
529         if (flags & MKDIR_FLAGS_PARENTS) {
530             /* in case of tailing '/', such as '/data/a/' */
531             if (*(s++) == '\0')
532                 break;
533             s = strchr(s, '/');
534             if (s)
535                 *s = '\0';
536         }
537 
538         if (!stat(path, &st)) {
539             if (S_ISDIR(st.st_mode))
540                 goto next;
541             LOGE(LOG_TAG, "make failed - %s already existed and not direcotry\n", path);
542             return -1;
543         }
544 
545         if (mkdir(path, 0777) < 0) {
546             LOGE(LOG_TAG, "mkdir %s failed\n", path);
547             return -1;
548         }
549 
550 next:
551         if (!s)
552             break;
553         *s = '/';
554     }
555     return 0;
556 }
557 
mod_os_mkdir(mp_obj_t path_in)558 STATIC mp_obj_t mod_os_mkdir(mp_obj_t path_in) {
559     // TODO: Accept mode param
560     const char *path = mp_obj_str_get_str(path_in);
561 
562     MP_THREAD_GIL_EXIT();
563     int r = py_mkdir_do(path, 0777);
564     MP_THREAD_GIL_ENTER();
565 
566     return mp_obj_new_int(r);
567 }
568 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_mkdir_obj, mod_os_mkdir);
569 
570 typedef struct _mp_obj_listdir_t {
571     mp_obj_base_t base;
572     mp_fun_1_t iternext;
573     DIR *dir;
574 } mp_obj_listdir_t;
575 
listdir_next(mp_obj_t self_in)576 STATIC mp_obj_t listdir_next(mp_obj_t self_in) {
577     mp_obj_listdir_t *self = MP_OBJ_TO_PTR(self_in);
578 
579     if (self->dir == NULL) {
580         goto done;
581     }
582     MP_THREAD_GIL_EXIT();
583     struct dirent *dirent = readdir(self->dir);
584     if (dirent == NULL) {
585         closedir(self->dir);
586         MP_THREAD_GIL_ENTER();
587         self->dir = NULL;
588     done:
589         return MP_OBJ_STOP_ITERATION;
590     }
591     MP_THREAD_GIL_ENTER();
592 
593     mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
594     t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name));
595 
596     #ifdef _DIRENT_HAVE_D_TYPE
597     #ifdef DTTOIF
598     t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type));
599     #else
600     if (dirent->d_type == DT_DIR) {
601         t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
602     } else if (dirent->d_type == DT_REG) {
603         t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
604     } else {
605         t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
606     }
607     #endif
608     #else
609     // DT_UNKNOWN should have 0 value on any reasonable system
610     t->items[1] = MP_OBJ_NEW_SMALL_INT(0);
611     #endif
612 
613     #ifdef _DIRENT_HAVE_D_INO
614     t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
615     #else
616     t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
617     #endif
618     return MP_OBJ_FROM_PTR(t);
619 }
620 
mod_os_ilistdir(size_t n_args,const mp_obj_t * args)621 STATIC mp_obj_t mod_os_ilistdir(size_t n_args, const mp_obj_t *args) {
622     const char *path = ".";
623     if (n_args > 0) {
624         path = mp_obj_str_get_str(args[0]);
625     }
626 
627     char abspath[256] = {0};
628     path = py_get_realpath(path, abspath, sizeof(abspath));
629 
630     mp_obj_listdir_t *o = m_new_obj(mp_obj_listdir_t);
631     o->base.type = &mp_type_polymorph_iter;
632     MP_THREAD_GIL_EXIT();
633     o->dir = opendir(path);
634     MP_THREAD_GIL_ENTER();
635     o->iternext = listdir_next;
636     return MP_OBJ_FROM_PTR(o);
637 }
638 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_ilistdir_obj, 0, 1, mod_os_ilistdir);
639 
mod_os_listdir(size_t n_args,const mp_obj_t * args)640 STATIC mp_obj_t mod_os_listdir(size_t n_args, const mp_obj_t *args) {
641     const char *path = ".";
642     if (n_args > 0) {
643         path = mp_obj_str_get_str(args[0]);
644     }
645 
646     mp_raise_OSError(EPERM);
647     return mp_const_none;
648 
649     mp_obj_listdir_t *o = m_new_obj(mp_obj_listdir_t);
650 
651     MP_THREAD_GIL_EXIT();
652     MP_THREAD_GIL_ENTER();
653 
654     return MP_OBJ_FROM_PTR(o);
655 }
656 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_listdir_obj, 0, 1, mod_os_listdir);
657 
mod_os_errno(size_t n_args,const mp_obj_t * args)658 STATIC mp_obj_t mod_os_errno(size_t n_args, const mp_obj_t *args) {
659     if (n_args == 0) {
660         return MP_OBJ_NEW_SMALL_INT(errno);
661     }
662 
663     errno = mp_obj_get_int(args[0]);
664     return mp_const_none;
665 }
666 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_errno_obj, 0, 1, mod_os_errno);
667 
mod_os_chdir(mp_obj_t path_in)668 STATIC mp_obj_t mod_os_chdir(mp_obj_t path_in) {
669     // TODO: Accept mode param
670     char *path = mp_obj_str_get_str(path_in);
671     char abspath[256] = {0};
672     path = py_get_realpath(path, abspath, sizeof(abspath));
673 
674     MP_THREAD_GIL_EXIT();
675     int r = chdir(path);
676     MP_THREAD_GIL_ENTER();
677 
678     return MP_OBJ_NEW_SMALL_INT(r);
679 }
680 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_chdir_obj, mod_os_chdir);
681 
682 
mod_os_getcwd()683 STATIC mp_obj_t mod_os_getcwd() {
684     // TODO: Accept mode param
685 
686     char buf[MICROPY_ALLOC_PATH_MAX + 1];
687     const char *ret = getcwd(buf, sizeof(buf));
688     if (ret == NULL) {
689         mp_raise_OSError(errno);
690     }
691     return mp_obj_new_str(ret, strlen(ret));
692 }
693 
694 STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_os_getcwd_obj, mod_os_getcwd);
695 
mod_os_file_open(mp_obj_t path_in,mp_obj_t mode_in)696 STATIC mp_obj_t mod_os_file_open(mp_obj_t path_in, mp_obj_t mode_in)
697 {
698     const char *path = mp_obj_str_get_str(path_in);
699     const char *mode = mp_obj_str_get_str(mode_in);
700 
701     if(path == NULL || mode == NULL) {
702         mp_raise_OSError(EINVAL);
703         return mp_const_none;
704     }
705 
706     FILE *stream = fopen(path, mode);
707     if (stream == NULL) {
708         mp_raise_OSError(ENOENT);
709         return mp_const_none;
710     }
711     return MP_OBJ_FROM_PTR(stream);
712 }
713 MP_DEFINE_CONST_FUN_OBJ_2(mod_os_file_open_obj, mod_os_file_open);
714 
mod_os_file_close(mp_obj_t stream_in)715 STATIC mp_obj_t mod_os_file_close(mp_obj_t stream_in)
716 {
717     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
718 
719     int ret = -1;
720     if (stream != NULL) {
721         ret = fclose(stream);
722     }
723     else {
724         ret = 0;
725     }
726     return mp_obj_new_int(ret);
727 }
728 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_file_close_obj, mod_os_file_close);
729 
mod_os_file_read(size_t n_args,const mp_obj_t * args)730 STATIC mp_obj_t mod_os_file_read(size_t n_args, const mp_obj_t *args)
731 {
732     if (n_args < 4) {
733         mp_raise_OSError(EINVAL);
734         return mp_const_false;
735     }
736 
737     mp_int_t size = mp_obj_get_int(args[1]);
738     mp_int_t nmemb = mp_obj_get_int(args[2]);
739     FILE *stream = (FILE *)MP_OBJ_TO_PTR(args[3]);
740 
741     mp_buffer_info_t bufinfo;
742     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
743 
744     int ret = -1;
745     if (stream != NULL) {
746         ret = fread(bufinfo.buf, size, nmemb, stream);
747     }
748     return mp_obj_new_int(ret);
749 }
750 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mod_os_file_read_obj, 4, mod_os_file_read);
751 
mod_os_file_write(size_t n_args,const mp_obj_t * args)752 STATIC mp_obj_t mod_os_file_write(size_t n_args, const mp_obj_t *args)
753 {
754     if (n_args < 4) {
755         mp_raise_OSError(EINVAL);
756         return mp_const_false;
757     }
758     mp_int_t size = mp_obj_get_int(args[1]);
759     mp_int_t nmemb = mp_obj_get_int(args[2]);
760     FILE *stream = (FILE *)MP_OBJ_TO_PTR(args[3]);
761 
762     mp_buffer_info_t bufinfo;
763     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
764 
765     int ret = -1;
766     if (stream != NULL) {
767         ret = fwrite(bufinfo.buf, size, nmemb, stream);
768     }
769     return mp_obj_new_int(ret);
770 }
771 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(mod_os_file_write_obj, 4, mod_os_file_write);
772 
mod_os_file_seek(mp_obj_t stream_in,mp_obj_t offset_in,mp_obj_t whence_in)773 STATIC mp_obj_t mod_os_file_seek(mp_obj_t stream_in, mp_obj_t offset_in, mp_obj_t whence_in)
774 {
775     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
776     if (stream == NULL) {
777         mp_raise_OSError(EINVAL);
778         return mp_const_false;
779     }
780 
781     mp_int_t offset = mp_obj_get_int(offset_in);
782     mp_int_t whence = mp_obj_get_int(whence_in);
783 
784     int ret = fseek(stream, offset, whence);
785     return mp_obj_new_int(ret);
786 }
787 MP_DEFINE_CONST_FUN_OBJ_3(mod_os_file_seek_obj, mod_os_file_seek);
788 
mod_os_file_tell(mp_obj_t stream_in)789 STATIC mp_obj_t mod_os_file_tell(mp_obj_t stream_in)
790 {
791     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
792     if (stream == NULL) {
793         mp_raise_OSError(EINVAL);
794         return mp_const_false;
795     }
796 
797     int ret = ftell(stream);
798     return mp_obj_new_int(ret);
799 }
800 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_file_tell_obj, mod_os_file_tell);
801 
mod_os_file_rewind(mp_obj_t stream_in)802 STATIC mp_obj_t mod_os_file_rewind(mp_obj_t stream_in)
803 {
804     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
805     if (stream == NULL) {
806         mp_raise_OSError(EINVAL);
807         return mp_const_false;
808     }
809 
810     rewind(stream);
811     return mp_const_none;
812 }
813 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_file_rewind_obj, mod_os_file_rewind);
814 
mod_os_file_getpos(mp_obj_t stream_in)815 STATIC mp_obj_t mod_os_file_getpos(mp_obj_t stream_in)
816 {
817     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
818     if (stream == NULL) {
819         mp_raise_OSError(EINVAL);
820         return mp_const_false;
821     } else {
822         fpos_t pos = 0;
823         int ret = fgetpos(stream, &pos);
824         if (ret == 0) {
825             return mp_obj_new_int(pos);
826         } else {
827             return mp_obj_new_int(-1);
828         }
829     }
830 }
831 MP_DEFINE_CONST_FUN_OBJ_1(mod_os_file_getpos_obj, mod_os_file_getpos);
832 
mod_os_file_setpos(mp_obj_t stream_in,mp_obj_t pos_in)833 STATIC mp_obj_t mod_os_file_setpos(mp_obj_t stream_in, mp_obj_t pos_in)
834 {
835     FILE *stream = (FILE *)MP_OBJ_TO_PTR(stream_in);
836     if (stream == NULL) {
837         mp_raise_OSError(EINVAL);
838         return mp_const_false;
839     }
840 
841     mp_int_t pos = mp_obj_get_int(pos_in);
842     int ret = fsetpos(stream, &pos);
843     return mp_obj_new_int(ret);
844 }
845 MP_DEFINE_CONST_FUN_OBJ_2(mod_os_file_setpos_obj, mod_os_file_setpos);
846 
847 /// \function sync()
848 /// Sync all filesystems.
os_sync(void)849 STATIC mp_obj_t os_sync(void) {
850     #if MICROPY_VFS_FAT
851     for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
852         // this assumes that vfs->obj is fs_user_mount_t with block device functions
853         disk_ioctl(MP_OBJ_TO_PTR(vfs->obj), CTRL_SYNC, NULL);
854     }
855     #endif
856     return mp_const_none;
857 }
858 MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync);
859 
860 #if MICROPY_PY_OS_DUPTERM
mp_uos_dupterm_notify(mp_obj_t obj_in)861 STATIC mp_obj_t mp_uos_dupterm_notify(mp_obj_t obj_in) {
862     (void)obj_in;
863     for (;;) {
864         int c = mp_uos_dupterm_rx_chr();
865         if (c < 0) {
866             break;
867         }
868         ringbuf_put(&stdin_ringbuf, c);
869     }
870     return mp_const_none;
871 }
872 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_dupterm_notify_obj, mp_uos_dupterm_notify);
873 #endif
874 
875 STATIC const mp_rom_map_elem_t mp_module_os_globals_table[] = {
876     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) },
877     { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) },
878     { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) },
879 
880 #if MICROPY_PY_OS_DUPTERM
881     { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) },
882     { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&mp_uos_dupterm_notify_obj) },
883 #endif
884 
885     { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mod_os_ilistdir_obj) },
886     { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mod_os_listdir_obj) },
887     { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_os_mkdir_obj) },
888     { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mod_os_rmdir_obj) },
889     { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mod_os_chdir_obj) },
890     { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mod_os_getcwd_obj) },
891     { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mod_os_remove_obj) },
892     { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_os_rename_obj) },
893     { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_os_stat_obj) },
894     { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mod_os_statvfs_obj) },
895     // { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mod_os_mount_obj) },
896     // { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mod_os_umount_obj) },
897     { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mod_os_remove_obj) },
898 
899 #if MICROPY_VFS_FAT
900     { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) },
901 #endif
902 #if MICROPY_VFS_LFS1
903     { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) },
904 #endif
905 #if MICROPY_VFS_LFS2
906     { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) },
907 #endif
908 
909     { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) },
910     { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) },
911     { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mod_os_errno_obj) },
912     { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mod_os_system_obj) },
913     { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_os_getenv_obj) },
914     { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mod_os_putenv_obj) },
915     { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mod_os_unsetenv_obj) },
916 
917     // file operation
918     { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_os_file_open_obj) },
919     { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mod_os_file_read_obj) },
920     { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_os_file_write_obj) },
921     { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mod_os_file_close_obj) },
922     { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mod_os_file_seek_obj) },
923     { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mod_os_file_tell_obj) },
924     { MP_ROM_QSTR(MP_QSTR_rewind), MP_ROM_PTR(&mod_os_file_rewind_obj) },
925     { MP_ROM_QSTR(MP_QSTR_getpos), MP_ROM_PTR(&mod_os_file_getpos_obj) },
926     { MP_ROM_QSTR(MP_QSTR_setpos), MP_ROM_PTR(&mod_os_file_setpos_obj) },
927 };
928 
929 STATIC MP_DEFINE_CONST_DICT(mp_module_os_globals, mp_module_os_globals_table);
930 
931 const mp_obj_module_t mp_module_uos = {
932     .base = { &mp_type_module },
933     .globals = (mp_obj_dict_t *)&mp_module_os_globals,
934 };
935