Added a trailing null entry in NtQueryDirectoryObject().
[reactos.git] / reactos / ntoskrnl / ob / dirobj.c
index cc6bd8e..b94aecc 100644 (file)
@@ -1,4 +1,5 @@
-/*
+/* $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
  * FILE:           ntoskrnl/ob/dirobj.c
 
 /* INCLUDES ***************************************************************/
 
-#include <windows.h>
-#include <wstring.h>
 #include <ddk/ntddk.h>
 #include <internal/ob.h>
 #include <internal/io.h>
-#include <internal/string.h>
 
 #define NDEBUG
 #include <internal/debug.h>
 
 /* FUNCTIONS **************************************************************/
 
-NTSTATUS NtOpenDirectoryObject(PHANDLE DirectoryHandle,
-                              ACCESS_MASK DesiredAccess,
-                              POBJECT_ATTRIBUTES ObjectAttributes)
-{
-   return(ZwOpenDirectoryObject(DirectoryHandle,
-                               DesiredAccess,
-                               ObjectAttributes));
-}
 
-NTSTATUS ZwOpenDirectoryObject(PHANDLE DirectoryHandle,
-                              ACCESS_MASK DesiredAccess,
-                              POBJECT_ATTRIBUTES ObjectAttributes)
-/*
- * FUNCTION: 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
- * RETURNS: Status
- * NOTES: Undocumented
+/**********************************************************************
+ * NAME                                                        EXPORTED
+ *     NtOpenDirectoryObject
+ *
+ * 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.
  */
+NTSTATUS STDCALL NtOpenDirectoryObject(PHANDLE DirectoryHandle,
+                                      ACCESS_MASK DesiredAccess,
+                                      POBJECT_ATTRIBUTES ObjectAttributes)
 {
    PVOID Object;
    NTSTATUS Status;
-   
+
    *DirectoryHandle = 0;
    
    Status = ObReferenceObjectByName(ObjectAttributes->ObjectName,
@@ -60,211 +64,285 @@ NTSTATUS ZwOpenDirectoryObject(PHANDLE DirectoryHandle,
                                    &Object);
    if (!NT_SUCCESS(Status))
      {
-       return(Status);
+       return Status;
      }
-       
+   
    Status = ObCreateHandle(PsGetCurrentProcess(),
                           Object,
                           DesiredAccess,
                           FALSE,
                           DirectoryHandle);
-   return(STATUS_SUCCESS);
+   return STATUS_SUCCESS;
 }
 
-NTSTATUS NtQueryDirectoryObject(IN HANDLE DirObjHandle,
-                               OUT POBJDIR_INFORMATION DirObjInformation, 
-                               IN ULONG                BufferLength, 
-                               IN BOOLEAN              GetNextIndex, 
-                               IN BOOLEAN              IgnoreInputIndex, 
-                               IN OUT PULONG           ObjectIndex,
-                               OUT PULONG              DataWritten OPTIONAL)
-{
-   return(ZwQueryDirectoryObject(DirObjHandle,
-                                DirObjInformation,
-                                BufferLength,
-                                GetNextIndex,
-                                IgnoreInputIndex,
-                                ObjectIndex,
-                                DataWritten));
-}
 
-NTSTATUS ZwQueryDirectoryObject(IN HANDLE DirObjHandle,
-                               OUT POBJDIR_INFORMATION DirObjInformation, 
-                               IN ULONG                BufferLength, 
-                               IN BOOLEAN              GetNextIndex, 
-                               IN BOOLEAN              IgnoreInputIndex, 
-                               IN OUT PULONG           ObjectIndex,
-                               OUT PULONG              DataWritten OPTIONAL)
-/*
- * FUNCTION: Reads information from a namespace directory
- * ARGUMENTS:
- *        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
- *        IgnoreInputIndex = If TRUE start reading at index 0
- *                           If FALSE start reading at the index specified
- *                           by object index
- *        ObjectIndex = Zero based index into the directory, interpretation
- *                      depends on IgnoreInputIndex and GetNextIndex
- *        DataWritten (OUT) = Caller supplied storage for the number of bytes
- *                            written (or NULL)
- * RETURNS: Status
+/**********************************************************************
+ * NAME                                                        EXPORTED
+ *     NtQueryDirectoryObject
+ * 
+ * DESCRIPTION
+ *     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.
+ *             
+ *     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.
+ *             
+ *     ObjectIndex
+ *             Zero based index into the directory, interpretation
+ *             depends on RestartScan.
+ *             
+ *     DataWritten (OUT)
+ *             Caller supplied storage for the number of bytes
+ *             written (or NULL).
+ *
+ * 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 ReturnSingleEntry,
+                                        IN BOOLEAN RestartScan, 
+                                        IN OUT PULONG ObjectIndex,
+                                        OUT PULONG DataWritten OPTIONAL)
 {
-   PDIRECTORY_OBJECT dir = NULL;
-   ULONG EntriesToRead;
-   PLIST_ENTRY current_entry;
-   POBJECT_HEADER current;
-   ULONG i=0;
-   ULONG EntriesToSkip;
-   NTSTATUS Status;
-   
-   DPRINT("ZwQueryDirectoryObject(DirObjHandle %x)\n",DirObjHandle);
-   DPRINT("dir %x namespc_root %x\n",dir,HEADER_TO_BODY(&(namespc_root.hdr)));
-   
-//   assert_irql(PASSIVE_LEVEL);
-   
-   Status = ObReferenceObjectByHandle(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);
+
+    /* 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 (Status != STATUS_SUCCESS)
-     {
-       return(Status);
-     }
-   
-   EntriesToRead = BufferLength / sizeof(OBJDIR_INFORMATION);
-   *DataWritten = 0;
-   
-   DPRINT("EntriesToRead %d\n",EntriesToRead);
-   
-   current_entry = dir->head.Flink;
-   
-   /*
-    * 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;
-       while ( i<EntriesToSkip && current_entry!=NULL)
+       for (   ;
+               ((EntriesToSkip --) && (current_entry != & dir->head));
+               current_entry = current_entry->Flink
+               );
+       if ((EntriesToSkip) && (current_entry == & dir->head))
          {
-            current_entry = current_entry->Flink;
+            return (STATUS_NO_MORE_ENTRIES);
          }
-     }
-   
-   DPRINT("DirObjInformation %x\n",DirObjInformation);
-   
-   /*
-    * Read the maximum entries possible into the buffer
-    */
-   while ( i<EntriesToRead && current_entry!=(&(dir->head)))
-     {
-       current = CONTAINING_RECORD(current_entry,OBJECT_HEADER,Entry);
-       DPRINT("Scanning %w\n",current->Name.Buffer);
-       DirObjInformation[i].ObjectName.Buffer = 
-                      ExAllocatePool(NonPagedPool,(current->Name.Length+1)*2);
-       DirObjInformation[i].ObjectName.Length = current->Name.Length;
-       DirObjInformation[i].ObjectName.MaximumLength = current->Name.Length;
-       DPRINT("DirObjInformation[i].ObjectName.Buffer %x\n",
-              DirObjInformation[i].ObjectName.Buffer);
-       RtlCopyUnicodeString(&DirObjInformation[i].ObjectName,
-                            &(current->Name));
-       i++;
+      }
+    /*
+     * 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);
+  /*
+   * 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;
-       (*DataWritten) = (*DataWritten) + sizeof(OBJDIR_INFORMATION);
-       CHECKPOINT;
-     }
-   CHECKPOINT;
-   
-   /*
-    * Optionally, count the number of entries in the directory
-    */
-   if (GetNextIndex)
-     {
-       *ObjectIndex=i;
-     }
-   else
-     {
-       while ( current_entry!=(&(dir->head)) )
-         {
-            current_entry=current_entry->Flink;
-            i++;
-         }
-       *ObjectIndex=i;
-     }
-   return(STATUS_SUCCESS);
-}
-
 
-NTSTATUS NtCreateDirectoryObject(PHANDLE DirectoryHandle,
-                                ACCESS_MASK DesiredAccess,
-                                POBJECT_ATTRIBUTES ObjectAttributes)
-{
-   return(ZwCreateDirectoryObject(DirectoryHandle,
-                                 DesiredAccess,
-                                 ObjectAttributes));
+    } 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);
 }
 
-NTSTATUS ZwCreateDirectoryObject(PHANDLE DirectoryHandle,
-                                ACCESS_MASK DesiredAccess,
-                                POBJECT_ATTRIBUTES ObjectAttributes)
-/*
- * FUNCTION: Creates or opens a directory object (a container for other
- * objects)
- * ARGUMENTS:
- *        DirectoryHandle (OUT) = Caller supplied storage for the handle
- *                                of the directory
- *        DesiredAccess = Access desired to the directory
- *        ObjectAttributes = Object attributes initialized with
- *                           InitializeObjectAttributes
- * RETURNS: 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 
+ *             directory.
+ *             
+ *     DesiredAccess
+ *             Access desired to the directory.
+ *             
+ *     ObjectAttributes
+ *             Object attributes initialized with
+ *             InitializeObjectAttributes.
+ *             
+ * RETURN VALUE
+ *     Status.
  */
+NTSTATUS STDCALL
+NtCreateDirectoryObject(PHANDLE DirectoryHandle,
+                       ACCESS_MASK DesiredAccess,
+                       POBJECT_ATTRIBUTES ObjectAttributes)
 {
    PDIRECTORY_OBJECT dir;
-   
-   dir = ObCreateObject(DirectoryHandle,
-                       DesiredAccess,
-                       ObjectAttributes,
-                       ObDirectoryType);
-   return(STATUS_SUCCESS);
-}
 
-VOID InitializeObjectAttributes(POBJECT_ATTRIBUTES InitializedAttributes,
-                               PUNICODE_STRING ObjectName,
-                               ULONG Attributes,
-                               HANDLE RootDirectory,
-                               PSECURITY_DESCRIPTOR SecurityDescriptor)
-/*
- * FUNCTION: Sets up a parameter of type OBJECT_ATTRIBUTES for a 
- * subsequent call to ZwCreateXXX or ZwOpenXXX
- * ARGUMENTS:
- *        InitializedAttributes (OUT) = Caller supplied storage for the
- *                                      object attributes
- *        ObjectName = Full path name for object
- *        Attributes = Attributes for the object
- *        RootDirectory = Where the object should be placed or NULL
- *        SecurityDescriptor = Ignored
- * 
- * NOTE:
- *     Either ObjectName is a fully qualified pathname or a path relative
- *     to RootDirectory
- */
-{
-   DPRINT("InitializeObjectAttributes(InitializedAttributes %x "
-         "ObjectName %x Attributes %x RootDirectory %x)\n",
-         InitializedAttributes,ObjectName,Attributes,RootDirectory);
-   InitializedAttributes->Length=sizeof(OBJECT_ATTRIBUTES);
-   InitializedAttributes->RootDirectory=RootDirectory;
-   InitializedAttributes->ObjectName=ObjectName;
-   InitializedAttributes->Attributes=Attributes;
-   InitializedAttributes->SecurityDescriptor=SecurityDescriptor;
-   InitializedAttributes->SecurityQualityOfService=NULL;
+   DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
+         "DesiredAccess %x, ObjectAttributes %x, "
+         "ObjectAttributes->ObjectName %S)\n",
+         DirectoryHandle, DesiredAccess, ObjectAttributes,
+         ObjectAttributes->ObjectName);
+   
+   return(ObCreateObject(DirectoryHandle,
+                        DesiredAccess,
+                        ObjectAttributes,
+                        ObDirectoryType,
+                        (PVOID*)&dir));
 }
 
+/* EOF */