preliminary comment out the self-modifying code for RtlPrefetchMemoryNonTemporal
[reactos.git] / reactos / ntoskrnl / ob / dirobj.c
index 18ab363..e2558f0 100644 (file)
@@ -1,12 +1,11 @@
-/* $Id: dirobj.c,v 1.23 2004/08/15 16:39:09 chorns Exp $
+/* $Id$
  *
  * COPYRIGHT:      See COPYING in the top level directory
  * PROJECT:        ReactOS kernel
  * FILE:           ntoskrnl/ob/dirobj.c
  * PURPOSE:        Interface functions to directory object
- * PROGRAMMER:     David Welch (welch@mcmail.com)
- * UPDATE HISTORY:
- *                 22/05/98: Created
+ *
+ * PROGRAMMERS:    David Welch (welch@mcmail.com)
  */
 
 /* INCLUDES ***************************************************************/
  *
  * DESCRIPTION
  *     Opens a namespace directory object.
- *     
+ *
  * ARGUMENTS
  *     DirectoryHandle (OUT)
  *             Variable which receives the directory handle.
- *             
+ *
  *     DesiredAccess
  *             Desired access to the directory.
- *             
+ *
  *     ObjectAttributes
  *             Structure describing the directory.
- *             
+ *
  * RETURN VALUE
  *     Status.
- *     
+ *
  * NOTES
  *     Undocumented.
  */
@@ -47,77 +46,113 @@ NtOpenDirectoryObject (OUT PHANDLE DirectoryHandle,
                       IN ACCESS_MASK DesiredAccess,
                       IN POBJECT_ATTRIBUTES ObjectAttributes)
 {
-   PVOID Object;
-   NTSTATUS Status;
-
-   *DirectoryHandle = 0;
-   
-   Status = ObReferenceObjectByName(ObjectAttributes->ObjectName,
-                                   ObjectAttributes->Attributes,
-                                   NULL,
-                                   DesiredAccess,
-                                   ObDirectoryType,
-                                   UserMode,
-                                   NULL,
-                                   &Object);
-   if (!NT_SUCCESS(Status))
+   HANDLE hDirectory;
+   KPROCESSOR_MODE PreviousMode;
+   NTSTATUS Status = STATUS_SUCCESS;
+
+   PAGED_CODE();
+
+   PreviousMode = ExGetPreviousMode();
+
+   if(PreviousMode != KernelMode)
+   {
+     _SEH_TRY
+     {
+       ProbeForWriteHandle(DirectoryHandle);
+     }
+     _SEH_HANDLE
+     {
+       Status = _SEH_GetExceptionCode();
+     }
+     _SEH_END;
+
+     if(!NT_SUCCESS(Status))
+     {
+       DPRINT1("NtOpenDirectoryObject failed, Status: 0x%x\n", Status);
+       return Status;
+     }
+   }
+
+   Status = ObOpenObjectByName(ObjectAttributes,
+                               ObDirectoryType,
+                               NULL,
+                               PreviousMode,
+                               DesiredAccess,
+                               NULL,
+                               &hDirectory);
+   if(NT_SUCCESS(Status))
+   {
+     _SEH_TRY
+     {
+       *DirectoryHandle = hDirectory;
+     }
+     _SEH_HANDLE
      {
-       return Status;
+       Status = _SEH_GetExceptionCode();
      }
-   
-   Status = ObCreateHandle(PsGetCurrentProcess(),
-                          Object,
-                          DesiredAccess,
-                          FALSE,
-                          DirectoryHandle);
-   return STATUS_SUCCESS;
+     _SEH_END;
+   }
+
+   return Status;
 }
 
 
 /**********************************************************************
  * NAME                                                        EXPORTED
  *     NtQueryDirectoryObject
- * 
+ *
  * DESCRIPTION
  *     Reads information from a directory in the system namespace.
- *     
+ *
  * ARGUMENTS
  *     DirectoryHandle
  *             Handle, obtained with NtOpenDirectoryObject(), which
  *             must grant DIRECTORY_QUERY access to the directory
  *             object.
- *             
+ *
  *     Buffer (OUT)
  *             Buffer to hold the data read.
- *             
+ *
  *     BufferLength
  *             Size of the buffer in bytes.
- *             
+ *
  *     ReturnSingleEntry
  *             When TRUE, only 1 entry is written in DirObjInformation;
  *             otherwise as many as will fit in the buffer.
- *             
+ *
  *     RestartScan
  *             If TRUE start reading at index 0.
  *             If FALSE start reading at the index specified
  *             by object index *ObjectIndex.
- *             
+ *
  *     Context
  *             Zero based index into the directory, interpretation
  *             depends on RestartScan.
- *             
+ *
  *     ReturnLength (OUT)
  *             Caller supplied storage for the number of bytes
  *             written (or NULL).
  *
  * RETURN VALUE
- *     Status.
+ *     STATUS_SUCCESS - At least one (possibly more, depending on
+ *                       parameters and buffer size) dir entry is
+ *                       returned.
+ *      STATUS_NO_MORE_ENTRIES - Directory is exhausted
+ *      STATUS_BUFFER_TOO_SMALL - There isn't enough room in the
+ *                                buffer to return even 1 entry.
+ *                                ReturnLength will hold the required
+ *                                buffer size to return all remaining
+ *                                dir entries
+ *      Other - Status code
+ *
  *
- * REVISIONS
- *     2001-05-01 (ea)
- *             Changed 4th, and 5th parameter names after
- *             G.Nebbett "WNT/W2k Native API Reference".
- *             Mostly rewritten.
+ * NOTES
+ *      Although you can iterate over the directory by calling this
+ *      function multiple times, the directory is unlocked between
+ *      calls. This means that another thread can change the directory
+ *      and so iterating doesn't guarantee a consistent picture of the
+ *      directory. Best thing is to retrieve all directory entries in
+ *      one call.
  */
 NTSTATUS STDCALL
 NtQueryDirectoryObject (IN HANDLE DirectoryHandle,
@@ -128,205 +163,262 @@ NtQueryDirectoryObject (IN HANDLE DirectoryHandle,
                        IN OUT PULONG Context,
                        OUT PULONG ReturnLength OPTIONAL)
 {
-    PDIRECTORY_OBJECT   dir = NULL;
-    PLIST_ENTRY         current_entry = NULL;
-    POBJECT_HEADER      current = NULL;
-    ULONG               i = 0;
-    NTSTATUS            Status = STATUS_SUCCESS;
-    ULONG               DirectoryCount = 0;
-    ULONG               DirectorySize = 0;
-    ULONG               SpaceLeft = BufferLength;
-    ULONG               SpaceRequired = 0;
-    ULONG               NameLength = 0;
-    ULONG               TypeNameLength = 0;
-    PDIRECTORY_BASIC_INFORMATION current_odi = (PDIRECTORY_BASIC_INFORMATION) Buffer;
-    PUCHAR              FirstFree = (PUCHAR) Buffer;
-
-
-    DPRINT("NtQueryDirectoryObject(DirectoryHandle %x)\n", DirectoryHandle);
-
-    /* FIXME: if previous mode == user, use ProbeForWrite
-     * on user params. */
-
-    /* Check Context is not NULL */
-    if (NULL == Context)
+  PDIRECTORY_OBJECT Directory;
+  KPROCESSOR_MODE PreviousMode;
+  ULONG SkipEntries = 0;
+  ULONG NextEntry = 0;
+  ULONG CopyBytes = 0;
+  NTSTATUS Status = STATUS_SUCCESS;
+
+  PAGED_CODE();
+
+  PreviousMode = ExGetPreviousMode();
+
+  if(PreviousMode != KernelMode)
+  {
+    _SEH_TRY
+    {
+      /* a test showed that the Buffer pointer just has to be 16 bit aligned,
+         propably due to the fact that most information that needs to be copied
+         is unicode strings */
+      ProbeForWrite(Buffer,
+                    BufferLength,
+                    sizeof(WCHAR));
+      ProbeForWriteUlong(Context);
+      if(!RestartScan)
       {
-        return (STATUS_INVALID_PARAMETER);
+        SkipEntries = *Context;
       }
-
-    /* Reference the DIRECTORY_OBJECT */
-    Status = ObReferenceObjectByHandle(DirectoryHandle,
-                                     DIRECTORY_QUERY,
-                                     ObDirectoryType,
-                                     UserMode,
-                                     (PVOID*)&dir,
-                                     NULL);
-    if (!NT_SUCCESS(Status))
+      if(ReturnLength != NULL)
       {
-        return (Status);
+        ProbeForWriteUlong(ReturnLength);
       }
-
-    /*
-     * Compute the number of directory entries
-     * and the size of the array (in bytes).
-     * One more entry marks the end of the array.
-     */
-    if (FALSE == ReturnSingleEntry)
-    {
-        for ( current_entry = dir->head.Flink;
-              (current_entry != & dir->head);
-              current_entry = current_entry->Flink
-              )
-        {
-          ++ DirectoryCount;
-        }
     }
-    else
+    _SEH_HANDLE
     {
-       DirectoryCount = 1;
+      Status = _SEH_GetExceptionCode();
     }
-    // count is DirectoryCount + one null entry
-    DirectorySize = (DirectoryCount + 1) * sizeof (DIRECTORY_BASIC_INFORMATION);
-    if (DirectorySize > SpaceLeft)
+    _SEH_END;
+
+    if(!NT_SUCCESS(Status))
     {
-        ObDereferenceObject(dir);
-       return (STATUS_BUFFER_TOO_SMALL);
+      DPRINT1("NtQueryDirectoryObject failed, Status: 0x%x\n", Status);
+      return Status;
     }
-    /*
-     * Optionally, skip over some entries at the start of the directory
-     * (use *ObjectIndex value)
-     */
-    current_entry = dir->head.Flink;
-    if (FALSE == RestartScan)
+  }
+  else if(!RestartScan)
+  {
+    SkipEntries = *Context;
+  }
+
+  Status = ObReferenceObjectByHandle(DirectoryHandle,
+                                     DIRECTORY_QUERY,
+                                     ObDirectoryType,
+                                     PreviousMode,
+                                     (PVOID*)&Directory,
+                                     NULL);
+  if(NT_SUCCESS(Status))
+  {
+    PVOID TemporaryBuffer = ExAllocatePool(NonPagedPool,
+                                           BufferLength);
+    if(TemporaryBuffer != NULL)
+    {
+      POBJECT_HEADER EntryHeader;
+      PLIST_ENTRY ListEntry;
+      KIRQL OldLevel;
+      ULONG RequiredSize = sizeof(OBJECT_DIRECTORY_INFORMATION);
+      ULONG nDirectories = 0;
+      POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
+
+      Status = STATUS_NO_MORE_ENTRIES;
+
+      KeAcquireSpinLock(&Directory->Lock, &OldLevel);
+
+      for(ListEntry = Directory->head.Flink;
+          ListEntry != &Directory->head;
+          ListEntry = ListEntry->Flink)
       {
-       /* RestartScan == FALSE */
-        register ULONG EntriesToSkip = *Context;
-
-       CHECKPOINT;
-       
-       for (   ;
-               ((EntriesToSkip --) && (current_entry != & dir->head));
-               current_entry = current_entry->Flink
-               );
-       if ((EntriesToSkip) && (current_entry == & dir->head))
-         {
-            ObDereferenceObject(dir);
-            return (STATUS_NO_MORE_ENTRIES);
-         }
+        NextEntry++;
+        if(SkipEntries == 0)
+        {
+          PUNICODE_STRING Name, Type;
+          ULONG EntrySize;
+
+          EntryHeader = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, Entry);
+
+          /* calculate the size of the required buffer space for this entry */
+          Name = (HEADER_TO_OBJECT_NAME(EntryHeader)->Name.Length != 0 ? &HEADER_TO_OBJECT_NAME(EntryHeader)->Name : NULL);
+          Type = &EntryHeader->Type->Name;
+          EntrySize = sizeof(OBJECT_DIRECTORY_INFORMATION) +
+                      ((Name != NULL) ? ((ULONG)Name->Length + sizeof(WCHAR)) : 0) +
+                      (ULONG)EntryHeader->Type->Name.Length + sizeof(WCHAR);
+
+          if(RequiredSize + EntrySize <= BufferLength)
+          {
+            /* the buffer is large enough to receive this entry. It would've
+               been much easier if the strings were directly appended to the
+               OBJECT_DIRECTORY_INFORMATION structured written into the buffer */
+            if(Name != NULL)
+              DirInfo->ObjectName = *Name;
+            else
+            {
+              DirInfo->ObjectName.Length = DirInfo->ObjectName.MaximumLength = 0;
+              DirInfo->ObjectName.Buffer = NULL;
+            }
+            DirInfo->ObjectTypeName = *Type;
+
+            nDirectories++;
+            RequiredSize += EntrySize;
+
+            Status = STATUS_SUCCESS;
+
+            if(ReturnSingleEntry)
+            {
+              /* we're only supposed to query one entry, so bail and copy the
+                 strings to the buffer */
+              break;
+            }
+            DirInfo++;
+          }
+          else
+          {
+            if(ReturnSingleEntry)
+            {
+              /* the buffer is too small, so return the number of bytes that
+                 would've been required for this query */
+              RequiredSize += EntrySize;
+              Status = STATUS_BUFFER_TOO_SMALL;
+            }
+
+            /* we couldn't query this entry, so leave the index that will be stored
+               in Context to this entry so the caller can query it the next time
+               he queries (hopefully with a buffer that is large enough then...) */
+            NextEntry--;
+
+            /* just copy the entries that fit into the buffer */
+            break;
+          }
+        }
+        else
+        {
+          /* skip the entry */
+          SkipEntries--;
+        }
+      }
+
+      if(!ReturnSingleEntry && ListEntry != &Directory->head)
+      {
+        /* there are more entries to enumerate but the buffer is already full.
+           only tell this to the user if he queries multiple entries */
+        Status = STATUS_MORE_ENTRIES;
       }
-    /*
-     * Initialize the array of OBJDIR_INFORMATION.
-     */
-    RtlZeroMemory (FirstFree, DirectorySize);
-    /*
-     * Move FirstFree to point to the Unicode strings area
-     */
-    FirstFree += DirectorySize;
-    /*
-     * Compute how much space is left after allocating the
-     * array in the user buffer.
-     */
-    SpaceLeft -= DirectorySize;
-    /* Scan the directory */
-    do
-    { 
-        /*
-         * Check if we reached the end of the directory.
-         */
-        if (current_entry == & dir->head)
+
+      if(NT_SUCCESS(Status) && nDirectories > 0)
+      {
+        PWSTR strbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer + nDirectories + 1);
+        PWSTR deststrbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)Buffer + nDirectories + 1);
+        memset((POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer + nDirectories, 0, sizeof(OBJECT_DIRECTORY_INFORMATION));
+
+        CopyBytes = (nDirectories + 1) * sizeof(OBJECT_DIRECTORY_INFORMATION);
+
+        /* copy the names from the objects and append them to the list of the
+           objects. copy to the temporary buffer only because the directory
+           lock can't be released and the buffer might be pagable memory! */
+        for(DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
+            nDirectories > 0;
+            nDirectories--, DirInfo++)
+        {
+          ULONG NameLength;
+
+          if(DirInfo->ObjectName.Length > 0)
           {
-      /* Any data? */
-           if (i) break; /* DONE */
-           /* FIXME: better error handling here! */
-            ObDereferenceObject(dir);
-           return (STATUS_NO_MORE_ENTRIES);
+            RtlCopyMemory(strbuf,
+                          DirInfo->ObjectName.Buffer,
+                          DirInfo->ObjectName.Length);
+            /* change the buffer pointer to the buffer */
+            DirInfo->ObjectName.Buffer = deststrbuf;
+            NameLength = DirInfo->ObjectName.Length / sizeof(WCHAR);
+            /* NULL-terminate the string */
+            strbuf[NameLength] = L'\0';
+            strbuf += NameLength + 1;
+            deststrbuf += NameLength + 1;
+
+            CopyBytes += (NameLength + 1) * sizeof(WCHAR);
           }
-  /*
-        * Compute the current OBJECT_HEADER memory
-        * object's address.
-        */
-   current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
-  /*
-   * Compute the space required in the user buffer to copy
-   * the data from the current object:
-        *
-        * Name (WCHAR) 0 TypeName (WCHAR) 0
-   */
-       NameLength = (wcslen (current->Name.Buffer) * sizeof (WCHAR));
-       TypeNameLength = (wcslen (current->ObjectType->TypeName.Buffer) * sizeof (WCHAR));
-  SpaceRequired = (NameLength + 1) * sizeof (WCHAR)
-    + (TypeNameLength + 1) * sizeof (WCHAR);
-       /*
-        * Check for free space in the user buffer.
-        */
-       if (SpaceRequired > SpaceLeft)
-       {
-                ObDereferenceObject(dir);
-               return (STATUS_BUFFER_TOO_SMALL);
-       }
-  /*
-   * Copy the current directory entry's data into the buffer
-        * and update the OBJDIR_INFORMATION entry in the array.
-   */
-       /* --- Object's name --- */
-       current_odi->ObjectName.Length        = NameLength;
-       current_odi->ObjectName.MaximumLength = (NameLength + sizeof (WCHAR));
-       current_odi->ObjectName.Buffer        = (PWCHAR) FirstFree;
-       wcscpy ((PWCHAR) FirstFree, current->Name.Buffer);
-       FirstFree += (current_odi->ObjectName.MaximumLength);
-       /* --- Object type's name --- */
-       current_odi->ObjectTypeName.Length        = TypeNameLength;
-       current_odi->ObjectTypeName.MaximumLength = (TypeNameLength + sizeof (WCHAR));
-       current_odi->ObjectTypeName.Buffer        = (PWCHAR) FirstFree;
-       wcscpy ((PWCHAR) FirstFree, current->ObjectType->TypeName.Buffer);
-       FirstFree += (current_odi->ObjectTypeName.MaximumLength);
-       /* Next entry in the array */
-       ++ current_odi;
-       /* Decrease the space left count */     
-       SpaceLeft -= SpaceRequired;
-       /* Increase the object index number */
-       ++ i;
-       /* Next object in the directory */
-       current_entry = current_entry->Flink;
-
-    } while (FALSE == ReturnSingleEntry);
-    /*
-     * Store current index in Context
-     */
-    *Context += DirectoryCount;
-    /*
-     * Report to the caller how much bytes
-     * we wrote in the user buffer.
-     */
-    if (NULL != ReturnLength)
+
+          RtlCopyMemory(strbuf,
+                        DirInfo->ObjectTypeName.Buffer,
+                        DirInfo->ObjectTypeName.Length);
+          /* change the buffer pointer to the buffer */
+          DirInfo->ObjectTypeName.Buffer = deststrbuf;
+          NameLength = DirInfo->ObjectTypeName.Length / sizeof(WCHAR);
+          /* NULL-terminate the string */
+          strbuf[NameLength] = L'\0';
+          strbuf += NameLength + 1;
+          deststrbuf += NameLength + 1;
+
+          CopyBytes += (NameLength + 1) * sizeof(WCHAR);
+        }
+      }
+
+      KeReleaseSpinLock(&Directory->Lock, OldLevel);
+      ObDereferenceObject(Directory);
+
+      if(NT_SUCCESS(Status) || ReturnSingleEntry)
       {
-        *ReturnLength = (BufferLength - SpaceLeft);
+        _SEH_TRY
+        {
+          if(CopyBytes != 0)
+          {
+            RtlCopyMemory(Buffer,
+                          TemporaryBuffer,
+                          CopyBytes);
+          }
+          *Context = NextEntry;
+          if(ReturnLength != NULL)
+          {
+            *ReturnLength = RequiredSize;
+          }
+        }
+        _SEH_HANDLE
+        {
+          Status = _SEH_GetExceptionCode();
+        }
+        _SEH_END;
       }
-    ObDereferenceObject(dir);
-    return (STATUS_SUCCESS);
+
+      ExFreePool(TemporaryBuffer);
+    }
+    else
+    {
+      Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+  }
+
+  return Status;
 }
 
 
 /**********************************************************************
  * NAME                                                (EXPORTED as Zw)
  *     NtCreateDirectoryObject
- *     
+ *
  * DESCRIPTION
  *     Creates or opens a directory object (a container for other
  *     objects).
- *     
+ *
  * ARGUMENTS
  *     DirectoryHandle (OUT)
- *             Caller supplied storage for the handle of the 
+ *             Caller supplied storage for the handle of the
  *             directory.
- *             
+ *
  *     DesiredAccess
  *             Access desired to the directory.
- *             
+ *
  *     ObjectAttributes
  *             Object attributes initialized with
  *             InitializeObjectAttributes.
- *             
+ *
  * RETURN VALUE
  *     Status.
  */
@@ -335,37 +427,75 @@ NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle,
                         IN ACCESS_MASK DesiredAccess,
                         IN POBJECT_ATTRIBUTES ObjectAttributes)
 {
-  PDIRECTORY_OBJECT DirectoryObject;
-  NTSTATUS Status;
+  PDIRECTORY_OBJECT Directory;
+  HANDLE hDirectory;
+  KPROCESSOR_MODE PreviousMode;
+  NTSTATUS Status = STATUS_SUCCESS;
+
+  PAGED_CODE();
 
   DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
-        "DesiredAccess %x, ObjectAttributes %x, "
-        "ObjectAttributes->ObjectName %wZ)\n",
-        DirectoryHandle, DesiredAccess, ObjectAttributes,
-        ObjectAttributes->ObjectName);
-
-  Status = ObCreateObject (ExGetPreviousMode(),
-                          ObDirectoryType,
-                          ObjectAttributes,
-                          ExGetPreviousMode(),
-                          NULL,
-                          sizeof(DIRECTORY_OBJECT),
-                          0,
-                          0,
-                          (PVOID*)&DirectoryObject);
-  if (!NT_SUCCESS(Status))
+         "DesiredAccess %x, ObjectAttributes %x\n",
+          DirectoryHandle, DesiredAccess, ObjectAttributes);
+
+  PreviousMode = ExGetPreviousMode();
+
+  if(PreviousMode != KernelMode)
+  {
+    _SEH_TRY
     {
+      ProbeForWriteHandle(DirectoryHandle);
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+
+    if(!NT_SUCCESS(Status))
+    {
+      DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status);
       return Status;
     }
+  }
 
-  Status = ObInsertObject ((PVOID)DirectoryObject,
-                          NULL,
-                          DesiredAccess,
-                          0,
-                          NULL,
-                          DirectoryHandle);
+  Status = ObCreateObject(PreviousMode,
+                          ObDirectoryType,
+                          ObjectAttributes,
+                          PreviousMode,
+                          NULL,
+                          sizeof(DIRECTORY_OBJECT),
+                          0,
+                          0,
+                          (PVOID*)&Directory);
+
+  if(NT_SUCCESS(Status))
+  {
+    Status = ObInsertObject((PVOID)Directory,
+                            NULL,
+                            DesiredAccess,
+                            0,
+                            NULL,
+                            &hDirectory);
+    if (!NT_SUCCESS(Status))
+    {
+       ObMakeTemporaryObject(Directory);
+    }
+    ObDereferenceObject(Directory);
 
-  ObDereferenceObject(DirectoryObject);
+    if(NT_SUCCESS(Status))
+    {
+      _SEH_TRY
+      {
+        *DirectoryHandle = hDirectory;
+      }
+      _SEH_HANDLE
+      {
+        Status = _SEH_GetExceptionCode();
+      }
+      _SEH_END;
+    }
+  }
 
   return Status;
 }