Skip to content

Fix filesystem writability from USB - #10659

Merged
dhalbert merged 1 commit into
adafruit:mainfrom
dhalbert:fix-usb-writability
Oct 7, 2025
Merged

dhalbert merged 1 commit into
adafruit:mainfrom
dhalbert:fix-usb-writability

Conversation

@dhalbert

@dhalbert dhalbert commented Oct 7, 2025 •

Copy link
Copy Markdown
Collaborator

#10648 mistakenly prevented USB writes all the time. Fixing it was more complicated than one might think

  • The old way of doing self->blockdev.writeblocks[0] = MP_OBJ_NULL; turns off write for everyone, and is not a good way of setting read-only for host but not CircuitPython or vice-versa. So use the blockdev MP_BLOCKDEV_FLAGs exclusively. via filesystem_is_writable_by_python() and filesystem_is_writable_by_usb().
  • The above change caused boot_out.txt not to be written in most cases, because now the read-write check on the flags is done at a lower level. So add a new flag MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION to turn off write protection during boot.py execution. We can't just temporarily toggle MP_BLOCKDEV_FLAG_USB_WRITABLE, because boot.py might do a storage.remount(), which changes that flag, and we can't tell if it was changed in main.c or in the boot.py Python code.

Tested with all four combinations of {CIRCUITPY, /sd} x {read-write, readonly} on a Feather RP2040 with an Adalogger Featherwing. Also tested the three drives presented by Fruit Jam in their regular states to check proper read-only/read-write.

@dhalbert
dhalbert requested a review from tannewt October 7, 2025 22:49

@tannewt tannewt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thank you!

@dhalbert
dhalbert merged commit 97c6617 into adafruit:main Oct 7, 2025
1255 of 1256 checks passed
@dhalbert
dhalbert deleted the fix-usb-writability branch October 7, 2025 22:58
dhalbert pushed a commit that referenced this pull request Jun 10, 2026
… bypass STA_PROTECT while they hold the lock

Since #10659, filesystem_is_writable_by_python() returns false on any
USB-device-capable board after main.c boot (CONCURRENT_WRITE_PROTECTED
and USB_WRITABLE are both set). vfs_fat_diskio.c:disk_ioctl(IOCTL_STATUS)
calls this function and reports STA_PROTECT when it returns false, so
f_open(FA_WRITE) returns FR_WRITE_PROTECTED.

This affects every non-USB-MSC writer that calls f_open directly through
FatFS:

- BLE File Transfer (supervisor/shared/bluetooth/file_transfer.c): all
  WRITE/MOVE/MKDIR/DELETE commands fail with STATUS_ERROR_READONLY even
  when the board has no host on the bus. This is the original #10972
  repro on CLUE running from battery.
- Web workflow PUT/POST/MOVE/DELETE (supervisor/shared/web_workflow):
  same path, web-editor #460/#506 surfaces this as a read-only error.
- storage.remount(readonly=False): can take the lock but then can't
  actually write while the lock is held.

Each of these already calls filesystem_lock() to claim the blockdev
LOCKED flag before writing. USB MSC does NOT go through filesystem_lock;
it grabs LOCKED directly via blockdev_lock() inside tud_msc_is_writable_cb.
So filesystem_lock() is exactly the right place to grant temporary write
permission via IGNORE_WRITE_PROTECTION, mirroring the pattern main.c uses
around boot.py.

After this change:
- filesystem_lock() is the single source of truth for 'I'm the local
  writer right now'. Holders can f_open(FA_WRITE) and operate normally.
- USB MSC continues to be mutually excluded via LOCKED; it never sets
  IGNORE_WRITE_PROTECTION because it doesn't go through filesystem_lock.
- _is_writable_by_python and _is_writable_by_usb are unchanged; the
  symmetric mutex from #10659 is preserved.
- Python direct open(FA_WRITE) without a lock still requires
  storage.disable_usb_drive() or storage.remount(readonly=False) — same
  as current behavior.

Fixes #10972
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SD card mounted in code.py is not writable from USB even when storage.mount(..., readonly=True)

2 participants