Per-directory case sensitivity and WSL

If you have used the Windows Subsystem for Linux, you’re probably aware that it allows you to treat your Windows file systems (mounted under /mnt/c, /mnt/d, etc.) as case sensitive. This means, among other things, that you can create files whose names differ only by case (e.g. foo.txt and FOO.TXT).

However, using those files in Windows was not really possible. Since Windows applications treat the file system as case insensitive, they cannot distinguish between files whose names only differ in case. While File Explorer would show both files, only one would be opened regardless of which one you clicked.

Starting with Windows insider build 17093, we’ve introduced a new way to handle case sensitive files in Windows: per-directory case sensitivity. We use this ability in the Windows Subsystem for Linux to give you better interoperability when using case sensitive files, and you can also use it yourself with regular Windows applications. As of insider build 17110, this behavior is the default.

Case sensitivity in Windows

The Windows NT family of operating systems (including Windows 10) has always had the ability to perform case sensitive file system operations. Applications can pass the FILE_FLAG_POSIX_SEMANTICS flag to the CreateFile API to indicate that they want the path to be treated as case sensitive. However, for compatibility reasons, there is a global registry key that overrides this behavior; when this key is set, all file operations are case insensitive, even when the FILE_FLAG_POSIX_SEMANTICS flag is specified. Since Windows XP, this has been the default.

The Windows Subsystem for Linux uses another mechanism, which itself bypasses that registry key, allowing us to perform case sensitive file system operations. This is what allows Linux applications running in WSL to use file names that differ only by case, just like they can on real Linux, even with that global registry key set.

Unfortunately, this leaves you with files that can’t be accessed by Windows applications. While you could change the global registry key, that still would only work for those applications that use FILE_FLAG_POSIX_SEMANTICS, and this would change the behavior for all files on all drives, which may not be intended and may break some applications.

Per-directory case sensitivity

To solve this problem, we added a new case sensitive flag that can be applied to directories. For directories that have this flag set, all operations on files in that directory are case sensitive, regardless of whether FILE_FLAG_POSIX_SEMANTICS was specified. This means that if you have two files that differ only by case in a directory marked as case sensitive, all applications will be able to access them.

In Windows insider build 17107, we have added the ability to view and modify this flag to the fsutil.exe command. To check if a directory is case sensitive, run the following command:
fsutil.exe file queryCaseSensitiveInfo <path>

To mark a directory as case sensitive, or case insensitive respectively:
fsutil.exe file setCaseSensitiveInfo <path> enable
fsutil.exe file setCaseSensitiveInfo <path> disable

Note that the per-directory case sensitivity flag is not inherited; directories created in a case sensitive directory are not automatically case sensitive themselves. You must explicitly mark each directory as case sensitive. Changing the flag requires “write attributes” permission to the directory.

fsutil demo

Per-directory case sensitivity in WSL

The Windows Subsystem for Linux makes use of the per-directory case sensitivity flag to improve its interoperability with the Windows file systems mounted under /mnt/c, /mnt/d, etc. We have several new case sensitivity modes, which can be controlled with the “case” mount option for DrvFs. This option controls which directories are treated as case sensitive, and whether new directories created with WSL will have the flag set. There are three values: dir, off, and force. Their behavior is listed in the table below.

case=dir case=off case=force
Directory with flag enabled Case sensitive Case sensitive Case sensitive
Directory with flag disabled Case insensitive Case insensitive Case sensitive
Flag on directories created in WSL Enabled Disabled Enabled

Essentially, directories with the case sensitivity flag set are always treated as case sensitive. Directories without the flag set are treated as case insensitive, unless you’re using case=force. New directories created by DrvFs get the flag set unless you’re using case=off.

Using case=force is equivalent to WSL’s old behavior (except for the fact that the flag gets set on new directories).

To mount DrvFs with a specific case sensitivity setting, use /etc/wsl.conf to set the default mount options for all drives, or /etc/fstab to specify options for a specific drive.

Not all file systems support per-directory case sensitivity; currently, it only works on local NTFS drives. To make sure your drive was successfully mounted with the desired case sensitivity option, use the mount command and see if the option was applied.

mount command output

Using per-directory case sensitivity has a number of advantages. Not only can you create files that differ by case and have them accessible in Windows, you can also create case-differing files in Windows in those directories. And, since all other directories on your system are not marked case sensitive, you don’t need to care about case sensitivity from within WSL when accessing common Windows files. This means for example that you can launch applications using interop without matching the exact case of the  executable file (e.g. running “NOTEPAD.EXE” now works in WSL the same  as it does in Windows). NT symlinks whose targets don’t use the exact case of the actual file now also work  in WSL.

Notepad demo

If you need to create a case insensitive directory while using “case=dir” or “case=force”, or want to create a case sensitive directory while using “case=off”, you can simply use fsutil.exe after creating the directory. You can, of course, invoke fsutil.exe from WSL using interop.

Note: if you change the case sensitive flag on an existing directory while WSL is running, please make sure WSL has no references to that directory. That means no WSL processes may have that directory, or any of its descendants, open. That includes using that directory, or its descendants, as the current working directory. If you don’t, WSL continues to act like the directory has its old case sensitivity setting, which may lead to unexpected results.

Starting with build 17110, DrvFs’s default behavior is now equivalent to using “case=dir”. This means that any directories you created with WSL before build 17093 will not be treated as case sensitive anymore. To fix this, use fsutil.exe to mark your existing directories as case sensitive.

If you require the old behavior, you can still mount DrvFs using “case=force”, though we strongly recommend using per-directory case sensitivity going forward. As of build 17110, using “case=force” requires setting a registry key, which you can do by running the following command from an elevated command prompt:

reg.exe add HKLM\SYSTEM\CurrentControlSet\Services\lxss /v DrvFsAllowForceCaseSensitivity /t REG_DWORD /d 1

As you can see, per-directory case sensitivity improves interoperability between files created with WSL and Windows significantly. Let us know what you think in the comments!

Comments (14)

  1. fransv says:

    Nice to see this surfaced and it does help interop quite a bit.
    Would be great to see this flag supported by Windows commands throughout also (like xcopy, robocopy and explorer).
    Creating a copy of a directory with the flag enabled from command line or explorer currently does not set the flag on the destination directory.

  2. Can you explain how this flag is persisted? I take it the NTFS version hasn’t changed, so the options are limited. My guess would be attributes or extended attributes or a metafile analogous to $Extend\$Reparse.

    Will the respective functionality be documented in either the SDKs or WDKs in time?

    Last but not least, is there a special reason why the flag seemingly cares only about case-sensitivity? Wouldn’t it have been more logical to enable accessing other files as well, such as those whose name ends in a dot? Similarly my guess is that reserved file names such as CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9 could be created using WSL (they could be created in SUA, IIRC) but still not accessed from the Windows subsystem. Limiting the flag purely to case-sensitivity seems halfhearted (albeit a big step forward).

    PS: the term “NT symlinks” seems kind of ambiguous in the text. My first thought was “object manager!”, but in the context it seems to refer to file system symlinks, the kind implemented via reparse points.

    1. On the contrary, options are not limited at all; NTFS is very extensible. This new flag is simply stored in a previously unused bit in the MFT record. Nothing fancy. Downlevel NTFS won’t interpret the new flag, but nor does it cause any problems.

      Those reserved file names you listed aren’t just “forbidden”, they actually have meaning at the Win32 layer. For example CON is similar to /dev/tty, NUL is similar to /dev/null, etc. You wouldn’t want to just get rid of those and treat them as file names, as you would lose long-standing functionality. These can be created at the NT layer BTW, not just through WSL. It’s Win32 that treats them specially.

      I agree that “NT symlinks” is ambiguous. Sven is referring to file symlinks, implemented through reparse points like you said, first introduced in Vista.

      1. Thank you, Craig. I appreciate that you took the time to respond.

  3. Brian Ruff says:

    It is interesting how I found out about this and found this post. I had checked out something from a Subversion repository in WSL Ubuntu Bash. Then the CMake compiler detection, run from a cmd shell, was failing; which I tracked down to what seemed to be a case sensitivity issue and the compiler wasn’t able to create output files; using what appeared to be an all lower case path. I have not run down the specifics as what did what but worked around the issue by recursively setting that directory’s setCaseSensitiveInfo. This had me scratching my head for a bit.

    1. Thanks for reporting, but could we ask that you create/add-to issues in our GitHub where we actively triage & respond to issues more effectively than in blog comments? 😉

  4. Matt Waters says:

    I used WSL for git with a legacy compiler that always expected case-insensitivity. I think it makes file names all lower or all upper case at some point. I checked out an old branch which deleted some directories. The legacy compiler failed due to case sensitivity. I had no idea about this feature.

    Also, I did not just want to fix all new directories with fsutil. There were a lot of directories and I may have to change them on every checkout between branches.

    In case people with similar issues find this blog post first (like I did), I will give how I fixed the issue. I had to create /etc/wsl.conf within WSL. The following lines were added:

    options = case=off

    At least for me, I also had to restart my whole computer, not just WSL, to implement wsl.conf. Not sure why. The wsl.conf blog post just says to restart WSL, which didn’t work for me.

  5. Volune says:

    After reading this article, and setting up a new computer, I put case sensitivity on a few directories I though it would be convenient.
    At some point, I extracted a JDK in one of these directories, added it to the PATH. And I was surprised when the “java” command didn’t work.
    “where java” showed me the file. “java.exe” worked. But “java” didn’t.
    It took me some time to remember that this folder was case sensitive, and that Windows was trying to run “java.EXE” (uppercase extension).

    I didn’t find documentation to make Windows try “java.exe” with lowercase extension, so I set the bin directory as case insensitive.

    Hopefully this story will help someone.

    1. “I though it would be convenient” – famous last words 😉 😀

      Windows and many of the apps built for Windows have grown accustomed to the filesystem being case-insensitive. Randomly introducing case-sensitivity to the filesystem is likely to break A LOT of scripts, and apps that are not pathological about their filesystem casing, and/or which flagrantly .ToUpper() or .ToLower() at some point in their code.

      We STRONGLY recommend you only enable case sensitivity where absolutely necessary – usually when dealing with Git repo’s which originated on *NIX and which contain files that are differentiated only by case.

  6. How can Win32 applications detect case sensitive directories outside of running fsutil.exe programmatically and parsing the output?

    1. Hey Zoë: You can call NtQueryInformationFile(…) passing in FileCaseSensitiveInformation. If the output buffer’s flags == FILE_CS_FLAG_CASE_SENSITIVE_DIR, then case sensitivity is enabled.

  7. I think you should mention that in order to create files or directories whose names differ only in case, the parent directory must be case enabled, This applies even to files created by a linux shell command. Not knowing this has caused me a lot of grief in downloading stuff that has such names.

    1. The section under the “Per-directory case sensitivity in WSL” heading describes how to configure WSL to either honor, ignore, or force case sensitivity on folders it creates.

Skip to main content