Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2022 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 : : // ROMFS filesystem format
28 : : // =======================
29 : : //
30 : : // ROMFS is a flexible and extensible filesystem format designed to represent a
31 : : // directory hierarchy with files, where those files are read-only and their data
32 : : // can be memory mapped.
33 : : //
34 : : // Concepts:
35 : : // - varuint: An unsigned integer that is encoded in a variable number of bytes. It is
36 : : // stored big-endian with the high bit of the byte set if there are following bytes.
37 : : // - record: A variable sized element with a type. It is stored as two varuint's and then
38 : : // a payload. The first varuint is the record kind and the second varuint is the
39 : : // payload length (which may be zero bytes long).
40 : : //
41 : : // A ROMFS filesystem is a record with record kind 0x14a6b1, chosen so the encoded value
42 : : // is 0xd2-0xcd-0x31 which is "RM1" with the first two bytes having their high bit set.
43 : : // If the ROMFS record's payload is non-empty then it contains records.
44 : : //
45 : : // Record types:
46 : : // - 0 = unused, can be used to detect corruption of the filesystem.
47 : : // - 1 = padding/comments, can contain any data in their payload.
48 : : // - 2 = verbatim data, used to store file data.
49 : : // - 3 = indirect data, pointer to offset within the ROMFS payload.
50 : : // - 4 = a directory: payload contains a varuint which is the length of the directory
51 : : // name in bytes, then the name, then optional nested records for the contents
52 : : // of the directory (including optional metadata).
53 : : // - 5 = a file: payload contains a varuint which is the length of the filename in bytes
54 : : // then the name, then optional nested records.
55 : : //
56 : : // Remarks:
57 : : // - A varuint can be padded if needed by prepending with one or more 0x80 bytes. This
58 : : // padding does not change any semantics.
59 : : // - The size of the ROMFS record (including kind and length and payload) must be a
60 : : // multiple of 2 (because it's not possible to add a padding record of one byte).
61 : : // - File data can be optionally aligned using padding records and/or indirect data
62 : : // records.
63 : : // - There is no limit to the size of directory/file names or file data.
64 : : //
65 : : // Unknown record types must be skipped over. They may in the future add optional
66 : : // features, while still retaining backwards compatibility. Such features may be:
67 : : // - Alignment requirements of the ROMFS record.
68 : : // - Timestamps on directories/files.
69 : : // - A precomputed hash of a file, or other metadata.
70 : : // - An optimised lookup table indexing the directory hierarchy.
71 : :
72 : : #include <string.h>
73 : :
74 : : #include "py/bc.h"
75 : : #include "py/runtime.h"
76 : : #include "py/mperrno.h"
77 : : #include "extmod/vfs.h"
78 : : #include "extmod/vfs_rom.h"
79 : :
80 : : #if MICROPY_VFS_ROM
81 : :
82 : : #define ROMFS_SIZE_MIN (4)
83 : : #define ROMFS_HEADER_BYTE0 (0x80 | 'R')
84 : : #define ROMFS_HEADER_BYTE1 (0x80 | 'M')
85 : : #define ROMFS_HEADER_BYTE2 (0x00 | '1')
86 : :
87 : : // Values for `record_kind_t`.
88 : : #define ROMFS_RECORD_KIND_UNUSED (0)
89 : : #define ROMFS_RECORD_KIND_PADDING (1)
90 : : #define ROMFS_RECORD_KIND_DATA_VERBATIM (2)
91 : : #define ROMFS_RECORD_KIND_DATA_POINTER (3)
92 : : #define ROMFS_RECORD_KIND_DIRECTORY (4)
93 : : #define ROMFS_RECORD_KIND_FILE (5)
94 : : #define ROMFS_RECORD_KIND_FILESYSTEM (0x14a6b1)
95 : :
96 : : typedef mp_uint_t record_kind_t;
97 : :
98 : : struct _mp_obj_vfs_rom_t {
99 : : mp_obj_base_t base;
100 : : mp_obj_t memory;
101 : : const uint8_t *filesystem;
102 : : const uint8_t *filesystem_end;
103 : : };
104 : :
105 : : // Returns 0 for success, -1 for failure.
106 : 8288 : static int mp_decode_uint_checked(const uint8_t **ptr, const uint8_t *ptr_max, mp_uint_t *value_out) {
107 : 8288 : mp_uint_t unum = 0;
108 : 8288 : byte val;
109 : 8288 : const uint8_t *p = *ptr;
110 : 15522 : do {
111 [ + + ]: 15522 : if (p >= ptr_max) {
112 : : return -1;
113 : : }
114 : 15504 : val = *p++;
115 : 15504 : unum = (unum << 7) | (val & 0x7f);
116 [ + + ]: 15504 : } while ((val & 0x80) != 0);
117 : 8270 : *ptr = p;
118 : 8270 : *value_out = unum;
119 : 8270 : return 0;
120 : : }
121 : :
122 : 3984 : static record_kind_t extract_record(const uint8_t **fs, const uint8_t **fs_next, const uint8_t *fs_max) {
123 : 3984 : mp_uint_t record_kind;
124 [ + + ]: 3984 : if (mp_decode_uint_checked(fs, fs_max, &record_kind) != 0) {
125 : : return ROMFS_RECORD_KIND_UNUSED;
126 : : }
127 : 3978 : mp_uint_t record_len;
128 [ + + ]: 3978 : if (mp_decode_uint_checked(fs, fs_max, &record_len) != 0) {
129 : : return ROMFS_RECORD_KIND_UNUSED;
130 : : }
131 : 3974 : *fs_next = *fs + record_len;
132 : 3974 : return record_kind;
133 : : }
134 : :
135 : : // Returns 0 for success, a negative integer for failure.
136 : 92 : static int extract_data(mp_obj_vfs_rom_t *self, const uint8_t *fs, const uint8_t *fs_top, size_t *size_out, const uint8_t **data_out) {
137 [ + + ]: 100 : while (fs < fs_top) {
138 : 96 : const uint8_t *fs_next;
139 : 96 : record_kind_t record_kind = extract_record(&fs, &fs_next, fs_top);
140 [ + + ]: 96 : if (record_kind == ROMFS_RECORD_KIND_UNUSED) {
141 : : // Corrupt filesystem.
142 : : break;
143 [ + + ]: 92 : } else if (record_kind == ROMFS_RECORD_KIND_DATA_VERBATIM) {
144 : : // Verbatim data.
145 [ + + ]: 66 : if (size_out != NULL) {
146 : 58 : *size_out = fs_next - fs;
147 : 58 : *data_out = fs;
148 : : }
149 : 80 : return 0;
150 [ + + ]: 26 : } else if (record_kind == ROMFS_RECORD_KIND_DATA_POINTER) {
151 : : // Pointer to data.
152 : 18 : mp_uint_t size;
153 [ + + ]: 18 : if (mp_decode_uint_checked(&fs, fs_next, &size) != 0) {
154 : : break;
155 : : }
156 : 16 : mp_uint_t offset;
157 [ + + ]: 16 : if (mp_decode_uint_checked(&fs, fs_next, &offset) != 0) {
158 : : break;
159 : : }
160 [ + - ]: 14 : if (size_out != NULL) {
161 : 14 : *size_out = size;
162 : 14 : *data_out = self->filesystem + offset;
163 : : }
164 : 14 : return 0;
165 : : } else {
166 : : // Skip this record.
167 : 8 : fs = fs_next;
168 : : }
169 : : }
170 : : return -MP_EIO;
171 : : }
172 : :
173 : : // Searches for `path` in the filesystem.
174 : : // `path` must be null-terminated.
175 : 122 : mp_import_stat_t mp_vfs_rom_search_filesystem(mp_obj_vfs_rom_t *self, const char *path, size_t *size_out, const uint8_t **data_out) {
176 : 122 : const uint8_t *fs = self->filesystem;
177 : 122 : const uint8_t *fs_top = self->filesystem_end;
178 : 122 : size_t path_len = strlen(path);
179 [ + + ]: 122 : if (*path == '/') {
180 : : // An optional slash at the start of the path enters the top-level filesystem.
181 : 84 : ++path;
182 : 84 : --path_len;
183 : : }
184 [ + + ]: 370 : while (path_len > 0 && fs < fs_top) {
185 : 306 : const uint8_t *fs_next;
186 : 306 : record_kind_t record_kind = extract_record(&fs, &fs_next, fs_top);
187 [ + + ]: 306 : if (record_kind == ROMFS_RECORD_KIND_UNUSED) {
188 : : // Corrupt filesystem.
189 : 58 : return MP_IMPORT_STAT_NO_EXIST;
190 [ + + ]: 304 : } else if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) {
191 : : // A directory or file record.
192 : 240 : mp_uint_t name_len;
193 [ + + ]: 240 : if (mp_decode_uint_checked(&fs, fs_next, &name_len) != 0) {
194 : : // Corrupt filesystem.
195 : 56 : return MP_IMPORT_STAT_NO_EXIST;
196 : : }
197 [ + + ]: 238 : if ((name_len == path_len
198 [ + + + + ]: 126 : || (name_len < path_len && path[name_len] == '/'))
199 [ + + ]: 132 : && memcmp(path, fs, name_len) == 0) {
200 : : // Name matches, so enter this record.
201 : 84 : fs += name_len;
202 : 84 : fs_top = fs_next;
203 : 84 : path += name_len;
204 : 84 : path_len -= name_len;
205 [ + + ]: 84 : if (record_kind == ROMFS_RECORD_KIND_DIRECTORY) {
206 : : // Continue searching in this directory.
207 [ + + ]: 30 : if (*path == '/') {
208 : 20 : ++path;
209 : 20 : --path_len;
210 : : }
211 : : } else {
212 : : // Return this file.
213 [ + - ]: 54 : if (path_len != 0) {
214 : : return MP_IMPORT_STAT_NO_EXIST;
215 : : }
216 [ + + ]: 54 : if (extract_data(self, fs, fs_top, size_out, data_out) != 0) {
217 : : // Corrupt filesystem.
218 : : return MP_IMPORT_STAT_NO_EXIST;
219 : : }
220 : 44 : return MP_IMPORT_STAT_FILE;
221 : : }
222 : : } else {
223 : : // Skip this directory/file record.
224 : 154 : fs = fs_next;
225 : : }
226 : : } else {
227 : : // Skip this record.
228 : 64 : fs = fs_next;
229 : : }
230 : : }
231 [ + + ]: 64 : if (path_len == 0) {
232 [ + - ]: 44 : if (size_out != NULL) {
233 : 44 : *size_out = fs_top - fs;
234 : 44 : *data_out = fs;
235 : : }
236 : 44 : return MP_IMPORT_STAT_DIR;
237 : : }
238 : : return MP_IMPORT_STAT_NO_EXIST;
239 : : }
240 : :
241 : 3522 : static mp_obj_t vfs_rom_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
242 : 3522 : mp_arg_check_num(n_args, n_kw, 1, 1, false);
243 : :
244 : 3522 : mp_obj_vfs_rom_t *self = m_new_obj(mp_obj_vfs_rom_t);
245 : 3522 : self->base.type = type;
246 : 3522 : self->memory = args[0];
247 : :
248 : : // Get the ROMFS memory region.
249 : 3522 : mp_buffer_info_t bufinfo;
250 : 3522 : mp_get_buffer_raise(self->memory, &bufinfo, MP_BUFFER_READ);
251 [ + + ]: 3520 : if (bufinfo.len < ROMFS_SIZE_MIN) {
252 : 4 : mp_raise_OSError(MP_ENODEV);
253 : : }
254 : 3516 : self->filesystem = bufinfo.buf;
255 : :
256 : : // Verify it is a ROMFS.
257 [ + + ]: 3516 : if (!(self->filesystem[0] == ROMFS_HEADER_BYTE0
258 [ + - ]: 3514 : && self->filesystem[1] == ROMFS_HEADER_BYTE1
259 [ - + ]: 3514 : && self->filesystem[2] == ROMFS_HEADER_BYTE2)) {
260 : 2 : mp_raise_OSError(MP_ENODEV);
261 : : }
262 : :
263 : : // The ROMFS is a record itself, so enter into it and compute its limit.
264 : 3514 : record_kind_t record_kind = extract_record(&self->filesystem, &self->filesystem_end, self->filesystem + bufinfo.len);
265 [ + + ]: 3514 : if (record_kind != ROMFS_RECORD_KIND_FILESYSTEM) {
266 : 2 : mp_raise_OSError(MP_ENODEV);
267 : : }
268 : :
269 : : // Check the filesystem is within the limits of the input buffer.
270 [ + + ]: 3512 : if (self->filesystem_end > (const uint8_t *)bufinfo.buf + bufinfo.len) {
271 : 2 : mp_raise_OSError(MP_ENODEV);
272 : : }
273 : :
274 : 3510 : return MP_OBJ_FROM_PTR(self);
275 : : }
276 : :
277 : 3478 : static mp_obj_t vfs_rom_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
278 : 3478 : (void)self_in;
279 : 3478 : (void)readonly;
280 [ + + ]: 3478 : if (mp_obj_is_true(mkfs)) {
281 : 2 : mp_raise_OSError(MP_EPERM);
282 : : }
283 : 3476 : return mp_const_none;
284 : : }
285 : : static MP_DEFINE_CONST_FUN_OBJ_3(vfs_rom_mount_obj, vfs_rom_mount);
286 : :
287 : : // mp_vfs_rom_file_open is implemented in vfs_rom_file.c.
288 : : static MP_DEFINE_CONST_FUN_OBJ_3(vfs_rom_open_obj, mp_vfs_rom_file_open);
289 : :
290 : 6 : static mp_obj_t vfs_rom_chdir(mp_obj_t self_in, mp_obj_t path_in) {
291 : 6 : mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in);
292 : 6 : const char *path = mp_vfs_rom_get_path_str(self, path_in);
293 [ + - + + ]: 6 : if (path[0] == '/' && path[1] == '\0') {
294 : : // Allow chdir to the root of the filesystem.
295 : 4 : } else {
296 : : // Don't allow chdir to any subdirectory (not currently implemented).
297 : 2 : mp_raise_OSError(MP_EOPNOTSUPP);
298 : : }
299 : 4 : return mp_const_none;
300 : : }
301 : : static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_chdir_obj, vfs_rom_chdir);
302 : :
303 : 4 : static mp_obj_t vfs_rom_getcwd(mp_obj_t self_in) {
304 : 4 : (void)self_in;
305 : : // The current directory is always the root of the ROMFS.
306 : 4 : return MP_OBJ_NEW_QSTR(MP_QSTR_);
307 : : }
308 : : static MP_DEFINE_CONST_FUN_OBJ_1(vfs_rom_getcwd_obj, vfs_rom_getcwd);
309 : :
310 : : typedef struct _vfs_rom_ilistdir_it_t {
311 : : mp_obj_base_t base;
312 : : mp_fun_1_t iternext;
313 : : mp_obj_vfs_rom_t *vfs_rom;
314 : : bool is_str;
315 : : const uint8_t *index;
316 : : const uint8_t *index_top;
317 : : } vfs_rom_ilistdir_it_t;
318 : :
319 : 74 : static mp_obj_t vfs_rom_ilistdir_it_iternext(mp_obj_t self_in) {
320 : 74 : vfs_rom_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
321 : :
322 [ + + ]: 88 : while (self->index < self->index_top) {
323 : 68 : const uint8_t *index_next;
324 : 68 : record_kind_t record_kind = extract_record(&self->index, &index_next, self->index_top);
325 : 68 : uint32_t type;
326 : 68 : mp_uint_t name_len;
327 : 68 : size_t data_len;
328 [ + + ]: 68 : if (record_kind == ROMFS_RECORD_KIND_UNUSED) {
329 : : // Corrupt filesystem.
330 : 2 : self->index = self->index_top;
331 : 6 : break;
332 [ + + ]: 66 : } else if (record_kind == ROMFS_RECORD_KIND_DIRECTORY || record_kind == ROMFS_RECORD_KIND_FILE) {
333 : : // A directory or file record.
334 [ + + ]: 52 : if (mp_decode_uint_checked(&self->index, index_next, &name_len) != 0) {
335 : : // Corrupt filesystem.
336 : 2 : self->index = self->index_top;
337 : 2 : break;
338 : : }
339 [ + + ]: 50 : if (record_kind == ROMFS_RECORD_KIND_DIRECTORY) {
340 : : // A directory.
341 : 12 : type = MP_S_IFDIR;
342 : 12 : data_len = index_next - self->index - name_len;
343 : : } else {
344 : : // A file.
345 : 38 : type = MP_S_IFREG;
346 : 38 : const uint8_t *data_value;
347 [ + + ]: 38 : if (extract_data(self->vfs_rom, self->index + name_len, index_next, &data_len, &data_value) != 0) {
348 : : // Corrupt filesystem.
349 : : break;
350 : : }
351 : : }
352 : : } else {
353 : : // Skip this record.
354 : 14 : self->index = index_next;
355 : 14 : continue;
356 : : }
357 : :
358 : 48 : const uint8_t *name_str = self->index;
359 : 48 : self->index = index_next;
360 : :
361 : : // Make 4-tuple with info about this entry: (name, attr, inode, size)
362 : 48 : mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
363 : :
364 [ + + ]: 48 : if (self->is_str) {
365 : 42 : t->items[0] = mp_obj_new_str((const char *)name_str, name_len);
366 : : } else {
367 : 6 : t->items[0] = mp_obj_new_bytes(name_str, name_len);
368 : : }
369 : :
370 : 48 : t->items[1] = MP_OBJ_NEW_SMALL_INT(type);
371 : 48 : t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
372 : 48 : t->items[3] = mp_obj_new_int(data_len);
373 : :
374 : 48 : return MP_OBJ_FROM_PTR(t);
375 : : }
376 : :
377 : : return MP_OBJ_STOP_ITERATION;
378 : : }
379 : :
380 : 28 : static mp_obj_t vfs_rom_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
381 : 28 : mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in);
382 : 28 : vfs_rom_ilistdir_it_t *iter = m_new_obj(vfs_rom_ilistdir_it_t);
383 : 28 : iter->base.type = &mp_type_polymorph_iter;
384 : 28 : iter->iternext = vfs_rom_ilistdir_it_iternext;
385 : 28 : iter->vfs_rom = self;
386 : 28 : iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
387 : 28 : const char *path = mp_vfs_rom_get_path_str(self, path_in);
388 : 28 : size_t size;
389 [ + + ]: 28 : if (mp_vfs_rom_search_filesystem(self, path, &size, &iter->index) != MP_IMPORT_STAT_DIR) {
390 : 2 : mp_raise_OSError(MP_ENOENT);
391 : : }
392 : 26 : iter->index_top = iter->index + size;
393 : 26 : return MP_OBJ_FROM_PTR(iter);
394 : : }
395 : : static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_ilistdir_obj, vfs_rom_ilistdir);
396 : :
397 : 36 : static mp_obj_t vfs_rom_stat(mp_obj_t self_in, mp_obj_t path_in) {
398 : 36 : mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in);
399 : 36 : const char *path = mp_vfs_rom_get_path_str(self, path_in);
400 : 36 : size_t file_size;
401 : 36 : const uint8_t *file_data;
402 : 36 : mp_import_stat_t stat = mp_vfs_rom_search_filesystem(self, path, &file_size, &file_data);
403 [ + + ]: 36 : if (stat == MP_IMPORT_STAT_NO_EXIST) {
404 : 18 : mp_raise_OSError(MP_ENOENT);
405 : : }
406 : 18 : mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
407 [ + + ]: 18 : t->items[0] = MP_OBJ_NEW_SMALL_INT(stat == MP_IMPORT_STAT_FILE ? MP_S_IFREG : MP_S_IFDIR); // st_mode
408 : 18 : t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
409 : 18 : t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
410 : 18 : t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
411 : 18 : t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
412 : 18 : t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
413 : 18 : t->items[6] = MP_OBJ_NEW_SMALL_INT(file_size); // st_size
414 : 18 : t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // st_atime
415 : 18 : t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // st_mtime
416 : 18 : t->items[9] = MP_OBJ_NEW_SMALL_INT(0); // st_ctime
417 : 18 : return MP_OBJ_FROM_PTR(t);
418 : : }
419 : : static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_stat_obj, vfs_rom_stat);
420 : :
421 : 4 : static mp_obj_t vfs_rom_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
422 : 4 : mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in);
423 : 4 : (void)path_in;
424 : 4 : size_t filesystem_len = self->filesystem_end - self->filesystem;
425 : 4 : mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
426 : 4 : t->items[0] = MP_OBJ_NEW_SMALL_INT(1); // f_bsize
427 : 4 : t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // f_frsize
428 : 4 : t->items[2] = mp_obj_new_int_from_uint(filesystem_len); // f_blocks
429 : 4 : t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // f_bfree
430 : 4 : t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // f_bavail
431 : 4 : t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
432 : 4 : t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree
433 : 4 : t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail
434 : 4 : t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
435 : 4 : t->items[9] = MP_OBJ_NEW_SMALL_INT(32767); // f_namemax
436 : 4 : return MP_OBJ_FROM_PTR(t);
437 : : }
438 : : static MP_DEFINE_CONST_FUN_OBJ_2(vfs_rom_statvfs_obj, vfs_rom_statvfs);
439 : :
440 : : static const mp_rom_map_elem_t vfs_rom_locals_dict_table[] = {
441 : : { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_rom_mount_obj) },
442 : : { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_identity_obj) },
443 : : { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_rom_open_obj) },
444 : :
445 : : { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_rom_chdir_obj) },
446 : : { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_rom_getcwd_obj) },
447 : : { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_rom_ilistdir_obj) },
448 : : { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_rom_stat_obj) },
449 : : { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_rom_statvfs_obj) },
450 : : };
451 : : static MP_DEFINE_CONST_DICT(vfs_rom_locals_dict, vfs_rom_locals_dict_table);
452 : :
453 : 18 : static mp_import_stat_t mp_vfs_rom_import_stat(void *self_in, const char *path) {
454 : 18 : mp_obj_vfs_rom_t *self = MP_OBJ_TO_PTR(self_in);
455 : 18 : return mp_vfs_rom_search_filesystem(self, path, NULL, NULL);
456 : : }
457 : :
458 : : static const mp_vfs_proto_t vfs_rom_proto = {
459 : : .import_stat = mp_vfs_rom_import_stat,
460 : : };
461 : :
462 : : MP_DEFINE_CONST_OBJ_TYPE(
463 : : mp_type_vfs_rom,
464 : : MP_QSTR_VfsRom,
465 : : MP_TYPE_FLAG_NONE,
466 : : make_new, vfs_rom_make_new,
467 : : protocol, &vfs_rom_proto,
468 : : locals_dict, &vfs_rom_locals_dict
469 : : );
470 : :
471 : : #endif // MICROPY_VFS_ROM
|