If you’ve ever had a call to NtCreateFile or IoCreateFail fail with the very helpful “STATUS_INVALID_PARAMETER” status code, you can now how exciting it can be to track down which flag exactly you might’ve messed up. Hopefully this snippet of code should help you manually validate your call (I’m not sure if the latest PREFast checks for these things when compiling). This code is from ReactOS.
/* Check if we need to check parameters, or if we’re user mode */
if ((AccessMode != KernelMode) || (Options & IO_CHECK_CREATE_PARAMETERS))
{
   /* Validate parameters */
   if ((FileAttributes & ~FILE_ATTRIBUTE_VALID_FLAGS) ||
       (ShareAccess & ~FILE_SHARE_VALID_FLAGS) ||
       (Disposition > FILE_MAXIMUM_DISPOSITION) ||
      (CreateOptions & ~FILE_VALID_OPTION_FLAGS) ||
        (CreateOptions &
        (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT) &&
        (!(DesiredAccess & SYNCHRONIZE))) ||
       ((CreateOptions &
        (FILE_SYNCHRONOUS_IO_NONALERT | FILE_SYNCHRONOUS_IO_ALERT)) ==
        (FILE_SYNCHRONOUS_IO_NONALERT | FILE_SYNCHRONOUS_IO_ALERT)) ||
       ((CreateOptions & FILE_DIRECTORY_FILE) &&
        !(CreateOptions & FILE_NON_DIRECTORY_FILE) &&
         ((CreateOptions & ~(FILE_DIRECTORY_FILE |
                             FILE_SYNCHRONOUS_IO_ALERT |
                             FILE_SYNCHRONOUS_IO_NONALERT |
                             FILE_WRITE_THROUGH |
                             FILE_COMPLETE_IF_OPLOCKED |
                             FILE_OPEN_FOR_BACKUP_INTENT |
                             FILE_DELETE_ON_CLOSE |
                             FILE_OPEN_FOR_FREE_SPACE_QUERY |
                             FILE_OPEN_BY_FILE_ID |
                            FILE_OPEN_REPARSE_POINT)) ||
         ((Disposition != FILE_CREATE) &&
          (Disposition != FILE_OPEN) &&
          (Disposition != FILE_OPEN_IF)))) ||
       ((CreateOptions & FILE_COMPLETE_IF_OPLOCKED) &&
        (CreateOptions & FILE_RESERVE_OPFILTER)) ||
       ((CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING) &&
        (DesiredAccess & FILE_APPEND_DATA)))
   {
        /*
        * Parameter failure. We’ll be as unspecific as NT as to
        * why this happened though, to make debugging a pain!
        */
       DPRINT1(“File Create Parameter Failure!\nâ€);
      return STATUS_INVALID_PARAMETER;
    }
Â
    /* Now check if this is a named pipe */
    if (CreateFileType == CreateFileTypeNamedPipe)
    {
       /* Make sure we have extra parameters */
       if (!ExtraCreateParameters) return STATUS_INVALID_PARAMETER;
Â
       /* Get the parameters and validate them */
       NamedPipeCreateParameters = ExtraCreateParameters;
       if ((NamedPipeCreateParameters->NamedPipeType >
            FILE_PIPE_MESSAGE_TYPE) ||
           (NamedPipeCreateParameters->ReadMode >
            FILE_PIPE_MESSAGE_MODE) ||
           (NamedPipeCreateParameters->CompletionMode >
            FILE_PIPE_COMPLETE_OPERATION) ||
           (ShareAccess & FILE_SHARE_DELETE) ||
           ((Disposition < FILE_OPEN) || (Disposition > FILE_OPEN_IF)) ||
           (CreateOptions & ~FILE_VALID_PIPE_OPTION_FLAGS))
       {
           /* Invalid named pipe create */
           return STATUS_INVALID_PARAMETER;
       }
    }
   else if (CreateFileType == CreateFileTypeMailslot)
   {
       /* Make sure we have extra parameters */
       if (!ExtraCreateParameters) return STATUS_INVALID_PARAMETER;
Â
        /* Get the parameters and validate them */
       MailslotCreateParameters = ExtraCreateParameters;
       if ((ShareAccess & FILE_SHARE_DELETE) ||
           !(ShareAccess & ~FILE_SHARE_WRITE) ||
           (Disposition != FILE_CREATE) ||
           (CreateOptions & ~FILE_VALID_MAILSLOT_OPTION_FLAGS))
       {
           /* Invalid mailslot create */
           return STATUS_INVALID_PARAMETER;
       }
   }
}