diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c new file mode 100644 index 0000000000..0db45a0c1f --- /dev/null +++ b/extmod/vfs_posix.c @@ -0,0 +1,357 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "extmod/vfs_posix.h" + +#if MICROPY_VFS_POSIX + +#include +#include +#include + +typedef struct _mp_obj_vfs_posix_t { + mp_obj_base_t base; + vstr_t root; + size_t root_len; + bool readonly; +} mp_obj_vfs_posix_t; + +STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) { + if (self->root_len == 0) { + return mp_obj_str_get_str(path); + } else { + self->root.len = self->root_len; + vstr_add_str(&self->root, mp_obj_str_get_str(path)); + return vstr_null_terminated_str(&self->root); + } +} + +STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) { + if (self->root_len == 0) { + return path; + } else { + self->root.len = self->root_len; + vstr_add_str(&self->root, mp_obj_str_get_str(path)); + return mp_obj_new_str(self->root.buf, self->root.len); + } +} + +STATIC mp_obj_t vfs_posix_fun1_helper(mp_obj_t self_in, mp_obj_t path_in, int (*f)(const char*)) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + int ret = f(vfs_posix_get_path_str(self, path_in)); + if (ret != 0) { + mp_raise_OSError(errno); + } + return mp_const_none; +} + +mp_import_stat_t mp_vfs_posix_import_stat(mp_obj_vfs_posix_t *self, const char *path) { + if (self->root_len != 0) { + self->root.len = self->root_len; + vstr_add_str(&self->root, path); + path = vstr_null_terminated_str(&self->root); + } + struct stat st; + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + return MP_IMPORT_STAT_DIR; + } else if (S_ISREG(st.st_mode)) { + return MP_IMPORT_STAT_FILE; + } + } + return MP_IMPORT_STAT_NO_EXIST; +} + +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) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + mp_obj_vfs_posix_t *vfs = m_new_obj(mp_obj_vfs_posix_t); + vfs->base.type = type; + vstr_init(&vfs->root, 0); + if (n_args == 1) { + vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0])); + vstr_add_char(&vfs->root, '/'); + } + vfs->root_len = vfs->root.len; + vfs->readonly = false; + + return MP_OBJ_FROM_PTR(vfs); +} + +STATIC mp_obj_t vfs_posix_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + if (mp_obj_is_true(readonly)) { + self->readonly = true; + } + if (mp_obj_is_true(mkfs)) { + mp_raise_OSError(MP_EPERM); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_mount_obj, vfs_posix_mount); + +STATIC mp_obj_t vfs_posix_umount(mp_obj_t self_in) { + (void)self_in; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount); + +STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + const char *mode = mp_obj_str_get_str(mode_in); + if (self->readonly + && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) { + mp_raise_OSError(MP_EROFS); + } + if (!MP_OBJ_IS_SMALL_INT(path_in)) { + path_in = vfs_posix_get_path_obj(self, path_in); + } + return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open); + +STATIC mp_obj_t vfs_posix_chdir(mp_obj_t self_in, mp_obj_t path_in) { + return vfs_posix_fun1_helper(self_in, path_in, chdir); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_chdir_obj, vfs_posix_chdir); + +STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + const char *ret = getcwd(buf, sizeof(buf)); + if (ret == NULL) { + mp_raise_OSError(errno); + } + ret += self->root_len; + return mp_obj_new_str(ret, strlen(ret)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); + +typedef struct _vfs_posix_ilistdir_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + bool is_str; + DIR *dir; +} vfs_posix_ilistdir_it_t; + +STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { + vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->dir == NULL) { + return MP_OBJ_STOP_ITERATION; + } + + for (;;) { + struct dirent *dirent = readdir(self->dir); + if (dirent == NULL) { + closedir(self->dir); + self->dir = NULL; + return MP_OBJ_STOP_ITERATION; + } + const char *fn = dirent->d_name; + + if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) { + // skip . and .. + continue; + } + + // make 3-tuple with info about this entry + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); + + if (self->is_str) { + t->items[0] = mp_obj_new_str(fn, strlen(fn)); + } else { + t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn)); + } + + #ifdef _DIRENT_HAVE_D_TYPE + if (dirent->d_type == DT_DIR) { + t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); + } else if (dirent->d_type == DT_REG) { + t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG); + } else { + t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type); + } + #else + // DT_UNKNOWN should have 0 value on any reasonable system + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); + #endif + #ifdef _DIRENT_HAVE_D_INO + t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino); + #else + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); + #endif + + return MP_OBJ_FROM_PTR(t); + } +} + +STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + vfs_posix_ilistdir_it_t *iter = m_new_obj(vfs_posix_ilistdir_it_t); + iter->base.type = &mp_type_polymorph_iter; + iter->iternext = vfs_posix_ilistdir_it_iternext; + iter->is_str = mp_obj_get_type(path_in) == &mp_type_str; + const char *path = vfs_posix_get_path_str(self, path_in); + iter->dir = opendir(path); + if (iter->dir == NULL) { + mp_raise_OSError(errno); + } + return MP_OBJ_FROM_PTR(iter); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_ilistdir_obj, vfs_posix_ilistdir); + +typedef struct _mp_obj_listdir_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + DIR *dir; +} mp_obj_listdir_t; + +STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + int ret = mkdir(vfs_posix_get_path_str(self, path_in), 0777); + if (ret != 0) { + mp_raise_OSError(errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir); + +STATIC mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) { + return vfs_posix_fun1_helper(self_in, path_in, unlink); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove); + +STATIC mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + const char *old_path = vfs_posix_get_path_str(self, old_path_in); + const char *new_path = vfs_posix_get_path_str(self, new_path_in); + int ret = rename(old_path, new_path); + if (ret != 0) { + mp_raise_OSError(errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename); + +STATIC mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) { + return vfs_posix_fun1_helper(self_in, path_in, rmdir); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir); + +STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + struct stat sb; + int ret = stat(vfs_posix_get_path_str(self, path_in), &sb); + if (ret != 0) { + mp_raise_OSError(errno); + } + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode); + t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino); + t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev); + t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink); + t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid); + t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid); + t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size); + t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime); + t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime); + t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime); + return MP_OBJ_FROM_PTR(t); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat); + +#ifdef __ANDROID__ +#define USE_STATFS 1 +#endif + +#if USE_STATFS +#include +#define STRUCT_STATVFS struct statfs +#define STATVFS statfs +#define F_FAVAIL sb.f_ffree +#define F_NAMEMAX sb.f_namelen +#define F_FLAG sb.f_flags +#else +#include +#define STRUCT_STATVFS struct statvfs +#define STATVFS statvfs +#define F_FAVAIL sb.f_favail +#define F_NAMEMAX sb.f_namemax +#define F_FLAG sb.f_flag +#endif + +STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + STRUCT_STATVFS sb; + const char *path = vfs_posix_get_path_str(self, path_in); + int ret = STATVFS(path, &sb); + if (ret != 0) { + mp_raise_OSError(errno); + } + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize); + t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize); + t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks); + t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree); + t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail); + t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files); + t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree); + t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL); + t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG); + t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX); + return MP_OBJ_FROM_PTR(t); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs); + +STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) }, + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_posix_open_obj) }, + + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_posix_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_posix_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_posix_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_posix_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_posix_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table); + +const mp_obj_type_t mp_type_vfs_posix = { + { &mp_type_type }, + .name = MP_QSTR_VfsPosix, + .make_new = vfs_posix_make_new, + .locals_dict = (mp_obj_dict_t*)&vfs_posix_locals_dict, +}; + +#endif // MICROPY_VFS_POSIX diff --git a/extmod/vfs_posix.h b/extmod/vfs_posix.h new file mode 100644 index 0000000000..35a255ff62 --- /dev/null +++ b/extmod/vfs_posix.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H +#define MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H + +#include "py/lexer.h" +#include "py/obj.h" + +struct _mp_obj_vfs_posix_t; + +extern const mp_obj_type_t mp_type_vfs_posix; +extern const mp_obj_type_t mp_type_vfs_posix_fileio; +extern const mp_obj_type_t mp_type_vfs_posix_textio; + +mp_import_stat_t mp_vfs_posix_import_stat(struct _mp_obj_vfs_posix_t *self, const char *path_in); +mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in); + +#endif // MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c new file mode 100644 index 0000000000..435ac65cd4 --- /dev/null +++ b/extmod/vfs_posix_file.c @@ -0,0 +1,261 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/vfs_posix.h" + +#if MICROPY_VFS_POSIX + +#include + +#ifdef _WIN32 +#define fsync _commit +#endif + +typedef struct _mp_obj_vfs_posix_file_t { + mp_obj_base_t base; + int fd; +} mp_obj_vfs_posix_file_t; + +#ifdef MICROPY_CPYTHON_COMPAT +STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) { + if (o->fd < 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "I/O operation on closed file")); + } +} +#else +#define check_fd_is_open(o) +#endif + +STATIC void vfs_posix_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", mp_obj_get_type_str(self_in), self->fd); +} + +mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) { + mp_obj_vfs_posix_file_t *o = m_new_obj(mp_obj_vfs_posix_file_t); + const char *mode_s = mp_obj_str_get_str(mode_in); + + int mode_rw = 0, mode_x = 0; + while (*mode_s) { + switch (*mode_s++) { + case 'r': + mode_rw = O_RDONLY; + break; + case 'w': + mode_rw = O_WRONLY; + mode_x = O_CREAT | O_TRUNC; + break; + case 'a': + mode_rw = O_WRONLY; + mode_x = O_CREAT | O_APPEND; + break; + case '+': + mode_rw = O_RDWR; + break; + #if MICROPY_PY_IO_FILEIO + // If we don't have io.FileIO, then files are in text mode implicitly + case 'b': + type = &mp_type_vfs_posix_fileio; + break; + case 't': + type = &mp_type_vfs_posix_textio; + break; + #endif + } + } + + o->base.type = type; + + mp_obj_t fid = file_in; + + if (MP_OBJ_IS_SMALL_INT(fid)) { + o->fd = MP_OBJ_SMALL_INT_VALUE(fid); + return MP_OBJ_FROM_PTR(o); + } + + const char *fname = mp_obj_str_get_str(fid); + int fd = open(fname, mode_x | mode_rw, 0644); + if (fd == -1) { + mp_raise_OSError(errno); + } + o->fd = fd; + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, + { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} }, + }; + + mp_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, arg_vals); + return mp_vfs_posix_file_open(type, arg_vals[0].u_obj, arg_vals[1].u_obj); +} + +STATIC mp_obj_t vfs_posix_file_fileno(mp_obj_t self_in) { + mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); + check_fd_is_open(self); + return MP_OBJ_NEW_SMALL_INT(self->fd); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_file_fileno_obj, vfs_posix_file_fileno); + +STATIC mp_obj_t vfs_posix_file___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return mp_stream_close(args[0]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vfs_posix_file___exit__); + +STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { + mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); + check_fd_is_open(o); + mp_int_t r = read(o->fd, buf, size); + if (r == -1) { + *errcode = errno; + return MP_STREAM_ERROR; + } + return r; +} + +STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { + mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); + check_fd_is_open(o); + #if MICROPY_PY_OS_DUPTERM + if (o->fd <= STDERR_FILENO) { + mp_hal_stdout_tx_strn(buf, size); + return size; + } + #endif + mp_int_t r = write(o->fd, buf, size); + while (r == -1 && errno == EINTR) { + if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(obj); + } + r = write(o->fd, buf, size); + } + if (r == -1) { + *errcode = errno; + return MP_STREAM_ERROR; + } + return r; +} + +STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); + check_fd_is_open(o); + switch (request) { + case MP_STREAM_FLUSH: + if (fsync(o->fd) < 0) { + *errcode = errno; + return MP_STREAM_ERROR; + } + return 0; + case MP_STREAM_SEEK: { + struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg; + off_t off = lseek(o->fd, s->offset, s->whence); + if (off == (off_t)-1) { + *errcode = errno; + return MP_STREAM_ERROR; + } + s->offset = off; + return 0; + } + case MP_STREAM_CLOSE: + close(o->fd); + #ifdef MICROPY_CPYTHON_COMPAT + o->fd = -1; + #endif + return 0; + default: + *errcode = EINVAL; + return MP_STREAM_ERROR; + } +} + +STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); + +#if MICROPY_PY_IO_FILEIO +STATIC const mp_stream_p_t fileio_stream_p = { + .read = vfs_posix_file_read, + .write = vfs_posix_file_write, + .ioctl = vfs_posix_file_ioctl, +}; + +const mp_obj_type_t mp_type_vfs_posix_fileio = { + { &mp_type_type }, + .name = MP_QSTR_FileIO, + .print = vfs_posix_file_print, + .make_new = vfs_posix_file_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &fileio_stream_p, + .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, +}; +#endif + +STATIC const mp_stream_p_t textio_stream_p = { + .read = vfs_posix_file_read, + .write = vfs_posix_file_write, + .ioctl = vfs_posix_file_ioctl, + .is_text = true, +}; + +const mp_obj_type_t mp_type_vfs_posix_textio = { + { &mp_type_type }, + .name = MP_QSTR_TextIOWrapper, + .print = vfs_posix_file_print, + .make_new = vfs_posix_file_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &textio_stream_p, + .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, +}; + +const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO}; +const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO}; +const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO}; + +#endif // MICROPY_VFS_POSIX diff --git a/py/mpconfig.h b/py/mpconfig.h index 100e2a9810..dc0fc5d40e 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -681,6 +681,11 @@ typedef double mp_float_t; #define MICROPY_VFS (0) #endif +// Support for VFS POSIX component, to mount a POSIX filesystem within VFS +#ifndef MICROPY_VFS +#define MICROPY_VFS_POSIX (0) +#endif + /*****************************************************************************/ /* Fine control over Python builtins, classes, modules, etc */ diff --git a/py/py.mk b/py/py.mk index a918135268..f4b62a88af 100644 --- a/py/py.mk +++ b/py/py.mk @@ -241,6 +241,8 @@ PY_EXTMOD_O_BASENAME = \ extmod/modframebuf.o \ extmod/vfs.o \ extmod/vfs_reader.o \ + extmod/vfs_posix.o \ + extmod/vfs_posix_file.o \ extmod/vfs_fat.o \ extmod/vfs_fat_diskio.o \ extmod/vfs_fat_file.o \