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