Skip to content

On a case-insensitive Linux mount (WSL /mnt/c, exFAT/NTFS USB), Save.png and save.PNG overwrite each other's output yet both count as written, and -o icons over -i Icons replaces the sources #166

Open

Description

@matt-edmondson

What's wrong

Both safeguards added for #120 decide whether two paths are the same by comparing strings. The comparison mode depends only on the OS:

Case sensitivity is a property of the filesystem, not of the OS. Linux often works on case-insensitive volumes: WSL's /mnt/c, Docker Desktop bind mounts, exFAT/NTFS USB drives, and ext4 casefold directories. macOS can also format a volume case-sensitive.

Failure scenarios (traced)

  1. Silent overwrite between inputs. On WSL, an input folder /mnt/c/Icons holds Save.png and save.PNG. Their output names are Save.png and save.png, which are different Ordinal keys, so no collision is reported. On disk they are the same file. The second input overwrites the first, the summary says "2 file(s) written", and the exit code is 0.
  2. Sources overwritten. iconhelper -i /mnt/c/Icons -o /mnt/c/icons passes IsSameDirectory, so every .png source is replaced by its mask with exit 0. This is the Inputs sharing a base name (save.png + save.bmp) overwrite each other's output while the run reports both written; --output equal to --input overwrites the source icons #120 failure, reachable again.
  3. The reverse on a case-sensitive APFS volume. -i Icons -o icons is rejected, although they are two different directories. Inputs A.png and a.png are reported as a collision, and the run exits 2.

I could not mount a case-insensitive filesystem in the sandbox, so these are traced from the code rather than reproduced.

Suggested fix / acceptance criteria

  • Decide "same directory" by filesystem identity, not by string: device and inode via stat on Unix, or volume serial plus file ID on Windows. This would also cover --output that is a symlink to the --input folder passes validation and overwrites the source icons (exit 0) #157 (a symlinked --output), which has the same root cause. Resolving symlinks alone would not catch case aliases.
  • Decide output collisions from the output folder's actual behaviour. Either probe it once (write a temp file, then check whether its case-swapped name exists), or refuse to write over a file this run has already written, compared by identity.
  • Add tests for a case-alias output directory and for inputs whose names differ only in case. Where the CI host cannot provide a case-insensitive mount, an injectable comparer or probe is fine.

Related: #120 (closed, the original overwrite fix) and #157 (symlinked output, the directory half only).

Activity

  1. matt-edmondson commented on Nov 2, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    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