[NTOSKRNL] Rework ObpDeleteSymbolicLinkName and ObpCreateSymbolicLinkName
authorPierre Schweitzer <pierre@reactos.org>
Tue, 11 Jun 2019 19:18:22 +0000 (21:18 +0200)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 11 Jun 2019 19:21:52 +0000 (21:21 +0200)
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

index f8fbe5a..712143e 100644 (file)
@@ -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);
 }