[CMAKE]
[reactos.git] / ntoskrnl / ob / oblink.c
index 5b14747..3648aa7 100644 (file)
@@ -23,8 +23,160 @@ VOID
 NTAPI
 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
 {
-    /* FIXME: Device maps not supported yet */
+    POBJECT_HEADER ObjectHeader;
+    POBJECT_HEADER_NAME_INFO ObjectNameInfo;
+
+    /* 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)*/)
+    {
+        ObpDereferenceNameInfo(ObjectNameInfo);
+        return;
+    }
+
+    /* 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') ))
+    {
+        /* 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;
+    }
+
+    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;
+
+    if (! TargetPath || ! Object || ! Context || ! Directory ||
+        ! SymbolicLinkDirectory)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* FIXME: Need to support Device maps */
+
+    /* 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)
+        {
+            TargetPath->Buffer++;
+            TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
+        }
+
+        /* Remember the current component of the target path */
+        Name = *TargetPath;
+
+        /* Move forward to the next component of the target path */
+        while (TargetPath->Length >= sizeof(OBJ_NAME_PATH_SEPARATOR))
+        {
+            if (TargetPath->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
+            {
+                TargetPath->Buffer++;
+                TargetPath->Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
+            }
+            else
+                break;
+        }
+
+        Name.Length -= TargetPath->Length;
+
+        /* Finished processing the entire path, stop */
+        if (! Name.Length)
+            break;
 
+        /*
+         * 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)
+        {
+            /* Fake lock state so that ObpLookupEntryDirectory() doesn't attempt a lock */
+            ManualUnlock = TRUE;
+            Context->DirectoryLocked = TRUE;
+        }
+        else
+            ManualUnlock = FALSE;
+
+        *Object = ObpLookupEntryDirectory(*Directory,
+                                          &Name,
+                                          0,
+                                          FALSE,
+                                          Context);
+
+        /* Locking was faked, undo it now */
+        if (*Directory == SymbolicLinkDirectory && ManualUnlock)
+            Context->DirectoryLocked = FALSE;
+
+        /* Lookup failed, stop */
+        if (! *Object)
+            break;
+
+        if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObDirectoryType)
+        {
+            /* Make this current directory, and continue search */
+            *Directory = (POBJECT_DIRECTORY)*Object;
+        }
+        else if (OBJECT_TO_OBJECT_HEADER(*Object)->Type == ObSymbolicLinkType &&
+            (((POBJECT_SYMBOLIC_LINK)*Object)->DosDeviceDriveIndex == 0))
+        {
+            /* Symlink points to another initialized symlink, ask caller to reparse */
+            *Directory = ObpRootDirectoryObject;
+            TargetPath = &((POBJECT_SYMBOLIC_LINK)*Object)->LinkTarget;
+            return STATUS_REPARSE_OBJECT;
+        }
+        else
+        {
+            /* Neither directory or symlink, stop */
+            break;
+        }
+    }
+
+    /* Return a valid object, only if object type is IoDeviceObject */
+    if (*Object &&
+        OBJECT_TO_OBJECT_HEADER(*Object)->Type != IoDeviceObjectType)
+    {
+        *Object = NULL;
+    }
+    return STATUS_SUCCESS;
 }
 
 VOID
@@ -33,23 +185,124 @@ ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
 {
     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 = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
+    ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
 
     /* Check if we are not actually in a directory with a device map */
     if (!(ObjectNameInfo) ||
-        !(ObjectNameInfo->Directory) ||
-        !(ObjectNameInfo->Directory->DeviceMap))
+        !(ObjectNameInfo->Directory) /*||
+        !(ObjectNameInfo->Directory->DeviceMap)*/)
     {
-        /* There's nothing to do, return */
+        ObpDereferenceNameInfo(ObjectNameInfo);
         return;
     }
 
-    /* FIXME: We don't support device maps yet */
-    DPRINT1("Unhandled path!\n");
-    ASSERT(FALSE);
+    /* 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') ))
+    {
+        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)
+        {
+            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)
+            {
+            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 %ld for %wZ is not known or unhandled\n",
+                        ((PDEVICE_OBJECT)Object)->DeviceType,
+                        &SymbolicLink->LinkTarget);
+                DriveType = DOSDEVICE_DRIVE_UNKNOWN;
+            }
+        }
+
+        /* Add a new drive entry */
+        KeAcquireGuardedMutex(&ObpDeviceMapLock);
+        ObSystemDeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex-1] =
+            (UCHAR)DriveType;
+        ObSystemDeviceMap->DriveMap |=
+            1 << (SymbolicLink->DosDeviceDriveIndex-1);
+        KeReleaseGuardedMutex(&ObpDeviceMapLock);
+    }
+
+    /* Cleanup */
+    ObpDereferenceNameInfo(ObjectNameInfo);
 }
 
 /*++