[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / dosfiles.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c
5 * PURPOSE: DOS32 Files Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "ntvdm.h"
15 #include "emulator.h"
16 #include "../../memory.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20 #include "dosfiles.h"
21 #include "handle.h"
22 #include "process.h"
23
24 #include "bios/bios.h"
25
26 /* PRIVATE FUNCTIONS **********************************************************/
27
28 static VOID StoreNameInSft(LPCSTR FilePath, PDOS_FILE_DESCRIPTOR Descriptor)
29 {
30 CHAR ShortPath[MAX_PATH];
31 PCHAR Name;
32 PCHAR Extension;
33
34 /* Try to get the short path */
35 if (!GetShortPathNameA(FilePath, ShortPath, sizeof(ShortPath)))
36 {
37 /* If it failed, just use the uppercase long path */
38 strncpy(ShortPath, FilePath, sizeof(ShortPath) - 1);
39 _strupr(ShortPath);
40 }
41
42 /* Get the name part */
43 Name = strrchr(ShortPath, '\\');
44 if (Name == NULL) Name = ShortPath;
45
46 /* Find the extension */
47 Extension = strchr(Name, '.');
48
49 if (Extension)
50 {
51 /* Terminate the name string, and move the pointer to after the dot */
52 *Extension++ = 0;
53 }
54
55 /* Copy the name into the SFT descriptor */
56 RtlCopyMemory(Descriptor->FileName, Name, min(strlen(Name), 8));
57
58 if (Extension)
59 {
60 /* Copy the extension too */
61 RtlCopyMemory(&Descriptor->FileName[8], Extension, min(strlen(Extension), 3));
62 }
63 }
64
65 /* PUBLIC FUNCTIONS ***********************************************************/
66
67 BYTE DosFindFreeDescriptor(VOID)
68 {
69 UINT i;
70 BYTE Count = 0;
71 DWORD CurrentSft = SysVars->FirstSft;
72
73 while (LOWORD(CurrentSft) != 0xFFFF)
74 {
75 PDOS_SFT Sft = (PDOS_SFT)FAR_POINTER(CurrentSft);
76
77 for (i = 0; i < Sft->NumDescriptors; i++)
78 {
79 if (Sft->FileDescriptors[i].RefCount == 0) return Count;
80 Count++;
81 }
82
83 /* Go to the next table */
84 CurrentSft = Sft->Link;
85 }
86
87 /* Invalid ID */
88 return 0xFF;
89 }
90
91 BYTE DosFindWin32Descriptor(HANDLE Win32Handle)
92 {
93 UINT i;
94 BYTE Count = 0;
95 DWORD CurrentSft = SysVars->FirstSft;
96
97 while (LOWORD(CurrentSft) != 0xFFFF)
98 {
99 PDOS_SFT Sft = (PDOS_SFT)FAR_POINTER(CurrentSft);
100
101 for (i = 0; i < Sft->NumDescriptors; i++)
102 {
103 if ((Sft->FileDescriptors[i].RefCount > 0)
104 && !(Sft->FileDescriptors[i].DeviceInfo & FILE_INFO_DEVICE)
105 && (Sft->FileDescriptors[i].Win32Handle == Win32Handle))
106 {
107 return Count;
108 }
109
110 Count++;
111 }
112
113 /* Go to the next table */
114 CurrentSft = Sft->Link;
115 }
116
117 /* Invalid ID */
118 return 0xFF;
119 }
120
121 BYTE DosFindDeviceDescriptor(DWORD DevicePointer)
122 {
123 UINT i;
124 BYTE Count = 0;
125 DWORD CurrentSft = SysVars->FirstSft;
126
127 while (LOWORD(CurrentSft) != 0xFFFF)
128 {
129 PDOS_SFT Sft = (PDOS_SFT)FAR_POINTER(CurrentSft);
130
131 for (i = 0; i < Sft->NumDescriptors; i++)
132 {
133 if ((Sft->FileDescriptors[i].RefCount > 0)
134 && (Sft->FileDescriptors[i].DeviceInfo & FILE_INFO_DEVICE)
135 && (Sft->FileDescriptors[i].DevicePointer == DevicePointer))
136 {
137 return Count;
138 }
139
140 Count++;
141 }
142
143 /* Go to the next table */
144 CurrentSft = Sft->Link;
145 }
146
147 /* Invalid ID */
148 return 0xFF;
149 }
150
151 PDOS_FILE_DESCRIPTOR DosGetFileDescriptor(BYTE Id)
152 {
153 DWORD CurrentSft = SysVars->FirstSft;
154
155 while (LOWORD(CurrentSft) != 0xFFFF)
156 {
157 PDOS_SFT Sft = (PDOS_SFT)FAR_POINTER(CurrentSft);
158
159 /* Return it if it's in this table */
160 if (Id <= Sft->NumDescriptors) return &Sft->FileDescriptors[Id];
161
162 /* Go to the next table */
163 Id -= Sft->NumDescriptors;
164 CurrentSft = Sft->Link;
165 }
166
167 /* Invalid ID */
168 return NULL;
169 }
170
171 PDOS_FILE_DESCRIPTOR DosGetHandleFileDescriptor(WORD DosHandle)
172 {
173 BYTE DescriptorId = DosQueryHandle(DosHandle);
174 if (DescriptorId == 0xFF) return NULL;
175
176 return DosGetFileDescriptor(DescriptorId);
177 }
178
179 WORD DosCreateFileEx(LPWORD Handle,
180 LPWORD CreationStatus,
181 LPCSTR FilePath,
182 BYTE AccessShareModes,
183 WORD CreateActionFlags,
184 WORD Attributes)
185 {
186 WORD LastError;
187 HANDLE FileHandle;
188 PDOS_DEVICE_NODE Node;
189 WORD DosHandle;
190 ACCESS_MASK AccessMode = 0;
191 DWORD ShareMode = 0;
192 DWORD CreationDisposition = 0;
193 BOOL InheritableFile = FALSE;
194 SECURITY_ATTRIBUTES SecurityAttributes;
195 BYTE DescriptorId;
196 PDOS_FILE_DESCRIPTOR Descriptor;
197
198 DPRINT1("DosCreateFileEx: FilePath \"%s\", AccessShareModes 0x%04X, CreateActionFlags 0x%04X, Attributes 0x%04X\n",
199 FilePath, AccessShareModes, CreateActionFlags, Attributes);
200
201 //
202 // The article about OpenFile API: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365430(v=vs.85).aspx
203 // explains what those AccessShareModes are (see the uStyle flag).
204 //
205
206 Node = DosGetDevice(FilePath);
207 if (Node != NULL)
208 {
209 if (Node->OpenRoutine) Node->OpenRoutine(Node);
210 }
211 else
212 {
213 /* Parse the access mode */
214 switch (AccessShareModes & 0x03)
215 {
216 /* Read-only */
217 case 0:
218 AccessMode = GENERIC_READ;
219 break;
220
221 /* Write only */
222 case 1:
223 AccessMode = GENERIC_WRITE;
224 break;
225
226 /* Read and write */
227 case 2:
228 AccessMode = GENERIC_READ | GENERIC_WRITE;
229 break;
230
231 /* Invalid */
232 default:
233 return ERROR_INVALID_PARAMETER;
234 }
235
236 /* Parse the share mode */
237 switch ((AccessShareModes >> 4) & 0x07)
238 {
239 /* Compatibility mode */
240 case 0:
241 ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
242 break;
243
244 /* No sharing "DenyAll" */
245 case 1:
246 ShareMode = 0;
247 break;
248
249 /* No write share "DenyWrite" */
250 case 2:
251 ShareMode = FILE_SHARE_READ;
252 break;
253
254 /* No read share "DenyRead" */
255 case 3:
256 ShareMode = FILE_SHARE_WRITE;
257 break;
258
259 /* Full share "DenyNone" */
260 case 4:
261 ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
262 break;
263
264 /* Invalid */
265 default:
266 return ERROR_INVALID_PARAMETER;
267 }
268
269 /*
270 * Parse the creation action flags:
271 *
272 * Bitfields for action:
273 * Bit(s) Description
274 *
275 * 7-4 Action if file does not exist.
276 * 0000 Fail
277 * 0001 Create
278 *
279 * 3-0 Action if file exists.
280 * 0000 Fail
281 * 0001 Open
282 * 0010 Replace/open
283 */
284 switch (CreateActionFlags)
285 {
286 /* If the file exists, fail, otherwise, fail also */
287 case 0x00:
288 // A special case is used after the call to CreateFileA if it succeeds,
289 // in order to close the opened handle and return an adequate error.
290 CreationDisposition = OPEN_EXISTING;
291 break;
292
293 /* If the file exists, open it, otherwise, fail */
294 case 0x01:
295 CreationDisposition = OPEN_EXISTING;
296 break;
297
298 /* If the file exists, replace it, otherwise, fail */
299 case 0x02:
300 CreationDisposition = TRUNCATE_EXISTING;
301 break;
302
303 /* If the file exists, fail, otherwise, create it */
304 case 0x10:
305 CreationDisposition = CREATE_NEW;
306 break;
307
308 /* If the file exists, open it, otherwise, create it */
309 case 0x11:
310 CreationDisposition = OPEN_ALWAYS;
311 break;
312
313 /* If the file exists, replace it, otherwise, create it */
314 case 0x12:
315 CreationDisposition = CREATE_ALWAYS;
316 break;
317
318 /* Invalid */
319 default:
320 return ERROR_INVALID_PARAMETER;
321 }
322
323 /* Check for inheritance */
324 InheritableFile = ((AccessShareModes & 0x80) == 0);
325
326 /* Assign default security attributes to the file, and set the inheritance flag */
327 SecurityAttributes.nLength = sizeof(SecurityAttributes);
328 SecurityAttributes.lpSecurityDescriptor = NULL;
329 SecurityAttributes.bInheritHandle = InheritableFile;
330
331 /* Open the file */
332 FileHandle = CreateFileA(FilePath,
333 AccessMode,
334 ShareMode,
335 &SecurityAttributes,
336 CreationDisposition,
337 Attributes,
338 NULL);
339
340 LastError = (WORD)GetLastError();
341
342 if (FileHandle == INVALID_HANDLE_VALUE)
343 {
344 /* Return the error code */
345 return LastError;
346 }
347
348 /*
349 * Special case: CreateActionFlags == 0, we must fail because
350 * the file exists (if it didn't exist we already failed).
351 */
352 if (CreateActionFlags == 0)
353 {
354 /* Close the file and return the error code */
355 CloseHandle(FileHandle);
356 return ERROR_FILE_EXISTS;
357 }
358
359 /* Set the creation status */
360 switch (CreateActionFlags)
361 {
362 case 0x01:
363 *CreationStatus = 0x01; // The file was opened
364 break;
365
366 case 0x02:
367 *CreationStatus = 0x03; // The file was replaced
368 break;
369
370 case 0x10:
371 *CreationStatus = 0x02; // The file was created
372 break;
373
374 case 0x11:
375 {
376 if (LastError == ERROR_ALREADY_EXISTS)
377 *CreationStatus = 0x01; // The file was opened
378 else
379 *CreationStatus = 0x02; // The file was created
380
381 break;
382 }
383
384 case 0x12:
385 {
386 if (LastError == ERROR_ALREADY_EXISTS)
387 *CreationStatus = 0x03; // The file was replaced
388 else
389 *CreationStatus = 0x02; // The file was created
390
391 break;
392 }
393 }
394 }
395
396 DescriptorId = DosFindFreeDescriptor();
397 if (DescriptorId == 0xFF)
398 {
399 /* Close the file and return the error code */
400 CloseHandle(FileHandle);
401 return ERROR_TOO_MANY_OPEN_FILES;
402 }
403
404 /* Set up the new descriptor */
405 Descriptor = DosGetFileDescriptor(DescriptorId);
406 RtlZeroMemory(Descriptor, sizeof(*Descriptor));
407 RtlFillMemory(Descriptor->FileName, sizeof(Descriptor->FileName), ' ');
408
409 if (Node != NULL)
410 {
411 Descriptor->DevicePointer = Node->Driver;
412 Descriptor->DeviceInfo = Node->DeviceAttributes | FILE_INFO_DEVICE;
413 RtlCopyMemory(Descriptor->FileName, Node->Name.Buffer, Node->Name.Length);
414 }
415 else
416 {
417 Descriptor->OpenMode = AccessShareModes;
418 Descriptor->Attributes = LOBYTE(GetFileAttributesA(FilePath));
419 Descriptor->Size = GetFileSize(FileHandle, NULL);
420 Descriptor->Win32Handle = FileHandle;
421 StoreNameInSft(FilePath, Descriptor);
422 }
423
424 Descriptor->OwnerPsp = Sda->CurrentPsp;
425
426 /* Open the DOS handle */
427 DosHandle = DosOpenHandle(DescriptorId);
428 if (DosHandle == INVALID_DOS_HANDLE)
429 {
430 /* Close the file and return the error code */
431 CloseHandle(FileHandle);
432 return ERROR_TOO_MANY_OPEN_FILES;
433 }
434
435 /* It was successful */
436 *Handle = DosHandle;
437 return ERROR_SUCCESS;
438 }
439
440 WORD DosCreateFile(LPWORD Handle,
441 LPCSTR FilePath,
442 DWORD CreationDisposition,
443 WORD Attributes)
444 {
445 HANDLE FileHandle;
446 PDOS_DEVICE_NODE Node;
447 WORD DosHandle;
448 BYTE DescriptorId;
449 PDOS_FILE_DESCRIPTOR Descriptor;
450
451 DPRINT("DosCreateFile: FilePath \"%s\", CreationDisposition 0x%04X, Attributes 0x%04X\n",
452 FilePath, CreationDisposition, Attributes);
453
454 Node = DosGetDevice(FilePath);
455 if (Node != NULL)
456 {
457 if (Node->OpenRoutine) Node->OpenRoutine(Node);
458 }
459 else
460 {
461 /* Create the file */
462 FileHandle = CreateFileA(FilePath,
463 GENERIC_READ | GENERIC_WRITE,
464 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
465 NULL,
466 CreationDisposition,
467 Attributes,
468 NULL);
469 if (FileHandle == INVALID_HANDLE_VALUE)
470 {
471 /* Return the error code */
472 return (WORD)GetLastError();
473 }
474 }
475
476 DescriptorId = DosFindFreeDescriptor();
477 if (DescriptorId == 0xFF)
478 {
479 /* Close the file and return the error code */
480 CloseHandle(FileHandle);
481 return ERROR_TOO_MANY_OPEN_FILES;
482 }
483
484 /* Set up the new descriptor */
485 Descriptor = DosGetFileDescriptor(DescriptorId);
486 RtlZeroMemory(Descriptor, sizeof(*Descriptor));
487 RtlFillMemory(Descriptor->FileName, sizeof(Descriptor->FileName), ' ');
488
489 if (Node != NULL)
490 {
491 Descriptor->DevicePointer = Node->Driver;
492 Descriptor->DeviceInfo = Node->DeviceAttributes | FILE_INFO_DEVICE;
493 RtlCopyMemory(Descriptor->FileName, Node->Name.Buffer, Node->Name.Length);
494 }
495 else
496 {
497 Descriptor->Attributes = LOBYTE(GetFileAttributesA(FilePath));
498 Descriptor->Size = GetFileSize(FileHandle, NULL);
499 Descriptor->Win32Handle = FileHandle;
500 StoreNameInSft(FilePath, Descriptor);
501 }
502
503 Descriptor->OwnerPsp = Sda->CurrentPsp;
504
505 /* Open the DOS handle */
506 DosHandle = DosOpenHandle(DescriptorId);
507 if (DosHandle == INVALID_DOS_HANDLE)
508 {
509 /* Close the file and return the error code */
510 CloseHandle(FileHandle);
511 return ERROR_TOO_MANY_OPEN_FILES;
512 }
513
514 /* It was successful */
515 *Handle = DosHandle;
516 return ERROR_SUCCESS;
517 }
518
519 WORD DosOpenFile(LPWORD Handle,
520 LPCSTR FilePath,
521 BYTE AccessShareModes)
522 {
523 HANDLE FileHandle = NULL;
524 PDOS_DEVICE_NODE Node;
525 WORD DosHandle;
526 BYTE DescriptorId;
527 PDOS_FILE_DESCRIPTOR Descriptor;
528
529 DPRINT("DosOpenFile: FilePath \"%s\", AccessShareModes 0x%04X\n",
530 FilePath, AccessShareModes);
531
532 //
533 // The article about OpenFile API: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365430(v=vs.85).aspx
534 // explains what those AccessShareModes are (see the uStyle flag).
535 //
536
537 Node = DosGetDevice(FilePath);
538 if (Node != NULL)
539 {
540 if (Node->OpenRoutine) Node->OpenRoutine(Node);
541 }
542 else
543 {
544 ACCESS_MASK AccessMode = 0;
545 DWORD ShareMode = 0;
546 BOOL InheritableFile = FALSE;
547 SECURITY_ATTRIBUTES SecurityAttributes;
548
549 /* Parse the access mode */
550 switch (AccessShareModes & 0x03)
551 {
552 /* Read-only */
553 case 0:
554 AccessMode = GENERIC_READ;
555 break;
556
557 /* Write only */
558 case 1:
559 AccessMode = GENERIC_WRITE;
560 break;
561
562 /* Read and write */
563 case 2:
564 AccessMode = GENERIC_READ | GENERIC_WRITE;
565 break;
566
567 /* Invalid */
568 default:
569 return ERROR_INVALID_PARAMETER;
570 }
571
572 /* Parse the share mode */
573 switch ((AccessShareModes >> 4) & 0x07)
574 {
575 /* Compatibility mode */
576 case 0:
577 ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
578 break;
579
580 /* No sharing "DenyAll" */
581 case 1:
582 ShareMode = 0;
583 break;
584
585 /* No write share "DenyWrite" */
586 case 2:
587 ShareMode = FILE_SHARE_READ;
588 break;
589
590 /* No read share "DenyRead" */
591 case 3:
592 ShareMode = FILE_SHARE_WRITE;
593 break;
594
595 /* Full share "DenyNone" */
596 case 4:
597 ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
598 break;
599
600 /* Invalid */
601 default:
602 return ERROR_INVALID_PARAMETER;
603 }
604
605 /* Check for inheritance */
606 InheritableFile = ((AccessShareModes & 0x80) == 0);
607
608 /* Assign default security attributes to the file, and set the inheritance flag */
609 SecurityAttributes.nLength = sizeof(SecurityAttributes);
610 SecurityAttributes.lpSecurityDescriptor = NULL;
611 SecurityAttributes.bInheritHandle = InheritableFile;
612
613 /* Open the file */
614 FileHandle = CreateFileA(FilePath,
615 AccessMode,
616 ShareMode,
617 &SecurityAttributes,
618 OPEN_EXISTING,
619 FILE_ATTRIBUTE_NORMAL,
620 NULL);
621 if (FileHandle == INVALID_HANDLE_VALUE)
622 {
623 /* Return the error code */
624 return (WORD)GetLastError();
625 }
626 }
627
628 DescriptorId = DosFindFreeDescriptor();
629 if (DescriptorId == 0xFF)
630 {
631 /* Close the file and return the error code */
632 CloseHandle(FileHandle);
633 return ERROR_TOO_MANY_OPEN_FILES;
634 }
635
636 /* Set up the new descriptor */
637 Descriptor = DosGetFileDescriptor(DescriptorId);
638 RtlZeroMemory(Descriptor, sizeof(*Descriptor));
639 RtlFillMemory(Descriptor->FileName, sizeof(Descriptor->FileName), ' ');
640
641 if (Node != NULL)
642 {
643 Descriptor->DevicePointer = Node->Driver;
644 Descriptor->DeviceInfo = Node->DeviceAttributes | FILE_INFO_DEVICE;
645 RtlCopyMemory(Descriptor->FileName, Node->Name.Buffer, Node->Name.Length);
646 }
647 else
648 {
649 Descriptor->OpenMode = AccessShareModes;
650 Descriptor->Attributes = LOBYTE(GetFileAttributesA(FilePath));
651 Descriptor->Size = GetFileSize(FileHandle, NULL);
652 Descriptor->Win32Handle = FileHandle;
653 StoreNameInSft(FilePath, Descriptor);
654 }
655
656 Descriptor->OwnerPsp = Sda->CurrentPsp;
657
658 /* Open the DOS handle */
659 DosHandle = DosOpenHandle(DescriptorId);
660 if (DosHandle == INVALID_DOS_HANDLE)
661 {
662 /* Close the file and return the error code */
663 CloseHandle(FileHandle);
664 return ERROR_TOO_MANY_OPEN_FILES;
665 }
666
667 /* It was successful */
668 *Handle = DosHandle;
669 return ERROR_SUCCESS;
670 }
671
672 BYTE DosReadLineBuffered(WORD FileHandle, DWORD Buffer, BYTE MaxSize)
673 {
674 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
675 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
676 BYTE LineSize = 0;
677 PCHAR Pointer = FAR_POINTER(Buffer);
678 CHAR Character;
679
680 do
681 {
682 USHORT Amount = 1;
683
684 /* Read a character from the device */
685 Node->ReadRoutine(Node,
686 MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
687 DOS_DATA_SEGMENT),
688 &Amount);
689 if (Amount == 0) break;
690
691 Character = Sda->ByteBuffer;
692
693 if (LineSize == MaxSize - 1 && Character != '\r' && Character != '\b')
694 {
695 /* Line buffer full */
696 // TODO: Should we beep?
697 continue;
698 }
699
700 switch (Character)
701 {
702 /* Extended character */
703 case '\0':
704 {
705 /* Read the scancode and discard it */
706 Amount = 1;
707 Node->ReadRoutine(Node,
708 MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
709 DOS_DATA_SEGMENT),
710 &Amount);
711 break;
712 }
713
714 /* Ctrl-C */
715 case 0x03:
716 {
717 DosEchoCharacter(Character);
718
719 if (DosControlBreak())
720 {
721 /* Set the character to CR to end the loop */
722 Character = '\r';
723 }
724
725 break;
726 }
727
728 case '\n':
729 {
730 DosEchoCharacter('\r');
731 DosEchoCharacter('\n');
732 break;
733 }
734
735 case '\b':
736 {
737 if (LineSize > 0)
738 {
739 LineSize--;
740 DosEchoCharacter(Character);
741
742 /* Erase the '^' too */
743 if (Pointer[LineSize] > 0x00 && Pointer[LineSize] < 0x20)
744 {
745 DosEchoCharacter(Character);
746 }
747 }
748
749 break;
750 }
751
752 default:
753 {
754 /* Store the character in the buffer */
755 Pointer[LineSize++] = Character;
756 DosEchoCharacter(Character);
757 }
758 }
759
760 /* Stop on a carriage return */
761 } while (Character != '\r');
762
763 return LineSize - 1;
764 }
765
766 WORD DosReadFile(WORD FileHandle,
767 DWORD Buffer,
768 WORD Count,
769 LPWORD BytesRead)
770 {
771 WORD Result = ERROR_SUCCESS;
772 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
773 BYTE StaticBuffer[8192];
774
775 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
776
777 if (Descriptor == NULL)
778 {
779 /* Invalid handle */
780 return ERROR_INVALID_HANDLE;
781 }
782
783 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
784 {
785 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
786 if (!Node->ReadRoutine) return ERROR_INVALID_FUNCTION;
787
788 if (Descriptor->DeviceInfo & FILE_INFO_BINARY)
789 {
790 /* Read from the device directly */
791 Node->ReadRoutine(Node, Buffer, &Count);
792 *BytesRead = Count;
793 }
794 else if (Descriptor->DeviceInfo & FILE_INFO_STDIN)
795 {
796 /* Line-buffered CON input */
797 PCHAR ConBuffer = NULL;
798 PCHAR Pointer = FAR_POINTER(Buffer);
799
800 /* Check if the buffer is empty */
801 if (!SysVars->UnreadConInput)
802 {
803 SysVars->UnreadConInput = FIELD_OFFSET(DOS_DATA, UnreadConInputBuffer);
804
805 DosReadLineBuffered(FileHandle,
806 MAKELONG(SysVars->UnreadConInput, DOS_DATA_SEGMENT),
807 sizeof(DosData->UnreadConInputBuffer));
808 }
809
810 *BytesRead = 0;
811 ConBuffer = (PCHAR)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, SysVars->UnreadConInput);
812
813 while (*BytesRead < Count)
814 {
815 Pointer[(*BytesRead)++] = *ConBuffer;
816
817 if (*ConBuffer == '\r')
818 {
819 /* A carriage return turns into a line feed */
820 *ConBuffer = '\n';
821 }
822 else if (*ConBuffer == '\n')
823 {
824 /* A line feed marks the true end of the line */
825 SysVars->UnreadConInput = 0;
826
827 /* Echo the line feed */
828 DosEchoCharacter('\n');
829 break;
830 }
831 else
832 {
833 /* Move to the next character */
834 SysVars->UnreadConInput++;
835 ConBuffer++;
836 }
837 }
838 }
839 else
840 {
841 /* Translated input from a character device that isn't CON */
842 PCHAR Pointer = FAR_POINTER(Buffer);
843 CHAR Character;
844
845 while (*BytesRead < Count)
846 {
847 USHORT Amount = 1;
848
849 /* Read a character from the device */
850 Node->ReadRoutine(Node,
851 MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
852 DOS_DATA_SEGMENT),
853 &Amount);
854 if (Amount == 0) break;
855
856 Character = Sda->ByteBuffer;
857 // TODO: Process it somehow?
858
859 /* Store the character in the output buffer */
860 Pointer[(*BytesRead)++] = Character;
861
862 /* Check for EOF */
863 if (Character == 0x1A) break;
864 }
865 }
866 }
867 else
868 {
869 DWORD BytesRead32 = 0;
870 PVOID LocalBuffer;
871
872 if (Count <= sizeof(StaticBuffer))
873 {
874 LocalBuffer = StaticBuffer;
875 }
876 else
877 {
878 LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Count);
879 ASSERT(LocalBuffer != NULL);
880 }
881
882 /* Read from the file */
883 if (ReadFile(Descriptor->Win32Handle, LocalBuffer, Count, &BytesRead32, NULL))
884 {
885 /* Write to the memory */
886 EmulatorWriteMemory(&EmulatorContext,
887 TO_LINEAR(HIWORD(Buffer), LOWORD(Buffer)),
888 LocalBuffer,
889 LOWORD(BytesRead32));
890
891 /* Update the position */
892 Descriptor->Position += BytesRead32; // or LOWORD(BytesRead32); ?
893 }
894 else
895 {
896 /* Store the error code */
897 Result = (WORD)GetLastError();
898 }
899
900 /* The number of bytes read is always 16-bit */
901 *BytesRead = LOWORD(BytesRead32);
902
903 if (LocalBuffer != StaticBuffer)
904 RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
905 }
906
907 /* Return the error code */
908 return Result;
909 }
910
911 WORD DosWriteFile(WORD FileHandle,
912 DWORD Buffer,
913 WORD Count,
914 LPWORD BytesWritten)
915 {
916 WORD Result = ERROR_SUCCESS;
917 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
918 BYTE StaticBuffer[8192];
919
920 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
921
922 if (Descriptor == NULL)
923 {
924 /* Invalid handle */
925 return ERROR_INVALID_HANDLE;
926 }
927
928 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
929 {
930 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
931 if (!Node->WriteRoutine) return ERROR_INVALID_FUNCTION;
932
933 /* Read the device */
934 Node->WriteRoutine(Node, Buffer, &Count);
935 *BytesWritten = Count;
936 }
937 else
938 {
939 DWORD BytesWritten32 = 0;
940 PVOID LocalBuffer;
941
942 /*
943 * Writing zero bytes truncates or extends the file
944 * to the current position of the file pointer.
945 */
946 if (Count == 0)
947 {
948 if (!SetEndOfFile(Descriptor->Win32Handle))
949 {
950 /* Store the error code */
951 Result = (WORD)GetLastError();
952 }
953 *BytesWritten = 0;
954 return Result;
955 }
956
957 if (Count <= sizeof(StaticBuffer))
958 {
959 LocalBuffer = StaticBuffer;
960 }
961 else
962 {
963 LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Count);
964 ASSERT(LocalBuffer != NULL);
965 }
966
967 /* Read from the memory */
968 EmulatorReadMemory(&EmulatorContext,
969 TO_LINEAR(HIWORD(Buffer), LOWORD(Buffer)),
970 LocalBuffer,
971 Count);
972
973 /* Write to the file */
974 if (WriteFile(Descriptor->Win32Handle, LocalBuffer, Count, &BytesWritten32, NULL))
975 {
976 /* Update the position and size */
977 Descriptor->Position += BytesWritten32; // or LOWORD(BytesWritten32); ?
978 if (Descriptor->Position > Descriptor->Size) Descriptor->Size = Descriptor->Position;
979 }
980 else
981 {
982 /* Store the error code */
983 Result = (WORD)GetLastError();
984 }
985
986 /* The number of bytes written is always 16-bit */
987 *BytesWritten = LOWORD(BytesWritten32);
988
989 if (LocalBuffer != StaticBuffer)
990 RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
991 }
992
993 /* Return the error code */
994 return Result;
995 }
996
997 WORD DosSeekFile(WORD FileHandle,
998 LONG Offset,
999 BYTE Origin,
1000 LPDWORD NewOffset)
1001 {
1002 WORD Result = ERROR_SUCCESS;
1003 DWORD FilePointer;
1004 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
1005
1006 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
1007 FileHandle, Offset, Origin);
1008
1009 if (Descriptor == NULL)
1010 {
1011 /* Invalid handle */
1012 return ERROR_INVALID_HANDLE;
1013 }
1014
1015 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
1016 {
1017 /* For character devices, always return success */
1018 return ERROR_SUCCESS;
1019 }
1020
1021 /* Check if the origin is valid */
1022 if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END)
1023 {
1024 return ERROR_INVALID_FUNCTION;
1025 }
1026
1027 FilePointer = SetFilePointer(Descriptor->Win32Handle, Offset, NULL, Origin);
1028
1029 /* Check if there's a possibility the operation failed */
1030 if (FilePointer == INVALID_SET_FILE_POINTER)
1031 {
1032 /* Get the real error code */
1033 Result = (WORD)GetLastError();
1034 }
1035
1036 if (Result != ERROR_SUCCESS)
1037 {
1038 /* The operation did fail */
1039 return Result;
1040 }
1041
1042 /* Update the position */
1043 Descriptor->Position = FilePointer;
1044
1045 /* Return the file pointer, if requested */
1046 if (NewOffset) *NewOffset = FilePointer;
1047
1048 /* Return success */
1049 return ERROR_SUCCESS;
1050 }
1051
1052 BOOL DosFlushFileBuffers(WORD FileHandle)
1053 {
1054 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
1055
1056 if (Descriptor == NULL)
1057 {
1058 /* Invalid handle */
1059 Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1060 return FALSE;
1061 }
1062
1063 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
1064 {
1065 PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
1066
1067 if (Node->FlushInputRoutine) Node->FlushInputRoutine(Node);
1068 if (Node->FlushOutputRoutine) Node->FlushOutputRoutine(Node);
1069
1070 return TRUE;
1071 }
1072 else
1073 {
1074 return FlushFileBuffers(Descriptor->Win32Handle);
1075 }
1076 }
1077
1078 BOOLEAN DosLockFile(WORD DosHandle, DWORD Offset, DWORD Size)
1079 {
1080 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(DosHandle);
1081
1082 if (Descriptor == NULL)
1083 {
1084 /* Invalid handle */
1085 Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1086 return FALSE;
1087 }
1088
1089 /* Always succeed for character devices */
1090 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE) return TRUE;
1091
1092 if (!LockFile(Descriptor->Win32Handle, Offset, 0, Size, 0))
1093 {
1094 Sda->LastErrorCode = GetLastError();
1095 return FALSE;
1096 }
1097
1098 return TRUE;
1099 }
1100
1101 BOOLEAN DosUnlockFile(WORD DosHandle, DWORD Offset, DWORD Size)
1102 {
1103 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(DosHandle);
1104
1105 if (Descriptor == NULL)
1106 {
1107 /* Invalid handle */
1108 Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1109 return FALSE;
1110 }
1111
1112 /* Always succeed for character devices */
1113 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE) return TRUE;
1114
1115 if (!UnlockFile(Descriptor->Win32Handle, Offset, 0, Size, 0))
1116 {
1117 Sda->LastErrorCode = GetLastError();
1118 return FALSE;
1119 }
1120
1121 return TRUE;
1122 }
1123
1124 BOOLEAN DosDeviceIoControl(WORD FileHandle, BYTE ControlCode, DWORD Buffer, PWORD Length)
1125 {
1126 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(FileHandle);
1127 PDOS_DEVICE_NODE Node = NULL;
1128
1129 if (!Descriptor)
1130 {
1131 Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1132 return FALSE;
1133 }
1134
1135 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
1136 {
1137 Node = DosGetDriverNode(Descriptor->DevicePointer);
1138 }
1139
1140 switch (ControlCode)
1141 {
1142 /* Get Device Information */
1143 case 0x00:
1144 {
1145 /*
1146 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1147 * for a list of possible flags.
1148 */
1149 setDX(Descriptor->DeviceInfo);
1150 return TRUE;
1151 }
1152
1153 /* Set Device Information */
1154 case 0x01:
1155 {
1156 // TODO: NOT IMPLEMENTED
1157 UNIMPLEMENTED;
1158 return FALSE;
1159 }
1160
1161 /* Read from Device I/O Control Channel */
1162 case 0x02:
1163 {
1164 if (Node == NULL || !(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
1165 {
1166 Sda->LastErrorCode = ERROR_INVALID_FUNCTION;
1167 return FALSE;
1168 }
1169
1170 /* Do nothing if there is no IOCTL routine */
1171 if (!Node->IoctlReadRoutine)
1172 {
1173 *Length = 0;
1174 return TRUE;
1175 }
1176
1177 Node->IoctlReadRoutine(Node, Buffer, Length);
1178 return TRUE;
1179 }
1180
1181 /* Write to Device I/O Control Channel */
1182 case 0x03:
1183 {
1184 if (Node == NULL || !(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
1185 {
1186 Sda->LastErrorCode = ERROR_INVALID_FUNCTION;
1187 return FALSE;
1188 }
1189
1190 /* Do nothing if there is no IOCTL routine */
1191 if (!Node->IoctlWriteRoutine)
1192 {
1193 *Length = 0;
1194 return TRUE;
1195 }
1196
1197 Node->IoctlWriteRoutine(Node, Buffer, Length);
1198 return TRUE;
1199 }
1200
1201 /* Get Input Status */
1202 case 0x06:
1203 {
1204 /* Check if this is a file or a device */
1205 if (Node)
1206 {
1207 /* Device*/
1208
1209 if (!Node->InputStatusRoutine || Node->InputStatusRoutine(Node))
1210 {
1211 /* Set the length to 0xFF to mark that it's ready */
1212 *Length = 0xFF;
1213 }
1214 else
1215 {
1216 /* Not ready */
1217 *Length = 0;
1218 }
1219 }
1220 else
1221 {
1222 /* File */
1223
1224 if (Descriptor->Position < Descriptor->Size)
1225 {
1226 /* Set the length to 0xFF to mark that it's ready */
1227 *Length = 0xFF;
1228 }
1229 else
1230 {
1231 /* Not ready */
1232 *Length = 0;
1233 }
1234 }
1235
1236 return TRUE;
1237 }
1238
1239 /* Get Output Status */
1240 case 0x07:
1241 {
1242 /* Check if this is a file or a device */
1243 if (Node)
1244 {
1245 /* Device*/
1246
1247 if (!Node->OutputStatusRoutine || Node->OutputStatusRoutine(Node))
1248 {
1249 /* Set the length to 0xFF to mark that it's ready */
1250 *Length = 0xFF;
1251 }
1252 else
1253 {
1254 /* Not ready */
1255 *Length = 0;
1256 }
1257 }
1258 else
1259 {
1260 /* Files are always ready for output */
1261 *Length = 0xFF;
1262 }
1263
1264 return TRUE;
1265 }
1266
1267 /* Unsupported control code */
1268 default:
1269 {
1270 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
1271
1272 Sda->LastErrorCode = ERROR_INVALID_PARAMETER;
1273 return FALSE;
1274 }
1275 }
1276 }
1277
1278 /* EOF */