Process Hacker
iosup.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * I/O support functions
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #define _PH_IOSUP_PRIVATE
24 #include <ph.h>
25 #include <iosupp.h>
26 
28 
30  VOID
31  )
32 {
33  PH_OBJECT_TYPE_PARAMETERS parameters;
34 
35  parameters.FreeListSize = sizeof(PH_FILE_STREAM);
36  parameters.FreeListCount = 16;
37 
38  PhFileStreamType = PhCreateObjectTypeEx(L"FileStream", PH_OBJECT_TYPE_USE_FREE_LIST, PhpFileStreamDeleteProcedure, &parameters);
39 
40  return TRUE;
41 }
42 
66  _Out_ PHANDLE FileHandle,
67  _In_ PWSTR FileName,
68  _In_ ACCESS_MASK DesiredAccess,
69  _In_opt_ ULONG FileAttributes,
70  _In_ ULONG ShareAccess,
71  _In_ ULONG CreateDisposition,
72  _In_ ULONG CreateOptions
73  )
74 {
75  return PhCreateFileWin32Ex(
76  FileHandle,
77  FileName,
78  DesiredAccess,
79  FileAttributes,
80  ShareAccess,
81  CreateDisposition,
82  CreateOptions,
83  NULL
84  );
85 }
86 
123  _Out_ PHANDLE FileHandle,
124  _In_ PWSTR FileName,
125  _In_ ACCESS_MASK DesiredAccess,
126  _In_opt_ ULONG FileAttributes,
127  _In_ ULONG ShareAccess,
128  _In_ ULONG CreateDisposition,
129  _In_ ULONG CreateOptions,
130  _Out_opt_ PULONG CreateStatus
131  )
132 {
133  NTSTATUS status;
134  HANDLE fileHandle;
135  UNICODE_STRING fileName;
137  IO_STATUS_BLOCK isb;
138 
139  if (!FileAttributes)
140  FileAttributes = FILE_ATTRIBUTE_NORMAL;
141 
143  FileName,
144  &fileName,
145  NULL,
146  NULL
147  ))
148  return STATUS_OBJECT_NAME_NOT_FOUND;
149 
151  &oa,
152  &fileName,
154  NULL,
155  NULL
156  );
157 
158  status = NtCreateFile(
159  &fileHandle,
160  DesiredAccess,
161  &oa,
162  &isb,
163  NULL,
164  FileAttributes,
165  ShareAccess,
166  CreateDisposition,
167  CreateOptions,
168  NULL,
169  0
170  );
171 
172  RtlFreeHeap(RtlProcessHeap(), 0, fileName.Buffer);
173 
174  if (NT_SUCCESS(status))
175  {
176  *FileHandle = fileHandle;
177  }
178 
179  if (CreateStatus)
180  *CreateStatus = (ULONG)isb.Information;
181 
182  return status;
183 }
184 
192  _In_ PWSTR FileName,
193  _Out_ PFILE_NETWORK_OPEN_INFORMATION FileInformation
194  )
195 {
196  NTSTATUS status;
197  UNICODE_STRING fileName;
199 
201  FileName,
202  &fileName,
203  NULL,
204  NULL
205  ))
206  return STATUS_OBJECT_NAME_NOT_FOUND;
207 
209  &oa,
210  &fileName,
212  NULL,
213  NULL
214  );
215 
216  status = NtQueryFullAttributesFile(&oa, FileInformation);
217  RtlFreeHeap(RtlProcessHeap(), 0, fileName.Buffer);
218 
219  return status;
220 }
221 
228  _In_ PWSTR FileName
229  )
230 {
231  NTSTATUS status;
232  HANDLE fileHandle;
233 
234  status = PhCreateFileWin32(
235  &fileHandle,
236  FileName,
237  DELETE,
238  0,
239  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
240  FILE_OPEN,
242  );
243 
244  if (!NT_SUCCESS(status))
245  return status;
246 
247  NtClose(fileHandle);
248 
249  return status;
250 }
251 
253  _In_ HANDLE FileHandle,
254  _In_opt_ HANDLE Event,
255  _In_opt_ PIO_APC_ROUTINE ApcRoutine,
256  _In_opt_ PVOID ApcContext,
257  _Out_ PIO_STATUS_BLOCK IoStatusBlock
258  )
259 {
260  return NtFsControlFile(
261  FileHandle,
262  Event,
263  ApcRoutine,
264  ApcContext,
265  IoStatusBlock,
267  NULL,
268  0,
269  NULL,
270  0
271  );
272 }
273 
275  _In_ HANDLE FileHandle
276  )
277 {
278  NTSTATUS status;
279  IO_STATUS_BLOCK isb;
280 
281  status = NtFsControlFile(
282  FileHandle,
283  NULL,
284  NULL,
285  NULL,
286  &isb,
288  NULL,
289  0,
290  NULL,
291  0
292  );
293 
294  if (status == STATUS_PENDING)
295  {
296  status = NtWaitForSingleObject(FileHandle, FALSE, NULL);
297 
298  if (NT_SUCCESS(status))
299  status = isb.Status;
300  }
301 
302  return status;
303 }
304 
306  _In_ HANDLE FileHandle,
307  _Out_writes_bytes_opt_(Length) PVOID Buffer,
308  _In_ ULONG Length,
309  _Out_opt_ PULONG NumberOfBytesRead,
310  _Out_opt_ PULONG NumberOfBytesAvailable,
311  _Out_opt_ PULONG NumberOfBytesLeftInMessage
312  )
313 {
314  NTSTATUS status;
315  IO_STATUS_BLOCK isb;
316  PFILE_PIPE_PEEK_BUFFER peekBuffer;
317  ULONG peekBufferLength;
318 
319  peekBufferLength = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data) + Length;
320  peekBuffer = PhAllocate(peekBufferLength);
321 
322  status = NtFsControlFile(
323  FileHandle,
324  NULL,
325  NULL,
326  NULL,
327  &isb,
329  NULL,
330  0,
331  peekBuffer,
332  peekBufferLength
333  );
334 
335  if (status == STATUS_PENDING)
336  {
337  status = NtWaitForSingleObject(FileHandle, FALSE, NULL);
338 
339  if (NT_SUCCESS(status))
340  status = isb.Status;
341  }
342 
343  // STATUS_BUFFER_OVERFLOW means that there is data remaining; this is normal.
344  if (status == STATUS_BUFFER_OVERFLOW)
345  status = STATUS_SUCCESS;
346 
347  if (NT_SUCCESS(status))
348  {
349  ULONG numberOfBytesRead;
350 
351  if (Buffer || NumberOfBytesRead || NumberOfBytesLeftInMessage)
352  numberOfBytesRead = (ULONG)(isb.Information - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data));
353 
354  if (Buffer)
355  memcpy(Buffer, peekBuffer->Data, numberOfBytesRead);
356 
357  if (NumberOfBytesRead)
358  *NumberOfBytesRead = numberOfBytesRead;
359 
360  if (NumberOfBytesAvailable)
361  *NumberOfBytesAvailable = peekBuffer->ReadDataAvailable;
362 
363  if (NumberOfBytesLeftInMessage)
364  *NumberOfBytesLeftInMessage = peekBuffer->MessageLength - numberOfBytesRead;
365  }
366 
367  PhFree(peekBuffer);
368 
369  return status;
370 }
371 
373  _In_ HANDLE FileHandle,
374  _In_opt_ HANDLE Event,
375  _In_opt_ PIO_APC_ROUTINE ApcRoutine,
376  _In_opt_ PVOID ApcContext,
377  _Out_ PIO_STATUS_BLOCK IoStatusBlock,
378  _In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
379  _In_ ULONG InputBufferLength,
380  _Out_writes_bytes_(OutputBufferLength) PVOID OutputBuffer,
381  _In_ ULONG OutputBufferLength
382  )
383 {
384  return NtFsControlFile(
385  FileHandle,
386  Event,
387  ApcRoutine,
388  ApcContext,
389  IoStatusBlock,
391  InputBuffer,
392  InputBufferLength,
393  OutputBuffer,
394  OutputBufferLength
395  );
396 }
397 
399  _In_opt_ PUNICODE_STRING FileSystemName,
400  _In_ PUNICODE_STRING Name,
401  _In_opt_ PLARGE_INTEGER Timeout,
402  _In_ BOOLEAN UseDefaultTimeout
403  )
404 {
405  NTSTATUS status;
406  IO_STATUS_BLOCK isb;
407  UNICODE_STRING localNpfsName;
408  HANDLE fileSystemHandle;
410  PFILE_PIPE_WAIT_FOR_BUFFER waitForBuffer;
411  ULONG waitForBufferLength;
412 
413  if (!FileSystemName)
414  {
415  RtlInitUnicodeString(&localNpfsName, L"\\Device\\NamedPipe");
416  FileSystemName = &localNpfsName;
417  }
418 
420  &oa,
421  FileSystemName,
423  NULL,
424  NULL
425  );
426 
427  status = NtOpenFile(
428  &fileSystemHandle,
429  FILE_READ_ATTRIBUTES | SYNCHRONIZE,
430  &oa,
431  &isb,
432  FILE_SHARE_READ | FILE_SHARE_WRITE,
434  );
435 
436  if (!NT_SUCCESS(status))
437  return status;
438 
439  waitForBufferLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name) + Name->Length;
440  waitForBuffer = PhAllocate(waitForBufferLength);
441 
442  if (UseDefaultTimeout)
443  {
444  waitForBuffer->TimeoutSpecified = FALSE;
445  }
446  else
447  {
448  if (Timeout)
449  {
450  waitForBuffer->Timeout = *Timeout;
451  }
452  else
453  {
454  waitForBuffer->Timeout.LowPart = 0;
455  waitForBuffer->Timeout.HighPart = MINLONG; // a very long time
456  }
457 
458  waitForBuffer->TimeoutSpecified = TRUE;
459  }
460 
461  waitForBuffer->NameLength = (ULONG)Name->Length;
462  memcpy(waitForBuffer->Name, Name->Buffer, Name->Length);
463 
464  status = NtFsControlFile(
465  fileSystemHandle,
466  NULL,
467  NULL,
468  NULL,
469  &isb,
471  waitForBuffer,
472  waitForBufferLength,
473  NULL,
474  0
475  );
476 
477  PhFree(waitForBuffer);
478  NtClose(fileSystemHandle);
479 
480  return status;
481 }
482 
484  _In_ HANDLE FileHandle
485  )
486 {
487  NTSTATUS status;
488  IO_STATUS_BLOCK isb;
489 
490  status = NtFsControlFile(
491  FileHandle,
492  NULL,
493  NULL,
494  NULL,
495  &isb,
497  NULL,
498  0,
499  NULL,
500  0
501  );
502 
503  return status;
504 }
505 
507  _Out_ PPH_FILE_STREAM *FileStream,
508  _In_ PWSTR FileName,
509  _In_ ACCESS_MASK DesiredAccess,
510  _In_ ULONG ShareAccess,
511  _In_ ULONG CreateDisposition,
512  _In_ ULONG Flags
513  )
514 {
515  NTSTATUS status;
516  PPH_FILE_STREAM fileStream;
517  HANDLE fileHandle;
518  ULONG createOptions;
519 
520  if (Flags & PH_FILE_STREAM_ASYNCHRONOUS)
521  createOptions = FILE_NON_DIRECTORY_FILE;
522  else
524 
525  if (!NT_SUCCESS(status = PhCreateFileWin32(
526  &fileHandle,
527  FileName,
528  DesiredAccess,
529  0,
530  ShareAccess,
531  CreateDisposition,
532  createOptions
533  )))
534  return status;
535 
536  if (!NT_SUCCESS(status = PhCreateFileStream2(
537  &fileStream,
538  fileHandle,
539  Flags,
540  PAGE_SIZE
541  )))
542  {
543  NtClose(fileHandle);
544  return status;
545  }
546 
547  if (Flags & PH_FILE_STREAM_APPEND)
548  {
549  LARGE_INTEGER zero;
550 
551  zero.QuadPart = 0;
552 
554  fileStream,
555  &zero,
556  SeekEnd
557  )))
558  {
559  PhDereferenceObject(fileStream);
560  return status;
561  }
562  }
563 
564  *FileStream = fileStream;
565 
566  return status;
567 }
568 
570  _Out_ PPH_FILE_STREAM *FileStream,
571  _In_ HANDLE FileHandle,
572  _In_ ULONG Flags,
573  _In_ ULONG BufferLength
574  )
575 {
576  PPH_FILE_STREAM fileStream;
577 
578  fileStream = PhCreateObject(sizeof(PH_FILE_STREAM), PhFileStreamType);
579  fileStream->FileHandle = FileHandle;
580  fileStream->Flags = Flags;
581  fileStream->Position.QuadPart = 0;
582 
583  if (!(Flags & PH_FILE_STREAM_UNBUFFERED))
584  {
585  fileStream->Buffer = NULL;
586  fileStream->BufferLength = BufferLength;
587  }
588  else
589  {
590  fileStream->Buffer = NULL;
591  fileStream->BufferLength = 0;
592  }
593 
594  fileStream->ReadPosition = 0;
595  fileStream->ReadLength = 0;
596  fileStream->WritePosition = 0;
597 
598  *FileStream = fileStream;
599 
600  return STATUS_SUCCESS;
601 }
602 
604  _In_ PVOID Object,
605  _In_ ULONG Flags
606  )
607 {
608  PPH_FILE_STREAM fileStream = (PPH_FILE_STREAM)Object;
609 
610  PhFlushFileStream(fileStream, FALSE);
611 
612  if (!(fileStream->Flags & PH_FILE_STREAM_HANDLE_UNOWNED))
613  NtClose(fileStream->FileHandle);
614 
615  if (fileStream->Buffer)
616  PhFreePage(fileStream->Buffer);
617 }
618 
624  _In_ PPH_FILE_STREAM FileStream
625  )
626 {
627  NTSTATUS status;
628 
629  // If the file object is asynchronous, the file object doesn't maintain
630  // its position.
631  if (!(FileStream->Flags & (
634  )))
635  {
636  FILE_POSITION_INFORMATION positionInfo;
637  IO_STATUS_BLOCK isb;
638 
639  if (!NT_SUCCESS(status = NtQueryInformationFile(
640  FileStream->FileHandle,
641  &isb,
642  &positionInfo,
645  )))
646  PhRaiseStatus(status);
647 
648  if (FileStream->Position.QuadPart != positionInfo.CurrentByteOffset.QuadPart)
649  PhRaiseStatus(STATUS_INTERNAL_ERROR);
650  }
651 }
652 
654  _Inout_ PPH_FILE_STREAM FileStream
655  )
656 {
657  FileStream->Buffer = PhAllocatePage(FileStream->BufferLength, NULL);
658 
659  if (FileStream->Buffer)
660  return STATUS_SUCCESS;
661  else
662  return STATUS_NO_MEMORY;
663 }
664 
666  _Inout_ PPH_FILE_STREAM FileStream,
667  _Out_writes_bytes_(Length) PVOID Buffer,
668  _In_ ULONG Length,
669  _Out_opt_ PULONG ReadLength
670  )
671 {
672  NTSTATUS status;
673  IO_STATUS_BLOCK isb;
674  PLARGE_INTEGER position;
675 
676  position = NULL;
677 
678  if (FileStream->Flags & PH_FILE_STREAM_OWN_POSITION)
679  position = &FileStream->Position;
680 
681  status = NtReadFile(
682  FileStream->FileHandle,
683  NULL,
684  NULL,
685  NULL,
686  &isb,
687  Buffer,
688  Length,
689  position,
690  NULL
691  );
692 
693  if (status == STATUS_PENDING)
694  {
695  // Wait for the operation to finish. This probably means we got
696  // called on an asynchronous file object.
697  status = NtWaitForSingleObject(FileStream->FileHandle, FALSE, NULL);
698 
699  if (NT_SUCCESS(status))
700  status = isb.Status;
701  }
702 
703  if (NT_SUCCESS(status))
704  {
705  FileStream->Position.QuadPart += isb.Information;
706 
707  if (ReadLength)
708  *ReadLength = (ULONG)isb.Information;
709  }
710 
711  return status;
712 }
713 
715  _Inout_ PPH_FILE_STREAM FileStream,
716  _Out_writes_bytes_(Length) PVOID Buffer,
717  _In_ ULONG Length,
718  _Out_opt_ PULONG ReadLength
719  )
720 {
721  NTSTATUS status = STATUS_SUCCESS;
722  ULONG availableLength;
723  ULONG readLength;
724 
725  if (FileStream->Flags & PH_FILE_STREAM_UNBUFFERED)
726  {
727  return PhpReadFileStream(
728  FileStream,
729  Buffer,
730  Length,
731  ReadLength
732  );
733  }
734 
735  // How much do we have available to copy out of the buffer?
736  availableLength = FileStream->ReadLength - FileStream->ReadPosition;
737 
738  if (availableLength == 0)
739  {
740  // Make sure buffered writes are flushed.
741  if (FileStream->WritePosition != 0)
742  {
743  if (!NT_SUCCESS(status = PhpFlushWriteFileStream(FileStream)))
744  return status;
745  }
746 
747  // If this read is too big, pass it through.
748  if (Length >= FileStream->BufferLength)
749  {
750  // These are now invalid.
751  FileStream->ReadPosition = 0;
752  FileStream->ReadLength = 0;
753 
754  return PhpReadFileStream(
755  FileStream,
756  Buffer,
757  Length,
758  ReadLength
759  );
760  }
761 
762  if (!FileStream->Buffer)
763  {
764  if (!NT_SUCCESS(status = PhpAllocateBufferFileStream(FileStream)))
765  return status;
766  }
767 
768  // Read as much as we can into our buffer.
769  if (!NT_SUCCESS(status = PhpReadFileStream(
770  FileStream,
771  FileStream->Buffer,
772  FileStream->BufferLength,
773  &readLength
774  )))
775  return status;
776 
777  if (readLength == 0)
778  {
779  // No data read.
780  if (ReadLength)
781  *ReadLength = readLength;
782 
783  return status;
784  }
785 
786  FileStream->ReadPosition = 0;
787  FileStream->ReadLength = readLength;
788  }
789  else
790  {
791  readLength = availableLength;
792  }
793 
794  if (readLength > Length)
795  readLength = Length;
796 
797  // Try to satisfy the request from the buffer.
798  memcpy(
799  Buffer,
800  (PCHAR)FileStream->Buffer + FileStream->ReadPosition,
801  readLength
802  );
803  FileStream->ReadPosition += readLength;
804 
805  // If we didn't completely satisfy the request, read some more.
806  if (
807  readLength < Length &&
808  // Don't try to read more if the buffer wasn't even filled up
809  // last time. (No more to read.)
810  FileStream->ReadLength == FileStream->BufferLength
811  )
812  {
813  ULONG readLength2;
814 
815  if (NT_SUCCESS(status = PhpReadFileStream(
816  FileStream,
817  (PCHAR)Buffer + readLength,
818  Length - readLength,
819  &readLength2
820  )))
821  {
822  readLength += readLength2;
823  // These are now invalid.
824  FileStream->ReadPosition = 0;
825  FileStream->ReadLength = 0;
826  }
827  }
828 
829  if (NT_SUCCESS(status))
830  {
831  if (ReadLength)
832  *ReadLength = readLength;
833  }
834 
835  return status;
836 }
837 
839  _Inout_ PPH_FILE_STREAM FileStream,
840  _In_reads_bytes_(Length) PVOID Buffer,
841  _In_ ULONG Length
842  )
843 {
844  NTSTATUS status;
845  IO_STATUS_BLOCK isb;
846  PLARGE_INTEGER position;
847 
848  position = NULL;
849 
850  if (FileStream->Flags & PH_FILE_STREAM_OWN_POSITION)
851  position = &FileStream->Position;
852 
853  status = NtWriteFile(
854  FileStream->FileHandle,
855  NULL,
856  NULL,
857  NULL,
858  &isb,
859  Buffer,
860  Length,
861  position,
862  NULL
863  );
864 
865  if (status == STATUS_PENDING)
866  {
867  // Wait for the operation to finish. This probably means we got
868  // called on an asynchronous file object.
869  status = NtWaitForSingleObject(FileStream->FileHandle, FALSE, NULL);
870 
871  if (NT_SUCCESS(status))
872  status = isb.Status;
873  }
874 
875  if (NT_SUCCESS(status))
876  {
877  FileStream->Position.QuadPart += isb.Information;
878  FileStream->Flags |= PH_FILE_STREAM_WRITTEN;
879  }
880 
881  return status;
882 }
883 
885  _Inout_ PPH_FILE_STREAM FileStream,
886  _In_reads_bytes_(Length) PVOID Buffer,
887  _In_ ULONG Length
888  )
889 {
890  NTSTATUS status = STATUS_SUCCESS;
891  ULONG availableLength;
892  ULONG writtenLength;
893 
894  if (FileStream->Flags & PH_FILE_STREAM_UNBUFFERED)
895  {
896  return PhpWriteFileStream(
897  FileStream,
898  Buffer,
899  Length
900  );
901  }
902 
903  if (FileStream->WritePosition == 0)
904  {
905  // Make sure buffered reads are flushed.
906  if (!NT_SUCCESS(status = PhpFlushReadFileStream(FileStream)))
907  return status;
908  }
909 
910  if (FileStream->WritePosition != 0)
911  {
912  availableLength = FileStream->BufferLength - FileStream->WritePosition;
913 
914  // Try to satisfy the request by copying the data to the buffer.
915  if (availableLength != 0)
916  {
917  writtenLength = availableLength;
918 
919  if (writtenLength > Length)
920  writtenLength = Length;
921 
922  memcpy(
923  (PCHAR)FileStream->Buffer + FileStream->WritePosition,
924  Buffer,
925  writtenLength
926  );
927  FileStream->WritePosition += writtenLength;
928 
929  if (writtenLength == Length)
930  {
931  // The request has been completely satisfied.
932  return status;
933  }
934 
935  Buffer = (PCHAR)Buffer + writtenLength;
936  Length -= writtenLength;
937  }
938 
939  // If we didn't completely satisfy the request, it's because the
940  // buffer is full. Flush it.
941  if (!NT_SUCCESS(status = PhpWriteFileStream(
942  FileStream,
943  FileStream->Buffer,
944  FileStream->WritePosition
945  )))
946  return status;
947 
948  FileStream->WritePosition = 0;
949  }
950 
951  // If the write is too big, pass it through.
952  if (Length >= FileStream->BufferLength)
953  {
954  if (!NT_SUCCESS(status = PhpWriteFileStream(
955  FileStream,
956  Buffer,
957  Length
958  )))
959  return status;
960  }
961  else if (Length != 0)
962  {
963  if (!FileStream->Buffer)
964  {
965  if (!NT_SUCCESS(status = PhpAllocateBufferFileStream(FileStream)))
966  return status;
967  }
968 
969  // Completely satisfy the request by copying the data to the buffer.
970  memcpy(
971  FileStream->Buffer,
972  Buffer,
973  Length
974  );
975  FileStream->WritePosition = Length;
976  }
977 
978  return status;
979 }
980 
982  _Inout_ PPH_FILE_STREAM FileStream
983  )
984 {
985  NTSTATUS status = STATUS_SUCCESS;
986 
987  if (FileStream->ReadLength - FileStream->ReadPosition != 0)
988  {
989  LARGE_INTEGER offset;
990 
991  // We have some buffered read data, so our position is
992  // too far ahead. We need to move it back to the first
993  // unused byte.
994  offset.QuadPart = -(LONG)(FileStream->ReadLength - FileStream->ReadPosition);
995 
996  if (!NT_SUCCESS(status = PhpSeekFileStream(
997  FileStream,
998  &offset,
1000  )))
1001  return status;
1002  }
1003 
1004  FileStream->ReadPosition = 0;
1005  FileStream->ReadLength = 0;
1006 
1007  return status;
1008 }
1009 
1011  _Inout_ PPH_FILE_STREAM FileStream
1012  )
1013 {
1014  NTSTATUS status = STATUS_SUCCESS;
1015 
1016  if (!NT_SUCCESS(status = PhpWriteFileStream(
1017  FileStream,
1018  FileStream->Buffer,
1019  FileStream->WritePosition
1020  )))
1021  return status;
1022 
1023  FileStream->WritePosition = 0;
1024 
1025  return status;
1026 }
1027 
1037  _Inout_ PPH_FILE_STREAM FileStream,
1038  _In_ BOOLEAN Full
1039  )
1040 {
1041  NTSTATUS status = STATUS_SUCCESS;
1042 
1043  if (FileStream->WritePosition != 0)
1044  {
1045  if (!NT_SUCCESS(status = PhpFlushWriteFileStream(FileStream)))
1046  return status;
1047  }
1048 
1049  if (FileStream->ReadPosition != 0)
1050  {
1051  if (!NT_SUCCESS(status = PhpFlushReadFileStream(FileStream)))
1052  return status;
1053  }
1054 
1055  if (Full && (FileStream->Flags & PH_FILE_STREAM_WRITTEN))
1056  {
1057  IO_STATUS_BLOCK isb;
1058 
1059  if (!NT_SUCCESS(status = NtFlushBuffersFile(
1060  FileStream->FileHandle,
1061  &isb
1062  )))
1063  return status;
1064  }
1065 
1066  return status;
1067 }
1068 
1070  _In_ PPH_FILE_STREAM FileStream,
1071  _Out_ PLARGE_INTEGER Position
1072  )
1073 {
1074  Position->QuadPart =
1075  FileStream->Position.QuadPart +
1076  (FileStream->ReadPosition - FileStream->ReadLength) +
1077  FileStream->WritePosition;
1078 }
1079 
1081  _Inout_ PPH_FILE_STREAM FileStream,
1082  _In_ PLARGE_INTEGER Offset,
1083  _In_ PH_SEEK_ORIGIN Origin
1084  )
1085 {
1086  NTSTATUS status = STATUS_SUCCESS;
1087 
1088  switch (Origin)
1089  {
1090  case SeekStart:
1091  {
1092  FileStream->Position = *Offset;
1093  }
1094  break;
1095  case SeekCurrent:
1096  {
1097  FileStream->Position.QuadPart += Offset->QuadPart;
1098  }
1099  break;
1100  case SeekEnd:
1101  {
1102  if (!NT_SUCCESS(status = PhGetFileSize(
1103  FileStream->FileHandle,
1104  &FileStream->Position
1105  )))
1106  return status;
1107 
1108  FileStream->Position.QuadPart += Offset->QuadPart;
1109  }
1110  break;
1111  }
1112 
1113  if (!(FileStream->Flags & PH_FILE_STREAM_OWN_POSITION))
1114  {
1115  FILE_POSITION_INFORMATION positionInfo;
1116  IO_STATUS_BLOCK isb;
1117 
1118  positionInfo.CurrentByteOffset = FileStream->Position;
1119 
1120  if (!NT_SUCCESS(status = NtSetInformationFile(
1121  FileStream->FileHandle,
1122  &isb,
1123  &positionInfo,
1124  sizeof(FILE_POSITION_INFORMATION),
1126  )))
1127  return status;
1128  }
1129 
1130  return status;
1131 }
1132 
1134  _Inout_ PPH_FILE_STREAM FileStream,
1135  _In_ PLARGE_INTEGER Offset,
1136  _In_ PH_SEEK_ORIGIN Origin
1137  )
1138 {
1139  NTSTATUS status = STATUS_SUCCESS;
1140  LARGE_INTEGER offset;
1141 
1142  offset = *Offset;
1143 
1144  if (FileStream->WritePosition != 0)
1145  {
1146  if (!NT_SUCCESS(status = PhpFlushWriteFileStream(FileStream)))
1147  return status;
1148  }
1149  else if (FileStream->ReadPosition != 0)
1150  {
1151  if (Origin == SeekCurrent)
1152  {
1153  // We have buffered read data, which means our position is too
1154  // far ahead. Subtract this difference from the offset (which
1155  // will affect the position accordingly).
1156  offset.QuadPart -= FileStream->ReadLength - FileStream->ReadPosition;
1157  }
1158 
1159  // TODO: Try to keep some of the read buffer.
1160  FileStream->ReadPosition = 0;
1161  FileStream->ReadLength = 0;
1162  }
1163 
1164  if (!NT_SUCCESS(status = PhpSeekFileStream(
1165  FileStream,
1166  &offset,
1167  Origin
1168  )))
1169  return status;
1170 
1171  return status;
1172 }
1173 
1175  _Inout_ PPH_FILE_STREAM FileStream,
1176  _In_ PLARGE_INTEGER Position,
1177  _In_ PLARGE_INTEGER Length,
1178  _In_ BOOLEAN Wait,
1179  _In_ BOOLEAN Shared
1180  )
1181 {
1182  NTSTATUS status;
1183  IO_STATUS_BLOCK isb;
1184 
1185  status = NtLockFile(
1186  FileStream->FileHandle,
1187  NULL,
1188  NULL,
1189  NULL,
1190  &isb,
1191  Position,
1192  Length,
1193  0,
1194  !Wait,
1195  !Shared
1196  );
1197 
1198  if (status == STATUS_PENDING)
1199  {
1200  // Wait for the operation to finish. This probably means we got
1201  // called on an asynchronous file object.
1202  NtWaitForSingleObject(FileStream->FileHandle, FALSE, NULL);
1203  status = isb.Status;
1204  }
1205 
1206  return status;
1207 }
1208 
1210  _Inout_ PPH_FILE_STREAM FileStream,
1211  _In_ PLARGE_INTEGER Position,
1212  _In_ PLARGE_INTEGER Length
1213  )
1214 {
1215  IO_STATUS_BLOCK isb;
1216 
1217  return NtUnlockFile(
1218  FileStream->FileHandle,
1219  &isb,
1220  Position,
1221  Length,
1222  0
1223  );
1224 }
1225 
1227  _Inout_ PPH_FILE_STREAM FileStream,
1228  _In_ PPH_STRINGREF String
1229  )
1230 {
1231  return PhWriteStringAsUtf8FileStreamEx(FileStream, String->Buffer, String->Length);
1232 }
1233 
1235  _Inout_ PPH_FILE_STREAM FileStream,
1236  _In_ PWSTR String
1237  )
1238 {
1239  PH_STRINGREF string;
1240 
1241  PhInitializeStringRef(&string, String);
1242 
1243  return PhWriteStringAsUtf8FileStream(FileStream, &string);
1244 }
1245 
1247  _Inout_ PPH_FILE_STREAM FileStream,
1248  _In_ PWSTR Buffer,
1249  _In_ SIZE_T Length
1250  )
1251 {
1252  NTSTATUS status = STATUS_SUCCESS;
1253  PH_STRINGREF block;
1254  SIZE_T inPlaceUtf8Size;
1255  PCHAR inPlaceUtf8 = NULL;
1256  PPH_BYTES utf8 = NULL;
1257 
1258  if (Length > PAGE_SIZE)
1259  {
1260  // In UTF-8, the maximum number of bytes per code point is 4.
1261  inPlaceUtf8Size = PAGE_SIZE / sizeof(WCHAR) * 4;
1262  inPlaceUtf8 = PhAllocatePage(inPlaceUtf8Size, NULL);
1263  }
1264 
1265  while (Length != 0)
1266  {
1267  block.Buffer = Buffer;
1268  block.Length = PAGE_SIZE;
1269 
1270  if (block.Length > Length)
1271  block.Length = Length;
1272 
1273  if (inPlaceUtf8)
1274  {
1275  SIZE_T bytesInUtf8String;
1276 
1278  inPlaceUtf8,
1279  inPlaceUtf8Size,
1280  &bytesInUtf8String,
1281  block.Buffer,
1282  block.Length
1283  ))
1284  {
1285  status = STATUS_INVALID_PARAMETER;
1286  goto CleanupExit;
1287  }
1288 
1289  status = PhWriteFileStream(FileStream, inPlaceUtf8, (ULONG)bytesInUtf8String);
1290  }
1291  else
1292  {
1293  utf8 = PhConvertUtf16ToUtf8Ex(block.Buffer, block.Length);
1294 
1295  if (!utf8)
1296  {
1297  status = STATUS_INVALID_PARAMETER;
1298  goto CleanupExit;
1299  }
1300 
1301  status = PhWriteFileStream(FileStream, utf8->Buffer, (ULONG)utf8->Length);
1302  PhDereferenceObject(utf8);
1303  }
1304 
1305  if (!NT_SUCCESS(status))
1306  goto CleanupExit;
1307 
1308  Buffer += block.Length / sizeof(WCHAR);
1309  Length -= block.Length;
1310  }
1311 
1312 CleanupExit:
1313  if (inPlaceUtf8)
1314  PhFreePage(inPlaceUtf8);
1315 
1316  return status;
1317 }
1318 
1320  _Inout_ PPH_FILE_STREAM FileStream,
1321  _In_ _Printf_format_string_ PWSTR Format,
1322  _In_ va_list ArgPtr
1323  )
1324 {
1325  NTSTATUS status;
1326  PPH_STRING string;
1327 
1328  string = PhFormatString_V(Format, ArgPtr);
1329  status = PhWriteStringAsUtf8FileStream(FileStream, &string->sr);
1330  PhDereferenceObject(string);
1331 
1332  return status;
1333 }
1334 
1336  _Inout_ PPH_FILE_STREAM FileStream,
1337  _In_ _Printf_format_string_ PWSTR Format,
1338  ...
1339  )
1340 {
1341  va_list argptr;
1342 
1343  va_start(argptr, Format);
1344 
1345  return PhWriteStringFormatAsUtf8FileStream_V(FileStream, Format, argptr);
1346 }