<2012年5月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

文章分类

导航

订阅

5月25日小品一则

MSDN 里有一个文件操作相关的例程叫做 MoveFileEx。这个例程的一段声明引起了我的注意:

Windows 2000 : If you specify the MOVEFILE_DELAY_UNTIL_REBOOT flag for dwFlags,
you cannot also prepend the filename that is specified by lpExistingFileName with "\\?".

由于我们往往不能假定调用者的输入,\\?\ 前缀也属基本需求(且为什么只是 lpExistingFileName 不让加前缀),我便很好奇这则声明的深层次原因。

可以想见的是,由于 Delayed Operations 操作时网络还不可用,所以 MoveFile 的操作源不应属于 remote 类型。这一点泄露的 Win-2K 源码交代的也很清楚:

        ......

        //
        // Check to see if the existing file is on a remote share. If it
        // is, flag the error rather than let the operation silently fail
        // because the delayed operations are done before the net is
        // available. Rather than open the file and do a hard core file type,
        // we just check for UNC in the file name. This isn't perfect, but it is
        // pretty good. Chances are we can not open and manipulate the file. That is
        // why the caller is using the delay until reboot option !
        //

        if ( RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute ) {
                Status = STATUS_INVALID_PARAMETER;
        }

        ......


那么,微软人是如何判断操作源属于 remote share 的呢?用他们自己的话说是调用了“pretty”的 RtlDetermineDosPathNameType_U 函数。

RtlDetermineDosPathNameType_U 函数位于 ntdll 目录的 curdir.c 文件,判断的依据很简单,读取路径的前几个字节就够了:

RTL_PATH_TYPE
RtlDetermineDosPathNameType_U (
        IN PCWSTR DosFileName
        )
{
        RTL_PATH_TYPE ReturnValue;

        if ( IS_PATH_SEPARATOR_U(*DosFileName) ) {
                if ( IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
                        if ( DosFileName[2] == '.' ) {
                                if ( IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
                                        ReturnValue = RtlPathTypeLocalDevice;
                                        }
                                else if ( (*(DosFileName+3)) == UNICODE_NULL ){
                                        ReturnValue = RtlPathTypeRootLocalDevice;
                                        }
                                else {
                                        ReturnValue = RtlPathTypeUncAbsolute;
                                        }
                                }
                        else {
                                ReturnValue = RtlPathTypeUncAbsolute; // 可惜的是,这里存在 Bug
                                }
                        }
                else {
                        ReturnValue = RtlPathTypeRooted;
                        }
                }
        else if (*DosFileName && *(DosFileName+1)==L':') {
                        if (IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
                                ReturnValue = RtlPathTypeDriveAbsolute;
                                }
                        else {
                                ReturnValue = RtlPathTypeDriveRelative;
                                }
                        }
        else {
                ReturnValue = RtlPathTypeRelative;
                }

        return ReturnValue;
}


可惜的是,作者 Mark Lucovsky 前辈把情况想简单了。他认为两个 '\' (或'/') 符之后如果不跟 '.' 号的路径必定就是 UNC,可事实是 "\\?\" 被误判了。

路径 \\?\C:\A.txt 等 会被错误的认为是 RtlPathTypeUncAbsolute,进而 MoveFileEx 例程拒绝了这类参数 —— STATUS_INVALID_PARAMETER。

呵~ Mark Lucovsky 前辈也犯错误了...

这个 API 错误 Windows 2000 SP4 补丁都未予解决,类似的问题还存在于 Windows 2000 的 CreateHardLinkW 等例程内部。

(Mark Lucovsky 前辈在 26-Sep-1990 写过一个文件的测试小程序:base\client\tfile.c,看来案例的强度还不够,呵呵)

终于,Windows XP 解决了这个问题。“pretty”的 RtlDetermineDosPathNameType_U 函数被重写 —— 专门加入了 \\?\ 情况的判断,感兴趣您可以逆向对比。

而可爱的 ReactOS 团队依旧“沿袭”了这个 Bug:

http://doxygen.reactos.org/d9/d6e/lib_2rtl_2path_8c_source.html

也许他们还没有意识到“Bug 侵权” :P

回过头再看 MSDN,连则 Bug 的解释写的都和 “特性” 似的,险些被唬住...

霸气啊,微软。



keenjoy95
2011年5月25日18:21:56


posted on 2011年5月25日 18:22 由 WANGyu

# re: 5月25日小品一则 @ 2011年5月27日 10:12

Good catch!
看了下6.1.7600.16385,的确如此:
ntdll!RtlDetermineDosPathNameType_U+0x5d:
7708fb53 6683f93f cmp cx,3Fh ;; '?'
7708fb57 0f8452170000 je ntdll!RtlDetermineDosPathNameType_U+0x68 (770912af)

Raymond

# re: 5月25日小品一则 @ 2011年5月27日 18:21

所以 2000 的源码也不能随便“Copy”,除非一直很清楚它的设计。:)

WANGyu

Powered by Community Server Powered by CnForums.Net