Skip to content

Commit ddf9aec

Browse files
committed
Grab FS lock to test writability
Checking is_writable_from_python always follows USB policy even if it is unplugged. Fixes #8986 Also does bare minimum for #8961.
1 parent 0d2e62f commit ddf9aec

File tree

8 files changed

+34
-17
lines changed

8 files changed

+34
-17
lines changed

extmod/vfs_fat.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_
9999
}
100100

101101
STATIC void verify_fs_writable(fs_user_mount_t *vfs) {
102-
if (!filesystem_is_writable_by_python(vfs)) {
102+
if (!filesystem_lock(vfs)) {
103103
mp_raise_OSError(MP_EROFS);
104104
}
105105
}
@@ -218,7 +218,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_i
218218

219219
STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) {
220220
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
221-
verify_fs_writable(self);
222221
const char *path = mp_obj_str_get_str(path_in);
223222

224223
FILINFO fno;
@@ -230,7 +229,9 @@ STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_in
230229

231230
// check if path is a file or directory
232231
if ((fno.fattrib & AM_DIR) == attr) {
232+
verify_fs_writable(self);
233233
res = f_unlink(&self->fatfs, path);
234+
filesystem_unlock(self);
234235

235236
if (res != FR_OK) {
236237
mp_raise_OSError_fresult(res);
@@ -253,17 +254,18 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir);
253254

254255
STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) {
255256
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
256-
verify_fs_writable(self);
257257
const char *old_path = mp_obj_str_get_str(path_in);
258258
const char *new_path = mp_obj_str_get_str(path_out);
259259

260+
verify_fs_writable(self);
260261
FRESULT res = f_rename(&self->fatfs, old_path, new_path);
261262
if (res == FR_EXIST) {
262263
// if new_path exists then try removing it (but only if it's a file)
263264
fat_vfs_remove_internal(vfs_in, path_out, 0); // 0 == file attribute
264265
// try to rename again
265266
res = f_rename(&self->fatfs, old_path, new_path);
266267
}
268+
filesystem_unlock(self);
267269
if (res == FR_OK) {
268270
return mp_const_none;
269271
} else {
@@ -275,9 +277,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_rename_obj, fat_vfs_rename);
275277

276278
STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) {
277279
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
278-
verify_fs_writable(self);
279280
const char *path = mp_obj_str_get_str(path_o);
281+
verify_fs_writable(self);
280282
FRESULT res = f_mkdir(&self->fatfs, path);
283+
filesystem_unlock(self);
281284
if (res == FR_OK) {
282285
return mp_const_none;
283286
} else {
@@ -463,7 +466,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_utime_obj, vfs_fat_utime);
463466

464467
STATIC mp_obj_t vfs_fat_getreadonly(mp_obj_t self_in) {
465468
fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
466-
return mp_obj_new_bool(!filesystem_is_writable_by_python(self));
469+
bool writable = filesystem_lock(self);
470+
if (writable) {
471+
filesystem_unlock(self);
472+
}
473+
return mp_obj_new_bool(!writable);
467474
}
468475
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getreadonly_obj, vfs_fat_getreadonly);
469476
STATIC const mp_obj_property_t fat_vfs_readonly_obj = {
@@ -487,9 +494,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getlabel_obj, vfs_fat_getlabel);
487494

488495
STATIC mp_obj_t vfs_fat_setlabel(mp_obj_t self_in, mp_obj_t label_in) {
489496
fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
490-
verify_fs_writable(self);
491497
const char *label_str = mp_obj_str_get_str(label_in);
498+
verify_fs_writable(self);
492499
FRESULT res = f_setlabel(&self->fatfs, label_str);
500+
filesystem_unlock(self);
493501
if (res != FR_OK) {
494502
if (res == FR_WRITE_PROTECTED) {
495503
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Read-only filesystem"));

extmod/vfs_fat.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj);
5050
typedef struct _pyb_file_obj_t {
5151
mp_obj_base_t base;
5252
FIL fp;
53+
// CIRCUITPY-CHANGE: We need to unlock the fs on file close.
54+
fs_user_mount_t *fs_mount;
5355
} pyb_file_obj_t;
5456

5557
#endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H

extmod/vfs_fat_file.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg,
127127
} else if (request == MP_STREAM_CLOSE) {
128128
// if fs==NULL then the file is closed and in that case this method is a no-op
129129
if (self->fp.obj.fs != NULL) {
130+
if (self->fp.flag & FA_WRITE) {
131+
filesystem_unlock(self->fs_mount);
132+
}
130133
FRESULT res = f_close(&self->fp);
131134
if (res != FR_OK) {
132135
*errcode = fresult_to_errno_table[res];
@@ -243,13 +246,14 @@ STATIC mp_obj_t fat_vfs_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_i
243246
}
244247

245248
assert(self != NULL);
246-
if ((mode & FA_WRITE) != 0 && !filesystem_is_writable_by_python(self)) {
249+
if ((mode & FA_WRITE) != 0 && !filesystem_lock(self)) {
247250
mp_raise_OSError(MP_EROFS);
248251
}
249252

250253

251254
pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
252255
o->base.type = type;
256+
o->fs_mount = self;
253257

254258
const char *fname = mp_obj_str_get_str(path_in);
255259
FRESULT res = f_open(&self->fatfs, &o->fp, fname, mode);

ports/espressif/supervisor/internal_flash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32
151151
uint32_t block_address = lba + block;
152152
uint32_t sector_offset = block_address / blocks_per_sector * SECTOR_SIZE;
153153
uint8_t block_offset = block_address % blocks_per_sector;
154-
if (_cache_lba != block_address) {
154+
if (_cache_lba != sector_offset) {
155155
supervisor_flash_read_blocks(_cache, sector_offset / FILESYSTEM_BLOCK_SIZE, blocks_per_sector);
156156
_cache_lba = sector_offset;
157157
}

ports/raspberrypi/supervisor/internal_flash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32
127127
uint32_t sector_offset = block_address / blocks_per_sector * SECTOR_SIZE;
128128
uint8_t block_offset = block_address % blocks_per_sector;
129129

130-
if (_cache_lba != block_address) {
130+
if (_cache_lba != sector_offset) {
131131
port_internal_flash_flush();
132132
memcpy(_cache,
133133
(void *)(XIP_BASE + CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + sector_offset),

supervisor/filesystem.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ void filesystem_set_internal_writable_by_usb(bool usb_writable);
4141
void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection);
4242
void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable);
4343
void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection);
44-
bool filesystem_is_writable_by_python(fs_user_mount_t *vfs);
44+
45+
// This controls whether USB tries to grab the underlying block device lock
46+
// during enumeration. If another workflow is modifying the filesystem when this
47+
// happens, then USB will be readonly.
4548
bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs);
4649

4750
fs_user_mount_t *filesystem_circuitpy(void);

supervisor/shared/filesystem.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,6 @@ void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) {
208208
}
209209
}
210210

211-
bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) {
212-
return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 ||
213-
(vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0;
214-
}
215-
216211
bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) {
217212
return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 ||
218213
(vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0;

supervisor/shared/web_workflow/web_workflow.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,11 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
698698
uint32_t total_clusters = fatfs->n_fatent - 2;
699699

700700
const char *writable = "false";
701-
if (filesystem_is_writable_by_python(fs_mount)) {
701+
// Test to see if we can grab the write lock. USB will grab the underlying
702+
// blockdev lock once it says it is writable. Unlock immediately since we
703+
// aren't actually writing.
704+
if (filesystem_lock(fs_mount)) {
705+
filesystem_unlock(fs_mount);
702706
writable = "true";
703707
}
704708
mp_printf(&_socket_print,
@@ -920,7 +924,8 @@ static void _reply_with_diskinfo_json(socketpool_socket_obj_t *socket, _request
920924
size_t total_size = fatfs->n_fatent - 2;
921925

922926
const char *writable = "false";
923-
if (filesystem_is_writable_by_python(fs)) {
927+
if (filesystem_lock(fs)) {
928+
filesystem_unlock(fs);
924929
writable = "true";
925930
}
926931
mp_printf(&_socket_print,

0 commit comments

Comments
 (0)