- Fix Symbolic Link implementation:

- Use OBJECT_SYMBOLIC_LINK structure as used by Windows NT (and added the structure to the NDK)
  - Changed code to use the new names in the structure
  - Only free the symlink's name if it has one, to avoid a bugcheck.
  - Optimize parsing: if the new name is actually shorter then the target name, then don't allocate from pool, but re-use the buffer. This improves symlink parsing speed. Also fix a bug which made it depend on the incorrect implementation of NtCreateSymbolicLinkObject
  - Re-align the link target maximum length in NtCreateSymbolicLinkObject if it's odd, and fail if the length is odd, smaller then the maximum, or if the maximum is zero.
  - Originally allocate the symlink name in Paged Pool.
  - Initialize new members of the structure.
  - Fail in no-memory case, instead of continuing.
  - Properly probe the LinkTarget in NtQuerySymbolicLinkObject.
  - Properly handle which length is chosen for the copy and for the return in NtQuerySymbolicObject.
  - Lock the symbolic link object type while querying it. Eventually many more parts of Ob need to do this kind of locking.
  - SymLinkTest now only gives 2 failures.
- Fix some NDK bugs

svn path=/trunk/; revision=22000
This commit is contained in:
Alex Ionescu 2006-05-24 02:45:06 +00:00
parent 4b6154cfbf
commit 8a80070849
7 changed files with 231 additions and 107 deletions

View file

@ -858,7 +858,6 @@ ZwSetDefaultHardErrorPort(
IN HANDLE PortHandle
);
NTSYSAPI
NTSYSAPI
NTSTATUS
NTAPI

View file

@ -384,15 +384,29 @@ typedef struct _OBJECT_DIRECTORY_ENTRY
{
struct _OBJECT_DIRECTORY_ENTRY *ChainLink;
PVOID Object;
#if (NTDDI_VERSION >= NTDDI_WS03)
ULONG HashValue;
#endif
} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY;
typedef struct _OBJECT_DIRECTORY
{
struct _OBJECT_DIRECTORY_ENTRY *HashBuckets[NUMBER_HASH_BUCKETS];
struct _EX_PUSH_LOCK *Lock;
#if (NTDDI_VERSION < NTDDI_WINXP)
PERESOURCE Lock;
#elif (NTDDI_VERSION >= NTDDI_WINXP)
EX_PUSH_LOCK Lock;
#endif
#if (NTDDI_VERSION < NTDDI_WINXP)
BOOLEAN CurrentEntryValid;
#else
struct _DEVICE_MAP *DeviceMap;
#endif
ULONG SessionId;
#if (NTDDI_VERSION == NTDDI_WINXP)
USHORT Reserved;
USHORT SymbolicLinkUsageCount;
#endif
} OBJECT_DIRECTORY, *POBJECT_DIRECTORY;
//
@ -407,6 +421,18 @@ typedef struct _DEVICE_MAP
UCHAR DriveType[32];
} DEVICE_MAP, *PDEVICE_MAP;
//
// Symbolic Link Object
//
typedef struct _OBJECT_SYMBOLIC_LINK
{
LARGE_INTEGER CreationTime;
UNICODE_STRING LinkTarget;
UNICODE_STRING LinkTargetRemaining;
PVOID LinkTargetObject;
ULONG DosDeviceDriveIndex;
} OBJECT_SYMBOLIC_LINK, *POBJECT_SYMBOLIC_LINK;
//
// Kernel Exports
//

View file

@ -297,7 +297,6 @@ NtSetInformationProcess(
IN ULONG ProcessInformationLength
);
NTSYSCALLAPI
NTSYSCALLAPI
NTSTATUS
NTAPI

View file

@ -171,6 +171,42 @@ Author:
#define NLS_MB_CODE_PAGE_TAG NlsMbCodePageTag
#define NLS_MB_OEM_CODE_PAGE_TAG NlsMbOemCodePageTag
#define NLS_OEM_LEAD_BYTE_INFO NlsOemLeadByteInfo
//
// C++ CONST casting
//
#if defined(__cplusplus)
#define RTL_CONST_CAST(type) const_cast<type>
#else
#define RTL_CONST_CAST(type) (type)
#endif
//
// Constant String Macro
//
#define RTL_CONSTANT_STRING(__SOURCE_STRING__) \
{ \
sizeof(__SOURCE_STRING__) - sizeof((__SOURCE_STRING__)[0]), \
sizeof(__SOURCE_STRING__), \
(__SOURCE_STRING__) \
}
//
// Constant Object Attributes Macro
//
#define RTL_CONSTANT_OBJECT_ATTRIBUTES(n, a) \
{ \
sizeof(OBJECT_ATTRIBUTES), \
NULL, \
RTL_CONST_CAST(PUNICODE_STRING)(n), \
a, \
NULL, \
NULL \
}
#define RTL_INIT_OBJECT_ATTRIBUTES(n, a) \
RTL_CONSTANT_OBJECT_ATTRIBUTES(n, a)
#else
//
// Message Resource Flag

View file

@ -23,14 +23,6 @@ typedef struct _DIRECTORY_OBJECT
KSPIN_LOCK Lock;
} DIRECTORY_OBJECT, *PDIRECTORY_OBJECT;
typedef struct _SYMLINK_OBJECT
{
CSHORT Type;
CSHORT Size;
UNICODE_STRING TargetName;
LARGE_INTEGER CreateTime;
} SYMLINK_OBJECT, *PSYMLINK_OBJECT;
typedef struct _ROS_OBJECT_HEADER
{
LIST_ENTRY Entry;

View file

@ -210,7 +210,7 @@ NtQueryObject(IN HANDLE ObjectHandle,
if (ObjectHeader->Type == ObSymbolicLinkType)
{
BasicInfo->CreateTime.QuadPart =
((PSYMLINK_OBJECT)Object)->CreateTime.QuadPart;
((POBJECT_SYMBOLIC_LINK)Object)->CreationTime.QuadPart;
}
else
{

View file

@ -48,8 +48,15 @@ VOID
NTAPI
ObpDeleteSymbolicLink(PVOID ObjectBody)
{
PSYMLINK_OBJECT SymlinkObject = (PSYMLINK_OBJECT)ObjectBody;
ExFreePool(SymlinkObject->TargetName.Buffer);
POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ObjectBody;
/* Make sure that the symbolic link has a name */
if (SymlinkObject->LinkTarget.Buffer)
{
/* Free the name */
ExFreePool(SymlinkObject->LinkTarget.Buffer);
SymlinkObject->LinkTarget.Buffer = NULL;
}
}
/*++
@ -85,10 +92,10 @@ ObpParseSymbolicLink(PVOID Object,
PWSTR * RemainingPath,
ULONG Attributes)
{
PSYMLINK_OBJECT SymlinkObject = (PSYMLINK_OBJECT) Object;
UNICODE_STRING TargetPath;
DPRINT("ObpParseSymbolicLink (RemainingPath %S)\n", *RemainingPath);
POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)Object;
PUNICODE_STRING TargetPath;
PWSTR NewTargetPath;
ULONG LengthUsed, MaximumLength, RemainLength;
/*
* Stop parsing if the entire path has been parsed and
@ -97,33 +104,51 @@ ObpParseSymbolicLink(PVOID Object,
if (((*RemainingPath == NULL) || (**RemainingPath == 0)) &&
(Attributes & OBJ_OPENLINK))
{
DPRINT("Parsing stopped!\n");
*NextObject = NULL;
return(STATUS_SUCCESS);
}
/* Build the expanded path */
TargetPath.MaximumLength = SymlinkObject->TargetName.Length +
sizeof(WCHAR);
if (RemainingPath && *RemainingPath)
/* Set the target path and length */
TargetPath = &SymlinkObject->LinkTarget;
RemainLength = *RemainingPath ? wcslen(*RemainingPath) * sizeof(WCHAR) : 0;
LengthUsed = TargetPath->Length + RemainLength;
/* Optimization: check if the new name is shorter */
if (FullPath->MaximumLength <= LengthUsed)
{
TargetPath.MaximumLength += (wcslen(*RemainingPath) * sizeof(WCHAR));
}
TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
TargetPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
TargetPath.MaximumLength,
/* It's not, allocate a new one */
MaximumLength = LengthUsed + sizeof(WCHAR);
NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
MaximumLength,
TAG_SYMLINK_TTARGET);
wcscpy(TargetPath.Buffer, SymlinkObject->TargetName.Buffer);
if (RemainingPath && *RemainingPath)
}
else
{
wcscat(TargetPath.Buffer, *RemainingPath);
/* It is! Reuse the name... */
MaximumLength = FullPath->MaximumLength;
NewTargetPath = FullPath->Buffer;
}
/* Transfer target path buffer into FullPath */
ExFreePool(FullPath->Buffer);
FullPath->Length = TargetPath.Length;
FullPath->MaximumLength = TargetPath.MaximumLength;
FullPath->Buffer = TargetPath.Buffer;
/* Make sure we have a length */
if (RemainLength)
{
/* Copy the new path */
RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TargetPath->Length),
*RemainingPath,
RemainLength);
}
/* Copy the target path and null-terminate it */
RtlMoveMemory(NewTargetPath, TargetPath->Buffer, TargetPath->Length);
NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
/* If the optimization didn't work, free the old buffer */
if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
/* Update the path values */
FullPath->Length = LengthUsed;
FullPath->MaximumLength = MaximumLength;
FullPath->Buffer = NewTargetPath;
/* Reinitialize RemainingPath for reparsing */
*RemainingPath = FullPath->Buffer;
@ -152,13 +177,11 @@ ObInitSymbolicLinkImplementation(VOID)
UNICODE_STRING Name;
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
DPRINT("Creating SymLink Object Type\n");
/* Initialize the Directory type */
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
RtlInitUnicodeString(&Name, L"SymbolicLink");
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(SYMLINK_OBJECT);
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_SYMBOLIC_LINK);
ObjectTypeInitializer.GenericMapping = ObpSymbolicLinkMapping;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.ValidAccessMask = SYMBOLIC_LINK_ALL_ACCESS;
@ -201,72 +224,99 @@ NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
IN PUNICODE_STRING LinkTarget)
{
HANDLE hLink;
PSYMLINK_OBJECT SymbolicLink;
POBJECT_SYMBOLIC_LINK SymbolicLink;
UNICODE_STRING CapturedLinkTarget;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
DPRINT("NtCreateSymbolicLinkObject(LinkHandle %p, DesiredAccess %ul"
", ObjectAttributes %p, LinkTarget %wZ)\n",
LinkHandle,
DesiredAccess,
ObjectAttributes,
LinkTarget);
/* Check if we need to probe parameters */
if(PreviousMode != KernelMode)
{
_SEH_TRY
{
/* Probe the target */
ProbeForRead(LinkTarget, sizeof(UNICODE_STRING), sizeof(WCHAR));
CapturedLinkTarget = *LinkTarget;
ProbeForRead(CapturedLinkTarget.Buffer,
CapturedLinkTarget.MaximumLength,
sizeof(WCHAR));
/* Probe the return handle */
ProbeForWriteHandle(LinkHandle);
}
_SEH_HANDLE
{
/* Exception, get the error code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
if(!NT_SUCCESS(Status))
{
return Status;
}
/* Probing failed, return the error code */
if(!NT_SUCCESS(Status)) return Status;
}
Status = ProbeAndCaptureUnicodeString(&CapturedLinkTarget,
PreviousMode,
LinkTarget);
if(!NT_SUCCESS(Status))
else
{
DPRINT1("NtCreateSymbolicLinkObject: Capturing the target link failed!\n");
return Status;
/* No need to capture */
CapturedLinkTarget = *LinkTarget;
}
/* Check if the maximum length is odd */
if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
{
/* Round it down */
CapturedLinkTarget.MaximumLength =
ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
}
/* Fail if the length is odd, or if the maximum is smaller or 0 */
if ((CapturedLinkTarget.Length % sizeof(WCHAR)) ||
(CapturedLinkTarget.MaximumLength < CapturedLinkTarget.Length) ||
!(CapturedLinkTarget.MaximumLength))
{
/* This message is displayed on the debugger in Windows */
DbgPrint("OB: Invalid symbolic link target - %wZ\n",
&CapturedLinkTarget);
return STATUS_INVALID_PARAMETER;
}
/* Create the object */
Status = ObCreateObject(PreviousMode,
ObSymbolicLinkType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(SYMLINK_OBJECT),
sizeof(OBJECT_SYMBOLIC_LINK),
0,
0,
(PVOID*)&SymbolicLink);
if (NT_SUCCESS(Status))
{
SymbolicLink->TargetName.Length = 0;
SymbolicLink->TargetName.MaximumLength = CapturedLinkTarget.Length +
/* Success! Fill in the creation time immediately */
KeQuerySystemTime(&SymbolicLink->CreationTime);
/* Setup the target name */
SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.Length +
sizeof(WCHAR);
SymbolicLink->TargetName.Buffer =
ExAllocatePoolWithTag(NonPagedPool,
SymbolicLink->TargetName.MaximumLength,
SymbolicLink->LinkTarget.Buffer =
ExAllocatePoolWithTag(PagedPool,
CapturedLinkTarget.MaximumLength,
TAG_SYMLINK_TARGET);
if (!SymbolicLink->LinkTarget.Buffer) return STATUS_NO_MEMORY;
RtlCopyUnicodeString(&SymbolicLink->TargetName, &CapturedLinkTarget);
/* Copy it */
RtlMoveMemory(SymbolicLink->LinkTarget.Buffer,
CapturedLinkTarget.Buffer,
CapturedLinkTarget.MaximumLength);
DPRINT("DeviceName %S\n", SymbolicLink->TargetName.Buffer);
/* Initialize the remaining name, dos drive index and target object */
SymbolicLink->LinkTargetObject = NULL;
SymbolicLink->DosDeviceDriveIndex = 0;
RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
ZwQuerySystemTime (&SymbolicLink->CreateTime);
Status = ObInsertObject((PVOID)SymbolicLink,
/* Insert it into the object tree */
Status = ObInsertObject(SymbolicLink,
NULL,
DesiredAccess,
0,
@ -276,18 +326,22 @@ NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
{
_SEH_TRY
{
/* Return the handle to caller */
*LinkHandle = hLink;
}
_SEH_HANDLE
{
/* Get exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
}
/* ReactOS Hack: Our ObInsertObject references an object twice */
ObDereferenceObject(SymbolicLink);
}
ReleaseCapturedUnicodeString(&CapturedLinkTarget, PreviousMode);
/* Return status to caller */
return Status;
}
@ -322,27 +376,26 @@ NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
/* Check if we need to probe parameters */
if(PreviousMode != KernelMode)
{
_SEH_TRY
{
/* Probe the return handle */
ProbeForWriteHandle(LinkHandle);
}
_SEH_HANDLE
{
/* Exception, get the error code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
if(!NT_SUCCESS(Status))
{
return Status;
}
/* Probing failed, return the error code */
if(!NT_SUCCESS(Status)) return Status;
}
DPRINT("NtOpenSymbolicLinkObject (Name %wZ)\n",
ObjectAttributes->ObjectName);
/* Open the object */
Status = ObOpenObjectByName(ObjectAttributes,
ObSymbolicLinkType,
NULL,
@ -350,19 +403,22 @@ NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
DesiredAccess,
NULL,
&hLink);
if(NT_SUCCESS(Status))
if (NT_SUCCESS(Status))
{
_SEH_TRY
{
/* Return the handle to caller */
*LinkHandle = hLink;
}
_SEH_HANDLE
{
/* Get exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
}
/* Return status to caller */
return Status;
}
@ -393,46 +449,47 @@ NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
OUT PULONG ResultLength OPTIONAL)
{
UNICODE_STRING SafeLinkTarget;
PSYMLINK_OBJECT SymlinkObject;
POBJECT_SYMBOLIC_LINK SymlinkObject;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
ULONG LengthRequired;
ULONG LengthUsed;
PAGED_CODE();
if(PreviousMode != KernelMode)
{
_SEH_TRY
{
/* probe the unicode string and buffers supplied */
ProbeForWrite(LinkTarget, sizeof(UNICODE_STRING), sizeof(ULONG));
/* Probe the unicode string for read and write */
ProbeForRead(LinkTarget, sizeof(UNICODE_STRING), sizeof(WCHAR));
ProbeForWriteUshort(&LinkTarget->Length);
ProbeForWriteUshort(&LinkTarget->MaximumLength);
/* Probe the unicode string's buffer for write */
SafeLinkTarget = *LinkTarget;
ProbeForWrite(SafeLinkTarget.Buffer,
SafeLinkTarget.MaximumLength,
sizeof(WCHAR));
sizeof(CHAR));
if(ResultLength != NULL)
{
ProbeForWriteUlong(ResultLength);
}
/* Probe the return length */
if(ResultLength) ProbeForWriteUlong(ResultLength);
}
_SEH_HANDLE
{
/* Probe failure: get exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
if(!NT_SUCCESS(Status))
{
return Status;
}
/* Probe failed, return status */
if(!NT_SUCCESS(Status)) return Status;
}
else
{
/* No need to probe */
SafeLinkTarget = *LinkTarget;
}
/* Reference the object */
Status = ObReferenceObjectByHandle(LinkHandle,
SYMBOLIC_LINK_QUERY,
ObSymbolicLinkType,
@ -441,44 +498,59 @@ NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
NULL);
if (NT_SUCCESS(Status))
{
LengthRequired = SymlinkObject->TargetName.Length + sizeof(WCHAR);
/* Lock the object type */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&ObSymbolicLinkType->Mutex, TRUE);
/*
* So here's the thing: If you specify a return length, then the
* implementation will use the maximum length. If you don't, then
* it will use the length.
*/
LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
SymlinkObject->LinkTarget.Length;
/* Enter SEH so we can safely copy */
_SEH_TRY
{
if(SafeLinkTarget.MaximumLength >= LengthRequired)
/* Make sure our buffer will fit */
if (LengthUsed <= SafeLinkTarget.MaximumLength)
{
/*
* Don't pass TargetLink to RtlCopyUnicodeString here because
* the caller might have modified the structure which could
* lead to a copy into kernel memory!
*/
RtlCopyUnicodeString(&SafeLinkTarget,
&SymlinkObject->TargetName);
SafeLinkTarget.Buffer[SafeLinkTarget.Length /
sizeof(WCHAR)] = UNICODE_NULL;
/* Copy the buffer */
RtlMoveMemory(SafeLinkTarget.Buffer,
SymlinkObject->LinkTarget.Buffer,
LengthUsed);
/* Copy back the new UNICODE_STRING structure */
*LinkTarget = SafeLinkTarget;
/* Copy the new length */
LinkTarget->Length = SymlinkObject->LinkTarget.Length;
}
else
{
/* Otherwise set the failure status */
Status = STATUS_BUFFER_TOO_SMALL;
}
if(ResultLength != NULL)
/* In both cases, check if the required length was requested */
if (ResultLength)
{
*ResultLength = LengthRequired;
/* Then return it */
*ResultLength = SymlinkObject->LinkTarget.MaximumLength;
}
}
_SEH_HANDLE
{
/* Get the error code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
/* Unlock the object type and reference the object */
ExReleaseResourceLite(&ObSymbolicLinkType->Mutex);
KeLeaveCriticalRegion();
ObDereferenceObject(SymlinkObject);
}
/* Return query status */
return Status;
}