Skip to content

Persistence keys COM¹–COM³, LPT¹–LPT³, COM0 and LPT0 aren't escaped, so on Windows they resolve to device names instead of files #146

Closed

Description

@matt-edmondson

What's wrong

PersistenceProviderUtilities.ReservedDeviceNames (Essentials/PersistenceProviderUtilities.cs:22-27) lists CON, PRN, AUX, NUL, COM1-COM9 and LPT1-LPT9, and IsReservedDeviceName (:291-297) checks against that list.

Microsoft's file naming rules also reserve:

  • COM¹, COM², COM³, LPT¹, LPT² and LPT³, because Windows treats the ISO-8859-1 superscript digits ¹ ² ³ as digits in device names;
  • COM0 and LPT0.

None of these is in the list.

Failure scenario

  1. GetSafeFileName("COM¹") returns COM¹ unchanged: none of its characters are reserved or control characters, and the stem isn't in ReservedDeviceNames.
  2. FileSystemPersistenceProvider.StoreAsync("COM¹", obj) (and the DataHome, ConfigHome and Temp providers) therefore writes to <base>/COM¹.json.
  3. On Windows that path resolves to the serial-port device, not a file. The store then fails, blocks, or writes to the device, and a later retrieve can't read the data back.

Keys are free-form strings from callers, so any app that persists user-named items can hit this. The existing test (Essentials.Tests/PersistenceNamingTests.cs:60) only covers the ASCII names.

Suggested fix

Add COM0, LPT0, COM¹, COM², COM³, LPT¹, LPT² and LPT³ to ReservedDeviceNames. Names in that set already get their first character escaped, and GetKeyFromFileName reverses the escape, so nothing else should need to change.

Acceptance criteria

  • For each new name, GetSafeFileName returns an escaped stem.
  • GetKeyFromFileName(GetSafeFileName(name)) == name for each new name.
  • The data rows in PersistenceNamingTests are extended to cover these names.

Activity

  1. matt-edmondson commented on Oct 18, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: Medium. On Windows, a caller-supplied key of COM0, LPT0 or COM¹–LPT³ makes the file-backed providers write to a device instead of a file, so the store fails or the data can't be read back. The keys involved are unusual, but they are free-form caller input, and the fix is a few list entries.
    • Area / suggested assignee: PersistenceProviderUtilities.ReservedDeviceNames and PersistenceNamingTests. Owner: @matt-edmondson
    • Duplicates / in progress: not a duplicate, and no open PR covers it. It's in the same family as File-backed persistence: keys differing only in case ("Config" vs "config") overwrite each other on Windows and macOS #77 (case-only key collisions on file-backed persistence).
    • Next step: add the eight names to ReservedDeviceNames and extend the PersistenceNamingTests data rows with escape and round-trip assertions.

    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions