Test files › Folders and paths › reserved-and-awkward-dirs.zip

Sample ZIP file (reserved and awkward dirs)

Directories named after Windows reserved devices, plus one ending in a dot and one ending in a space.

↓ Download reserved-and-awkward-dirs.zip
Filenamereserved-and-awkward-dirs.zip
FormatZIP
Size1.3 KB (1,282 bytes)
MIME typeapplication/zip
SHA-2567fab653ffe32d53a5ec3921dcc7c589e6e0a942e8f2de92cf3c7549483effb65
What this file catchesReserved names used as DIRECTORIES rather than files, which is the case most sanitisers forget, and the two do not even fail the same way. On Windows, creating a directory called CON fails outright with error 267. Creating one called NUL reports SUCCESS and creates nothing at all, so an extractor that only checks the return value believes it worked. Trailing dots and spaces are a third case: the call succeeds and Windows hands back the trimmed name, so "trailing-dot." and "trailing-dot" become one directory.

Handle it in code

Each snippet below was run against this exact file with Python 3.14.4 and Node 24.15.0 when this page was built, and the output shown is what it printed.

Python

Check an archive's paths before extracting it on another system. Windows reserves device names, drops trailing dots and spaces, and treats \ as a separator; Windows and macOS compare names without case, so two folders that differ only in case merge. Use orig_filename: on Windows, zipfile quietly turns backslashes into slashes.

import unicodedata
import zipfile

RESERVED = {"CON", "PRN", "AUX", "NUL"} | {f"{d}{i}" for d in ("COM", "LPT") for i in range(1, 10)}
LONG = 200  # leaves room for the folder it is extracted into, under Windows' classic 260

def problems(names):
    paths = {}  # every entry and every parent folder, in order
    for name in names:
        parts = name.rstrip("/").split("/")
        for i in range(len(parts)):
            paths.setdefault("/".join(parts[:i + 1]), parts[i])
    for path, part in paths.items():
        if "\\" in part:
            yield f"{path!r}: a backslash is a folder separator on Windows"
        if part in ("", "."):
            yield f"{path!r}: empty or '.' segment"
        elif part.split(".")[0].upper() in RESERVED:
            yield f"{path!r}: {part} is a reserved device name on Windows"
        elif part != part.rstrip(". "):
            yield f"{path!r}: ends in a dot or space, which Windows drops"
        if unicodedata.normalize("NFC", part) != part:
            yield f"{ascii(path)}: not NFC-normalised, so it may not match the same name typed elsewhere"
    groups = {}
    for path in paths:
        groups.setdefault(unicodedata.normalize("NFC", path).casefold(), []).append(path)
    for same in groups.values():
        if len(same) > 1:
            yield " and ".join(map(repr, same)) + ": the same path on Windows and macOS"
    longest = max(names, key=len, default="")
    if len(longest) > LONG:
        yield f"longest entry is {len(longest)} characters: extracted into any folder, it passes the 260-character limit"

with zipfile.ZipFile("reserved-and-awkward-dirs.zip") as z:
    names = [info.orig_filename for info in z.infolist()]
found = list(problems(names))
print(f"{len(names)} entries: " + (f"{len(found)} portability problem{'s' * (len(found) != 1)}" if found else "portable"))
for p in found:
    print("  " + p)

Run against this file, it prints: 10 entries: 4 portability problems 'CON': CON is a reserved device name on Windows 'NUL': NUL is a reserved device name on Windows 'trailing-dot.': ends in a dot or space, which Windows drops 'trailing-space ': ends in a dot or space, which Windows drops

Related guides and tools

Sample ZIP fileFilename compatibilityFile systemsCheck a file's real typeVerify its SHA-256

Download & verify

curl -fsSL -o "reserved-and-awkward-dirs.zip" "https://files.hexaqa.com/folder/reserved-and-awkward-dirs.zip"
curl -fsSL "https://files.hexaqa.com/folder/reserved-and-awkward-dirs.zip" | sha256sum # expect: 7fab653ffe32d53a5ec3921dcc7c589e6e0a942e8f2de92cf3c7549483effb65

More folders and paths test files

case-collision-dirs.zipdeep-path-over-260.zipempty-directory.zipexplicit-dir-entries.zipseparator-confusion.zipunicode-dir-names.zip

← All Folders and paths files