[NTVDM] Implement INT21, AX=4408, Determine if a block device is removable.
[reactos.git] / 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 #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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */