VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestSessionImplTasks.cpp@ 67414

Last change on this file since 67414 was 64939, checked in by vboxsync, 8 years ago

Additions/win: Don't depend on the checked in certificate, but rather extract the code signing certificates from VBoxGuest.sys and VBoxCertUtil.exe.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.9 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 64939 2016-12-17 01:06:47Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session tasks.
4 */
5
6/*
7 * Copyright (C) 2012-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "GuestImpl.h"
23#ifndef VBOX_WITH_GUEST_CONTROL
24# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
25#endif
26#include "GuestSessionImpl.h"
27#include "GuestCtrlImplPrivate.h"
28
29#include "Global.h"
30#include "AutoCaller.h"
31#include "ConsoleImpl.h"
32#include "ProgressImpl.h"
33
34#include <memory> /* For auto_ptr. */
35
36#include <iprt/env.h>
37#include <iprt/file.h> /* For CopyTo/From. */
38#include <iprt/path.h>
39
40#ifdef LOG_GROUP
41 #undef LOG_GROUP
42#endif
43#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
44#include <VBox/log.h>
45
46
47/*********************************************************************************************************************************
48* Defines *
49*********************************************************************************************************************************/
50
51/**
52 * Update file flags.
53 */
54#define UPDATEFILE_FLAG_NONE 0
55/** Copy over the file from host to the
56 * guest. */
57#define UPDATEFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
58/** Execute file on the guest after it has
59 * been successfully transfered. */
60#define UPDATEFILE_FLAG_EXECUTE RT_BIT(7)
61/** File is optional, does not have to be
62 * existent on the .ISO. */
63#define UPDATEFILE_FLAG_OPTIONAL RT_BIT(8)
64
65
66// session task classes
67/////////////////////////////////////////////////////////////////////////////
68
69GuestSessionTask::GuestSessionTask(GuestSession *pSession)
70 : ThreadTask("GenericGuestSessionTask")
71{
72 mSession = pSession;
73}
74
75GuestSessionTask::~GuestSessionTask(void)
76{
77}
78
79HRESULT GuestSessionTask::createAndSetProgressObject()
80{
81 LogFlowThisFunc(("Task Description = %s, pTask=%p\n", mDesc.c_str(), this));
82
83 ComObjPtr<Progress> pProgress;
84 HRESULT hr = S_OK;
85 /* Create the progress object. */
86 hr = pProgress.createObject();
87 if (FAILED(hr))
88 return VERR_COM_UNEXPECTED;
89
90 hr = pProgress->init(static_cast<IGuestSession*>(mSession),
91 Bstr(mDesc).raw(),
92 TRUE /* aCancelable */);
93 if (FAILED(hr))
94 return VERR_COM_UNEXPECTED;
95
96 mProgress = pProgress;
97
98 LogFlowFuncLeave();
99
100 return hr;
101}
102
103int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
104{
105 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
106
107 mDesc = strDesc;
108 mProgress = pProgress;
109 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
110
111 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
112 return Global::vboxStatusCodeToCOM(hrc);
113}
114
115
116int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
117 const Utf8Str &strPath, Utf8Str &strValue)
118{
119 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
120 const ComPtr<IMachine> pMachine = pConsole->i_machine();
121
122 Assert(!pMachine.isNull());
123 Bstr strTemp, strFlags;
124 LONG64 i64Timestamp;
125 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
126 strTemp.asOutParam(),
127 &i64Timestamp, strFlags.asOutParam());
128 if (SUCCEEDED(hr))
129 {
130 strValue = strTemp;
131 return VINF_SUCCESS;
132 }
133 return VERR_NOT_FOUND;
134}
135
136int GuestSessionTask::setProgress(ULONG uPercent)
137{
138 if (mProgress.isNull()) /* Progress is optional. */
139 return VINF_SUCCESS;
140
141 BOOL fCanceled;
142 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
143 && fCanceled)
144 return VERR_CANCELLED;
145 BOOL fCompleted;
146 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
147 && fCompleted)
148 {
149 AssertMsgFailed(("Setting value of an already completed progress\n"));
150 return VINF_SUCCESS;
151 }
152 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
153 if (FAILED(hr))
154 return VERR_COM_UNEXPECTED;
155
156 return VINF_SUCCESS;
157}
158
159int GuestSessionTask::setProgressSuccess(void)
160{
161 if (mProgress.isNull()) /* Progress is optional. */
162 return VINF_SUCCESS;
163
164 BOOL fCanceled;
165 BOOL fCompleted;
166 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
167 && !fCanceled
168 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
169 && !fCompleted)
170 {
171 HRESULT hr = mProgress->i_notifyComplete(S_OK);
172 if (FAILED(hr))
173 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
174 }
175
176 return VINF_SUCCESS;
177}
178
179HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
180{
181 LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
182 hr, strMsg.c_str()));
183
184 if (mProgress.isNull()) /* Progress is optional. */
185 return hr; /* Return original rc. */
186
187 BOOL fCanceled;
188 BOOL fCompleted;
189 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
190 && !fCanceled
191 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
192 && !fCompleted)
193 {
194 HRESULT hr2 = mProgress->i_notifyComplete(hr,
195 COM_IIDOF(IGuestSession),
196 GuestSession::getStaticComponentName(),
197 strMsg.c_str());
198 if (FAILED(hr2))
199 return hr2;
200 }
201 return hr; /* Return original rc. */
202}
203
204SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
205 uint32_t uFlags,
206 uint32_t uTimeoutMS)
207 : GuestSessionTask(pSession),
208 mFlags(uFlags),
209 mTimeoutMS(uTimeoutMS)
210{
211 m_strTaskName = "gctlSesOpen";
212}
213
214SessionTaskOpen::~SessionTaskOpen(void)
215{
216
217}
218
219int SessionTaskOpen::Run(void)
220{
221 LogFlowThisFuncEnter();
222
223 ComObjPtr<GuestSession> pSession = mSession;
224 Assert(!pSession.isNull());
225
226 AutoCaller autoCaller(pSession);
227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
228
229 int vrc = pSession->i_startSessionInternal(NULL /*pvrcGuest*/);
230 /* Nothing to do here anymore. */
231
232 LogFlowFuncLeaveRC(vrc);
233 return vrc;
234}
235
236SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
237 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
238 : GuestSessionTask(pSession),
239 mSource(strSource),
240 mSourceFile(NULL),
241 mSourceOffset(0),
242 mSourceSize(0),
243 mDest(strDest)
244{
245 mCopyFileFlags = uFlags;
246 m_strTaskName = "gctlCpyTo";
247}
248
249/** @todo Merge this and the above call and let the above call do the open/close file handling so that the
250 * inner code only has to deal with file handles. No time now ... */
251SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
252 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
253 const Utf8Str &strDest, uint32_t uFlags)
254 : GuestSessionTask(pSession)
255{
256 mSourceFile = pSourceFile;
257 mSourceOffset = cbSourceOffset;
258 mSourceSize = cbSourceSize;
259 mDest = strDest;
260 mCopyFileFlags = uFlags;
261 m_strTaskName = "gctlCpyTo";
262}
263
264SessionTaskCopyTo::~SessionTaskCopyTo(void)
265{
266
267}
268
269int SessionTaskCopyTo::Run(void)
270{
271 LogFlowThisFuncEnter();
272
273 ComObjPtr<GuestSession> pSession = mSession;
274 Assert(!pSession.isNull());
275
276 AutoCaller autoCaller(pSession);
277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
278
279 if (mCopyFileFlags)
280 {
281 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
282 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
283 mCopyFileFlags));
284 return VERR_INVALID_PARAMETER;
285 }
286
287 int rc;
288
289 RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
290
291 RTFILE fileLocal;
292 PRTFILE pFile = &fileLocal;
293
294 if (!mSourceFile)
295 {
296 /* Does our source file exist? */
297 if (!RTFileExists(mSource.c_str()))
298 {
299 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
300 Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
301 mSource.c_str()));
302 }
303 else
304 {
305 rc = RTFileOpen(pFile, mSource.c_str(),
306 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
307 if (RT_FAILURE(rc))
308 {
309 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
310 Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
311 mSource.c_str(), rc));
312 }
313 else
314 {
315 rc = RTFileGetSize(*pFile, &mSourceSize);
316 if (RT_FAILURE(rc))
317 {
318 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
319 Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
320 mSource.c_str(), rc));
321 }
322 }
323 }
324 }
325 else
326 {
327 rc = VINF_SUCCESS;
328 pFile = mSourceFile;
329 /* Size + offset are optional. */
330 }
331
332 /*
333 * Query information about our destination first.
334 */
335 int guestRc = VERR_IPE_UNINITIALIZED_STATUS;
336 if (RT_SUCCESS(rc))
337 {
338 GuestFsObjData objData;
339 rc = pSession->i_directoryQueryInfoInternal(mDest, true /* fFollowSymlinks */, objData, &guestRc);
340 if (RT_SUCCESS(rc))
341 {
342 mDest = Utf8StrFmt("%s/%s", mDest.c_str(), RTPathFilename(mSource.c_str()));
343 }
344 else if (rc == VERR_NOT_A_DIRECTORY)
345 {
346 rc = VINF_SUCCESS;
347 }
348 }
349
350 /** @todo Implement sparse file support? */
351
352 /*
353 * Start the actual copying process by cat'ing the source file to the
354 * destination file on the guest.
355 */
356 GuestProcessStartupInfo procInfo;
357 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_CAT);
358 procInfo.mFlags = ProcessCreateFlag_Hidden;
359
360 /* Set arguments.*/
361 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
362 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
363
364 /* Startup process. */
365 ComObjPtr<GuestProcess> pProcess;
366 if (RT_SUCCESS(rc))
367 rc = pSession->i_processCreateExInternal(procInfo, pProcess);
368 if (RT_SUCCESS(rc))
369 {
370 Assert(!pProcess.isNull());
371 rc = pProcess->i_startProcess(msTimeout, &guestRc);
372 }
373
374 if (RT_FAILURE(rc))
375 {
376 switch (rc)
377 {
378 case VERR_GSTCTL_GUEST_ERROR:
379 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
380 GuestProcess::i_guestErrorToString(guestRc));
381 break;
382
383 default:
384 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
385 Utf8StrFmt(GuestSession::tr(
386 "Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
387 mSource.c_str(), rc));
388 break;
389 }
390 }
391
392 if (RT_SUCCESS(rc))
393 {
394 ProcessWaitResult_T waitRes;
395 BYTE byBuf[_64K];
396
397 BOOL fCanceled = FALSE;
398 uint64_t cbWrittenTotal = 0;
399 uint64_t cbToRead = mSourceSize;
400
401 for (;;)
402 {
403 rc = pProcess->i_waitFor(ProcessWaitForFlag_StdIn, msTimeout, waitRes, &guestRc);
404 if ( RT_FAILURE(rc)
405 || ( waitRes != ProcessWaitResult_StdIn
406 && waitRes != ProcessWaitResult_WaitFlagNotSupported))
407 {
408 break;
409 }
410
411 /* If the guest does not support waiting for stdin, we now yield in
412 * order to reduce the CPU load due to busy waiting. */
413 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
414 RTThreadYield(); /* Optional, don't check rc. */
415
416 size_t cbRead = 0;
417 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
418 {
419 /** @todo Not very efficient, but works for now. */
420 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
421 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
422 if (RT_SUCCESS(rc))
423 {
424 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
425 RT_MIN((size_t)cbToRead, sizeof(byBuf)), &cbRead);
426 /*
427 * Some other error occured? There might be a chance that RTFileRead
428 * could not resolve/map the native error code to an IPRT code, so just
429 * print a generic error.
430 */
431 if (RT_FAILURE(rc))
432 {
433 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
434 Utf8StrFmt(GuestSession::tr("Could not read from host file \"%s\" (%Rrc)"),
435 mSource.c_str(), rc));
436 break;
437 }
438 }
439 else
440 {
441 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
442 Utf8StrFmt(GuestSession::tr("Seeking host file \"%s\" to offset %RU64 failed: %Rrc"),
443 mSource.c_str(), cbWrittenTotal, rc));
444 break;
445 }
446 }
447
448 uint32_t fFlags = ProcessInputFlag_None;
449
450 /* Did we reach the end of the content we want to transfer (last chunk)? */
451 if ( (cbRead < sizeof(byBuf))
452 /* Did we reach the last block which is exactly _64K? */
453 || (cbToRead - cbRead == 0)
454 /* ... or does the user want to cancel? */
455 || ( !mProgress.isNull()
456 && SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
457 && fCanceled)
458 )
459 {
460 LogFlowThisFunc(("Writing last chunk cbRead=%RU64\n", cbRead));
461 fFlags |= ProcessInputFlag_EndOfFile;
462 }
463
464 uint32_t cbWritten;
465 Assert(sizeof(byBuf) >= cbRead);
466 rc = pProcess->i_writeData(0 /* StdIn */, fFlags,
467 byBuf, cbRead,
468 msTimeout, &cbWritten, &guestRc);
469 if (RT_FAILURE(rc))
470 {
471 switch (rc)
472 {
473 case VERR_GSTCTL_GUEST_ERROR:
474 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
475 GuestProcess::i_guestErrorToString(guestRc));
476 break;
477
478 default:
479 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
480 Utf8StrFmt(GuestSession::tr("Writing to guest file \"%s\" (offset %RU64) failed: %Rrc"),
481 mDest.c_str(), cbWrittenTotal, rc));
482 break;
483 }
484
485 break;
486 }
487
488 /* Only subtract bytes reported written by the guest. */
489 Assert(cbToRead >= cbWritten);
490 cbToRead -= cbWritten;
491
492 /* Update total bytes written to the guest. */
493 cbWrittenTotal += cbWritten;
494 Assert(cbWrittenTotal <= mSourceSize);
495
496 LogFlowThisFunc(("rc=%Rrc, cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
497 rc, cbWritten, cbToRead, cbWrittenTotal, mSourceSize));
498
499 /* Did the user cancel the operation above? */
500 if (fCanceled)
501 break;
502
503 /* Update the progress.
504 * Watch out for division by zero. */
505 mSourceSize > 0
506 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
507 : rc = setProgress(100);
508 if (RT_FAILURE(rc))
509 break;
510
511 /* End of file reached? */
512 if (!cbToRead)
513 break;
514 } /* for */
515
516 LogFlowThisFunc(("Copy loop ended with rc=%Rrc, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
517 rc, cbToRead, cbWrittenTotal, mSourceSize));
518
519 /*
520 * Wait on termination of guest process until it completed all operations.
521 */
522 if ( !fCanceled
523 || RT_SUCCESS(rc))
524 {
525 rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &guestRc);
526 if ( RT_FAILURE(rc)
527 || waitRes != ProcessWaitResult_Terminate)
528 {
529 if (RT_FAILURE(rc))
530 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
531 Utf8StrFmt(
532 GuestSession::tr("Waiting on termination for copying file \"%s\" to guest failed: %Rrc"),
533 mSource.c_str(), rc));
534 else
535 {
536 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
537 Utf8StrFmt(GuestSession::tr(
538 "Waiting on termination for copying file \"%s\" to guest failed with wait result %ld"),
539 mSource.c_str(), waitRes));
540 rc = VERR_GENERAL_FAILURE; /* Fudge. */
541 }
542 }
543 }
544
545 if (RT_SUCCESS(rc))
546 {
547 /*
548 * Newer VBoxService toolbox versions report what went wrong via exit code.
549 * So handle this first.
550 */
551 /** @todo This code sequence is duplicated in CopyFrom... */
552 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
553 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
554 if (!SUCCEEDED(hrc))
555 procStatus = ProcessStatus_TerminatedAbnormally;
556
557 LONG exitCode = 42424242;
558 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
559 if (!SUCCEEDED(hrc))
560 exitCode = 42424242;
561
562 if ( procStatus != ProcessStatus_TerminatedNormally
563 || exitCode != 0)
564 {
565 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
566 if (procStatus == ProcessStatus_TerminatedNormally)
567 rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
568 else
569 rc = VERR_GENERAL_FAILURE;
570 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
571 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed: %Rrc"),
572 mSource.c_str(), rc));
573 }
574 /*
575 * Even if we succeeded until here make sure to check whether we really transfered
576 * everything.
577 */
578 else if ( mSourceSize > 0
579 && cbWrittenTotal == 0)
580 {
581 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
582 * to the destination -> access denied. */
583 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
584 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to guest \"%s\""),
585 mSource.c_str(), mDest.c_str()));
586 rc = VERR_ACCESS_DENIED;
587 }
588 else if (cbWrittenTotal < mSourceSize)
589 {
590 /* If we did not copy all let the user know. */
591 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
592 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed (%RU64/%RU64 bytes transfered)"),
593 mSource.c_str(), cbWrittenTotal, mSourceSize));
594 rc = VERR_INTERRUPTED;
595 }
596 else
597 rc = setProgressSuccess();
598 }
599 } /* processCreateExInteral */
600
601 if (!mSourceFile) /* Only close locally opened files. */
602 RTFileClose(*pFile);
603
604 LogFlowFuncLeaveRC(rc);
605 return rc;
606}
607
608SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
609 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
610 : GuestSessionTask(pSession)
611{
612 mSource = strSource;
613 mDest = strDest;
614 mFlags = uFlags;
615 m_strTaskName = "gctlCpyFrom";
616}
617
618SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
619{
620
621}
622
623int SessionTaskCopyFrom::Run(void)
624{
625 LogFlowThisFuncEnter();
626
627 ComObjPtr<GuestSession> pSession = mSession;
628 Assert(!pSession.isNull());
629
630 AutoCaller autoCaller(pSession);
631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
632
633 RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
634
635 /*
636 * Note: There will be races between querying file size + reading the guest file's
637 * content because we currently *do not* lock down the guest file when doing the
638 * actual operations.
639 ** @todo Use the IGuestFile API for locking down the file on the guest!
640 */
641 GuestFsObjData objData; int guestRc;
642 int rc = pSession->i_fileQueryInfoInternal(Utf8Str(mSource), false /*fFollowSymlinks*/, objData, &guestRc);
643 if (RT_FAILURE(rc))
644 {
645 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
646 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
647 mSource.c_str(), rc));
648 }
649 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
650 {
651 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
652 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
653 rc = VERR_NOT_A_FILE;
654 }
655
656 if (RT_SUCCESS(rc))
657 {
658 RTFILE fileDest;
659 rc = RTFileOpen(&fileDest, mDest.c_str(),
660 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
661 if (RT_FAILURE(rc))
662 {
663 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
664 Utf8StrFmt(GuestSession::tr("Opening/creating destination file on host \"%s\" failed: %Rrc"),
665 mDest.c_str(), rc));
666 }
667 else
668 {
669 GuestProcessStartupInfo procInfo;
670 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
671 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
672 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_CAT);
673 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
674
675 /* Set arguments.*/
676 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
677 procInfo.mArguments.push_back(mSource); /* Which file to output? */
678
679 /* Startup process. */
680 ComObjPtr<GuestProcess> pProcess;
681 rc = pSession->i_processCreateExInternal(procInfo, pProcess);
682 if (RT_SUCCESS(rc))
683 rc = pProcess->i_startProcess(msTimeout, &guestRc);
684 if (RT_FAILURE(rc))
685 {
686 switch (rc)
687 {
688 case VERR_GSTCTL_GUEST_ERROR:
689 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(guestRc));
690 break;
691
692 default:
693 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
694 Utf8StrFmt(GuestSession::tr(
695 "Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
696 mSource.c_str(), rc));
697 break;
698 }
699 }
700 else
701 {
702 ProcessWaitResult_T waitRes;
703 BYTE byBuf[_64K];
704
705 BOOL fCanceled = FALSE;
706 uint64_t cbWrittenTotal = 0;
707 uint64_t cbToRead = objData.mObjectSize;
708
709 for (;;)
710 {
711 rc = pProcess->i_waitFor(ProcessWaitForFlag_StdOut, msTimeout, waitRes, &guestRc);
712 if (RT_FAILURE(rc))
713 {
714 switch (rc)
715 {
716 case VERR_GSTCTL_GUEST_ERROR:
717 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
718 GuestProcess::i_guestErrorToString(guestRc));
719 break;
720
721 default:
722 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
723 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
724 mSource.c_str(), rc));
725 break;
726 }
727
728 break;
729 }
730
731 if ( waitRes == ProcessWaitResult_StdOut
732 || waitRes == ProcessWaitResult_WaitFlagNotSupported)
733 {
734 /* If the guest does not support waiting for stdin, we now yield in
735 * order to reduce the CPU load due to busy waiting. */
736 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
737 RTThreadYield(); /* Optional, don't check rc. */
738
739 uint32_t cbRead = 0; /* readData can return with VWRN_GSTCTL_OBJECTSTATE_CHANGED. */
740 rc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
741 msTimeout, byBuf, sizeof(byBuf),
742 &cbRead, &guestRc);
743 if (RT_FAILURE(rc))
744 {
745 switch (rc)
746 {
747 case VERR_GSTCTL_GUEST_ERROR:
748 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
749 GuestProcess::i_guestErrorToString(guestRc));
750 break;
751
752 default:
753 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
754 Utf8StrFmt(GuestSession::tr("Reading from guest file \"%s\" (offset %RU64) failed: %Rrc"),
755 mSource.c_str(), cbWrittenTotal, rc));
756 break;
757 }
758
759 break;
760 }
761
762 if (cbRead)
763 {
764 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
765 if (RT_FAILURE(rc))
766 {
767 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
768 Utf8StrFmt(GuestSession::tr("Writing to host file \"%s\" (%RU64 bytes left) failed: %Rrc"),
769 mDest.c_str(), cbToRead, rc));
770 break;
771 }
772
773 /* Only subtract bytes reported written by the guest. */
774 Assert(cbToRead >= cbRead);
775 cbToRead -= cbRead;
776
777 /* Update total bytes written to the guest. */
778 cbWrittenTotal += cbRead;
779 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
780
781 /* Did the user cancel the operation above? */
782 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
783 && fCanceled)
784 break;
785
786 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
787 if (RT_FAILURE(rc))
788 break;
789 }
790 }
791 else
792 {
793 break;
794 }
795
796 } /* for */
797
798 LogFlowThisFunc(("rc=%Rrc, guestrc=%Rrc, waitRes=%ld, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
799 rc, guestRc, waitRes, cbWrittenTotal, objData.mObjectSize, cbToRead));
800
801 /*
802 * Wait on termination of guest process until it completed all operations.
803 */
804 if ( !fCanceled
805 || RT_SUCCESS(rc))
806 {
807 rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &guestRc);
808 if ( RT_FAILURE(rc)
809 || waitRes != ProcessWaitResult_Terminate)
810 {
811 if (RT_FAILURE(rc))
812 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
813 Utf8StrFmt(
814 GuestSession::tr("Waiting on termination for copying file \"%s\" from guest failed: %Rrc"),
815 mSource.c_str(), rc));
816 else
817 {
818 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
819 Utf8StrFmt(GuestSession::tr(
820 "Waiting on termination for copying file \"%s\" from guest failed with wait result %ld"),
821 mSource.c_str(), waitRes));
822 rc = VERR_GENERAL_FAILURE; /* Fudge. */
823 }
824 }
825 }
826
827 if (RT_SUCCESS(rc))
828 {
829 /** @todo this code sequence is duplicated in CopyTo */
830 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
831 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
832 if (!SUCCEEDED(hrc))
833 procStatus = ProcessStatus_TerminatedAbnormally;
834
835 LONG exitCode = 42424242;
836 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
837 if (!SUCCEEDED(hrc))
838 exitCode = 42424242;
839
840 if ( procStatus != ProcessStatus_TerminatedNormally
841 || exitCode != 0)
842 {
843 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
844 if (procStatus == ProcessStatus_TerminatedNormally)
845 rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
846 else
847 rc = VERR_GENERAL_FAILURE;
848 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
849 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to host failed: %Rrc"),
850 mSource.c_str(), rc));
851 }
852 /*
853 * Even if we succeeded until here make sure to check whether we really transfered
854 * everything.
855 */
856 else if ( objData.mObjectSize > 0
857 && cbWrittenTotal == 0)
858 {
859 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
860 * to the destination -> access denied. */
861 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
862 Utf8StrFmt(GuestSession::tr("Writing guest file \"%s\" to host to \"%s\" failed: Access denied"),
863 mSource.c_str(), mDest.c_str()));
864 rc = VERR_ACCESS_DENIED;
865 }
866 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
867 {
868 /* If we did not copy all let the user know. */
869 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
870 Utf8StrFmt(GuestSession::tr("Copying guest file \"%s\" to host to \"%s\" failed (%RU64/%RI64 bytes transfered)"),
871 mSource.c_str(), mDest.c_str(), cbWrittenTotal, objData.mObjectSize));
872 rc = VERR_INTERRUPTED;
873 }
874 else
875 rc = setProgressSuccess();
876 }
877 }
878
879 RTFileClose(fileDest);
880 }
881 }
882
883 LogFlowFuncLeaveRC(rc);
884 return rc;
885}
886
887SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
888 const Utf8Str &strSource,
889 const ProcessArguments &aArguments,
890 uint32_t uFlags)
891 : GuestSessionTask(pSession)
892{
893 mSource = strSource;
894 mArguments = aArguments;
895 mFlags = uFlags;
896 m_strTaskName = "gctlUpGA";
897}
898
899SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
900{
901
902}
903
904int SessionTaskUpdateAdditions::i_addProcessArguments(ProcessArguments &aArgumentsDest,
905 const ProcessArguments &aArgumentsSource)
906{
907 int rc = VINF_SUCCESS;
908
909 try
910 {
911 /* Filter out arguments which already are in the destination to
912 * not end up having them specified twice. Not the fastest method on the
913 * planet but does the job. */
914 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
915 while (itSource != aArgumentsSource.end())
916 {
917 bool fFound = false;
918 ProcessArguments::iterator itDest = aArgumentsDest.begin();
919 while (itDest != aArgumentsDest.end())
920 {
921 if ((*itDest).equalsIgnoreCase((*itSource)))
922 {
923 fFound = true;
924 break;
925 }
926 ++itDest;
927 }
928
929 if (!fFound)
930 aArgumentsDest.push_back((*itSource));
931
932 ++itSource;
933 }
934 }
935 catch(std::bad_alloc &)
936 {
937 return VERR_NO_MEMORY;
938 }
939
940 return rc;
941}
942
943int SessionTaskUpdateAdditions::i_copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
944 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
945 bool fOptional, uint32_t *pcbSize)
946{
947 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
948 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
949 /* pcbSize is optional. */
950
951 uint32_t cbOffset;
952 size_t cbSize;
953
954 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
955 if (RT_FAILURE(rc))
956 {
957 if (fOptional)
958 return VINF_SUCCESS;
959
960 return rc;
961 }
962
963 Assert(cbOffset);
964 Assert(cbSize);
965 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
966
967 HRESULT hr = S_OK;
968 /* Copy over the Guest Additions file to the guest. */
969 if (RT_SUCCESS(rc))
970 {
971 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
972 strFileSource.c_str(), strFileDest.c_str()));
973
974 if (RT_SUCCESS(rc))
975 {
976 SessionTaskCopyTo *pTask = NULL;
977 ComObjPtr<Progress> pProgressCopyTo;
978 try
979 {
980 try
981 {
982 pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
983 &pISO->file, cbOffset, cbSize,
984 strFileDest, FileCopyFlag_None);
985 }
986 catch(...)
987 {
988 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
989 GuestSession::tr("Failed to create SessionTaskCopyTo object "));
990 throw;
991 }
992
993 hr = pTask->Init(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
994 mSource.c_str(), strFileDest.c_str()));
995 if (FAILED(hr))
996 {
997 delete pTask;
998 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
999 GuestSession::tr("Creating progress object for SessionTaskCopyTo object failed"));
1000 throw hr;
1001 }
1002
1003 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
1004
1005 if (SUCCEEDED(hr))
1006 {
1007 /* Return progress to the caller. */
1008 pProgressCopyTo = pTask->GetProgressObject();
1009 }
1010 else
1011 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1012 GuestSession::tr("Starting thread for updating additions failed "));
1013 }
1014 catch(std::bad_alloc &)
1015 {
1016 hr = E_OUTOFMEMORY;
1017 }
1018 catch(HRESULT eHR)
1019 {
1020 hr = eHR;
1021 LogFlowThisFunc(("Exception was caught in the function \n"));
1022 }
1023
1024 if (SUCCEEDED(hr))
1025 {
1026 BOOL fCanceled = FALSE;
1027 hr = pProgressCopyTo->WaitForCompletion(-1);
1028 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
1029 && fCanceled)
1030 {
1031 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1032 }
1033 else if (FAILED(hr))
1034 {
1035 Assert(FAILED(hr));
1036 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1037 }
1038 }
1039 }
1040 }
1041
1042 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
1043 * between finished copying, the verification and the actual execution. */
1044
1045 /* Determine where the installer image ended up and if it has the correct size. */
1046 if (RT_SUCCESS(rc))
1047 {
1048 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n",
1049 strFileDest.c_str()));
1050
1051 GuestFsObjData objData;
1052 int64_t cbSizeOnGuest; int guestRc;
1053 rc = pSession->i_fileQuerySizeInternal(strFileDest, false /*fFollowSymlinks*/, &cbSizeOnGuest, &guestRc);
1054 if ( RT_SUCCESS(rc)
1055 && cbSize == (uint64_t)cbSizeOnGuest)
1056 {
1057 LogFlowThisFunc(("Guest Additions installer file \"%s\" successfully verified\n",
1058 strFileDest.c_str()));
1059 }
1060 else
1061 {
1062 if (RT_SUCCESS(rc)) /* Size does not match. */
1063 {
1064 LogRel(("Size of Guest Additions installer file \"%s\" does not match: %RI64 bytes copied, %RU64 bytes expected\n",
1065 strFileDest.c_str(), cbSizeOnGuest, cbSize));
1066 rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
1067 }
1068 else
1069 {
1070 switch (rc)
1071 {
1072 case VERR_GSTCTL_GUEST_ERROR:
1073 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1074 GuestProcess::i_guestErrorToString(guestRc));
1075 break;
1076
1077 default:
1078 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1079 Utf8StrFmt(GuestSession::tr("Error while querying size for file \"%s\": %Rrc"),
1080 strFileDest.c_str(), rc));
1081 break;
1082 }
1083 }
1084 }
1085
1086 if (RT_SUCCESS(rc))
1087 {
1088 if (pcbSize)
1089 *pcbSize = (uint32_t)cbSizeOnGuest;
1090 }
1091 }
1092
1093 return rc;
1094}
1095
1096int SessionTaskUpdateAdditions::i_runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
1097{
1098 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1099
1100 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
1101
1102 GuestProcessTool procTool; int guestRc;
1103 int vrc = procTool.Init(pSession, procInfo, false /* Async */, &guestRc);
1104 if (RT_SUCCESS(vrc))
1105 {
1106 if (RT_SUCCESS(guestRc))
1107 vrc = procTool.i_wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
1108 if (RT_SUCCESS(vrc))
1109 vrc = procTool.i_terminatedOk();
1110 }
1111
1112 if (RT_FAILURE(vrc))
1113 {
1114 switch (vrc)
1115 {
1116 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
1117 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1118 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
1119 procInfo.mExecutable.c_str(), procTool.i_getRc()));
1120 break;
1121
1122 case VERR_GSTCTL_GUEST_ERROR:
1123 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1124 GuestProcess::i_guestErrorToString(guestRc));
1125 break;
1126
1127 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
1128 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1129 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
1130 procInfo.mExecutable.c_str()));
1131 break;
1132
1133 default:
1134 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1135 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
1136 procInfo.mExecutable.c_str(), vrc));
1137 break;
1138 }
1139 }
1140
1141 return vrc;
1142}
1143
1144int SessionTaskUpdateAdditions::Run(void)
1145{
1146 LogFlowThisFuncEnter();
1147
1148 ComObjPtr<GuestSession> pSession = mSession;
1149 Assert(!pSession.isNull());
1150
1151 AutoCaller autoCaller(pSession);
1152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1153
1154 int rc = setProgress(10);
1155 if (RT_FAILURE(rc))
1156 return rc;
1157
1158 HRESULT hr = S_OK;
1159
1160 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
1161
1162 ComObjPtr<Guest> pGuest(mSession->i_getParent());
1163#if 0
1164 /*
1165 * Wait for the guest being ready within 30 seconds.
1166 */
1167 AdditionsRunLevelType_T addsRunLevel;
1168 uint64_t tsStart = RTTimeSystemMilliTS();
1169 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1170 && ( addsRunLevel != AdditionsRunLevelType_Userland
1171 && addsRunLevel != AdditionsRunLevelType_Desktop))
1172 {
1173 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
1174 {
1175 rc = VERR_TIMEOUT;
1176 break;
1177 }
1178
1179 RTThreadSleep(100); /* Wait a bit. */
1180 }
1181
1182 if (FAILED(hr)) rc = VERR_TIMEOUT;
1183 if (rc == VERR_TIMEOUT)
1184 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1185 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
1186#else
1187 /*
1188 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
1189 * can continue.
1190 */
1191 AdditionsRunLevelType_T addsRunLevel;
1192 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1193 || ( addsRunLevel != AdditionsRunLevelType_Userland
1194 && addsRunLevel != AdditionsRunLevelType_Desktop))
1195 {
1196 if (addsRunLevel == AdditionsRunLevelType_System)
1197 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1198 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1199 else
1200 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1201 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1202 rc = VERR_NOT_SUPPORTED;
1203 }
1204#endif
1205
1206 if (RT_SUCCESS(rc))
1207 {
1208 /*
1209 * Determine if we are able to update automatically. This only works
1210 * if there are recent Guest Additions installed already.
1211 */
1212 Utf8Str strAddsVer;
1213 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1214 if ( RT_SUCCESS(rc)
1215 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1216 {
1217 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1218 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1219 strAddsVer.c_str()));
1220 rc = VERR_NOT_SUPPORTED;
1221 }
1222 }
1223
1224 Utf8Str strOSVer;
1225 eOSType osType = eOSType_Unknown;
1226 if (RT_SUCCESS(rc))
1227 {
1228 /*
1229 * Determine guest OS type and the required installer image.
1230 */
1231 Utf8Str strOSType;
1232 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1233 if (RT_SUCCESS(rc))
1234 {
1235 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1236 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1237 {
1238 osType = eOSType_Windows;
1239
1240 /*
1241 * Determine guest OS version.
1242 */
1243 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1244 if (RT_FAILURE(rc))
1245 {
1246 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1247 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1248 rc = VERR_NOT_SUPPORTED;
1249 }
1250
1251 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1252 * can't do automated updates here. */
1253 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1254 if ( RT_SUCCESS(rc)
1255 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1256 {
1257 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1258 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
1259 {
1260 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1261 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1262 * flag is set this update routine ends successfully as soon as the installer was started
1263 * (and the user has to deal with it in the guest). */
1264 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1265 {
1266 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1267 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1268 rc = VERR_NOT_SUPPORTED;
1269 }
1270 }
1271 }
1272 else
1273 {
1274 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1275 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
1276 strOSType.c_str(), strOSVer.c_str()));
1277 rc = VERR_NOT_SUPPORTED;
1278 }
1279 }
1280 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1281 {
1282 osType = eOSType_Solaris;
1283 }
1284 else /* Everything else hopefully means Linux :-). */
1285 osType = eOSType_Linux;
1286
1287#if 1 /* Only Windows is supported (and tested) at the moment. */
1288 if ( RT_SUCCESS(rc)
1289 && osType != eOSType_Windows)
1290 {
1291 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1292 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1293 strOSType.c_str()));
1294 rc = VERR_NOT_SUPPORTED;
1295 }
1296#endif
1297 }
1298 }
1299
1300 RTISOFSFILE iso;
1301 if (RT_SUCCESS(rc))
1302 {
1303 /*
1304 * Try to open the .ISO file to extract all needed files.
1305 */
1306 rc = RTIsoFsOpen(&iso, mSource.c_str());
1307 if (RT_FAILURE(rc))
1308 {
1309 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1310 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1311 mSource.c_str(), rc));
1312 }
1313 else
1314 {
1315 /* Set default installation directories. */
1316 Utf8Str strUpdateDir = "/tmp/";
1317 if (osType == eOSType_Windows)
1318 strUpdateDir = "C:\\Temp\\";
1319
1320 rc = setProgress(5);
1321
1322 /* Try looking up the Guest Additions installation directory. */
1323 if (RT_SUCCESS(rc))
1324 {
1325 /* Try getting the installed Guest Additions version to know whether we
1326 * can install our temporary Guest Addition data into the original installation
1327 * directory.
1328 *
1329 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1330 * a different location then.
1331 */
1332 bool fUseInstallDir = false;
1333
1334 Utf8Str strAddsVer;
1335 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1336 if ( RT_SUCCESS(rc)
1337 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1338 {
1339 fUseInstallDir = true;
1340 }
1341
1342 if (fUseInstallDir)
1343 {
1344 if (RT_SUCCESS(rc))
1345 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1346 if (RT_SUCCESS(rc))
1347 {
1348 if (osType == eOSType_Windows)
1349 {
1350 strUpdateDir.findReplace('/', '\\');
1351 strUpdateDir.append("\\Update\\");
1352 }
1353 else
1354 strUpdateDir.append("/update/");
1355 }
1356 }
1357 }
1358
1359 if (RT_SUCCESS(rc))
1360 LogRel(("Guest Additions update directory is: %s\n",
1361 strUpdateDir.c_str()));
1362
1363 /* Create the installation directory. */
1364 int guestRc;
1365 rc = pSession->i_directoryCreateInternal(strUpdateDir,
1366 755 /* Mode */, DirectoryCreateFlag_Parents, &guestRc);
1367 if (RT_FAILURE(rc))
1368 {
1369 switch (rc)
1370 {
1371 case VERR_GSTCTL_GUEST_ERROR:
1372 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1373 GuestProcess::i_guestErrorToString(guestRc));
1374 break;
1375
1376 default:
1377 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1378 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1379 strUpdateDir.c_str(), rc));
1380 break;
1381 }
1382 }
1383 if (RT_SUCCESS(rc))
1384 rc = setProgress(10);
1385
1386 if (RT_SUCCESS(rc))
1387 {
1388 /* Prepare the file(s) we want to copy over to the guest and
1389 * (maybe) want to run. */
1390 switch (osType)
1391 {
1392 case eOSType_Windows:
1393 {
1394 /* Do we need to install our certificates? We do this for W2K and up. */
1395 bool fInstallCert = false;
1396
1397 /* Only Windows 2000 and up need certificates to be installed. */
1398 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1399 {
1400 fInstallCert = true;
1401 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1402 }
1403 else
1404 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1405
1406 if (fInstallCert)
1407 {
1408 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
1409 {
1410 { "vbox.cer", "CERT/VBOX.CER" },
1411 { "vbox-sha1.cer", "CERT/VBOX_SHA1.CER" },
1412 { "vbox-sha256.cer", "CERT/VBOX_SHA256.CER" },
1413 { "vbox-sha256-r3.cer", "CERT/VBOX_SHA256_R3.CER" },
1414 { "oracle-vbox.cer", "CERT/ORACLE_VBOX.CER" },
1415 };
1416 uint32_t fCopyCertUtil = UPDATEFILE_FLAG_COPY_FROM_ISO;
1417 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
1418 {
1419 /* Skip if not present on the ISO. */
1420 uint32_t offIgn;
1421 size_t cbIgn;
1422 rc = RTIsoFsGetFileInfo(&iso, s_aCertFiles[i].pszIso, &offIgn, &cbIgn);
1423 if (RT_FAILURE(rc))
1424 continue;
1425
1426 /* Copy the certificate certificate. */
1427 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
1428 mFiles.push_back(InstallerFile(s_aCertFiles[i].pszIso,
1429 strDstCert,
1430 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
1431
1432 /* Out certificate installation utility. */
1433 /* First pass: Copy over the file (first time only) + execute it to remove any
1434 * existing VBox certificates. */
1435 GuestProcessStartupInfo siCertUtilRem;
1436 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
1437 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1438 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1439 siCertUtilRem.mArguments.push_back(strDstCert);
1440 siCertUtilRem.mArguments.push_back(strDstCert);
1441 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1442 strUpdateDir + "VBoxCertUtil.exe",
1443 fCopyCertUtil | UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1444 siCertUtilRem));
1445 fCopyCertUtil = 0;
1446 /* Second pass: Only execute (but don't copy) again, this time installng the
1447 * recent certificates just copied over. */
1448 GuestProcessStartupInfo siCertUtilAdd;
1449 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
1450 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1451 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1452 siCertUtilAdd.mArguments.push_back(strDstCert);
1453 siCertUtilAdd.mArguments.push_back(strDstCert);
1454 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1455 strUpdateDir + "VBoxCertUtil.exe",
1456 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1457 siCertUtilAdd));
1458 }
1459 }
1460 /* The installers in different flavors, as we don't know (and can't assume)
1461 * the guest's bitness. */
1462 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
1463 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1464 UPDATEFILE_FLAG_COPY_FROM_ISO));
1465 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1466 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1467 UPDATEFILE_FLAG_COPY_FROM_ISO));
1468 /* The stub loader which decides which flavor to run. */
1469 GuestProcessStartupInfo siInstaller;
1470 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1471 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
1472 * setup can take quite a while, so be on the safe side. */
1473 siInstaller.mTimeoutMS = 5 * 60 * 1000;
1474 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1475 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1476 /* Don't quit VBoxService during upgrade because it still is used for this
1477 * piece of code we're in right now (that is, here!) ... */
1478 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1479 /* Tell the installer to report its current installation status
1480 * using a running VBoxTray instance via balloon messages in the
1481 * Windows taskbar. */
1482 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1483 /* Add optional installer command line arguments from the API to the
1484 * installer's startup info. */
1485 rc = i_addProcessArguments(siInstaller.mArguments, mArguments);
1486 AssertRC(rc);
1487 /* If the caller does not want to wait for out guest update process to end,
1488 * complete the progress object now so that the caller can do other work. */
1489 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1490 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1491 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
1492 strUpdateDir + "VBoxWindowsAdditions.exe",
1493 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
1494 break;
1495 }
1496 case eOSType_Linux:
1497 /** @todo Add Linux support. */
1498 break;
1499 case eOSType_Solaris:
1500 /** @todo Add Solaris support. */
1501 break;
1502 default:
1503 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
1504 break;
1505 }
1506 }
1507
1508 if (RT_SUCCESS(rc))
1509 {
1510 /* We want to spend 40% total for all copying operations. So roughly
1511 * calculate the specific percentage step of each copied file. */
1512 uint8_t uOffset = 20; /* Start at 20%. */
1513 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
1514
1515 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1516
1517 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
1518 while (itFiles != mFiles.end())
1519 {
1520 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
1521 {
1522 bool fOptional = false;
1523 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
1524 fOptional = true;
1525 rc = i_copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
1526 fOptional, NULL /* cbSize */);
1527 if (RT_FAILURE(rc))
1528 {
1529 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1530 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
1531 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
1532 break;
1533 }
1534 }
1535
1536 rc = setProgress(uOffset);
1537 if (RT_FAILURE(rc))
1538 break;
1539 uOffset += uStep;
1540
1541 ++itFiles;
1542 }
1543 }
1544
1545 /* Done copying, close .ISO file. */
1546 RTIsoFsClose(&iso);
1547
1548 if (RT_SUCCESS(rc))
1549 {
1550 /* We want to spend 35% total for all copying operations. So roughly
1551 * calculate the specific percentage step of each copied file. */
1552 uint8_t uOffset = 60; /* Start at 60%. */
1553 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
1554
1555 LogRel(("Executing Guest Additions update files ...\n"));
1556
1557 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
1558 while (itFiles != mFiles.end())
1559 {
1560 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
1561 {
1562 rc = i_runFileOnGuest(pSession, itFiles->mProcInfo);
1563 if (RT_FAILURE(rc))
1564 break;
1565 }
1566
1567 rc = setProgress(uOffset);
1568 if (RT_FAILURE(rc))
1569 break;
1570 uOffset += uStep;
1571
1572 ++itFiles;
1573 }
1574 }
1575
1576 if (RT_SUCCESS(rc))
1577 {
1578 LogRel(("Automatic update of Guest Additions succeeded\n"));
1579 rc = setProgressSuccess();
1580 }
1581 }
1582 }
1583
1584 if (RT_FAILURE(rc))
1585 {
1586 if (rc == VERR_CANCELLED)
1587 {
1588 LogRel(("Automatic update of Guest Additions was canceled\n"));
1589
1590 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1591 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
1592 }
1593 else
1594 {
1595 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
1596 if (!mProgress.isNull()) /* Progress object is optional. */
1597 {
1598 com::ProgressErrorInfo errorInfo(mProgress);
1599 if ( errorInfo.isFullAvailable()
1600 || errorInfo.isBasicAvailable())
1601 {
1602 strError = errorInfo.getText();
1603 }
1604 }
1605
1606 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
1607 strError.c_str(), hr));
1608 }
1609
1610 LogRel(("Please install Guest Additions manually\n"));
1611 }
1612
1613 /** @todo Clean up copied / left over installation files. */
1614
1615 LogFlowFuncLeaveRC(rc);
1616 return rc;
1617}
1618
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette