blob: bbdd21cfb91760b2861ae0799eb9c92d1c3a4a84 [file] [log] [blame]
Damien Georgea0995052019-10-18 17:25:08 +11001/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
Damien George2acc0872020-07-29 01:01:48 +10006 * Copyright (c) 2019-2020 Damien P. George
Damien Georgea0995052019-10-18 17:25:08 +11007 *
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
Damien Georgea5135582022-11-11 15:43:55 +110027// This file should be compiled when included from vfs_lfs.c.
28#if defined(LFS_BUILD_VERSION)
29
Damien Georgea0995052019-10-18 17:25:08 +110030#include <stdio.h>
31#include <string.h>
32
33#include "py/runtime.h"
34#include "py/stream.h"
35#include "py/binary.h"
36#include "py/objarray.h"
Damien George7dffbfd2020-05-14 21:37:59 +100037#include "py/objstr.h"
Damien Georgea0995052019-10-18 17:25:08 +110038#include "py/mperrno.h"
39#include "extmod/vfs.h"
Damien George136369d2021-07-09 14:19:15 +100040#include "shared/timeutils/timeutils.h"
Damien Georgea0995052019-10-18 17:25:08 +110041
Andrew Leech4e0964b2022-09-09 09:48:01 +100042#if !MICROPY_ENABLE_FINALISER
43#error "MICROPY_VFS_LFS requires MICROPY_ENABLE_FINALISER"
44#endif
45
Angus Grattondecf8e62024-02-27 15:32:29 +110046static int MP_VFS_LFSx(dev_ioctl)(const struct LFSx_API (config) * c, int cmd, int arg, bool must_return_int) {
Damien Georgea0995052019-10-18 17:25:08 +110047 mp_obj_t ret = mp_vfs_blockdev_ioctl(c->context, cmd, arg);
48 int ret_i = 0;
49 if (must_return_int || ret != mp_const_none) {
50 ret_i = mp_obj_get_int(ret);
51 }
52 return ret_i;
53}
54
Angus Grattondecf8e62024-02-27 15:32:29 +110055static int MP_VFS_LFSx(dev_read)(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) {
Damien Georgea0995052019-10-18 17:25:08 +110056 return mp_vfs_blockdev_read_ext(c->context, block, off, size, buffer);
57}
58
Angus Grattondecf8e62024-02-27 15:32:29 +110059static int MP_VFS_LFSx(dev_prog)(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, const void *buffer, LFSx_API(size_t) size) {
Damien Georgea0995052019-10-18 17:25:08 +110060 return mp_vfs_blockdev_write_ext(c->context, block, off, size, buffer);
61}
62
Angus Grattondecf8e62024-02-27 15:32:29 +110063static int MP_VFS_LFSx(dev_erase)(const struct LFSx_API (config) * c, LFSx_API(block_t) block) {
Damien George4cf054a2019-10-29 12:29:56 +110064 return MP_VFS_LFSx(dev_ioctl)(c, MP_BLOCKDEV_IOCTL_BLOCK_ERASE, block, true);
Damien Georgea0995052019-10-18 17:25:08 +110065}
66
Angus Grattondecf8e62024-02-27 15:32:29 +110067static int MP_VFS_LFSx(dev_sync)(const struct LFSx_API (config) * c) {
Damien Georgecfe1c5a2019-10-29 12:25:30 +110068 return MP_VFS_LFSx(dev_ioctl)(c, MP_BLOCKDEV_IOCTL_SYNC, 0, false);
Damien Georgea0995052019-10-18 17:25:08 +110069}
70
Angus Grattondecf8e62024-02-27 15:32:29 +110071static void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size_t read_size, size_t prog_size, size_t lookahead) {
Damien Georgea0995052019-10-18 17:25:08 +110072 self->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
73 mp_vfs_blockdev_init(&self->blockdev, bdev);
74
Damien George69661f32020-02-27 15:36:53 +110075 struct LFSx_API (config) * config = &self->config;
Damien Georgea0995052019-10-18 17:25:08 +110076 memset(config, 0, sizeof(*config));
77
78 config->context = &self->blockdev;
79
80 config->read = MP_VFS_LFSx(dev_read);
81 config->prog = MP_VFS_LFSx(dev_prog);
82 config->erase = MP_VFS_LFSx(dev_erase);
83 config->sync = MP_VFS_LFSx(dev_sync);
84
Damien George5634a312019-11-14 16:30:10 +110085 MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_INIT, 1, false); // initialise block device
Damien Georgecfe1c5a2019-10-29 12:25:30 +110086 int bs = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_SIZE, 0, true); // get block size
87 int bc = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_COUNT, 0, true); // get block count
Damien Georgea0995052019-10-18 17:25:08 +110088 self->blockdev.block_size = bs;
89
90 config->read_size = read_size;
91 config->prog_size = prog_size;
92 config->block_size = bs;
93 config->block_count = bc;
94
95 #if LFS_BUILD_VERSION == 1
96 config->lookahead = lookahead;
97 config->read_buffer = m_new(uint8_t, config->read_size);
98 config->prog_buffer = m_new(uint8_t, config->prog_size);
99 config->lookahead_buffer = m_new(uint8_t, config->lookahead / 8);
100 #else
101 config->block_cycles = 100;
Peter Zügerce42c9e2023-12-21 00:17:15 +0100102 config->cache_size = MIN(config->block_size, (4 * MAX(read_size, prog_size)));
Damien Georgea0995052019-10-18 17:25:08 +1100103 config->lookahead_size = lookahead;
104 config->read_buffer = m_new(uint8_t, config->cache_size);
105 config->prog_buffer = m_new(uint8_t, config->cache_size);
106 config->lookahead_buffer = m_new(uint8_t, config->lookahead_size);
Andrew Leech6515cd02025-06-26 14:58:06 +1000107 #ifdef LFS2_MULTIVERSION
108 // This can be set to override the on-disk lfs version.
109 // eg. for compat with lfs2 < v2.6 add the following to make:
110 // CFLAGS += '-DLFS2_MULTIVERSION=0x00020000'
111 config->disk_version = LFS2_MULTIVERSION;
112 #endif
Damien Georgea0995052019-10-18 17:25:08 +1100113 #endif
114}
115
Damien George69661f32020-02-27 15:36:53 +1100116const char *MP_VFS_LFSx(make_path)(MP_OBJ_VFS_LFSx * self, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100117 const char *path = mp_obj_str_get_str(path_in);
118 if (path[0] != '/') {
119 size_t l = vstr_len(&self->cur_dir);
120 if (l > 0) {
121 vstr_add_str(&self->cur_dir, path);
122 path = vstr_null_terminated_str(&self->cur_dir);
123 self->cur_dir.len = l;
124 }
125 }
126 return path;
127}
128
Angus Grattondecf8e62024-02-27 15:32:29 +1100129static mp_obj_t MP_VFS_LFSx(make_new)(const mp_obj_type_t * type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
Damien Georgea0995052019-10-18 17:25:08 +1100130 mp_arg_val_t args[MP_ARRAY_SIZE(lfs_make_allowed_args)];
131 mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(lfs_make_allowed_args), lfs_make_allowed_args, args);
132
133 MP_OBJ_VFS_LFSx *self = m_new0(MP_OBJ_VFS_LFSx, 1);
134 self->base.type = type;
135 vstr_init(&self->cur_dir, 16);
136 vstr_add_byte(&self->cur_dir, '/');
Damien George2acc0872020-07-29 01:01:48 +1000137 #if LFS_BUILD_VERSION == 2
138 self->enable_mtime = args[LFS_MAKE_ARG_mtime].u_bool;
139 #endif
Damien Georgea0995052019-10-18 17:25:08 +1100140 MP_VFS_LFSx(init_config)(self, args[LFS_MAKE_ARG_bdev].u_obj,
141 args[LFS_MAKE_ARG_readsize].u_int, args[LFS_MAKE_ARG_progsize].u_int, args[LFS_MAKE_ARG_lookahead].u_int);
142 int ret = LFSx_API(mount)(&self->lfs, &self->config);
143 if (ret < 0) {
144 mp_raise_OSError(-ret);
145 }
146 return MP_OBJ_FROM_PTR(self);
147}
148
Angus Grattondecf8e62024-02-27 15:32:29 +1100149static mp_obj_t MP_VFS_LFSx(mkfs)(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Damien Georgea0995052019-10-18 17:25:08 +1100150 mp_arg_val_t args[MP_ARRAY_SIZE(lfs_make_allowed_args)];
151 mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(lfs_make_allowed_args), lfs_make_allowed_args, args);
152
153 MP_OBJ_VFS_LFSx self;
154 MP_VFS_LFSx(init_config)(&self, args[LFS_MAKE_ARG_bdev].u_obj,
155 args[LFS_MAKE_ARG_readsize].u_int, args[LFS_MAKE_ARG_progsize].u_int, args[LFS_MAKE_ARG_lookahead].u_int);
156 int ret = LFSx_API(format)(&self.lfs, &self.config);
157 if (ret < 0) {
158 mp_raise_OSError(-ret);
159 }
160 return mp_const_none;
161}
Angus Grattondecf8e62024-02-27 15:32:29 +1100162static MP_DEFINE_CONST_FUN_OBJ_KW(MP_VFS_LFSx(mkfs_fun_obj), 0, MP_VFS_LFSx(mkfs));
163static MP_DEFINE_CONST_STATICMETHOD_OBJ(MP_VFS_LFSx(mkfs_obj), MP_ROM_PTR(&MP_VFS_LFSx(mkfs_fun_obj)));
Damien Georgea0995052019-10-18 17:25:08 +1100164
165// Implementation of mp_vfs_lfs_file_open is provided in vfs_lfsx_file.c
Angus Grattondecf8e62024-02-27 15:32:29 +1100166static MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(open_obj), MP_VFS_LFSx(file_open));
Damien Georgea0995052019-10-18 17:25:08 +1100167
Damien George69661f32020-02-27 15:36:53 +1100168typedef struct MP_VFS_LFSx (_ilistdir_it_t) {
Damien Georgea0995052019-10-18 17:25:08 +1100169 mp_obj_base_t base;
170 mp_fun_1_t iternext;
Andrew Leech4e0964b2022-09-09 09:48:01 +1000171 mp_fun_1_t finaliser;
Damien Georgea0995052019-10-18 17:25:08 +1100172 bool is_str;
173 MP_OBJ_VFS_LFSx *vfs;
174 LFSx_API(dir_t) dir;
175} MP_VFS_LFSx(ilistdir_it_t);
176
Angus Grattondecf8e62024-02-27 15:32:29 +1100177static mp_obj_t MP_VFS_LFSx(ilistdir_it_iternext)(mp_obj_t self_in) {
Damien George69661f32020-02-27 15:36:53 +1100178 MP_VFS_LFSx(ilistdir_it_t) * self = MP_OBJ_TO_PTR(self_in);
Damien Georgea0995052019-10-18 17:25:08 +1100179
Andrew Leech4e0964b2022-09-09 09:48:01 +1000180 if (self->vfs == NULL) {
181 return MP_OBJ_STOP_ITERATION;
182 }
183
Damien George69661f32020-02-27 15:36:53 +1100184 struct LFSx_API (info) info;
Damien Georgea0995052019-10-18 17:25:08 +1100185 for (;;) {
186 int ret = LFSx_API(dir_read)(&self->vfs->lfs, &self->dir, &info);
187 if (ret == 0) {
188 LFSx_API(dir_close)(&self->vfs->lfs, &self->dir);
Andrew Leech4e0964b2022-09-09 09:48:01 +1000189 self->vfs = NULL;
Damien Georgea0995052019-10-18 17:25:08 +1100190 return MP_OBJ_STOP_ITERATION;
191 }
192 if (!(info.name[0] == '.' && (info.name[1] == '\0'
Damien George69661f32020-02-27 15:36:53 +1100193 || (info.name[1] == '.' && info.name[2] == '\0')))) {
Damien Georgea0995052019-10-18 17:25:08 +1100194 break;
195 }
196 }
197
198 // make 4-tuple with info about this entry
199 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
200 if (self->is_str) {
Jon Foster92484d82024-04-01 19:23:49 +0100201 t->items[0] = mp_obj_new_str_from_cstr(info.name);
Damien Georgea0995052019-10-18 17:25:08 +1100202 } else {
Damien George69661f32020-02-27 15:36:53 +1100203 t->items[0] = mp_obj_new_bytes((const byte *)info.name, strlen(info.name));
Damien Georgea0995052019-10-18 17:25:08 +1100204 }
205 t->items[1] = MP_OBJ_NEW_SMALL_INT(info.type == LFSx_MACRO(_TYPE_REG) ? MP_S_IFREG : MP_S_IFDIR);
206 t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
207 t->items[3] = MP_OBJ_NEW_SMALL_INT(info.size);
208
209 return MP_OBJ_FROM_PTR(t);
210}
211
Angus Grattondecf8e62024-02-27 15:32:29 +1100212static mp_obj_t MP_VFS_LFSx(ilistdir_it_del)(mp_obj_t self_in) {
Andrew Leech4e0964b2022-09-09 09:48:01 +1000213 MP_VFS_LFSx(ilistdir_it_t) * self = MP_OBJ_TO_PTR(self_in);
214 if (self->vfs != NULL) {
215 LFSx_API(dir_close)(&self->vfs->lfs, &self->dir);
216 }
217 return mp_const_none;
218}
219
Angus Grattondecf8e62024-02-27 15:32:29 +1100220static mp_obj_t MP_VFS_LFSx(ilistdir_func)(size_t n_args, const mp_obj_t *args) {
Damien Georgea0995052019-10-18 17:25:08 +1100221 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(args[0]);
222 bool is_str_type = true;
223 const char *path;
224 if (n_args == 2) {
225 if (mp_obj_get_type(args[1]) == &mp_type_bytes) {
226 is_str_type = false;
227 }
228 path = MP_VFS_LFSx(make_path)(self, args[1]);
229 } else {
230 path = vstr_null_terminated_str(&self->cur_dir);
231 }
232
Damien Georgecae690d2024-02-16 11:02:58 +1100233 MP_VFS_LFSx(ilistdir_it_t) * iter = mp_obj_malloc_with_finaliser(MP_VFS_LFSx(ilistdir_it_t), &mp_type_polymorph_iter_with_finaliser);
Andrew Leech4e0964b2022-09-09 09:48:01 +1000234
Damien Georgea0995052019-10-18 17:25:08 +1100235 iter->iternext = MP_VFS_LFSx(ilistdir_it_iternext);
Andrew Leech4e0964b2022-09-09 09:48:01 +1000236 iter->finaliser = MP_VFS_LFSx(ilistdir_it_del);
Damien Georgea0995052019-10-18 17:25:08 +1100237 iter->is_str = is_str_type;
Damien Georgea0995052019-10-18 17:25:08 +1100238 int ret = LFSx_API(dir_open)(&self->lfs, &iter->dir, path);
239 if (ret < 0) {
240 mp_raise_OSError(-ret);
241 }
Andrew Leech4e0964b2022-09-09 09:48:01 +1000242 iter->vfs = self;
Damien Georgea0995052019-10-18 17:25:08 +1100243 return MP_OBJ_FROM_PTR(iter);
244}
Angus Grattondecf8e62024-02-27 15:32:29 +1100245static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(MP_VFS_LFSx(ilistdir_obj), 1, 2, MP_VFS_LFSx(ilistdir_func));
Damien Georgea0995052019-10-18 17:25:08 +1100246
Angus Grattondecf8e62024-02-27 15:32:29 +1100247static mp_obj_t MP_VFS_LFSx(remove)(mp_obj_t self_in, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100248 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
249 const char *path = MP_VFS_LFSx(make_path)(self, path_in);
250 int ret = LFSx_API(remove)(&self->lfs, path);
251 if (ret < 0) {
252 mp_raise_OSError(-ret);
253 }
254 return mp_const_none;
255}
Angus Grattondecf8e62024-02-27 15:32:29 +1100256static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(remove_obj), MP_VFS_LFSx(remove));
Damien Georgea0995052019-10-18 17:25:08 +1100257
Angus Grattondecf8e62024-02-27 15:32:29 +1100258static mp_obj_t MP_VFS_LFSx(rmdir)(mp_obj_t self_in, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100259 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
260 const char *path = MP_VFS_LFSx(make_path)(self, path_in);
261 int ret = LFSx_API(remove)(&self->lfs, path);
262 if (ret < 0) {
263 mp_raise_OSError(-ret);
264 }
265 return mp_const_none;
266}
Angus Grattondecf8e62024-02-27 15:32:29 +1100267static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(rmdir_obj), MP_VFS_LFSx(rmdir));
Damien Georgea0995052019-10-18 17:25:08 +1100268
Angus Grattondecf8e62024-02-27 15:32:29 +1100269static mp_obj_t MP_VFS_LFSx(rename)(mp_obj_t self_in, mp_obj_t path_old_in, mp_obj_t path_new_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100270 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
271 const char *path_old = MP_VFS_LFSx(make_path)(self, path_old_in);
robert0f83ef32020-05-04 15:34:12 +0200272 const char *path = mp_obj_str_get_str(path_new_in);
Damien Georgea0995052019-10-18 17:25:08 +1100273 vstr_t path_new;
274 vstr_init(&path_new, vstr_len(&self->cur_dir));
robert0f83ef32020-05-04 15:34:12 +0200275 if (path[0] != '/') {
276 vstr_add_strn(&path_new, vstr_str(&self->cur_dir), vstr_len(&self->cur_dir));
277 }
278 vstr_add_str(&path_new, path);
Damien Georgea0995052019-10-18 17:25:08 +1100279 int ret = LFSx_API(rename)(&self->lfs, path_old, vstr_null_terminated_str(&path_new));
280 vstr_clear(&path_new);
281 if (ret < 0) {
282 mp_raise_OSError(-ret);
283 }
284 return mp_const_none;
285}
Angus Grattondecf8e62024-02-27 15:32:29 +1100286static MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(rename_obj), MP_VFS_LFSx(rename));
Damien Georgea0995052019-10-18 17:25:08 +1100287
Angus Grattondecf8e62024-02-27 15:32:29 +1100288static mp_obj_t MP_VFS_LFSx(mkdir)(mp_obj_t self_in, mp_obj_t path_o) {
Damien Georgea0995052019-10-18 17:25:08 +1100289 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
290 const char *path = MP_VFS_LFSx(make_path)(self, path_o);
291 int ret = LFSx_API(mkdir)(&self->lfs, path);
292 if (ret < 0) {
293 mp_raise_OSError(-ret);
294 }
295 return mp_const_none;
296}
Angus Grattondecf8e62024-02-27 15:32:29 +1100297static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(mkdir_obj), MP_VFS_LFSx(mkdir));
Damien Georgea0995052019-10-18 17:25:08 +1100298
Angus Grattondecf8e62024-02-27 15:32:29 +1100299static mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100300 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
301
302 // Check path exists
303 const char *path = MP_VFS_LFSx(make_path)(self, path_in);
304 if (path[1] != '\0') {
305 // Not at root, check it exists
Damien George69661f32020-02-27 15:36:53 +1100306 struct LFSx_API (info) info;
Damien Georgea0995052019-10-18 17:25:08 +1100307 int ret = LFSx_API(stat)(&self->lfs, path, &info);
308 if (ret < 0 || info.type != LFSx_MACRO(_TYPE_DIR)) {
Damien George62d26bf2025-05-13 12:54:47 +1000309 mp_raise_OSError(MP_ENOENT);
Damien Georgea0995052019-10-18 17:25:08 +1100310 }
311 }
312
313 // Update cur_dir with new path
314 if (path == vstr_str(&self->cur_dir)) {
315 self->cur_dir.len = strlen(path);
316 } else {
317 vstr_reset(&self->cur_dir);
318 vstr_add_str(&self->cur_dir, path);
319 }
320
321 // If not at root add trailing / to make it easy to build paths
robertd3ea28d2020-05-03 21:05:08 +0200322 // and then normalise the path
Damien Georgea0995052019-10-18 17:25:08 +1100323 if (vstr_len(&self->cur_dir) != 1) {
324 vstr_add_byte(&self->cur_dir, '/');
robertd3ea28d2020-05-03 21:05:08 +0200325
326 #define CWD_LEN (vstr_len(&self->cur_dir))
327 size_t to = 1;
328 size_t from = 1;
329 char *cwd = vstr_str(&self->cur_dir);
330 while (from < CWD_LEN) {
Mingjie Shena9fc0342023-04-20 18:20:37 -0400331 for (; from < CWD_LEN && cwd[from] == '/'; ++from) {
robertd3ea28d2020-05-03 21:05:08 +0200332 // Scan for the start
333 }
334 if (from > to) {
335 // Found excessive slash chars, squeeze them out
336 vstr_cut_out_bytes(&self->cur_dir, to, from - to);
337 from = to;
338 }
Mingjie Shena9fc0342023-04-20 18:20:37 -0400339 for (; from < CWD_LEN && cwd[from] != '/'; ++from) {
robertd3ea28d2020-05-03 21:05:08 +0200340 // Scan for the next /
341 }
342 if ((from - to) == 1 && cwd[to] == '.') {
343 // './', ignore
344 vstr_cut_out_bytes(&self->cur_dir, to, ++from - to);
345 from = to;
346 } else if ((from - to) == 2 && cwd[to] == '.' && cwd[to + 1] == '.') {
347 // '../', skip back
348 if (to > 1) {
349 // Only skip back if not at the tip
350 for (--to; to > 1 && cwd[to - 1] != '/'; --to) {
351 // Skip back
352 }
353 }
354 vstr_cut_out_bytes(&self->cur_dir, to, ++from - to);
355 from = to;
356 } else {
357 // Normal element, keep it and just move the offset
358 to = ++from;
359 }
360 }
Damien Georgea0995052019-10-18 17:25:08 +1100361 }
362
363 return mp_const_none;
364}
Angus Grattondecf8e62024-02-27 15:32:29 +1100365static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(chdir_obj), MP_VFS_LFSx(chdir));
Damien Georgea0995052019-10-18 17:25:08 +1100366
Angus Grattondecf8e62024-02-27 15:32:29 +1100367static mp_obj_t MP_VFS_LFSx(getcwd)(mp_obj_t self_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100368 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
369 if (vstr_len(&self->cur_dir) == 1) {
370 return MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
371 } else {
372 // don't include trailing /
373 return mp_obj_new_str(self->cur_dir.buf, self->cur_dir.len - 1);
374 }
375}
Angus Grattondecf8e62024-02-27 15:32:29 +1100376static MP_DEFINE_CONST_FUN_OBJ_1(MP_VFS_LFSx(getcwd_obj), MP_VFS_LFSx(getcwd));
Damien Georgea0995052019-10-18 17:25:08 +1100377
Angus Grattondecf8e62024-02-27 15:32:29 +1100378static mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100379 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
roberta5ea4b92020-04-29 18:13:22 +0200380 const char *path = MP_VFS_LFSx(make_path)(self, path_in);
Damien George69661f32020-02-27 15:36:53 +1100381 struct LFSx_API (info) info;
Damien Georgea0995052019-10-18 17:25:08 +1100382 int ret = LFSx_API(stat)(&self->lfs, path, &info);
383 if (ret < 0) {
384 mp_raise_OSError(-ret);
385 }
386
Yoctopuce devdf05cae2025-07-01 13:16:20 +0200387 mp_timestamp_t mtime = 0;
Damien George2acc0872020-07-29 01:01:48 +1000388 #if LFS_BUILD_VERSION == 2
389 uint8_t mtime_buf[8];
390 lfs2_ssize_t sz = lfs2_getattr(&self->lfs, path, LFS_ATTR_MTIME, &mtime_buf, sizeof(mtime_buf));
391 if (sz == sizeof(mtime_buf)) {
392 uint64_t ns = 0;
393 for (size_t i = sizeof(mtime_buf); i > 0; --i) {
394 ns = ns << 8 | mtime_buf[i - 1];
395 }
Damien George8f20cdc2020-09-14 12:15:03 +1000396 // On-disk storage of timestamps uses 1970 as the Epoch, so convert to host's Epoch.
397 mtime = timeutils_seconds_since_epoch_from_nanoseconds_since_1970(ns);
Damien George2acc0872020-07-29 01:01:48 +1000398 }
399 #endif
400
Damien Georgea0995052019-10-18 17:25:08 +1100401 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
402 t->items[0] = MP_OBJ_NEW_SMALL_INT(info.type == LFSx_MACRO(_TYPE_REG) ? MP_S_IFREG : MP_S_IFDIR); // st_mode
403 t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
404 t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
405 t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
406 t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
407 t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
408 t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size
Yoctopuce devdf05cae2025-07-01 13:16:20 +0200409 t->items[7] = timeutils_obj_from_timestamp(mtime); // st_atime
410 t->items[8] = timeutils_obj_from_timestamp(mtime); // st_mtime
411 t->items[9] = timeutils_obj_from_timestamp(mtime); // st_ctime
Damien Georgea0995052019-10-18 17:25:08 +1100412
413 return MP_OBJ_FROM_PTR(t);
414}
Angus Grattondecf8e62024-02-27 15:32:29 +1100415static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(stat_obj), MP_VFS_LFSx(stat));
Damien Georgea0995052019-10-18 17:25:08 +1100416
Angus Grattondecf8e62024-02-27 15:32:29 +1100417static int LFSx_API(traverse_cb)(void *data, LFSx_API(block_t) bl) {
Damien Georgea0995052019-10-18 17:25:08 +1100418 (void)bl;
Damien George69661f32020-02-27 15:36:53 +1100419 uint32_t *n = (uint32_t *)data;
Damien Georgea0995052019-10-18 17:25:08 +1100420 *n += 1;
421 return LFSx_MACRO(_ERR_OK);
422}
423
Angus Grattondecf8e62024-02-27 15:32:29 +1100424static mp_obj_t MP_VFS_LFSx(statvfs)(mp_obj_t self_in, mp_obj_t path_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100425 (void)path_in;
426 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
427 uint32_t n_used_blocks = 0;
428 #if LFS_BUILD_VERSION == 1
429 int ret = LFSx_API(traverse)(&self->lfs, LFSx_API(traverse_cb), &n_used_blocks);
430 #else
431 int ret = LFSx_API(fs_traverse)(&self->lfs, LFSx_API(traverse_cb), &n_used_blocks);
432 #endif
433 if (ret < 0) {
434 mp_raise_OSError(-ret);
435 }
436
437 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
438 t->items[0] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_size); // f_bsize
439 t->items[1] = t->items[0]; // f_frsize
440 t->items[2] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_count); // f_blocks
441 t->items[3] = MP_OBJ_NEW_SMALL_INT(self->lfs.cfg->block_count - n_used_blocks); // f_bfree
442 t->items[4] = t->items[3]; // f_bavail
443 t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
444 t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree
445 t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail
446 t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
447 t->items[9] = MP_OBJ_NEW_SMALL_INT(LFSx_MACRO(_NAME_MAX)); // f_namemax
448
449 return MP_OBJ_FROM_PTR(t);
450}
Angus Grattondecf8e62024-02-27 15:32:29 +1100451static MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(statvfs_obj), MP_VFS_LFSx(statvfs));
Damien Georgea0995052019-10-18 17:25:08 +1100452
Angus Grattondecf8e62024-02-27 15:32:29 +1100453static mp_obj_t MP_VFS_LFSx(mount)(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
Damien George03a1f942020-10-29 11:31:53 +1100454 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
Damien Georgea0995052019-10-18 17:25:08 +1100455 (void)mkfs;
Damien George03a1f942020-10-29 11:31:53 +1100456
457 // Make block device read-only if requested.
458 if (mp_obj_is_true(readonly)) {
459 self->blockdev.writeblocks[0] = MP_OBJ_NULL;
460 }
461
462 // Already called LFSx_API(mount) in MP_VFS_LFSx(make_new) so the filesystem is ready.
463
Damien Georgea0995052019-10-18 17:25:08 +1100464 return mp_const_none;
465}
Angus Grattondecf8e62024-02-27 15:32:29 +1100466static MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(mount_obj), MP_VFS_LFSx(mount));
Damien Georgea0995052019-10-18 17:25:08 +1100467
Angus Grattondecf8e62024-02-27 15:32:29 +1100468static mp_obj_t MP_VFS_LFSx(umount)(mp_obj_t self_in) {
Damien Georgea0995052019-10-18 17:25:08 +1100469 MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in);
470 // LFS unmount never fails
471 LFSx_API(unmount)(&self->lfs);
472 return mp_const_none;
473}
Angus Grattondecf8e62024-02-27 15:32:29 +1100474static MP_DEFINE_CONST_FUN_OBJ_1(MP_VFS_LFSx(umount_obj), MP_VFS_LFSx(umount));
Damien Georgea0995052019-10-18 17:25:08 +1100475
Angus Grattondecf8e62024-02-27 15:32:29 +1100476static const mp_rom_map_elem_t MP_VFS_LFSx(locals_dict_table)[] = {
Damien Georgea0995052019-10-18 17:25:08 +1100477 { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&MP_VFS_LFSx(mkfs_obj)) },
478 { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&MP_VFS_LFSx(open_obj)) },
479 { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&MP_VFS_LFSx(ilistdir_obj)) },
480 { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&MP_VFS_LFSx(mkdir_obj)) },
481 { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&MP_VFS_LFSx(rmdir_obj)) },
482 { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&MP_VFS_LFSx(chdir_obj)) },
483 { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&MP_VFS_LFSx(getcwd_obj)) },
484 { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&MP_VFS_LFSx(remove_obj)) },
485 { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&MP_VFS_LFSx(rename_obj)) },
486 { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&MP_VFS_LFSx(stat_obj)) },
487 { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&MP_VFS_LFSx(statvfs_obj)) },
488 { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&MP_VFS_LFSx(mount_obj)) },
489 { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&MP_VFS_LFSx(umount_obj)) },
490};
Angus Grattondecf8e62024-02-27 15:32:29 +1100491static MP_DEFINE_CONST_DICT(MP_VFS_LFSx(locals_dict), MP_VFS_LFSx(locals_dict_table));
Damien Georgea0995052019-10-18 17:25:08 +1100492
Angus Grattondecf8e62024-02-27 15:32:29 +1100493static mp_import_stat_t MP_VFS_LFSx(import_stat)(void *self_in, const char *path) {
Damien Georgea0995052019-10-18 17:25:08 +1100494 MP_OBJ_VFS_LFSx *self = self_in;
Damien George69661f32020-02-27 15:36:53 +1100495 struct LFSx_API (info) info;
Damien George7dffbfd2020-05-14 21:37:59 +1000496 mp_obj_str_t path_obj = { { &mp_type_str }, 0, 0, (const byte *)path };
Damien George0c776682020-06-25 16:31:33 +1000497 path = MP_VFS_LFSx(make_path)(self, MP_OBJ_FROM_PTR(&path_obj));
Damien Georgea0995052019-10-18 17:25:08 +1100498 int ret = LFSx_API(stat)(&self->lfs, path, &info);
499 if (ret == 0) {
500 if (info.type == LFSx_MACRO(_TYPE_REG)) {
501 return MP_IMPORT_STAT_FILE;
502 } else {
503 return MP_IMPORT_STAT_DIR;
504 }
505 }
506 return MP_IMPORT_STAT_NO_EXIST;
507}
508
Angus Grattondecf8e62024-02-27 15:32:29 +1100509static const mp_vfs_proto_t MP_VFS_LFSx(proto) = {
Damien Georgea0995052019-10-18 17:25:08 +1100510 .import_stat = MP_VFS_LFSx(import_stat),
511};
512
Jim Mussaredb7d6ee92022-06-24 16:22:38 +1000513#if LFS_BUILD_VERSION == 1
514#define VFS_LFSx_QSTR MP_QSTR_VfsLfs1
515#else
516#define VFS_LFSx_QSTR MP_QSTR_VfsLfs2
517#endif
518
Jim Mussared662b9762021-07-14 14:38:38 +1000519MP_DEFINE_CONST_OBJ_TYPE(
520 MP_TYPE_VFS_LFSx,
Jim Mussaredb7d6ee92022-06-24 16:22:38 +1000521 VFS_LFSx_QSTR,
Jim Mussared662b9762021-07-14 14:38:38 +1000522 MP_TYPE_FLAG_NONE,
Jim Mussared94beeab2022-09-17 00:31:23 +1000523 make_new, MP_VFS_LFSx(make_new),
Jim Mussared662b9762021-07-14 14:38:38 +1000524 protocol, &MP_VFS_LFSx(proto),
Jim Mussared9dce8272022-06-24 16:27:46 +1000525 locals_dict, &MP_VFS_LFSx(locals_dict)
Jim Mussared662b9762021-07-14 14:38:38 +1000526 );
Jim Mussaredb7d6ee92022-06-24 16:22:38 +1000527
528#undef VFS_LFSx_QSTR
Damien Georgea5135582022-11-11 15:43:55 +1100529
530#endif // defined(LFS_BUILD_VERSION)