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
{
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 %lu 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);
}
/*++
POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ParsedObject;
PUNICODE_STRING TargetPath;
PWSTR NewTargetPath;
- ULONG LengthUsed, MaximumLength;
+ ULONG LengthUsed, MaximumLength, TempLength;
NTSTATUS Status;
PAGED_CODE();
return STATUS_OBJECT_TYPE_MISMATCH;
}
+ /* Check if this symlink is bound to a specific object */
+ if (SymlinkObject->LinkTargetObject)
+ {
+ UNIMPLEMENTED;
+ }
+
/* Set the target path and length */
TargetPath = &SymlinkObject->LinkTarget;
- LengthUsed = TargetPath->Length + RemainingName->Length;
+ TempLength = TargetPath->Length;
+
+ /*
+ * Strip off the extra trailing '\', if we don't do this we will end up
+ * adding a extra '\' between TargetPath and RemainingName
+ * causing caller's like ObpLookupObjectName() to fail.
+ */
+ if (TempLength && RemainingName->Length)
+ {
+ /* The target and remaining names aren't empty, so check for slashes */
+ if ((TargetPath->Buffer[TempLength / sizeof(WCHAR) - 1] ==
+ OBJ_NAME_PATH_SEPARATOR) &&
+ (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
+ {
+ /* Reduce the length by one to cut off the extra '\' */
+ TempLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
+ }
+ }
+
+ /* Calculate the new length */
+ LengthUsed = TempLength + RemainingName->Length;
+
+ /* Check if it's not too much */
+ if (LengthUsed > 0xFFF0)
+ return STATUS_NAME_TOO_LONG;
/* Optimization: check if the new name is shorter */
if (FullPath->MaximumLength <= LengthUsed)
if (RemainingName->Length)
{
/* Copy the new path */
- RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TargetPath->Length),
+ RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TempLength),
RemainingName->Buffer,
RemainingName->Length);
}
/* Copy the target path and null-terminate it */
- RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TargetPath->Length);
+ RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TempLength);
NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
/* If the optimization didn't work, free the old buffer */
ExAllocatePoolWithTag(PagedPool,
CapturedLinkTarget.MaximumLength,
TAG_SYMLINK_TARGET);
- if (!SymbolicLink->LinkTarget.Buffer) return STATUS_NO_MEMORY;
+ if (!SymbolicLink->LinkTarget.Buffer)
+ {
+ /* Dereference the symbolic link object and fail */
+ ObDereferenceObject(SymbolicLink);
+ return STATUS_NO_MEMORY;
+ }
/* Copy it */
RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,