四。目录对象结构(ObpLookupDirectoryEntry、ObpLookupObjectName) (二)

四。目录对象结构(ObpLookupDirectoryEntry、ObpLookupObjectName) (二)
2010年05月12日 星期三 10:11

看过了查找指定目录下是否有指定名称的对象的函数后
再来看看一个功能更强大的函数ObpLookupObjectName
它可以根据路径来查找对象, 路径里还可以包含符号链接等。。。总之很复杂很强大。。。

NTSTATUS
ObpLookupObjectName (
    IN HANDLE RootDirectoryHandle OPTIONAL,       // 父目录句柄
    IN PUNICODE_STRING ObjectName,                // 路径
    IN ULONG Attributes,                          // 属性
    IN POBJECT_TYPE ObjectType,                   // 对象类型
    IN KPROCESSOR_MODE AccessMode,               
    IN PVOID ParseContext OPTIONAL,               // 传递给_OBJECT_TYPE_INITIALIZER.ParseProcedure 的参数
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, // 不管....
    IN PVOID InsertObject OPTIONAL,
    IN OUT PACCESS_STATE AccessState,
    OUT POBP_LOOKUP_CONTEXT LookupContext,        // 查询对象使用的结构, 查询详细信息通过它返回
    OUT PVOID *FoundObject                        // 找到的对象体
    ) {
    .....
   
    // 初始化
    ObpInitializeLookupContext(LookupContext);
    *FoundObject = NULL;
    Status = STATUS_SUCCESS;
    Object = NULL;
   
    // 全局的ObpCaseInsensitive 和 对象类型决定了对象是否区分大小写
    if ( ObpCaseInsensitive ) {
        if ( (ObjectType == NULL) ||
             (ObjectType->TypeInfo.CaseInsensitive)
           ) {
            Attributes |= OBJ_CASE_INSENSITIVE;
        }
    }
   
    // 指定了OBJ_FORCE_ACCESS_CHECK属性后要强制检测参数, 所以AccessCheckMode设置为UserMode
    if (Attributes & OBJ_FORCE_ACCESS_CHECK) {
        AccessCheckMode = UserMode;
    } else {
        AccessCheckMode = AccessMode;
    }
   
    if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
        ....
        // 这里处理RootDirectoryHandle不为空的情况, 先略过
    }
    else {
        // 没有指定RootDirectoryHandle, 上级目录是根目录
        RootDirectory = ObpRootDirectoryObject;
       
        // 以为没有指定上级目录, 对象名要合法必须以 '/' 开头
        if ((ObjectName->Length == 0) ||
            (ObjectName->Buffer == NULL) ||
            (*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR)) {
            return( STATUS_OBJECT_PATH_SYNTAX_BAD );
        }
       
        if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
            // 对象名是 '/' 表示打开根目录, 暂时略过
            ......
        }
        else {
            // 如果不是打开根目录或指定了上级目录, 这里不会成立的, 略...
ParseFromRoot:
            if (DeviceMap != NULL) {
                ObfDereferenceDeviceMap(DeviceMap);
                DeviceMap = NULL;
            }
           
            // 如果ObjectName->Buffer 是以8字节为分配粒度的
            // 应该检测对象名是否在 /??/ 目录下, 我们需要重定位它到 /GLOBAL??/ 目录
            // 不过为什么要根据分配力度决定是否检测??????
            if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))) {
                // 如果对象目录是 /??/
                if ((ObjectName->Length >= ObpDosDevicesShortName.Length) &&
                    (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
                   
                    // 获取设备表
                    if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
                        if (DeviceMap->DosDevicesDirectory != NULL ) {
                           
                            // 设置父目录是根目录, 当前目录是 /GLOBAL??/ 更新对象名 开始解析
                            ParentDirectory = RootDirectory;
                            Directory = DeviceMap->DosDevicesDirectory;
                            RemainingName = *ObjectName;
                            RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
                            RemainingName.Length = (USHORT)(RemainingName.Length - ObpDosDevicesShortName.Length);

                            goto quickStart;

                        }
                    }
                }
                // 对象名是/?? 说明它打开的是GLOBAL?? 目录
                else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof( WCHAR )) &&
                         (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
                         (*((PWCHAR)(ObjectName->Buffer)+2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) {
                   
                    // 我们直接返回 GLOBAL?? 目录给用户就可以了
                    if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
                        if (DeviceMap->DosDevicesDirectory != NULL ) {
                            Status = ObReferenceObjectByPointer( DeviceMap->DosDevicesDirectory,
                                                                 0,
                                                                 ObjectType,
                                                                 AccessMode );
                            if (NT_SUCCESS( Status )) {
                                *FoundObject = DeviceMap->DosDevicesDirectory;
                            }
                            ObfDereferenceDeviceMap(DeviceMap);
                            return( Status );
                        }
                    }
                }
            } // /??/到/GLOBAL??/ 的转换处理完毕
        }
    } // 没有指定RootDirectoryHandle, 上级目录是根目录 处理完毕
   
    // 晕了吧... 幸好WRK在这里有个注释
    //
    // At this point either (在这里会有三种情况)
    //
    // (这个没看懂....)
    // the user specified a directory that is not the object
    // type directory and got reparsed back to the root directory
    //
    // (用户指定了父目录, 并且给定了一个路径用作查找)
    // the user specified the object type directory and gave us
    // a name to actually look up
    //
    // (用户没有指定父目录, 并且给定的路径不是以/??/开头的。)
    // (以/??/开头的路径会直接跳到下面的quickStart, 或直接返回)
    // the user did not specify a search directory (default
    // to root object directory) and if the name did start off
    // with the dos device prefix we've munged outselves back to
    // it to the dos device directory for the process
    //
   
    // 第一次是执行到这里时ReparsedSymbolicLink一定为FALSE
    // 之所以会再到这里是因为下面的某些代码使用了goto ParseFromRoot ....烂风格
    if( ReparsedSymbolicLink == FALSE ) {
        Reparse = TRUE;
        MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
    }
   
    while(Reparse) {
       // 这才是这个函数的精髓
       ......
    }
   
    // 一些资源释放
    return Status;
}

///华丽的分割线//

ObpLookupObjectName函数的逻辑太复杂了, 里面又goto来goto去的很乱, 所以我们把解析的部分提取出来。
while 这个循环里面的东西才是我们想学的, 下面仔细看看这个循环

        while (TRUE) {
            Object = NULL;
            // /aaaa/bbbbb/cccc
            // 路径起始为 '/' 跳过这个字符 变为 aaaa/bbbbb/cccc
            if ( (RemainingName.Length != 0) &&
                 (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) ) {

                RemainingName.Buffer++;
                RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
            }
           
            // 分离出第一个对象名
            // ComponentName指向aaaa RemainingName指向/bbbbb/cccc
            ComponentName = RemainingName;
            while (RemainingName.Length != 0) {
                if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
                    break;
                }
                RemainingName.Buffer++;
                RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
            }
            ComponentName.Length = (USHORT)(ComponentName.Length - RemainingName.Length);
           
            // 非法文件名
            if (ComponentName.Length == 0) {
                Status = STATUS_OBJECT_NAME_INVALID;
                break;
            }
           
            // 如果当前文件名为空说明是根目录
            if ( Directory == NULL ) {
                Directory = RootDirectory;
            }
           
            if ( (AccessCheckMode != KernelMode) &&
                 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
               // 略过权限检测
               ......
            }
          
          
            // 这里注释写的比较清楚了
            if (RemainingName.Length == 0) {

                //
                // If we are searching the last name, take an additional reference
                // to the directory. We need this to either insert a new object into
                // this directory, or to check for traverse access if an insert object
                // is not specified. If not referenced the directory could go
                // away since ObpLookupDirectoryEntry releases the existing reference
                //
                if (ReferencedDirectory == NULL) {

                    ObReferenceObject( Directory );
                    ReferencedDirectory = Directory;
                }

                if (InsertObject != NULL) {
                    //
                    // We lock the context exclusively before the lookup if we have an object
                    // to insert. An insertion is likely to occur after this lookup if it fails,
                    // so we need to protect this directory to be changed until the
                    // ObpInsertDirectoryEntry call
                    //
   
                    ObpLockLookupContext( LookupContext, Directory );
                }
            }
           
            // 从Directory(当前目录)里查找名字为ComponentName的对象
            Object = ObpLookupDirectoryEntry( Directory,
                                              &ComponentName,
                                              Attributes,
                                              InsertObject == NULL ? TRUE : FALSE,
                                              LookupContext );
          
           if (!Object) {
               // 如果对象不存在, 并且用户指定了InsertObject, 这里面把InsertObject插入到目录里
               // 注意只有当前已经没有剩余路径时才插入
               // 之后直接退出 代码很简单 不过这里的逻辑已经够复杂了   略过
               .......
           }
          
           
ReparseObject:
            // 找到了路径中的对象aaaa 获得它的ParsePrecedure
            ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
            ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
           
            if (ParseProcedure && (!InsertObject || (ParseProcedure == ObpParseSymbolicLink))) {
                // 参考一下ObInitSystem可以发现其实目录对象是没有ParseProcedure的
                // 如果存在ParseProcedure并且不需要插入对象 或者
                // 存在ParseProcedure并且当前对象是一个符号链接
                // 直接调用ParsePrecedure并把剩余的路径传递进去 然后就结束了
                // 略过
                ......
            }
            else {
                // 如果当前对象是目录就会到这里
               
                // 已经到了路径的尽头, 说明已经解析完成了, 返回当前的Object
                if (RemainingName.Length == 0) {
                    if (!InsertObject) {
                        if ( (AccessCheckMode != KernelMode) &&
                             !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
                            // 权限检测
                            .....
                        }
                        Status = ObReferenceObjectByPointer( Object,
                                                             0,
                                                             ObjectType,
                                                             AccessMode );
                        if (!NT_SUCCESS( Status )) {
                            Object = NULL;
                        }
                    }
                    break;

                } else {
                    // 如果当前对象是个目录, 继续循环查找
                    if (ObjectHeader->Type == ObpDirectoryObjectType) {
                        if (ReferencedParentDirectory != NULL) {
                            ObDereferenceObject( ReferencedParentDirectory );
                        }
                        ReferencedParentDirectory = ReferencedDirectory;
                        ReferencedDirectory = NULL;
                        ParentDirectory = Directory;
                        Directory = (POBJECT_DIRECTORY)Object;
                    } else {
                    // 不是目录就出错
                        Status = STATUS_OBJECT_TYPE_MISMATCH;
                        Object = NULL;
                        break;
                    }
                }
            }
        }

到这里while循环也结束了。
总结一下ObpLookupObjectName函数的功能
1。根据指定路径搜索对象, 可以处理开头为/??/、包含符号链接等特殊路径
2。若路径中的某个对象不是目录, 则调用它的ParseProcedure并把剩余路径名传递进去 (注册表、文件路径的解析就是这方法)
3。InsertObject参数可以用来在目录中插入一个对象, 但全路径必须都是目录, 最后一个对象是InsertObject插入后的名字。(如 aaa/bbb/ccc aaa、bbb都是目录对象 ccc不存在, 若指定了InsertObject, 这个对象便会插入到aaa/bbb/中, 名字叫ccc)

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值