Author: pschweitzer
Date: Fri Jan 20 20:21:16 2012
New Revision: 55017
URL:
http://svn.reactos.org/svn/reactos?rev=55017&view=rev
Log:
[RTL]
- Reimplement RtlSetCurrentDirectory_U. This fixes bugs & implements references count
- Fix the FIXME in RtlpDosPathNameToRelativeNtPathName_Ustr by incrementing references
count
Modified:
trunk/reactos/lib/rtl/path.c
Modified: trunk/reactos/lib/rtl/path.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/path.c?rev=55017&a…
==============================================================================
--- trunk/reactos/lib/rtl/path.c [iso-8859-1] (original)
+++ trunk/reactos/lib/rtl/path.c [iso-8859-1] Fri Jan 20 20:21:16 2012
@@ -6,6 +6,8 @@
* PROGRAMMERS: Wine team
* Thomas Weidenmueller
* Gunnar Dalsnes
+ * Alex Ionescu (alex.ionescu(a)reactos.org)
+ * Pierre Schweitzer (pierre(a)reactos.org)
*/
/* INCLUDES *****************************************************************/
@@ -20,6 +22,11 @@
#define MAX_PFX_SIZE 16
#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 ********************************************************************/
@@ -617,7 +624,7 @@
if (InputPathType == RtlPathTypeRelative)
{
/* Get current directory */
- CurrentDirectory = (PCURDIR)&(NtCurrentPeb
()->ProcessParameters->CurrentDirectory.DosPath);
+ CurrentDirectory =
&(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
if (CurrentDirectory->Handle)
{
Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
@@ -660,7 +667,7 @@
RelativeName->CurDirRef = RtlpCurDirRef;
if (RelativeName->CurDirRef)
{
- /* FIXME: Increment reference count */
+ InterlockedIncrement(&RtlpCurDirRef->RefCount);
}
RelativeName->ContainingDirectory = CurrentDirectory->Handle;
@@ -831,8 +838,13 @@
/* Check if a directory reference was grabbed */
if (RelativeName->CurDirRef)
{
- /* FIXME: Not yet supported */
- UNIMPLEMENTED;
+ /* Decrease reference count */
+ 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;
}
}
@@ -984,89 +996,201 @@
/*
* @implemented
*/
-NTSTATUS NTAPI
-RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
-{
- UNICODE_STRING full;
- FILE_FS_DEVICE_INFORMATION device_info;
- OBJECT_ATTRIBUTES Attr;
- IO_STATUS_BLOCK iosb;
- PCURDIR cd;
- NTSTATUS Status;
- USHORT size;
- HANDLE handle = NULL;
- PWSTR ptr;
-
- DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
-
- full.Buffer = NULL;
-
- RtlAcquirePebLock ();
-
- cd = (PCURDIR)&NtCurrentPeb
()->ProcessParameters->CurrentDirectory.DosPath;
-
- if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
- {
- RtlReleasePebLock ();
- return STATUS_OBJECT_NAME_INVALID;
- }
-
- DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
-
- InitializeObjectAttributes (&Attr,
- &full,
- OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
- NULL,
- NULL);
-
- Status = ZwOpenFile (&handle,
- SYNCHRONIZE | FILE_TRAVERSE,
- &Attr,
- &iosb,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
-
- if (!NT_SUCCESS(Status))
- {
- RtlFreeUnicodeString( &full);
- RtlReleasePebLock ();
- return Status;
- }
-
- /* don't keep the directory handle open on removable media */
- 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");
- ZwClose( handle );
- handle = 0;
- }
-
- if (cd->Handle)
- ZwClose(cd->Handle);
- cd->Handle = handle;
-
- /* append trailing \ if missing */
- size = full.Length / sizeof(WCHAR);
- ptr = full.Buffer;
- ptr += 4; /* skip \??\ prefix */
- size -= 4;
-
- /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
- * So the nullterm is replaced with \
- * -Gunnar
- */
- if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
-
- memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
- cd->DosPath.Buffer[size] = 0;
- cd->DosPath.Length = size * sizeof(WCHAR);
-
- RtlFreeUnicodeString( &full);
- RtlReleasePebLock();
-
- return STATUS_SUCCESS;
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
+{
+ PCURDIR CurDir;
+ NTSTATUS Status;
+ RTL_PATH_TYPE PathType;
+ IO_STATUS_BLOCK IoStatusBlock;
+ 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_U %wZ\n", Path);
+
+ /* Can't set current directory on DOS device */
+ if (RtlIsDosDeviceName_Ustr(Path))
+ {
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* Get current directory */
+ RtlAcquirePebLock();
+ CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
+
+ /* Check if we have to drop current handle */
+ 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,
+ NULL, NULL);
+
+ /* If previous current directory was removable, then check it for dropping */
+ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) ==
RTL_CURDIR_ALL_FLAGS)
+ {
+ /* Get back normal handle */
+ CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) &
~RTL_CURDIR_ALL_FLAGS);
+ 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))
+ {
+ Status = RtlSetCurrentDirectory_U(Path);
+ goto Leave;
+ }
+ }
+ else
+ {
+ /* Open directory */
+ Status = NtOpenFile(&CurDirHandle,
+ 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 device is removable, mark handle */
+ if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
+ {
+ CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
+ }
+
+ FullPath.Length = FullPathLength;
+
+ /* If full path isn't \ terminated, do it */
+ if (FullPath.Buffer[CharLength - 1] != L'\\')
+ {
+ if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
+ {
+ Status = STATUS_NAME_TOO_LONG;
+ goto Leave;
+ }
+
+ FullPath.Buffer[CharLength] = L'\\';
+ FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
+ FullPath.Length += sizeof(WCHAR);
+ }
+
+ /* 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();
+
+ 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;
}