- Implement proper version of WaitNamedPipeW to be used when NPFS will be modified...
[reactos.git] / reactos / lib / kernel32 / file / npipe.c
index fa8338e..1143236 100644 (file)
@@ -72,7 +72,6 @@ CreateNamedPipeW(LPCWSTR lpName,
    HANDLE PipeHandle;
    ACCESS_MASK DesiredAccess;
    ULONG CreateOptions;
-   ULONG CreateDisposition;
    ULONG WriteModeMessage;
    ULONG ReadModeMessage;
    ULONG NonBlocking;
@@ -81,6 +80,12 @@ CreateNamedPipeW(LPCWSTR lpName,
    LARGE_INTEGER DefaultTimeOut;
    PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
 
+   if (nMaxInstances == 0 || nMaxInstances > PIPE_UNLIMITED_INSTANCES)
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return INVALID_HANDLE_VALUE;
+   }
+
    Result = RtlDosPathNameToNtPathName_U((LPWSTR)lpName,
                                         &NamedPipeName,
                                         NULL,
@@ -108,77 +113,40 @@ CreateNamedPipeW(LPCWSTR lpName,
                              NULL,
                              SecurityDescriptor);
 
-   DesiredAccess = 0;
+   DesiredAccess = SYNCHRONIZE | (dwOpenMode & (WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY));
    ShareAccess = 0;
-   CreateDisposition = FILE_OPEN_IF;
    CreateOptions = 0;
+
    if (dwOpenMode & FILE_FLAG_WRITE_THROUGH)
-     {
-       CreateOptions = CreateOptions | FILE_WRITE_THROUGH;
-     }
+      CreateOptions = CreateOptions | FILE_WRITE_THROUGH;
+
    if (!(dwOpenMode & FILE_FLAG_OVERLAPPED))
-     {
-       CreateOptions = CreateOptions | FILE_SYNCHRONOUS_IO_NONALERT;
-     }
-   if (dwOpenMode & PIPE_ACCESS_DUPLEX)
-     {
-       CreateOptions = CreateOptions | FILE_PIPE_FULL_DUPLEX;
-       DesiredAccess |= (FILE_GENERIC_READ | FILE_GENERIC_WRITE);
-     }
-   else if (dwOpenMode & PIPE_ACCESS_INBOUND)
-     {
-       CreateOptions = CreateOptions | FILE_PIPE_INBOUND;
-       DesiredAccess |= FILE_GENERIC_READ;
-     }
-   else if (dwOpenMode & PIPE_ACCESS_OUTBOUND)
-     {
-       CreateOptions = CreateOptions | FILE_PIPE_OUTBOUND;
-       DesiredAccess |= FILE_GENERIC_WRITE;
-     }
+      CreateOptions = CreateOptions | FILE_SYNCHRONOUS_IO_NONALERT;
 
-   if (dwPipeMode & PIPE_TYPE_BYTE)
+   if (dwOpenMode & PIPE_ACCESS_OUTBOUND)
      {
-       WriteModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
+       ShareAccess |= FILE_SHARE_READ;
+       DesiredAccess |= GENERIC_WRITE;
      }
-   else if (dwPipeMode & PIPE_TYPE_MESSAGE)
+   if (dwOpenMode & PIPE_ACCESS_INBOUND)
      {
-       WriteModeMessage = FILE_PIPE_MESSAGE_MODE;
+       ShareAccess |= FILE_SHARE_WRITE;
+       DesiredAccess |= GENERIC_READ;
      }
+   if (dwPipeMode & PIPE_TYPE_MESSAGE)
+      WriteModeMessage = FILE_PIPE_MESSAGE_MODE;
    else
-     {
-       WriteModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
-     }
+      WriteModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
 
-   if (dwPipeMode & PIPE_READMODE_BYTE)
-     {
-       ReadModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
-     }
-   else if (dwPipeMode & PIPE_READMODE_MESSAGE)
-     {
-       ReadModeMessage = FILE_PIPE_MESSAGE_MODE;
-     }
+   if (dwPipeMode & PIPE_READMODE_MESSAGE)
+      ReadModeMessage = FILE_PIPE_MESSAGE_MODE;
    else
-     {
-       ReadModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
-     }
+      ReadModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
 
-   if (dwPipeMode & PIPE_WAIT)
-     {
-       NonBlocking = FILE_PIPE_QUEUE_OPERATION;
-     }
-   else if (dwPipeMode & PIPE_NOWAIT)
-     {
-       NonBlocking = FILE_PIPE_COMPLETE_OPERATION;
-     }
+   if (dwPipeMode & PIPE_NOWAIT)
+       NonBlocking = FILE_PIPE_COMPLETE_OPERATION;
    else
-     {
-       NonBlocking = FILE_PIPE_QUEUE_OPERATION;
-     }
-
-   if (nMaxInstances >= PIPE_UNLIMITED_INSTANCES)
-     {
-       nMaxInstances = 0xFFFFFFFF;
-     }
+      NonBlocking = FILE_PIPE_QUEUE_OPERATION;
 
    DefaultTimeOut.QuadPart = nDefaultTimeOut * -10000LL;
 
@@ -187,7 +155,7 @@ CreateNamedPipeW(LPCWSTR lpName,
                                  &ObjectAttributes,
                                  &Iosb,
                                  ShareAccess,
-                                 CreateDisposition,
+                                 FILE_OPEN_IF,
                                  CreateOptions,
                                  WriteModeMessage,
                                  ReadModeMessage,
@@ -231,7 +199,195 @@ WaitNamedPipeA(LPCSTR lpNamedPipeName,
    return(r);
 }
 
+/*
+ * When NPFS will work properly, use this code instead. It is compatible with
+ * Microsoft's NPFS.SYS. The main difference is that:
+ *      - This code actually respects the timeout instead of ignoring it!
+ *      - This code validates and creates the proper names for both UNC and local pipes
+ *      - On NT, you open the *root* pipe directory (either \DosDevices\Pipe or 
+ *        \DosDevices\Unc\Server\Pipe) and then send the pipe to wait on in the 
+ *        FILE_PIPE_WAIT_FOR_BUFFER structure.
+ */
+#ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+WaitNamedPipeW(LPCWSTR lpNamedPipeName,
+               DWORD nTimeOut)
+{
+    UNICODE_STRING NamedPipeName, NewName, DevicePath, PipePrefix;
+    ULONG NameLength;
+    ULONG i;
+    PWCHAR p;
+    ULONG Type;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS Status;
+    HANDLE FileHandle;
+    IO_STATUS_BLOCK IoStatusBlock;
+    ULONG WaitPipeInfoSize;
+    PFILE_PIPE_WAIT_FOR_BUFFER WaitPipeInfo;
+
+    /* Start by making a unicode string of the name */
+    DPRINT("Sent path: %S\n", lpNamedPipeName);
+    RtlCreateUnicodeString(&NamedPipeName, lpNamedPipeName);
+    NameLength = NamedPipeName.Length / sizeof(WCHAR);
+
+    /* All slashes must become backslashes */
+    for (i = 0; i < NameLength; i++)
+    {
+        /* Check and convert */
+        if (NamedPipeName.Buffer[i] == L'/') NamedPipeName.Buffer[i] = L'\\';
+    }
+
+    /* Find the path type of the name we were given */
+    NewName = NamedPipeName;
+    Type = RtlDetermineDosPathNameType_U(lpNamedPipeName);
+    /* Check if this was a device path, ie : "\\.\pipe\name" */
+    if (Type == DEVICE_PATH)
+    {
+        /* Make sure it's a valid prefix */
+        RtlInitUnicodeString(&PipePrefix, L"\\\\.\\pipe\\");
+        RtlPrefixString((PANSI_STRING)&PipePrefix, (PANSI_STRING)&NewName, TRUE);
+
+        /* Move past it */
+        NewName.Buffer += 9;
+        NewName.Length -= 9 * sizeof(WCHAR);
+
+        /* Initialize the Dos Devices name */
+        DPRINT("NewName: %wZ\n", &NewName);
+        RtlInitUnicodeString(&DevicePath, L"\\DosDevices\\pipe\\");
+    }
+    else if (Type == UNC_PATH)
+    {
+        /* The path is \\server\\pipe\name; find the pipename itself */
+        p = &NewName.Buffer[2];
+
+        /* First loop to get past the server name */
+        do
+        {
+            /* Check if this is a backslash */
+            if (*p == L'\\') break;
+
+            /* Check next */
+            p++;
+        } while (*p);
+
+        /* Now make sure the full name contains "pipe\" */
+        if ((*p) && !(_wcsnicmp(p + 1, L"pipe\\", sizeof("pipe\\"))))
+        {
+            /* Get to the pipe name itself now */
+            p += sizeof("pipe\\") - 1;
+        }
+        else
+        {
+            /* The name is invalid */
+            DPRINT1("Invalid name!\n");
+               SetLastErrorByStatus(STATUS_OBJECT_PATH_SYNTAX_BAD);
+               return FALSE;
+        }
+
+        /* FIXME: Open \DosDevices\Unc\Server\Pipe\Name */
+    }
+    else
+    {
+        DPRINT1("Invalid path type\n");
+        SetLastErrorByStatus(STATUS_OBJECT_PATH_SYNTAX_BAD);
+        return FALSE;
+    }
+
+    /* Initialize the object attributes */
+    DPRINT("Opening: %wZ\n", &DevicePath);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DevicePath,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    /* Open the path */
+    Status = NtOpenFile(&FileHandle,
+                        FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        FILE_SYNCHRONOUS_IO_NONALERT);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail; couldn't open */
+        DPRINT1("Status: %lx\n", Status);
+        SetLastErrorByStatus(Status);
+        RtlFreeUnicodeString(&NamedPipeName);
+        return(FALSE);
+    }
+
+    /* Now calculate the total length of the structure and allocate it */
+    WaitPipeInfoSize = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) +
+                       NewName.Length;
+    WaitPipeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, WaitPipeInfoSize);
 
+    /* Check what timeout we got */
+    if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
+    {
+        /* Don't use a timeout */
+        WaitPipeInfo->TimeoutSpecified = FALSE;
+    }
+    else
+    {
+        /* Check if we should wait forever */
+        if (nTimeOut == NMPWAIT_WAIT_FOREVER)
+        {
+            /* Set the max */
+            WaitPipeInfo->Timeout.LowPart = 0;
+            WaitPipeInfo->Timeout.HighPart = 0x80000000;
+        }
+        else
+        {
+            /* Convert to NT format */
+            WaitPipeInfo->Timeout.QuadPart = UInt32x32To64(-10000, nTimeOut);
+        }
+
+        /* In both cases, we do have a timeout */
+        WaitPipeInfo->TimeoutSpecified = FALSE;
+    }
+
+    /* Set the length and copy the name */
+    WaitPipeInfo->NameLength = NewName.Length;
+    RtlCopyMemory(WaitPipeInfo->Name, NewName.Buffer, NewName.Length);
+
+    /* Get rid of the full name */
+    RtlFreeUnicodeString(&NamedPipeName);
+
+    /* Let NPFS know of our request */
+    Status = NtFsControlFile(FileHandle,
+                             NULL,
+                             NULL,
+                             NULL,
+                             &IoStatusBlock,
+                             FSCTL_PIPE_WAIT,
+                             WaitPipeInfo,
+                             WaitPipeInfoSize,
+                             NULL,
+                             0);
+
+    /* Free our pipe info data and close the handle */
+    RtlFreeHeap(RtlGetProcessHeap(), 0, WaitPipeInfo);
+    NtClose(FileHandle);
+
+    /* Check the status */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Failure to wait on the pipe */
+        DPRINT1("Status: %lx\n", Status);
+        SetLastErrorByStatus (Status);
+        return FALSE;
+     }
+
+    /* Success */
+    return TRUE;
+}
+#else
 /*
  * @implemented
  */
@@ -294,7 +450,7 @@ WaitNamedPipeW(LPCWSTR lpNamedPipeName,
 
    return(TRUE);
 }
-
+#endif
 
 /*
  * @implemented
@@ -367,11 +523,10 @@ ConnectNamedPipe(IN HANDLE hNamedPipe,
    return TRUE;
 }
 
-
 /*
  * @implemented
  */
-BOOL 
+BOOL
 STDCALL
 SetNamedPipeHandleState(HANDLE hNamedPipe,
                         LPDWORD lpMode,
@@ -394,7 +549,7 @@ SetNamedPipeHandleState(HANDLE hNamedPipe,
         Settings.ReadMode = (*lpMode & PIPE_READMODE_MESSAGE) ?
                             FILE_PIPE_MESSAGE_MODE: FILE_PIPE_BYTE_STREAM_MODE;
 
-        /* Send the changes to the Driver */ 
+        /* Send the changes to the Driver */
         Status = NtSetInformationFile(hNamedPipe,
                                       &Iosb,
                                       &Settings,
@@ -406,7 +561,7 @@ SetNamedPipeHandleState(HANDLE hNamedPipe,
             return(FALSE);
         }
     }
-    
+
     /* Check if the Collection count or Timeout are being changed */
     if (lpMaxCollectionCount || lpCollectDataTimeout)
     {
@@ -428,9 +583,9 @@ SetNamedPipeHandleState(HANDLE hNamedPipe,
             }
         }
 
-        /* Now set the new settings */    
-        RemoteSettings.MaximumCollectionCount = (lpMaxCollectionCount) ? 
-                                                *lpMaxCollectionCount : 
+        /* Now set the new settings */
+        RemoteSettings.MaximumCollectionCount = (lpMaxCollectionCount) ?
+                                                *lpMaxCollectionCount :
                                                 RemoteSettings.MaximumCollectionCount;
         if (lpCollectDataTimeout)
         {