Understanding case sensitivity in Windows: obcaseinsensitive, FILE_CASE_SENSITIVE_SEARCH

Written by Nick Lowe on February 21, 2012 Categories: Insight

It is a seldom known fact that, as of the NT branch, Windows supports case sensitivity. Using it, however, is non-obvious, requiring knowledge of the design and inner workings of the operating system.

Beneath the Win32 environment subsystem that most programmers are familiar with lies the NT Executive. It is accessed via the NT Native API and, at this level, programmers must be familiar with the OBJECT_ATTRIBUTES structure that is used when creating or opening executive objects. It is a knowledge of this structure, and how the Win32 subsystem uses it, that is required to truly understand case handling in Windows.

Examples of such objects are Files, Registry Keys, I/O Completion Ports, Events, Jobs, KeyedEvents, Mutants (Mutexs), Object Directories, Processes, Sections, Semaphores, Symbolic Links, Threads and Timers.

When it comes to case sensitivity, however, most users will be concerned with only one object type, Files.

The OBJECT_ATTRIBUTES structure, as documented, “specifies attributes that can be applied to objects or object handles by routines that create objects and/or return handles to objects“.

One of its members is the Attributes bitmask that specifies the attributes for the handle to an object.

The flag within this that is of interest is the OBJ_CASE_INSENSITIVE flag.
It is documented as follows:

If this flag is specified, a case-insensitive comparison is used when matching the name pointed to by the ObjectName member against the names of existing objects. Otherwise, object names are compared using the default system settings.

This means that when the flag is specified, case insensitive operation is guaranteed, however, when it is not, it is left to the system to decide.

This documentation is arguably incomplete as it does not state what the ‘default system settings’ are, or how to control them.

It turns out that there are two parts to this. Firstly, a single setting that controls case sensitivity on system-wide basis, obcaseinsensitive, and then, assuming that case sensitive operation is supported, file system support on a per-volume basis.

The obcaseinsensitive value is located in the registry at “\REGISTRY\MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel” and is a DWORD. It is read only as a system boots.
(In Win32 nomenclature, the path is “HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel”.)

  • When set to 0, the object manager runs in case sensitive mode.
  • When set to 1, the object manager runs in case insensitive mode.
  • When unspecified, NT 5.1 (Windows XP) and later editions default to running in case insensitive mode.
  • obcaseinsensitive has no meaning in NT 5.0 (Windows 2000) and prior versions of NT, which always run in case sensitive mode.

Case insensitive mode is only implemented for certain object types and not for all. These are Object Directories (NtCreateDirectoryObject, NtOpenDirectoryObject), Symbolic Links (NtCreateSymbolicLinkObject and NtOpenSymbolicLinkObject) and I/O objects, such as Files (NtCreateFile, NtOpenFile) and I/O Completion Ports (NtCreateIoCompletion, NtOpenIoCompletion).

At the Win32 environment subsystem, the only exposure to case sensitivity is for File objects and via the following APIs:

  • CreateFile / CreateFileTransacted and the FILE_FLAG_POSIX_SEMANTICS flag, which ensures that the OBJ_CASE_INSENSITIVE flag is not set in the OBJECT_ATTRIBUTES structure used in internal calls to NtCreateFile.
  • FindFirstFileEx / FindFirstFileTransacted and the FIND_FIRST_EX_CASE_SENSITIVE flag, which ensures that the OBJ_CASE_INSENSITIVE flag is not set in the OBJECT_ATTRIBUTES structure used in internal calls to NtCreateFile to open a directory file before subsequent calls to NtQueryDirectoryFile.

(It is worth noting that most existing Win32 applications will not set the FILE_FLAG_POSIX_SEMANTICS flag or the FIND_FIRST_EX_CASE_SENSITIVE flag when calling these APIs, or will rely on other APIs that internally do not. Because of this, the OBJ_CASE_INSENSITIVE flag will always end up being set by the Win32 subsystem in the OBJECT_ATTRIBUTES structures that it creates to call NT Native APIs. This always enforces insensitivity for such applications.)

Official documentation from Microsoft for obcaseinsensitive is sparse and patchy:

The value is also managed by group policy, where the best explanation given by Microsoft can also be found:

This security setting determines whether case insensitivity is enforced for all subsystems. The Win32 subsystem is case insensitive. However, the kernel supports case sensitivity for other subsystems, such as POSIX.

If this setting is enabled, case insensitivity is enforced for all directory objects, symbolic links, and IO objects, including file objects. Disabling this setting does not allow the Win32 subsystem to become case sensitive.

There are times that applications need to determine if a Windows system is running with case insensitivity, perhaps for path comparison. A naive implementation will simply query the obcaseinsensitive value from the registry, but this has drawbacks:

  1. The value only represents how the NT executive’s object manager will operate at the next boot, not necessarily how the system is right now.
  2. NT 5.0 (Windows 2000) and prior, if supported, must be special cased as case insensitivity is not supported. Further, the obcaseinsensitive value may be present in the registry even though it is meaningless.
  3. Future versions of Windows may decide to implement things differently, such as in a more granular way. (Perhaps on a per-thread or per-process basis via a privilege.)

The solution is to use NtOpenSymbolicLinkObject to attempt to open the known symbolic link, “SystemRoot”, which resides in the root directory of the object namespace with incorrect casing.

By attempting to open “\SYSTEMROOT” with a DesiredAccess of 0×0, which means no access rights to the object, and with no flags specified in the attributes member of the OBJECT_ATTRIBUTES structure passed, the current operational mode can be easily and efficiently determined.

  • Where the object manager is running in case insensitive mode, a NTSTATUS value of STATUS_ACCESS_DENIED will be returned.
  • Where the object manager is running in case sensitive mode, a NTSTATUS value of STATUS_OBJECT_NAME_NOT_FOUND will be returned.

(If the SYMBOLIC_LINK_QUERY specific access right (0×1) is requested, the open operation will succeed if the user is permitted to open the symbolic link via its DACL. This is the case typically only if the user is an Administrator. The operation will fail and STATUS_ACCESS_DENIED will be returned otherwise.)

Redhat’s Corinna Vinschen of Cygwin fame pointed out that the same check can also be achieved via NtOpenDirectoryObject in much the same way.

  • Where the object manager is running in case insensitive mode, a NTSTATUS value of STATUS_OBJECT_TYPE_MISMATCH will be returned.
  • Where the object manager is running in case sensitive mode, a NTSTATUS value of STATUS_OBJECT_NAME_NOT_FOUND will be returned.

(Cygwin has had a patch committed to its source code to ensure that it now tests for case sensitivity rather than reading the obcaseinsensitive value from the registry.)

As mentioned previously, once support for case sensitivity system-wide has been established, the ability to use files in a case sensitive way is then determined by the file system in use on a per-volume basis.

This is established by the presence of the FILE_CASE_SENSITIVE_SEARCH file system attribute flag for a volume, documented as meaning that “the specified volume supports case-sensitive file names”.

File system attributes for a volume are queried via either:

No Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>