Added a trailing null entry in NtQueryDirectoryObject().
[reactos.git] / reactos / ntoskrnl / ob / dirobj.c
index 78606ac..b94aecc 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: dirobj.c,v 1.9 2000/10/22 16:36:52 ekohl Exp $
+/* $Id: dirobj.c,v 1.14 2002/03/17 17:51:33 hbirr Exp $
  *
  * COPYRIGHT:      See COPYING in the top level directory
  * PROJECT:        ReactOS kernel
@@ -81,29 +81,32 @@ NTSTATUS STDCALL NtOpenDirectoryObject(PHANDLE DirectoryHandle,
  *     NtQueryDirectoryObject
  * 
  * DESCRIPTION
- *     Reads information from a namespace directory.
+ *     Reads information from a directory in the system namespace.
  *     
  * ARGUMENTS
+ *     DirObjHandle
+ *             Handle, obtained with NtOpenDirectoryObject(), which
+ *             must grant DIRECTORY_QUERY access to the directory
+ *             object.
+ *             
  *     DirObjInformation (OUT)
  *             Buffer to hold the data read.
  *             
  *     BufferLength
  *             Size of the buffer in bytes.
  *             
- *     GetNextIndex
- *             If TRUE then set ObjectIndex to the index of the
- *             next object.
- *             If FALSE then set ObjectIndex to the number of
- *             objects in the directory.
+ *     ReturnSingleEntry
+ *             When TRUE, only 1 entry is written in DirObjInformation;
+ *             otherwise as many as will fit in the buffer.
  *             
- *     IgnoreInputIndex
+ *     RestartScan
  *             If TRUE start reading at index 0.
  *             If FALSE start reading at the index specified
- *             by object index.
+ *             by object index *ObjectIndex.
  *             
  *     ObjectIndex
  *             Zero based index into the directory, interpretation
- *             depends on IgnoreInputIndex and GetNextIndex.
+ *             depends on RestartScan.
  *             
  *     DataWritten (OUT)
  *             Caller supplied storage for the number of bytes
@@ -111,134 +114,191 @@ NTSTATUS STDCALL NtOpenDirectoryObject(PHANDLE DirectoryHandle,
  *
  * RETURN VALUE
  *     Status.
+ *
+ * REVISIONS
+ *     2001-05-01 (ea)
+ *             Changed 4th, and 5th parameter names after
+ *             G.Nebbett "WNT/W2k Native API Reference".
+ *             Mostly rewritten.
  */
 NTSTATUS STDCALL NtQueryDirectoryObject (IN HANDLE DirObjHandle,
                                         OUT POBJDIR_INFORMATION 
                                                    DirObjInformation, 
                                         IN ULONG BufferLength, 
-                                        IN BOOLEAN GetNextIndex, 
-                                        IN BOOLEAN IgnoreInputIndex
+                                        IN BOOLEAN ReturnSingleEntry,
+                                        IN BOOLEAN RestartScan
                                         IN OUT PULONG ObjectIndex,
                                         OUT PULONG DataWritten OPTIONAL)
 {
-   PDIRECTORY_OBJECT dir = NULL;
-   PLIST_ENTRY current_entry;
-   POBJECT_HEADER current;
-   ULONG i = 0;
-   ULONG EntriesToSkip;
-   NTSTATUS Status;
-   ULONG SpaceRequired;
-   ULONG FirstFree;
-   
-   DPRINT("NtQueryDirectoryObject(DirObjHandle %x)\n", DirObjHandle);
+    PDIRECTORY_OBJECT   dir = NULL;
+    PLIST_ENTRY         current_entry = NULL;
+    POBJECT_HEADER      current = NULL;
+    ULONG               i = 0;
+    NTSTATUS            Status = STATUS_SUCCESS;
+    DWORD              DirectoryCount = 0;
+    DWORD              DirectorySize = 0;
+    ULONG               SpaceLeft = BufferLength;
+    ULONG               SpaceRequired = 0;
+    ULONG               NameLength = 0;
+    ULONG               TypeNameLength = 0;
+    POBJDIR_INFORMATION current_odi = DirObjInformation;
+    PBYTE               FirstFree = (PBYTE) DirObjInformation;
+
+
+    DPRINT("NtQueryDirectoryObject(DirObjHandle %x)\n", DirObjHandle);
 
-   Status = ObReferenceObjectByHandle(DirObjHandle,
+    /* FIXME: if previous mode == user, use ProbeForWrite
+     * on user params. */
+
+    /* Reference the DIRECTORY_OBJECT */
+    Status = ObReferenceObjectByHandle(DirObjHandle,
                                      DIRECTORY_QUERY,
                                      ObDirectoryType,
                                      UserMode,
                                      (PVOID*)&dir,
                                      NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   /*
-    * Optionally, skip over some entries at the start of the directory
-    */
-   if (!IgnoreInputIndex)
-     {
+    if (!NT_SUCCESS(Status))
+      {
+        return (Status);
+      }
+    /* Check ObjectIndex is not NULL */
+    if (NULL == ObjectIndex)
+      {
+        return (STATUS_INVALID_PARAMETER);
+      }
+    /*
+     * 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
+    {
+       DirectoryCount = 1;
+    }
+    // count is DirectoryCount + one null entry
+    DirectorySize = (DirectoryCount + 1) * sizeof (OBJDIR_INFORMATION);
+    if (DirectorySize > SpaceLeft)
+    {
+       return (STATUS_BUFFER_TOO_SMALL);
+    }
+    /*
+     * Optionally, skip over some entries at the start of the directory
+     * (use *ObjectIndex value)
+     */
+    current_entry = dir->head.Flink;
+    if (FALSE == RestartScan)
+      {
+       /* RestartScan == FALSE */
+        register ULONG EntriesToSkip = *ObjectIndex;
+
        CHECKPOINT;
        
-       EntriesToSkip = *ObjectIndex;
-       i = 0;
-       current_entry = dir->head.Flink;
-       
-       while ((i < EntriesToSkip) && (current_entry != &dir->head))
+       for (   ;
+               ((EntriesToSkip --) && (current_entry != & dir->head));
+               current_entry = current_entry->Flink
+               );
+       if ((EntriesToSkip) && (current_entry == & dir->head))
          {
-            current_entry = current_entry->Flink;
-            i++;
+            return (STATUS_NO_MORE_ENTRIES);
          }
-     }
-   else
-     {
-       current_entry = dir->head.Flink;
-       i = 0;
-     }
-   
-   /*
-    * Check if we have reached the end of the directory
-    */
-   if (current_entry != &dir->head)
-     {
-       *DataWritten = 0;
-       return(STATUS_NO_MORE_ENTRIES);
-     }
-   
-   /*
-    * Read the current entry into the buffer
-    */
-   FirstFree = sizeof(OBJDIR_INFORMATION);
-   
+      }
+    /*
+     * 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)
+          {
+      /* Any data? */
+           if (i) break; /* DONE */
+           /* FIXME: better error handling here! */
+           return (STATUS_NO_MORE_ENTRIES);
+          }
+  /*
+        * Compute the current OBJECT_HEADER memory
+        * object's address.
+        */
    current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
-   
-   SpaceRequired = (wcslen(current->Name.Buffer) + 1) * 2;
-   SpaceRequired = SpaceRequired + 
-     ((wcslen(current->ObjectType->TypeName.Buffer) + 1) * 2);
-   SpaceRequired = SpaceRequired + sizeof(OBJDIR_INFORMATION);
-   
-   if (SpaceRequired <= BufferLength)
-     {
-       
-       DirObjInformation->ObjectName.Length = 
-         current->Name.Length;
-       DirObjInformation->ObjectName.MaximumLength = 
-         current->Name.Length;
-       DirObjInformation->ObjectName.Buffer = 
-         (((PVOID)DirObjInformation) + FirstFree);
-       FirstFree = FirstFree + (wcslen(current->Name.Buffer + 1) * 2);
-       wcscpy(DirObjInformation->ObjectName.Buffer,
-              current->Name.Buffer);
-               
-       DirObjInformation->ObjectTypeName.Length = 
-         current->ObjectType->TypeName.Length;
-       DirObjInformation->ObjectTypeName.MaximumLength = 
-         current->ObjectType->TypeName.Length;
-       DirObjInformation->ObjectName.Buffer = 
-         (((PVOID)DirObjInformation) + FirstFree);
-       FirstFree = FirstFree + 
-         (wcslen(current->ObjectType->TypeName.Buffer + 1) * 2);
-       wcscpy(DirObjInformation->ObjectTypeName.Buffer,
-              current->ObjectType->TypeName.Buffer);
-       
-       *DataWritten = SpaceRequired;
-       Status = STATUS_SUCCESS;
-     }
-   else
-     {
-       Status = STATUS_BUFFER_TOO_SMALL;
-     }
-   
-   /*
-    * Store into ObjectIndex
-    */
-   if (GetNextIndex)
-     {
-       *ObjectIndex = i + 1;
-     }
-   else
-     {
-       i = 0;
-       current_entry = dir->head.Flink;
-       while (current_entry != (&dir->head))
-         {
-            current_entry = current_entry->Flink;
-            i++;
-         }
-       *ObjectIndex = i;
-     }
-   
-   return(STATUS_SUCCESS);
+  /*
+   * 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)
+       {
+               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 ObjectIndex
+     */
+    *ObjectIndex += DirectoryCount;
+    /*
+     * Report to the caller how much bytes
+     * we wrote in the user buffer.
+     */
+    if (NULL != DataWritten) 
+      {
+        *DataWritten = (BufferLength - SpaceLeft);
+      }
+    return (STATUS_SUCCESS);
 }
 
 
@@ -265,9 +325,10 @@ NTSTATUS STDCALL NtQueryDirectoryObject (IN HANDLE DirObjHandle,
  * RETURN VALUE
  *     Status.
  */
-NTSTATUS STDCALL NtCreateDirectoryObject (PHANDLE DirectoryHandle,
-                                         ACCESS_MASK DesiredAccess,
-                                         POBJECT_ATTRIBUTES ObjectAttributes)
+NTSTATUS STDCALL
+NtCreateDirectoryObject(PHANDLE DirectoryHandle,
+                       ACCESS_MASK DesiredAccess,
+                       POBJECT_ATTRIBUTES ObjectAttributes)
 {
    PDIRECTORY_OBJECT dir;
 
@@ -277,11 +338,11 @@ NTSTATUS STDCALL NtCreateDirectoryObject (PHANDLE DirectoryHandle,
          DirectoryHandle, DesiredAccess, ObjectAttributes,
          ObjectAttributes->ObjectName);
    
-   dir = ObCreateObject(DirectoryHandle,
-                       DesiredAccess,
-                       ObjectAttributes,
-                       ObDirectoryType);
-   return(STATUS_SUCCESS);
+   return(ObCreateObject(DirectoryHandle,
+                        DesiredAccess,
+                        ObjectAttributes,
+                        ObDirectoryType,
+                        (PVOID*)&dir));
 }
 
 /* EOF */