1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2017-2018 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include "py/runtime.h"
28 #include "py/mperrno.h"
29 #include "py/mphal.h"
30 #include "py/mpthread.h"
31 #include "extmod/vfs.h"
32 #include "extmod/vfs_posix.h"
33 
34 #if MICROPY_VFS_POSIX
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <dirent.h>
40 
41 typedef struct _mp_obj_vfs_posix_t {
42     mp_obj_base_t base;
43     vstr_t root;
44     size_t root_len;
45     bool readonly;
46 } mp_obj_vfs_posix_t;
47 
vfs_posix_get_path_str(mp_obj_vfs_posix_t * self,mp_obj_t path)48 STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) {
49     if (self->root_len == 0) {
50         return mp_obj_str_get_str(path);
51     } else {
52         self->root.len = self->root_len;
53         vstr_add_str(&self->root, mp_obj_str_get_str(path));
54         return vstr_null_terminated_str(&self->root);
55     }
56 }
57 
vfs_posix_get_path_obj(mp_obj_vfs_posix_t * self,mp_obj_t path)58 STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) {
59     if (self->root_len == 0) {
60         return path;
61     } else {
62         self->root.len = self->root_len;
63         vstr_add_str(&self->root, mp_obj_str_get_str(path));
64         return mp_obj_new_str(self->root.buf, self->root.len);
65     }
66 }
67 
vfs_posix_fun1_helper(mp_obj_t self_in,mp_obj_t path_in,int (* f)(const char *))68 STATIC mp_obj_t vfs_posix_fun1_helper(mp_obj_t self_in, mp_obj_t path_in, int (*f)(const char *)) {
69     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
70     int ret = f(vfs_posix_get_path_str(self, path_in));
71     if (ret != 0) {
72         mp_raise_OSError(errno);
73     }
74     return mp_const_none;
75 }
76 
mp_vfs_posix_import_stat(void * self_in,const char * path)77 STATIC mp_import_stat_t mp_vfs_posix_import_stat(void *self_in, const char *path) {
78     mp_obj_vfs_posix_t *self = self_in;
79     if (self->root_len != 0) {
80         self->root.len = self->root_len;
81         vstr_add_str(&self->root, path);
82         path = vstr_null_terminated_str(&self->root);
83     }
84     struct stat st;
85     if (stat(path, &st) == 0) {
86         if (S_ISDIR(st.st_mode)) {
87             return MP_IMPORT_STAT_DIR;
88         } else if (S_ISREG(st.st_mode)) {
89             return MP_IMPORT_STAT_FILE;
90         }
91     }
92     return MP_IMPORT_STAT_NO_EXIST;
93 }
94 
vfs_posix_make_new(const mp_obj_type_t * type,size_t n_args,size_t n_kw,const mp_obj_t * args)95 STATIC mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
96     mp_arg_check_num(n_args, n_kw, 0, 1, false);
97 
98     mp_obj_vfs_posix_t *vfs = m_new_obj(mp_obj_vfs_posix_t);
99     vfs->base.type = type;
100     vstr_init(&vfs->root, 0);
101     if (n_args == 1) {
102         vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0]));
103         vstr_add_char(&vfs->root, '/');
104     }
105     vfs->root_len = vfs->root.len;
106     vfs->readonly = false;
107 
108     return MP_OBJ_FROM_PTR(vfs);
109 }
110 
vfs_posix_mount(mp_obj_t self_in,mp_obj_t readonly,mp_obj_t mkfs)111 STATIC mp_obj_t vfs_posix_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
112     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
113     if (mp_obj_is_true(readonly)) {
114         self->readonly = true;
115     }
116     if (mp_obj_is_true(mkfs)) {
117         mp_raise_OSError(MP_EPERM);
118     }
119     return mp_const_none;
120 }
121 STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_mount_obj, vfs_posix_mount);
122 
vfs_posix_umount(mp_obj_t self_in)123 STATIC mp_obj_t vfs_posix_umount(mp_obj_t self_in) {
124     (void)self_in;
125     return mp_const_none;
126 }
127 STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount);
128 
vfs_posix_open(mp_obj_t self_in,mp_obj_t path_in,mp_obj_t mode_in)129 STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) {
130     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
131     const char *mode = mp_obj_str_get_str(mode_in);
132     if (self->readonly
133         && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) {
134         mp_raise_OSError(MP_EROFS);
135     }
136     if (!mp_obj_is_small_int(path_in)) {
137         path_in = vfs_posix_get_path_obj(self, path_in);
138     }
139     return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in);
140 }
141 STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open);
142 
vfs_posix_chdir(mp_obj_t self_in,mp_obj_t path_in)143 STATIC mp_obj_t vfs_posix_chdir(mp_obj_t self_in, mp_obj_t path_in) {
144     return vfs_posix_fun1_helper(self_in, path_in, chdir);
145 }
146 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_chdir_obj, vfs_posix_chdir);
147 
vfs_posix_getcwd(mp_obj_t self_in)148 STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) {
149     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
150     char buf[MICROPY_ALLOC_PATH_MAX + 1];
151     const char *ret = getcwd(buf, sizeof(buf));
152     if (ret == NULL) {
153         mp_raise_OSError(errno);
154     }
155     ret += self->root_len;
156     return mp_obj_new_str(ret, strlen(ret));
157 }
158 STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd);
159 
160 typedef struct _vfs_posix_ilistdir_it_t {
161     mp_obj_base_t base;
162     mp_fun_1_t iternext;
163     bool is_str;
164     DIR *dir;
165 } vfs_posix_ilistdir_it_t;
166 
vfs_posix_ilistdir_it_iternext(mp_obj_t self_in)167 STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) {
168     vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
169 
170     if (self->dir == NULL) {
171         return MP_OBJ_STOP_ITERATION;
172     }
173 
174     for (;;) {
175         MP_THREAD_GIL_EXIT();
176         struct dirent *dirent = readdir(self->dir);
177         if (dirent == NULL) {
178             closedir(self->dir);
179             MP_THREAD_GIL_ENTER();
180             self->dir = NULL;
181             return MP_OBJ_STOP_ITERATION;
182         }
183         MP_THREAD_GIL_ENTER();
184         const char *fn = dirent->d_name;
185 
186         if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) {
187             // skip . and ..
188             continue;
189         }
190 
191         // make 3-tuple with info about this entry
192         mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
193 
194         if (self->is_str) {
195             t->items[0] = mp_obj_new_str(fn, strlen(fn));
196         } else {
197             t->items[0] = mp_obj_new_bytes((const byte *)fn, strlen(fn));
198         }
199 
200         #ifdef _DIRENT_HAVE_D_TYPE
201         #ifdef DTTOIF
202         t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type));
203         #else
204         if (dirent->d_type == DT_DIR) {
205             t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
206         } else if (dirent->d_type == DT_REG) {
207             t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
208         } else {
209             t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
210         }
211         #endif
212         #else
213         // DT_UNKNOWN should have 0 value on any reasonable system
214         t->items[1] = MP_OBJ_NEW_SMALL_INT(0);
215         #endif
216 
217         #ifdef _DIRENT_HAVE_D_INO
218         t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
219         #else
220         t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
221         #endif
222 
223         return MP_OBJ_FROM_PTR(t);
224     }
225 }
226 
vfs_posix_ilistdir(mp_obj_t self_in,mp_obj_t path_in)227 STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
228     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
229     vfs_posix_ilistdir_it_t *iter = m_new_obj(vfs_posix_ilistdir_it_t);
230     iter->base.type = &mp_type_polymorph_iter;
231     iter->iternext = vfs_posix_ilistdir_it_iternext;
232     iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
233     const char *path = vfs_posix_get_path_str(self, path_in);
234     if (path[0] == '\0') {
235         path = ".";
236     }
237     MP_THREAD_GIL_EXIT();
238     iter->dir = opendir(path);
239     MP_THREAD_GIL_ENTER();
240     if (iter->dir == NULL) {
241         mp_raise_OSError(errno);
242     }
243     return MP_OBJ_FROM_PTR(iter);
244 }
245 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_ilistdir_obj, vfs_posix_ilistdir);
246 
247 typedef struct _mp_obj_listdir_t {
248     mp_obj_base_t base;
249     mp_fun_1_t iternext;
250     DIR *dir;
251 } mp_obj_listdir_t;
252 
vfs_posix_mkdir(mp_obj_t self_in,mp_obj_t path_in)253 STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) {
254     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
255     const char *path = vfs_posix_get_path_str(self, path_in);
256     MP_THREAD_GIL_EXIT();
257     int ret = mkdir(path, 0777);
258     MP_THREAD_GIL_ENTER();
259     if (ret != 0) {
260         mp_raise_OSError(errno);
261     }
262     return mp_const_none;
263 }
264 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir);
265 
vfs_posix_remove(mp_obj_t self_in,mp_obj_t path_in)266 STATIC mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) {
267     return vfs_posix_fun1_helper(self_in, path_in, unlink);
268 }
269 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove);
270 
vfs_posix_rename(mp_obj_t self_in,mp_obj_t old_path_in,mp_obj_t new_path_in)271 STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) {
272     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
273     const char *old_path = vfs_posix_get_path_str(self, old_path_in);
274     const char *new_path = vfs_posix_get_path_str(self, new_path_in);
275     MP_THREAD_GIL_EXIT();
276     int ret = rename(old_path, new_path);
277     MP_THREAD_GIL_ENTER();
278     if (ret != 0) {
279         mp_raise_OSError(errno);
280     }
281     return mp_const_none;
282 }
283 STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename);
284 
vfs_posix_rmdir(mp_obj_t self_in,mp_obj_t path_in)285 STATIC mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) {
286     return vfs_posix_fun1_helper(self_in, path_in, rmdir);
287 }
288 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir);
289 
vfs_posix_stat(mp_obj_t self_in,mp_obj_t path_in)290 STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) {
291     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
292     struct stat sb;
293     const char *path = vfs_posix_get_path_str(self, path_in);
294     int ret;
295     MP_HAL_RETRY_SYSCALL(ret, stat(path, &sb), mp_raise_OSError(err));
296     mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
297     t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
298     t->items[1] = mp_obj_new_int_from_uint(sb.st_ino);
299     t->items[2] = mp_obj_new_int_from_uint(sb.st_dev);
300     t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink);
301     t->items[4] = mp_obj_new_int_from_uint(sb.st_uid);
302     t->items[5] = mp_obj_new_int_from_uint(sb.st_gid);
303     t->items[6] = mp_obj_new_int_from_uint(sb.st_size);
304     t->items[7] = mp_obj_new_int_from_uint(sb.st_atime);
305     t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime);
306     t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime);
307     return MP_OBJ_FROM_PTR(t);
308 }
309 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat);
310 
311 #ifdef __ANDROID__
312 #define USE_STATFS 1
313 #endif
314 
315 #if USE_STATFS
316 #include <sys/vfs.h>
317 #define STRUCT_STATVFS struct statfs
318 #define STATVFS statfs
319 #define F_FAVAIL sb.f_ffree
320 #define F_NAMEMAX sb.f_namelen
321 #define F_FLAG sb.f_flags
322 #else
323 #include <sys/statvfs.h>
324 #define STRUCT_STATVFS struct statvfs
325 #define STATVFS statvfs
326 #define F_FAVAIL sb.f_favail
327 #define F_NAMEMAX sb.f_namemax
328 #define F_FLAG sb.f_flag
329 #endif
330 
vfs_posix_statvfs(mp_obj_t self_in,mp_obj_t path_in)331 STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
332     mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in);
333     STRUCT_STATVFS sb;
334     const char *path = vfs_posix_get_path_str(self, path_in);
335     int ret;
336     MP_HAL_RETRY_SYSCALL(ret, STATVFS(path, &sb), mp_raise_OSError(err));
337     mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
338     t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
339     t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
340     t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
341     t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
342     t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
343     t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
344     t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
345     t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL);
346     t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG);
347     t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX);
348     return MP_OBJ_FROM_PTR(t);
349 }
350 STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs);
351 
352 STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = {
353     { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) },
354     { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) },
355     { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_posix_open_obj) },
356 
357     { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_posix_chdir_obj) },
358     { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_posix_getcwd_obj) },
359     { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_posix_ilistdir_obj) },
360     { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_posix_mkdir_obj) },
361     { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_posix_remove_obj) },
362     { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) },
363     { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) },
364     { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) },
365     { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) },
366 };
367 STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table);
368 
369 STATIC const mp_vfs_proto_t vfs_posix_proto = {
370     .import_stat = mp_vfs_posix_import_stat,
371 };
372 
373 const mp_obj_type_t mp_type_vfs_posix = {
374     { &mp_type_type },
375     .name = MP_QSTR_VfsPosix,
376     .make_new = vfs_posix_make_new,
377     .protocol = &vfs_posix_proto,
378     .locals_dict = (mp_obj_dict_t *)&vfs_posix_locals_dict,
379 };
380 
381 #endif // MICROPY_VFS_POSIX
382