https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f6eb13a969e5108f0e58bc71473f04ab4dce399f
commit f6eb13a969e5108f0e58bc71473f04ab4dce399f Author: Pierre Schweitzer <pie...@reactos.org> AuthorDate: Tue Jun 11 21:18:22 2019 +0200 Commit: Pierre Schweitzer <pie...@reactos.org> CommitDate: Tue Jun 11 21:21:52 2019 +0200 [NTOSKRNL] Rework ObpDeleteSymbolicLinkName and ObpCreateSymbolicLinkName So that they handle LUID mappings and process device maps. Get rid of the ObpParseSymbolicLinkToIoDeviceObject helper and introduce a new helper ObpProcessDosDeviceSymbolicLink that will do the same things but also handle name creation/deletion as well as device map handling. All this is based on previous code (hence the same comments :-)). What's left to do now is to add support for device maps in ObpLookupObjectName --- ntoskrnl/ob/oblink.c | 499 +++++++++++++++++++++++++++++---------------------- 1 file changed, 283 insertions(+), 216 deletions(-) diff --git a/ntoskrnl/ob/oblink.c b/ntoskrnl/ob/oblink.c index f8fbe5ad500..712143e1ed7 100644 --- a/ntoskrnl/ob/oblink.c +++ b/ntoskrnl/ob/oblink.c @@ -20,288 +20,355 @@ POBJECT_TYPE ObpSymbolicLinkObjectType = NULL; /* PRIVATE FUNCTIONS *********************************************************/ VOID -NTAPI -ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink) +ObpProcessDosDeviceSymbolicLink(IN POBJECT_SYMBOLIC_LINK SymbolicLink, + IN BOOLEAN DeleteLink) { + PDEVICE_MAP DeviceMap; + UNICODE_STRING TargetPath, LocalTarget; + POBJECT_DIRECTORY NameDirectory, DirectoryObject; + ULONG MaxReparse; + OBP_LOOKUP_CONTEXT LookupContext; + ULONG DriveType; POBJECT_HEADER ObjectHeader; POBJECT_HEADER_NAME_INFO ObjectNameInfo; + BOOLEAN DirectoryLocked; + PVOID Object; - /* FIXME: Need to support Device maps */ + /* + * To prevent endless reparsing, setting an upper limit on the + * number of reparses. + */ + MaxReparse = 32; + NameDirectory = NULL; /* Get header data */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); - ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); - - /* Check if we are not actually in a directory with a device map */ - if (!(ObjectNameInfo) || - !(ObjectNameInfo->Directory) /*|| - !(ObjectNameInfo->Directory->DeviceMap)*/) - { - ObpDereferenceNameInfo(ObjectNameInfo); - return; - } + ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); - /* Check if it's a DOS drive letter, and remove the entry from drive map if needed */ - if (SymbolicLink->DosDeviceDriveIndex != 0 && - ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR) && - ObjectNameInfo->Name.Buffer[1] == L':' && - ( (ObjectNameInfo->Name.Buffer[0] >= L'A' && - ObjectNameInfo->Name.Buffer[0] <= L'Z') || - (ObjectNameInfo->Name.Buffer[0] >= L'a' && - ObjectNameInfo->Name.Buffer[0] <= L'z') )) + /* Check if we have a directory in our symlink to use */ + if (ObjectNameInfo != NULL) { - /* Remove the drive entry */ - KeAcquireGuardedMutex(&ObpDeviceMapLock); - ObSystemDeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex-1] = - DOSDEVICE_DRIVE_UNKNOWN; - ObSystemDeviceMap->DriveMap &= - ~(1 << (SymbolicLink->DosDeviceDriveIndex-1)); - KeReleaseGuardedMutex(&ObpDeviceMapLock); - - /* Reset the drive index, valid drive index starts from 1 */ - SymbolicLink->DosDeviceDriveIndex = 0; + NameDirectory = ObjectNameInfo->Directory; } - ObpDereferenceNameInfo(ObjectNameInfo); -} - -NTSTATUS -NTAPI -ObpParseSymbolicLinkToIoDeviceObject(IN POBJECT_DIRECTORY SymbolicLinkDirectory, - IN OUT POBJECT_DIRECTORY *Directory, - IN OUT PUNICODE_STRING TargetPath, - IN OUT POBP_LOOKUP_CONTEXT Context, - OUT PVOID *Object) -{ - UNICODE_STRING Name; - BOOLEAN ManualUnlock; + /* Initialize lookup context */ + ObpInitializeLookupContext(&LookupContext); - if (! TargetPath || ! Object || ! Context || ! Directory || - ! SymbolicLinkDirectory) + /* + * If we have to create the link, locate the IoDeviceObject if any + * this symbolic link points to. + */ + if (SymbolicLink->LinkTargetObject != NULL || !DeleteLink) { - return STATUS_INVALID_PARAMETER; - } + /* Start the search from the root */ + DirectoryObject = ObpRootDirectoryObject; - /* FIXME: Need to support Device maps */ + /* Keep track of our progress while parsing the name */ + LocalTarget = SymbolicLink->LinkTarget; - /* Try walking the target path and open each part of the path */ - while (TargetPath->Length) - { - /* Strip '\' if present at the beginning of the target path */ - if (TargetPath->Length >= sizeof(OBJ_NAME_PATH_SEPARATOR)&& - TargetPath->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) + /* If LUID mappings are enabled, use system map */ + if (ObpLUIDDeviceMapsEnabled != 0) + { + DeviceMap = ObSystemDeviceMap; + } + /* Otherwise, use the one in the process */ + else { - TargetPath->Buffer++; - TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); + DeviceMap = PsGetCurrentProcess()->DeviceMap; } - /* Remember the current component of the target path */ - Name = *TargetPath; +ReparseTargetPath: + /* + * If we have a device map active, check whether we have a drive + * letter prefixed with ??, if so, chomp it + */ + if (DeviceMap != NULL) + { + if (!((ULONG_PTR)(LocalTarget.Buffer) & 7)) + { + if (DeviceMap->DosDevicesDirectory != NULL) + { + if (LocalTarget.Length >= ObpDosDevicesShortName.Length && + (*(PULONGLONG)LocalTarget.Buffer == + ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) + { + DirectoryObject = DeviceMap->DosDevicesDirectory; + + LocalTarget.Length -= ObpDosDevicesShortName.Length; + LocalTarget.Buffer += (ObpDosDevicesShortName.Length / sizeof(WCHAR)); + } + } + } + } - /* Move forward to the next component of the target path */ - while (TargetPath->Length >= sizeof(OBJ_NAME_PATH_SEPARATOR)) + /* Try walking the target path and open each part of the path */ + while (TRUE) { - if (TargetPath->Buffer[0] != OBJ_NAME_PATH_SEPARATOR) + if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { - TargetPath->Buffer++; - TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); + ++LocalTarget.Buffer; + LocalTarget.Length -= sizeof(WCHAR); + } + + /* Remember the current component of the target path */ + TargetPath = LocalTarget; + + /* Move forward to the next component of the target path */ + if (LocalTarget.Length != 0) + { + do + { + if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) + { + break; + } + + ++LocalTarget.Buffer; + LocalTarget.Length -= sizeof(WCHAR); + } + while (LocalTarget.Length != 0); + } + + TargetPath.Length -= LocalTarget.Length; + + /* + * Finished processing the entire path, stop + * That's a failure case, we quit here + */ + if (TargetPath.Length == 0) + { + ObpReleaseLookupContext(&LookupContext); + return; + } + + + /* + * Make sure a deadlock does not occur as an exclusive lock on a pushlock + * would have already taken one in ObpLookupObjectName() on the parent + * directory where the symlink is being created [ObInsertObject()]. + * Prevent recursive locking by faking lock state in the lookup context + * when the current directory is same as the parent directory where + * the symlink is being created. If the lock state is not faked, + * ObpLookupEntryDirectory() will try to get a recursive lock on the + * pushlock and hang. For e.g. this happens when a substed drive is pointed to + * another substed drive. + */ + if (DirectoryObject == NameDirectory) + { + DirectoryLocked = LookupContext.DirectoryLocked; + LookupContext.DirectoryLocked = TRUE; } else + { + DirectoryLocked = FALSE; + } + + Object = ObpLookupEntryDirectory(DirectoryObject, + &TargetPath, + 0, + FALSE, + &LookupContext); + + /* Locking was faked, undo it now */ + if (DirectoryObject == NameDirectory) + { + LookupContext.DirectoryLocked = DirectoryLocked; + } + + /* Lookup failed, stop */ + if (Object == NULL) + { break; + } + + /* If we don't have a directory object, we'll have to handle the object */ + if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpDirectoryObjectType) + { + /* If that's not a symbolic link, stop here, nothing to do */ + if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpSymbolicLinkObjectType || + (((POBJECT_SYMBOLIC_LINK)Object)->DosDeviceDriveIndex != 0)) + { + break; + } + + /* We're out of reparse attempts */ + if (MaxReparse == 0) + { + Object = NULL; + break; + } + + --MaxReparse; + + /* Symlink points to another initialized symlink, ask caller to reparse */ + DirectoryObject = ObpRootDirectoryObject; + + LocalTarget = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTarget; + + goto ReparseTargetPath; + } + + /* Make this current directory, and continue search */ + DirectoryObject = Object; } + } - Name.Length -= TargetPath->Length; + DeviceMap = NULL; + /* That's a drive letter, find a suitable device map */ + if (SymbolicLink->DosDeviceDriveIndex != 0) + { + ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); + ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); + if (ObjectNameInfo != NULL) + { + if (ObjectNameInfo->Directory != NULL) + { + DeviceMap = ObjectNameInfo->Directory->DeviceMap; + } - /* Finished processing the entire path, stop */ - if (! Name.Length) - break; + ObpDereferenceNameInfo(ObjectNameInfo); + } + } - /* - * Make sure a deadlock does not occur as an exclusive lock on a pushlock - * would have already taken one in ObpLookupObjectName() on the parent - * directory where the symlink is being created [ObInsertObject()]. - * Prevent recursive locking by faking lock state in the lookup context - * when the current directory is same as the parent directory where - * the symlink is being created. If the lock state is not faked, - * ObpLookupEntryDirectory() will try to get a recursive lock on the - * pushlock and hang. For e.g. this happens when a substed drive is pointed to - * another substed drive. - */ - if (*Directory == SymbolicLinkDirectory && ! Context->DirectoryLocked) + /* If we were asked to delete the symlink */ + if (DeleteLink) + { + /* Zero its target */ + RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL); + + /* If we had a target objected, dereference it */ + if (SymbolicLink->LinkTargetObject != NULL) { - /* Fake lock state so that ObpLookupEntryDirectory() doesn't attempt a lock */ - ManualUnlock = TRUE; - Context->DirectoryLocked = TRUE; + ObDereferenceObject(SymbolicLink->LinkTargetObject); + SymbolicLink->LinkTargetObject = NULL; } - else - ManualUnlock = FALSE; - *Object = ObpLookupEntryDirectory(*Directory, - &Name, - 0, - FALSE, - Context); + /* If it was a drive letter */ + if (DeviceMap != NULL) + { + /* Acquire the device map lock */ + KeAcquireGuardedMutex(&ObpDeviceMapLock); - /* Locking was faked, undo it now */ - if (*Directory == SymbolicLinkDirectory && ManualUnlock) - Context->DirectoryLocked = FALSE; + /* Remove the drive entry */ + DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] = + DOSDEVICE_DRIVE_UNKNOWN; + DeviceMap->DriveMap &= + ~(1 << (SymbolicLink->DosDeviceDriveIndex - 1)); - /* Lookup failed, stop */ - if (! *Object) - break; + /* Release the device map lock */ + KeReleaseGuardedMutex(&ObpDeviceMapLock); - if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObpDirectoryObjectType) - { - /* Make this current directory, and continue search */ - *Directory = (POBJECT_DIRECTORY)*Object; + /* Reset the drive index, valid drive index starts from 1 */ + SymbolicLink->DosDeviceDriveIndex = 0; } - else if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObpSymbolicLinkObjectType && - (((POBJECT_SYMBOLIC_LINK)*Object)->DosDeviceDriveIndex == 0)) + } + else + { + DriveType = DOSDEVICE_DRIVE_CALCULATE; + + /* If we have a drive letter and a pointer device object */ + if (Object != NULL && SymbolicLink->DosDeviceDriveIndex != 0 && + OBJECT_TO_OBJECT_HEADER(Object)->Type == IoDeviceObjectType) { - /* Symlink points to another initialized symlink, ask caller to reparse */ - *Directory = ObpRootDirectoryObject; - TargetPath = &((POBJECT_SYMBOLIC_LINK)*Object)->LinkTarget; - return STATUS_REPARSE_OBJECT; + /* Calculate the drive type */ + switch(((PDEVICE_OBJECT)Object)->DeviceType) + { + case FILE_DEVICE_VIRTUAL_DISK: + DriveType = DOSDEVICE_DRIVE_RAMDISK; + break; + case FILE_DEVICE_CD_ROM: + case FILE_DEVICE_CD_ROM_FILE_SYSTEM: + DriveType = DOSDEVICE_DRIVE_CDROM; + break; + case FILE_DEVICE_DISK: + case FILE_DEVICE_DISK_FILE_SYSTEM: + case FILE_DEVICE_FILE_SYSTEM: + if (((PDEVICE_OBJECT)Object)->Characteristics & FILE_REMOVABLE_MEDIA) + DriveType = DOSDEVICE_DRIVE_REMOVABLE; + else + DriveType = DOSDEVICE_DRIVE_FIXED; + break; + case FILE_DEVICE_NETWORK: + case FILE_DEVICE_NETWORK_FILE_SYSTEM: + DriveType = DOSDEVICE_DRIVE_REMOTE; + break; + default: + DPRINT1("Device Type %lu for %wZ is not known or unhandled\n", + ((PDEVICE_OBJECT)Object)->DeviceType, + &SymbolicLink->LinkTarget); + DriveType = DOSDEVICE_DRIVE_UNKNOWN; + } } - else + + /* Add a new drive entry */ + if (DeviceMap != NULL) { - /* Neither directory or symlink, stop */ - break; + /* Acquire the device map lock */ + KeAcquireGuardedMutex(&ObpDeviceMapLock); + + DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] = + (UCHAR)DriveType; + DeviceMap->DriveMap |= + 1 << (SymbolicLink->DosDeviceDriveIndex - 1); + + /* Release the device map lock */ + KeReleaseGuardedMutex(&ObpDeviceMapLock); } } - /* Return a valid object, only if object type is IoDeviceObject */ - if (*Object && - OBJECT_TO_OBJECT_HEADER(*Object)->Type != IoDeviceObjectType) - { - *Object = NULL; - } - return STATUS_SUCCESS; + /* Cleanup */ + ObpReleaseLookupContext(&LookupContext); +} + +VOID +NTAPI +ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink) +{ + /* Just call the helper */ + ObpProcessDosDeviceSymbolicLink(SymbolicLink, TRUE); } VOID NTAPI ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink) { + WCHAR UpperDrive; POBJECT_HEADER ObjectHeader; POBJECT_HEADER_NAME_INFO ObjectNameInfo; - PVOID Object = NULL; - POBJECT_DIRECTORY Directory; - UNICODE_STRING TargetPath; - NTSTATUS Status; - ULONG DriveType = DOSDEVICE_DRIVE_CALCULATE; - ULONG ReparseCnt; - const ULONG MaxReparseAttempts = 20; - OBP_LOOKUP_CONTEXT Context; - - /* FIXME: Need to support Device maps */ /* Get header data */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink); ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader); - /* Check if we are not actually in a directory with a device map */ - if (!(ObjectNameInfo) || - !(ObjectNameInfo->Directory) /*|| - !(ObjectNameInfo->Directory->DeviceMap)*/) + /* No name info, nothing to create */ + if (ObjectNameInfo == NULL) { - ObpDereferenceNameInfo(ObjectNameInfo); return; } - /* Check if it's a DOS drive letter, and set the drive index accordingly */ - if (ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR) && - ObjectNameInfo->Name.Buffer[1] == L':' && - ( (ObjectNameInfo->Name.Buffer[0] >= L'A' && - ObjectNameInfo->Name.Buffer[0] <= L'Z') || - (ObjectNameInfo->Name.Buffer[0] >= L'a' && - ObjectNameInfo->Name.Buffer[0] <= L'z') )) + /* If we have a device map, look for creating a letter based drive */ + if (ObjectNameInfo->Directory != NULL && + ObjectNameInfo->Directory->DeviceMap != NULL) { - SymbolicLink->DosDeviceDriveIndex = - RtlUpcaseUnicodeChar(ObjectNameInfo->Name.Buffer[0]) - L'A'; - /* The Drive index start from 1 */ - SymbolicLink->DosDeviceDriveIndex++; - - /* Initialize lookup context */ - ObpInitializeLookupContext(&Context); - - /* Start the search from the root */ - Directory = ObpRootDirectoryObject; - TargetPath = SymbolicLink->LinkTarget; - - /* - * Locate the IoDeviceObject if any this symbolic link points to. - * To prevent endless reparsing, setting an upper limit on the - * number of reparses. - */ - Status = STATUS_REPARSE_OBJECT; - ReparseCnt = 0; - while (Status == STATUS_REPARSE_OBJECT && - ReparseCnt < MaxReparseAttempts) + /* Is it a drive letter based name? */ + if (ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR)) { - Status = - ObpParseSymbolicLinkToIoDeviceObject(ObjectNameInfo->Directory, - &Directory, - &TargetPath, - &Context, - &Object); - if (Status == STATUS_REPARSE_OBJECT) - ReparseCnt++; - } - - /* Cleanup lookup context */ - ObpReleaseLookupContext(&Context); - - /* Error, or max resparse attemtps exceeded */ - if (! NT_SUCCESS(Status) || ReparseCnt >= MaxReparseAttempts) - { - /* Cleanup */ - ObpDereferenceNameInfo(ObjectNameInfo); - return; - } - - if (Object) - { - /* Calculate the drive type */ - switch(((PDEVICE_OBJECT)Object)->DeviceType) + if (ObjectNameInfo->Name.Buffer[1] == L':') { - case FILE_DEVICE_VIRTUAL_DISK: - DriveType = DOSDEVICE_DRIVE_RAMDISK; - break; - case FILE_DEVICE_CD_ROM: - case FILE_DEVICE_CD_ROM_FILE_SYSTEM: - DriveType = DOSDEVICE_DRIVE_CDROM; - break; - case FILE_DEVICE_DISK: - case FILE_DEVICE_DISK_FILE_SYSTEM: - case FILE_DEVICE_FILE_SYSTEM: - if (((PDEVICE_OBJECT)Object)->Characteristics & FILE_REMOVABLE_MEDIA) - DriveType = DOSDEVICE_DRIVE_REMOVABLE; - else - DriveType = DOSDEVICE_DRIVE_FIXED; - break; - case FILE_DEVICE_NETWORK: - case FILE_DEVICE_NETWORK_FILE_SYSTEM: - DriveType = DOSDEVICE_DRIVE_REMOTE; - break; - default: - DPRINT1("Device Type %lu for %wZ is not known or unhandled\n", - ((PDEVICE_OBJECT)Object)->DeviceType, - &SymbolicLink->LinkTarget); - DriveType = DOSDEVICE_DRIVE_UNKNOWN; + UpperDrive = RtlUpcaseUnicodeChar(ObjectNameInfo->Name.Buffer[0]); + if (UpperDrive >= L'A' && UpperDrive <= L'Z') + { + /* Compute its index (it's 1 based - 0 means no letter) */ + SymbolicLink->DosDeviceDriveIndex = UpperDrive - (L'A' - 1); + } } } - /* Add a new drive entry */ - KeAcquireGuardedMutex(&ObpDeviceMapLock); - ObSystemDeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex-1] = - (UCHAR)DriveType; - ObSystemDeviceMap->DriveMap |= - 1 << (SymbolicLink->DosDeviceDriveIndex-1); - KeReleaseGuardedMutex(&ObpDeviceMapLock); + /* Call the helper */ + ObpProcessDosDeviceSymbolicLink(SymbolicLink, FALSE); } - /* Cleanup */ + /* We're done */ ObpDereferenceNameInfo(ObjectNameInfo); }