How to make NTFS case sensitive

NTFS默认是不区分大小写,但是有时候当我们从其他系统(Linux或者压缩包)拿到文件后,可能需要处理大小写不同的同名文件,具体配置方法如下:

  • 编辑注册表
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel]
"obcaseinsensitive"=dword:00000000
  • 重启系统

  • 测试同名文件共存

打开Ubuntu on Windows (WSL),创建README和readme两个文件

$ echo "Hello" > README
$ echo "Windows NTFS" > readme
$ ls -l
total 0
-rwxrwxrwx 1 ken ken 13 Jul  3 18:40 readme
-rwxrwxrwx 1 ken ken  6 Jul  3 18:34 README

$ cat README
Hello
$ cat readme
Windows NTFS

可以看出NTFS在WSL中已经可以正确使用同名文件。

  • 用Windows Powershell命令行检查
$ dir
    Directory: D:\Temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018-07-03  06:34 PM              6 README
-a----       2018-07-03  06:40 PM             13 readme

$ cat README
Hello
$ cat readme
Hello

可以发现Powershell不能正确处理同名文件的内容,尽管可以正确的列举出来。

  • 再试试Windows CMD
$ dir
 Volume in drive D is DATA
 Volume Serial Number is 648D-91A7

 Directory of D:\Temp

...
2018-07-03  06:34 PM                 6 README
2018-07-03  06:40 PM                13 readme
               2 File(s)             19 bytes
               2 Dir(s)  ? bytes free

$ type README
Hello
Hello

$ type readme
Hello
Hello

同样的,与Powershell一样CMD可以区分同名文件,但是不能正确处理其内容。

MSDN上关于CreateFile有如下参数:

FILE_FLAG_POSIX_SEMANTICS 0x0100000

Access will occur according to POSIX rules. This includes allowing multiple files with names, differing only in case, for file systems that support that naming. Use care when using this option, because files created with this flag may not be accessible by applications that are written for MS-DOS or 16-bit Windows.

直觉判断,CMD和Powershell的这个错误应该是没有传入该参数FILE_FLAG_POSIX_SEMANTICS导致,为了验证这个判断,用下面的小程序测试一下:

#include <windows.h>
#include <iostream>
#include <string>
 
using namespace std;
 
std::string get_content(HANDLE handle)
{
    const int BUFSIZE = 100;
    char buffer[BUFSIZE];
    DWORD nRead = 0;
    memset(buffer, 0, BUFSIZE);
    ReadFile(handle, buffer, BUFSIZE - 1, &nRead, NULL);
    return buffer;
}
 
void main()
{
    auto h1 = CreateFile(
        "readme", 
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
     
    auto h2 = CreateFile(
        "readme", 
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_POSIX_SEMANTICS,
        NULL);
 
    cout << "Content of readme (using FILE_ATTRIBUTE_NORMAL):\n'" << get_content(h1) << "'" << endl;
    cout << "Content of readme (using FILE_FLAG_POSIX_SEMANTICS):\n'" << get_content(h2) << "'" << endl;
     
    CloseHandle(h1);
    CloseHandle(h2);
}

运行一下:

$ ./test.exe
Content of readme (using FILE_ATTRIBUTE_NORMAL):
'Hello
'
Content of readme (using FILE_FLAG_POSIX_SEMANTICS):
'Windows NTFS
'

上述测试代码验证了同名文件的内容可以被正确读取,前提是使用了正确的参数。