- Reimplement RtlSetCurrentDirectory_U. This fixes bugs & implements references count
- Fix the FIXME in RtlpDosPathNameToRelativeNtPathName_Ustr by incrementing references count

svn path=/trunk/; revision=55017
This commit is contained in:
Pierre Schweitzer 2012-01-20 20:21:16 +00:00
parent 823685091e
commit f65034e760

View file

@ -6,6 +6,8 @@
* PROGRAMMERS: Wine team * PROGRAMMERS: Wine team
* Thomas Weidenmueller * Thomas Weidenmueller
* Gunnar Dalsnes * Gunnar Dalsnes
* Alex Ionescu (alex.ionescu@reactos.org)
* Pierre Schweitzer (pierre@reactos.org)
*/ */
/* INCLUDES *****************************************************************/ /* INCLUDES *****************************************************************/
@ -21,6 +23,11 @@
#define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/')) #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
#define RTL_CURDIR_IS_REMOVABLE 0x1
#define RTL_CURDIR_DROP_OLD_HANDLE 0x2
#define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
/* GLOBALS ********************************************************************/ /* GLOBALS ********************************************************************/
@ -617,7 +624,7 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
if (InputPathType == RtlPathTypeRelative) if (InputPathType == RtlPathTypeRelative)
{ {
/* Get current directory */ /* Get current directory */
CurrentDirectory = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath); CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
if (CurrentDirectory->Handle) if (CurrentDirectory->Handle)
{ {
Status = RtlInitUnicodeStringEx(&FullPath, Buffer); Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
@ -660,7 +667,7 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
RelativeName->CurDirRef = RtlpCurDirRef; RelativeName->CurDirRef = RtlpCurDirRef;
if (RelativeName->CurDirRef) if (RelativeName->CurDirRef)
{ {
/* FIXME: Increment reference count */ InterlockedIncrement(&RtlpCurDirRef->RefCount);
} }
RelativeName->ContainingDirectory = CurrentDirectory->Handle; RelativeName->ContainingDirectory = CurrentDirectory->Handle;
@ -831,8 +838,13 @@ RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
/* Check if a directory reference was grabbed */ /* Check if a directory reference was grabbed */
if (RelativeName->CurDirRef) if (RelativeName->CurDirRef)
{ {
/* FIXME: Not yet supported */ /* Decrease reference count */
UNIMPLEMENTED; if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount))
{
/* If no one uses it any longer, close handle & free */
NtClose(RelativeName->CurDirRef->Handle);
RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef);
}
RelativeName->CurDirRef = NULL; RelativeName->CurDirRef = NULL;
} }
} }
@ -984,89 +996,201 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
/* /*
* @implemented * @implemented
*/ */
NTSTATUS NTAPI NTSTATUS
RtlSetCurrentDirectory_U(PUNICODE_STRING dir) NTAPI
RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
{ {
UNICODE_STRING full; PCURDIR CurDir;
FILE_FS_DEVICE_INFORMATION device_info;
OBJECT_ATTRIBUTES Attr;
IO_STATUS_BLOCK iosb;
PCURDIR cd;
NTSTATUS Status; NTSTATUS Status;
USHORT size; RTL_PATH_TYPE PathType;
HANDLE handle = NULL; IO_STATUS_BLOCK IoStatusBlock;
PWSTR ptr; UNICODE_STRING FullPath, NtName;
PRTLP_CURDIR_REF OldCurDir = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo;
ULONG SavedLength, CharLength, FullPathLength;
HANDLE OldHandle = 0, CurDirHandle, OldCurDirHandle = 0;
DPRINT("RtlSetCurrentDirectory %wZ\n", dir); DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
full.Buffer = NULL; /* Can't set current directory on DOS device */
if (RtlIsDosDeviceName_Ustr(Path))
RtlAcquirePebLock ();
cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
{ {
RtlReleasePebLock (); return STATUS_NOT_A_DIRECTORY;
return STATUS_OBJECT_NAME_INVALID;
} }
DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full); /* Get current directory */
RtlAcquirePebLock();
CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
InitializeObjectAttributes (&Attr, /* Check if we have to drop current handle */
&full, if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE)
{
OldHandle = CurDir->Handle;
CurDir->Handle = NULL;
}
/* Allocate a buffer for full path (using max possible length */
FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength);
if (!FullPath.Buffer)
{
Status = STATUS_NO_MEMORY;
goto Leave;
}
/* Init string */
FullPath.Length = 0;
FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
/* Get new directory full path */
FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType);
if (!FullPathLength)
{
Status = STATUS_OBJECT_NAME_INVALID;
goto Leave;
}
SavedLength = FullPath.MaximumLength;
CharLength = FullPathLength / sizeof(WCHAR);
if (FullPathLength > FullPath.MaximumLength)
{
Status = STATUS_NAME_TOO_LONG;
goto Leave;
}
/* Translate it to NT name */
if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
{
Status = STATUS_OBJECT_NAME_INVALID;
goto Leave;
}
InitializeObjectAttributes(&ObjectAttributes, &NtName,
OBJ_CASE_INSENSITIVE | OBJ_INHERIT, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
NULL, NULL, NULL);
NULL);
Status = ZwOpenFile (&handle, /* If previous current directory was removable, then check it for dropping */
SYNCHRONIZE | FILE_TRAVERSE, if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
&Attr, {
&iosb, /* Get back normal handle */
FILE_SHARE_READ | FILE_SHARE_WRITE, CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); CurDir->Handle = 0;
/* Get device information */
Status = NtQueryVolumeInformationFile(CurDirHandle,
&IoStatusBlock,
&FileFsDeviceInfo,
sizeof(FileFsDeviceInfo),
FileFsDeviceInformation);
/* Retry without taking care of removable device */
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
RtlFreeUnicodeString( &full); Status = RtlSetCurrentDirectory_U(Path);
RtlReleasePebLock (); goto Leave;
return Status;
} }
}
/* don't keep the directory handle open on removable media */ else
if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
sizeof(device_info), FileFsDeviceInformation )) &&
(device_info.Characteristics & FILE_REMOVABLE_MEDIA))
{ {
DPRINT1("don't keep the directory handle open on removable media\n"); /* Open directory */
ZwClose( handle ); Status = NtOpenFile(&CurDirHandle,
handle = 0; SYNCHRONIZE | FILE_TRAVERSE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status)) goto Leave;
/* Get device information */
Status = NtQueryVolumeInformationFile(CurDirHandle,
&IoStatusBlock,
&FileFsDeviceInfo,
sizeof(FileFsDeviceInfo),
FileFsDeviceInformation);
if (!NT_SUCCESS(Status)) goto Leave;
} }
if (cd->Handle) /* If device is removable, mark handle */
ZwClose(cd->Handle); if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
cd->Handle = handle; {
CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
}
/* append trailing \ if missing */ FullPath.Length = FullPathLength;
size = full.Length / sizeof(WCHAR);
ptr = full.Buffer;
ptr += 4; /* skip \??\ prefix */
size -= 4;
/* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string. /* If full path isn't \ terminated, do it */
* So the nullterm is replaced with \ if (FullPath.Buffer[CharLength - 1] != L'\\')
* -Gunnar {
*/ if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
if (size && ptr[size - 1] != '\\') ptr[size++] = '\\'; {
Status = STATUS_NAME_TOO_LONG;
goto Leave;
}
memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR)); FullPath.Buffer[CharLength] = L'\\';
cd->DosPath.Buffer[size] = 0; FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
cd->DosPath.Length = size * sizeof(WCHAR); FullPath.Length += sizeof(WCHAR);
}
RtlFreeUnicodeString( &full); /* If we have previous current directory with only us as reference, save it */
if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1)
{
OldCurDirHandle = RtlpCurDirRef->Handle;
}
else
{
/* Allocate new current directory struct saving previous one */
OldCurDir = RtlpCurDirRef;
RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF));
if (!RtlpCurDirRef)
{
RtlpCurDirRef = OldCurDir;
OldCurDir = NULL;
Status = STATUS_NO_MEMORY;
goto Leave;
}
/* Set reference to 1 (us) */
RtlpCurDirRef->RefCount = 1;
}
/* Save new data */
CurDir->Handle = CurDirHandle;
RtlpCurDirRef->Handle = CurDirHandle;
CurDirHandle = 0;
/* Copy full path */
RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR));
CurDir->DosPath.Length = FullPath.Length;
Status = STATUS_SUCCESS;
Leave:
RtlReleasePebLock(); RtlReleasePebLock();
return STATUS_SUCCESS; if (FullPath.Buffer)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
}
if (NtName.Buffer)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
}
if (CurDirHandle) NtClose(CurDirHandle);
if (OldHandle) NtClose(OldHandle);
if (OldCurDirHandle) NtClose(OldCurDirHandle);
if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
{
NtClose(OldCurDir->Handle);
RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
}
return Status;
} }