Jump to content











Photo
* * * * * 1 votes

Booting VirtualBox with USB workaround

usb virtualbox grub4dos plop

Best Answer steve6375 , 26 February 2013 - 12:18 PM

Is the Win7 64-bit or special in any way? Did you try an plain MS Win7 32-bit ISO extracted to a USB drive?

How about creating a virtual HDD, mounting the HDD, copying the USB stick to the virtual HDD, dismounting it, then boot from it via VBox?

Go to the full post


  • Please log in to reply
307 replies to this topic

#301 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 19 September 2018 - 08:53 PM

Yes. I created this MS-DOS 5.0 VHD in VirtualBox, copied it to a USB pendrive, created a MENU.LST entry, and could boot to DOS under Qemu from RMPrepUSB without problems. There was no such error message box when leaving DOSSHELL. I modified AUTOEXEC.BAT and CONFIG.SYS, and everything worked fine and wasn't forgotten after the next VHD boot.

As I did not use RMPrepUSB for anything else than starting Qemu, I tried to find a more direct way to do this, from the scripting level and without the help of RMPrepUSB, but then stumbled over this write lock problem and saw that Steve made use of a binary named start_VM.exe in his StartFromUSB.cmd script.

For direct comparison with my other scripts and in order to simplify things, I can take the start_VM.exe that is contained in "QEMU Starter exe.zip" (post #44) and place it somewhere within the binary search path, e.g. in the %windir% folder, and create a small wrapper script just providing the parameters start_VM.exe needs:

start_VM_run.cmd:

set arg=file=\\.\PhysicalDrive7,if=ide,index=0,media=disk,format=raw
start_VM 7 500 "%ProgramFiles%\qemu\qemu-system-i386.exe" -boot c -m 512 -drive %arg%
pause

I also have to right-click on this script in Windows Explorer and run it as administrator to get Qemu running, but this works as desired. For example, I can type "COPY CON HELLO.TXT"<Enter> and "HELLO WORLD"<F6><Enter> at the DOS prompt to create a file. After closing Qemu and finishing the wrapper script, this file can be found in Windows Explorer if the VHD gets attached in Windows Disk Management.

Does anybody dare to check out these mini-scripts on his or her own computer? I did not suffer any loss of data during my experiments, except for the DOSSHELL.INI and HELLO.TXT inside the VHD when not using start_VM.exe. Of course the volume letter (O:) and the physical drive number (7) must be corrected for other machines. You won't tell me that only the Oracle of Delphi knows the solution to this problem, at the price of 411 KiB, will you?

 

Gerolf



#302 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 20 September 2018 - 05:08 PM

So, the issue is not Windows 10 or some other "peculiar" settings in your environment, good :) (which in this case means bad :()

 

I am short of possible ideas, only one that comes to my mind is using instead of mountvol good ol' dosdev .

 

But then probably the volume cannot be re-mounted without removal/insertion or something like mountstorPE or showdrive will be needed? :dubbio:

 

Some reference, JFYI:

http://reboot.pro/to...-line-possible/

 

:duff:

Wonko



#303 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 22 September 2018 - 09:22 PM

The fifth dismounting alternative then would be "chkdsk O: /X" (for my computer), as DavidB suggests in post #13. I'm just taking a closer look at his stat_VM.dpr code (in "QEMU Starter Sources.zip", post #44):

...
var VolLet: array of Char;
...
for i := 0 to High(VolLet) do begin
   FlushToDisk(VolLet[i]);
   Sleep(WaitTime);
   if not DisMountVolume(VolLet[i]) then
      if MessageBox(0, PChar('Unable to dismount volume ''' + VolLet[i] + 
        ''' !'#13#10'Possible reason: ' + SysErrorMessage(LastError) + 
        #13#10'Are you sure you want to continue...?'), 
        PChar('Warning'), MB_YESNO or MB_ICONWARNING or MB_DEFBUTTON2) = mrNO
      then Exit; 
end;
...   
if CreateProcessW(nil, zAppName, nil, nil, false, CREATE_NEW_CONSOLE or
   HIGH_PRIORITY_CLASS, nil, PQFolder, StartupInfo, ProcessInfo) then begin
...

Do I understand right that he flushes and dismounts all volumes temporarily before starting a new console process? What would be the abstract algorithm of DavidB's program?

 

Gerolf



#304 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 24 September 2018 - 09:34 PM

I'm having difficulties to get DavidB's start_VM.dpr source code compiled in both Delphi 10.2 and Lazarus (FreePascal). What files must actually be present to build this project?

 

Gerolf



#305 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 28 September 2018 - 02:16 AM

With a few modifications I could now compile DavidB's start_VM console programm on Free Pascal (and it still works, allowing writes to disk from within Qemu):

// start_VM.pas
// based on start_VM.dpr by DavidB, * converted for FreePascal by Gerolf

program start_VM;

uses
  Windows,
  SysUtils,
  // * Dialogs, Classes, Controls, TntSysUtils,
  // * Math, (required on Delphi)
  ShellApi;

type
  _STORAGE_DEVICE_NUMBER = record
    DeviceType: DWORD;
    DeviceNumber: DWORD;
    PartitionNumber: DWORD;
  end;
  STORAGE_DEVICE_NUMBER = _STORAGE_DEVICE_NUMBER;
  TPWideCharArray = array[0..0] of PWideChar;

const
  METHOD_BUFFERED = 0;
  FILE_ANY_ACCESS = 0;
  // * FILE_DEVICE_FILE_SYSTEM = $00000009;
  FILE_DEVICE_MASS_STORAGE = $0000002D;
  IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
  IOCTL_STORAGE_GET_DEVICE_NUMBER =
    ((IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0420 shl 2) or METHOD_BUFFERED);

var
  VolumesHandles: array of THandle;
  LastError: integer;

  procedure FlushToDisk(sDriveLetter: char);
  var
    hDrive: THandle;
  begin
    hDrive := 0;
    try
      hDrive := CreateFile(PAnsiChar('\\.\' + sDriveLetter + ':'),
        GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE,
        nil, OPEN_EXISTING, 0, 0);
      if FlushFileBuffers(hDrive) then // * FlushFileBuffers(hDrive);
        writeln('Flushed drive ' + sDriveLetter + ':'); // *
    finally
      FileClose(hDrive); // * CloseHandle(hDrive);
    end;
  end;

  function DismountVolume(ADrive: char): boolean;
  const
    FSCTL_DISMOUNT_VOLUME = (9 shl 16) or (0 shl 14) or (8 shl 2) or 0;
  var
    VolumeName: string;
    BytesReturned: cardinal = 0;
  begin
    Result := False;
    VolumeName := Format('\\.\%s:', [ADrive]);
    SetLength(VolumesHandles, Length(VolumesHandles) + 1);
    VolumesHandles[High(VolumesHandles)] :=
      CreateFile(PChar(VolumeName), GENERIC_READ or GENERIC_WRITE,
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    if VolumesHandles[High(VolumesHandles)] = INVALID_HANDLE_VALUE then
    begin
      LastError := GetLastError;
      Exit;
    end
    else
      writeln('Dismounted drive ' + ADrive + ':'); // *
    Result := DeviceIoControl(VolumesHandles[High(VolumesHandles)],
      FSCTL_DISMOUNT_VOLUME, nil, 0, nil, 0, BytesReturned, nil);
    if not Result then
      LastError := GetLastError;
  end;

  function StrLCopyW(Dest, Source: PWideChar; MaxLen: cardinal): PWideChar;
  var
    Count: cardinal;
  begin
    Result := Dest;
    Count := 0;
    while (Count < MaxLen) and (Source^ <> #0) do
    begin
      Dest^ := Source^;
      Inc(Source);
      Inc(Dest);
      Inc(Count);
    end;
    Dest^ := #0;
  end;

  function StrPCopyW(Dest: PWideChar; const Source: WideString): PWideChar;
  begin
    Result := StrLCopyW(Dest, PWideChar(Source), Length(Source));
  end;

var
  zAppName: array[0..512] of widechar;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  Res: DWORD = 0;
  i, udn, WaitTime: integer;
  VolLet: array of char;
  hVolume: THandle;
  dwBytesReturned: DWORD;
  sdn: STORAGE_DEVICE_NUMBER;
  ComLine, QFolder: WideString;
  NumArgs: PLongint = nil; // * NumArgs: integer = 0;
  TempArgs: PPWideChar;
  ParamStrW: array of WideString;
  ParamCountW: integer;
  StartInfo: TStartupInfo;
  PQFolder: PWideChar;
  ErrorMode: word;
begin

  SetLength(ParamStrW, 0);
  ParamCountW := -1;
  TempArgs := nil;
  try
    TempArgs := CommandLineToArgvW(GetCommandLineW, NumArgs);
  except;
  end;
  if TempArgs <> nil then
  begin
    SetLength(ParamStrW, NumArgs^); // * SetLength(ParamStrW, NumArgs);
    ParamCountW := NumArgs^ - 1; // * ParamCountW := NumArgs - 1;
    try
      for i := 0 to NumArgs^ - 1 do // * for i := 0 to NumArgs - 1 do
        ParamStrW[i] := TPWideCharArray(TempArgs^)[i];
    except;
    end;
    try;
      LocalFree(THandle(TempArgs)); // * Fixme: pointer conversion not portable
    except;
    end;
  end;
  if ParamCount > ParamCountW then
  begin
    SetLength(ParamStrW, ParamCount + 1);
    for i := ParamCountW + 1 to ParamCount do
      ParamStrW[i] := WideString(ParamStr(i));
    ParamCountW := i; // * 
  end;

  if ParamCountW < 3 then
  begin
    writeln('Insufficient command line parameters !'); // *
    writeln( // *
      'Please use "[USB_drive_number] [WaitTime] [QEMUexe] [QEMU_parameters]"');
    writeln('Press Enter to continue ...'); // *
    readln; // *
    // MessageBox(0, 'Insufficient command line parameters !'#13#10 +
    // 'Please use "[USB_drive_number] [WaitTime] [QEMUexe] [QEMU_parameters]"',
    // 'Warning', MB_OK or MB_ICONWARNING);
    Exit;
  end;

  udn := Min(Max(StrToIntDef(ansistring(ParamStrW[1]), 1), 0), 99);
  WaitTime := Min(Max(StrToIntDef(ansistring(ParamStrW[2]), 500), 1), 10000);

  ErrorMode := SetErrorMode(SEM_FailCriticalErrors);
  SetLength(VolLet, 0);
  for i := byte('C') to byte('Z') do
  begin
    case GetDriveType(PChar(char(i) + ':\')) of
      DRIVE_REMOVABLE, DRIVE_FIXED:
      begin
        try
          hVolume := CreateFile(PAnsiChar('\\.\' + char(i) + ':'),
            0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
        except
          hVolume := INVALID_HANDLE_VALUE;
        end;
        if hVolume <> INVALID_HANDLE_VALUE then
        begin
          dwBytesReturned := 0;
          if DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER,
            nil, 0, @sdn, sizeof(sdn), dwBytesReturned, nil) then
            if sdn.DeviceNumber = cardinal(udn) then
            begin
              SetLength(VolLet, Length(VolLet) + 1);
              VolLet[High(VolLet)] := char(i);
            end;
        end;
      end
      else
        Continue;
    end;
  end;
  SetErrorMode(ErrorMode);

  SetLength(VolumesHandles, 0);

  for i := 0 to High(VolLet) do
  begin
    FlushToDisk(VolLet[i]);
    Sleep(WaitTime);
    if not DisMountVolume(VolLet[i]) then
    begin // *
      writeln('Unable to dismount volume ''' + VolLet[i] + ''' !'); // *
      writeln('Possible reason: ' + SysErrorMessage(LastError)); // *
      writeln('Press Enter to continue ...'); // *
      readln; // * Fixme: Check answer 'y'/'Y' to continue instead of exiting
      //    if MessageBox(0, PChar('Unable to dismount volume ''' +
      //      VolLet[i] + ''' !'#13#10'Possible reason: ' +
      //      SysErrorMessage(LastError) +
      //      #13#10'Are you sure you want to continue...?'), PChar('Warning'),
      //      MB_YESNO or MB_ICONWARNING or MB_DEFBUTTON2) = mrNo then
      Exit;
    end; // *
  end;

  ComLine := '';
  for i := 3 to ParamCountW do
    ComLine := ComLine + '"' + ParamStrW[i] + '" ';
    
  GetStartUpInfo(StartInfo);
  StrPCopyW(zAppName, ComLine);
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);
  StartupInfo.dwFlags := StartInfo.dwFlags;
  StartupInfo.wShowWindow := StartInfo.wShowWindow;
  // QFolder := WideExtractFilePath(ParamStrW[3]);
  QFolder := ExtractFilePath(ParamStrW[3]); // *
  if QFolder <> '' then
    PQFolder := PWideChar(QFolder)
  else
    PQFolder := nil;
  if CreateProcessW(nil, zAppName, nil, nil, False,
    // CREATE_NEW_CONSOLE or HIGH_PRIORITY_CLASS, * this is not the QEMU process
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, 
    nil, PQFolder, Windows.StartupInfoW(StartupInfo), ProcessInfo) then
  begin
    WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
    GetExitCodeProcess(ProcessInfo.hProcess, Res);
    FileClose(ProcessInfo.hProcess); // * CloseHandle (ProcessInfo.hProcess);
    FileClose(ProcessInfo.hThread); // * CloseHandle(ProcessInfo.hThread);
  end
  else
  begin // *
    writeln('Unable to launch QEMU !');
    writeln('Press Enter to continue ...');
    readln;
    //  ShowMessage('Unable to launch QEMU !');
  end; // *
  for i := 0 to High(VolumesHandles) do
    try
      FileClose(VolumesHandles[i]); // * CloseHandle(VolumesHandles[i]);
    except;
    end;
end.

I would have preferred to post this long code as attachment in a ZIP archive, together with the binary, but I do not see an uploader button. Since I let the error messages go to the console, not to a message box, there is no need to link GUI units any longer, and the binary shrinks to 230 KiB.

 



#306 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 28 September 2018 - 07:13 AM

With a few modifications I could now compile DavidB's start_VM console programm on Free Pascal (and it still works, allowing writes to disk from within Qemu):


I would have preferred to post this long code as attachment in a ZIP archive, together with the binary, but I do not see an uploader button. Since I let the error messages go to the console, not to a message box, there is no need to link GUI units any longer, and the binary shrinks to 230 KiB.

 

You need (I seem to remember ) minimum 50 posts (or e-mail, NOT PM Nuno asking to be authorized) in order to be able to add attachments.

 

:duff:

Wonko



#307 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 29 September 2018 - 11:12 PM

At the time DavidB suggested "a solution: Using mountvol command to dismount USB drive's volume(s) before starting the VM and to mount them again after closing the VM. All can be done in a batch file so it will be very easy to use..." (post #14), he was still running Windows XP.

 

But "Microsoft changed the way programs can directly write to physical drives in Windows Vista and later", as TheK mentioned in the LockDismount thread (post #1). Did this impose restrictions to the command processor such that the suggested batch file solution is no longer possible?

 

TheK's LockDismount.exe does exactly the same as DavidB's start_VM.exe. If called from the command line, it requires path to and parameters for the qemu binary and then waits until the virtual machine is closed. LockDismount also allows Qemu guests to write to the USB drive, and it is only 68 KiB large. Sadly TheK did not share the source code.

 

Gerolf



#308 Gerolf

Gerolf

    Member

  • Members
  • 75 posts
  •  
    Germany

Posted 30 September 2018 - 01:09 AM

Microsoft gave a little background information on their blocking of direct write operations to volumes and disks:

 

"The application needs to lock the volume, dismount the volume, or both, before it can issue DASD [direct access to storage devices] I/O. This is new to Windows Vista and was done to address potentially malicious techniques. The file system will block all write operations to reserved sections of the disk. In this case, those reserved sections include the MBR and the two FAT areas. To block these areas, you need to lock the volume by sending FSCTL_LOCK_VOLUME. You must issue this structure on the same volume handle that performs the actual write operations. This request can fail if there are open file handles. In this case, the application can force a dismount of the file system by issuing FSCTL_DISMOUNT_VOLUME. However, the volume is not actually dismounted until the file handle is closed. Until then, the application can continue to issue DASD I/O by using the same file handle that is currently open."

 

That method can be recognized in DavidB's source code. Is it part of Microsoft's "compelling" safety strategy that this cannot be done on the scripting level using the command line tools shipped with Windows?

 

Gerolf






1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users