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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |