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 */ 1351