VirtualBox

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

Last change on this file since 42905 was 42897, checked in by vboxsync, 12 years ago

Guest Control 2.0: Bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.0 KB
Line 
1
2/* $Id: GuestSessionImplTasks.cpp 42897 2012-08-21 10:03:52Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestImpl.h"
24#include "GuestSessionImpl.h"
25#include "GuestCtrlImplPrivate.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ProgressImpl.h"
30
31#include <memory> /* For auto_ptr. */
32
33#include <iprt/env.h>
34#include <iprt/file.h> /* For CopyTo/From. */
35
36#ifdef LOG_GROUP
37 #undef LOG_GROUP
38#endif
39#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
40#include <VBox/log.h>
41
42
43/*
44 * If the following define is enabled the Guest Additions update code also
45 * checks for locations which were not supposed to happen due to not resolving
46 * environment variables in VBoxService toolbox arguments. So a file copied
47 * to "%TEMP%\foo.exe" became "C:\Windows\EMPfoo.exe".
48 */
49#define VBOX_SERVICE_ENVARG_BUG
50
51// session task classes
52/////////////////////////////////////////////////////////////////////////////
53
54GuestSessionTask::GuestSessionTask(GuestSession *pSession)
55{
56 mSession = pSession;
57}
58
59GuestSessionTask::~GuestSessionTask(void)
60{
61}
62
63int GuestSessionTask::setProgress(ULONG uPercent)
64{
65 if (mProgress.isNull()) /* Progress is optional. */
66 return VINF_SUCCESS;
67
68 BOOL fCanceled;
69 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
70 && fCanceled)
71 return VERR_CANCELLED;
72 BOOL fCompleted;
73 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
74 && !fCompleted)
75 return VINF_SUCCESS;
76 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
77 if (FAILED(hr))
78 return VERR_COM_UNEXPECTED;
79
80 return VINF_SUCCESS;
81}
82
83int GuestSessionTask::setProgressSuccess(void)
84{
85 if (mProgress.isNull()) /* Progress is optional. */
86 return VINF_SUCCESS;
87
88 BOOL fCanceled;
89 BOOL fCompleted;
90 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
91 && !fCanceled
92 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
93 && !fCompleted)
94 {
95 HRESULT hr = mProgress->notifyComplete(S_OK);
96 if (FAILED(hr))
97 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
98 }
99
100 return VINF_SUCCESS;
101}
102
103HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
104{
105 if (mProgress.isNull()) /* Progress is optional. */
106 return hr; /* Return original rc. */
107
108 BOOL fCanceled;
109 BOOL fCompleted;
110 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
111 && !fCanceled
112 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
113 && !fCompleted)
114 {
115 HRESULT hr2 = mProgress->notifyComplete(hr,
116 COM_IIDOF(IGuestSession),
117 GuestSession::getStaticComponentName(),
118 strMsg.c_str());
119 if (FAILED(hr2))
120 return hr2;
121 }
122 return hr; /* Return original rc. */
123}
124
125SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
126 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
127 : mSource(strSource),
128 mDest(strDest),
129 mSourceFile(NULL),
130 mSourceOffset(0),
131 mSourceSize(0),
132 GuestSessionTask(pSession)
133{
134 mCopyFileFlags = uFlags;
135}
136
137/** @todo Merge this and the above call and let the above call do the open/close file handling so that the
138 * inner code only has to deal with file handles. No time now ... */
139SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
140 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
141 const Utf8Str &strDest, uint32_t uFlags)
142 : GuestSessionTask(pSession)
143{
144 mSourceFile = pSourceFile;
145 mSourceOffset = cbSourceOffset;
146 mSourceSize = cbSourceSize;
147 mDest = strDest;
148 mCopyFileFlags = uFlags;
149}
150
151SessionTaskCopyTo::~SessionTaskCopyTo(void)
152{
153
154}
155
156int SessionTaskCopyTo::Run(void)
157{
158 LogFlowThisFuncEnter();
159
160 ComObjPtr<GuestSession> pSession = mSession;
161 Assert(!pSession.isNull());
162
163 AutoCaller autoCaller(pSession);
164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
165
166 if (mCopyFileFlags)
167 {
168 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
169 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
170 mCopyFileFlags));
171 return VERR_INVALID_PARAMETER;
172 }
173
174 int rc;
175
176 RTFILE fileLocal;
177 PRTFILE pFile = &fileLocal;
178
179 if (!mSourceFile)
180 {
181 /* Does our source file exist? */
182 if (!RTFileExists(mSource.c_str()))
183 {
184 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
185 Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
186 mSource.c_str()));
187 }
188 else
189 {
190 rc = RTFileOpen(pFile, mSource.c_str(),
191 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
192 if (RT_FAILURE(rc))
193 {
194 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
195 Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
196 mSource.c_str(), rc));
197 }
198 else
199 {
200 rc = RTFileGetSize(*pFile, &mSourceSize);
201 if (RT_FAILURE(rc))
202 {
203 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
204 Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
205 mSource.c_str(), rc));
206 }
207 }
208 }
209 }
210 else
211 {
212 pFile = mSourceFile;
213 /* Size + offset are optional. */
214 }
215
216 GuestProcessStartupInfo procInfo;
217 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to the guest to \"%s\" (%RU64 bytes)"),
218 mSource.c_str(), mDest.c_str(), mSourceSize);
219 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
220 procInfo.mFlags = ProcessCreateFlag_Hidden;
221
222 /* Set arguments.*/
223 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
224
225 /* Startup process. */
226 ComObjPtr<GuestProcess> pProcess;
227 rc = pSession->processCreateExInteral(procInfo, pProcess);
228 if (RT_SUCCESS(rc))
229 rc = pProcess->startProcess();
230 if (RT_FAILURE(rc))
231 {
232 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
233 Utf8StrFmt(GuestSession::tr("Unable to start guest process: %Rrc"), rc));
234 }
235 else
236 {
237 GuestProcessWaitResult waitRes;
238 BYTE byBuf[_64K];
239
240 BOOL fCanceled = FALSE;
241 uint64_t cbWrittenTotal = 0;
242 uint64_t cbToRead = mSourceSize;
243
244 for (;;)
245 {
246 rc = pProcess->waitFor(ProcessWaitForFlag_StdIn,
247 30 * 1000 /* Timeout */, waitRes);
248 if ( RT_FAILURE(rc)
249 || ( waitRes.mResult != ProcessWaitResult_StdIn
250 && waitRes.mResult != ProcessWaitResult_WaitFlagNotSupported))
251 {
252 break;
253 }
254
255 /* If the guest does not support waiting for stdin, we now yield in
256 * order to reduce the CPU load due to busy waiting. */
257 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
258 RTThreadYield(); /* Optional, don't check rc. */
259
260 size_t cbRead = 0;
261 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
262 {
263 /** @todo Not very efficient, but works for now. */
264 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
265 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
266 if (RT_SUCCESS(rc))
267 {
268 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
269 RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
270 /*
271 * Some other error occured? There might be a chance that RTFileRead
272 * could not resolve/map the native error code to an IPRT code, so just
273 * print a generic error.
274 */
275 if (RT_FAILURE(rc))
276 {
277 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
278 Utf8StrFmt(GuestSession::tr("Could not read from file \"%s\" (%Rrc)"),
279 mSource.c_str(), rc));
280 break;
281 }
282 }
283 else
284 {
285 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
286 Utf8StrFmt(GuestSession::tr("Seeking file \"%s\" offset %RU64 failed: %Rrc"),
287 mSource.c_str(), cbWrittenTotal, rc));
288 break;
289 }
290 }
291
292 uint32_t fFlags = ProcessInputFlag_None;
293
294 /* Did we reach the end of the content we want to transfer (last chunk)? */
295 if ( (cbRead < sizeof(byBuf))
296 /* Did we reach the last block which is exactly _64K? */
297 || (cbToRead - cbRead == 0)
298 /* ... or does the user want to cancel? */
299 || ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
300 && fCanceled)
301 )
302 {
303 fFlags |= ProcessInputFlag_EndOfFile;
304 }
305
306 uint32_t cbWritten;
307 Assert(sizeof(byBuf) >= cbRead);
308 rc = pProcess->writeData(0 /* StdIn */, fFlags,
309 byBuf, cbRead,
310 30 * 1000 /* Timeout */, &cbWritten);
311 if (RT_FAILURE(rc))
312 {
313 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
314 Utf8StrFmt(GuestSession::tr("Writing to file \"%s\" (offset %RU64) failed: %Rrc"),
315 mDest.c_str(), cbWrittenTotal, rc));
316 break;
317 }
318
319 LogFlowThisFunc(("cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
320 cbWritten, cbToRead - cbWritten, cbWrittenTotal + cbWritten, mSourceSize));
321
322 /* Only subtract bytes reported written by the guest. */
323 Assert(cbToRead >= cbWritten);
324 cbToRead -= cbWritten;
325
326 /* Update total bytes written to the guest. */
327 cbWrittenTotal += cbWritten;
328 Assert(cbWrittenTotal <= mSourceSize);
329
330 /* Did the user cancel the operation above? */
331 if (fCanceled)
332 break;
333
334 /* Update the progress.
335 * Watch out for division by zero. */
336 mSourceSize > 0
337 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
338 : rc = setProgress(100);
339 if (RT_FAILURE(rc))
340 break;
341
342 /* End of file reached? */
343 if (!cbToRead)
344 break;
345 } /* for */
346
347 if ( !fCanceled
348 || RT_SUCCESS(rc))
349 {
350 /*
351 * Even if we succeeded until here make sure to check whether we really transfered
352 * everything.
353 */
354 if ( mSourceSize > 0
355 && cbWrittenTotal == 0)
356 {
357 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
358 * to the destination -> access denied. */
359 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
360 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
361 mSource.c_str(), mDest.c_str()));
362 }
363 else if (cbWrittenTotal < mSourceSize)
364 {
365 /* If we did not copy all let the user know. */
366 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
367 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RU64 bytes transfered)"),
368 mSource.c_str(), cbWrittenTotal, mSourceSize));
369 }
370 else
371 {
372 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
373 30 * 1000 /* Timeout */, waitRes);
374 if ( RT_FAILURE(rc)
375 || waitRes.mResult != ProcessWaitResult_Terminate)
376 {
377 if (RT_FAILURE(rc))
378 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
379 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed: %Rrc"),
380 mSource.c_str(), rc));
381 else
382 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
383 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed with wait result %ld"),
384 mSource.c_str(), waitRes.mResult));
385 }
386
387 if (RT_SUCCESS(rc))
388 {
389 ProcessStatus_T procStatus;
390 LONG exitCode;
391 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
392 && procStatus != ProcessStatus_TerminatedNormally)
393 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
394 && exitCode != 0)
395 )
396 {
397 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
398 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %ld"),
399 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
400 }
401 }
402
403 if (RT_SUCCESS(rc))
404 rc = setProgressSuccess();
405 }
406 }
407
408 if (!pProcess.isNull())
409 pProcess->uninit();
410 } /* processCreateExInteral */
411
412 if (!mSourceFile) /* Only close locally opened files. */
413 RTFileClose(*pFile);
414
415 LogFlowFuncLeaveRC(rc);
416 return rc;
417}
418
419int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
420{
421 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
422 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
423
424 mDesc = strDesc;
425 mProgress = pProgress;
426
427 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
428 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
429 "gctlCpyTo");
430 LogFlowFuncLeaveRC(rc);
431 return rc;
432}
433
434/* static */
435int SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
436{
437 std::auto_ptr<SessionTaskCopyTo> task(static_cast<SessionTaskCopyTo*>(pvUser));
438 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
439
440 LogFlowFunc(("pTask=%p\n", task.get()));
441 return task->Run();
442}
443
444SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
445 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
446 : GuestSessionTask(pSession)
447{
448 mSource = strSource;
449 mDest = strDest;
450 mFlags = uFlags;
451}
452
453SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
454{
455
456}
457
458int SessionTaskCopyFrom::Run(void)
459{
460 LogFlowThisFuncEnter();
461
462 ComObjPtr<GuestSession> pSession = mSession;
463 Assert(!pSession.isNull());
464
465 AutoCaller autoCaller(pSession);
466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
467
468 /*
469 * Note: There will be races between querying file size + reading the guest file's
470 * content because we currently *do not* lock down the guest file when doing the
471 * actual operations.
472 ** @todo Implement guest file locking!
473 */
474 GuestFsObjData objData;
475 int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData);
476 if (RT_FAILURE(rc))
477 {
478 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
479 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
480 mSource.c_str(), rc));
481 }
482 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
483 {
484 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
485 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
486 }
487
488 if (RT_SUCCESS(rc))
489 {
490 RTFILE fileDest;
491 rc = RTFileOpen(&fileDest, mDest.c_str(),
492 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
493 if (RT_FAILURE(rc))
494 {
495 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
496 Utf8StrFmt(GuestSession::tr("Error opening destination file \"%s\": %Rrc"),
497 mDest.c_str(), rc));
498 }
499 else
500 {
501 GuestProcessStartupInfo procInfo;
502 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
503 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
504 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
505 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
506
507 /* Set arguments.*/
508 procInfo.mArguments.push_back(mSource); /* Which file to output? */
509
510 /* Startup process. */
511 ComObjPtr<GuestProcess> pProcess;
512 rc = pSession->processCreateExInteral(procInfo, pProcess);
513 if (RT_SUCCESS(rc))
514 rc = pProcess->startProcess();
515 if (RT_FAILURE(rc))
516 {
517 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
518 Utf8StrFmt(GuestSession::tr("Unable to start guest process for copying data from guest to host: %Rrc"), rc));
519 }
520 else
521 {
522 GuestProcessWaitResult waitRes;
523 BYTE byBuf[_64K];
524
525 BOOL fCanceled = FALSE;
526 uint64_t cbWrittenTotal = 0;
527 uint64_t cbToRead = objData.mObjectSize;
528
529 for (;;)
530 {
531 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
532 30 * 1000 /* Timeout */, waitRes);
533 if ( waitRes.mResult == ProcessWaitResult_StdOut
534 || waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
535 {
536 /* If the guest does not support waiting for stdin, we now yield in
537 * order to reduce the CPU load due to busy waiting. */
538 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
539 RTThreadYield(); /* Optional, don't check rc. */
540
541 size_t cbRead;
542 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
543 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
544 &cbRead);
545 if (RT_FAILURE(rc))
546 {
547 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
548 Utf8StrFmt(GuestSession::tr("Reading from file \"%s\" (offset %RU64) failed: %Rrc"),
549 mSource.c_str(), cbWrittenTotal, rc));
550 break;
551 }
552
553 if (cbRead)
554 {
555 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
556 if (RT_FAILURE(rc))
557 {
558 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
559 Utf8StrFmt(GuestSession::tr("Error writing to file \"%s\" (%RU64 bytes left): %Rrc"),
560 mDest.c_str(), cbToRead, rc));
561 break;
562 }
563
564 /* Only subtract bytes reported written by the guest. */
565 Assert(cbToRead >= cbRead);
566 cbToRead -= cbRead;
567
568 /* Update total bytes written to the guest. */
569 cbWrittenTotal += cbRead;
570 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
571
572 /* Did the user cancel the operation above? */
573 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
574 && fCanceled)
575 break;
576
577 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
578 if (RT_FAILURE(rc))
579 break;
580 }
581 }
582 else if ( RT_FAILURE(rc)
583 || waitRes.mResult == ProcessWaitResult_Terminate
584 || waitRes.mResult == ProcessWaitResult_Error
585 || waitRes.mResult == ProcessWaitResult_Timeout)
586 {
587 if (RT_FAILURE(waitRes.mRC))
588 rc = waitRes.mRC;
589 break;
590 }
591 } /* for */
592
593 LogFlowThisFunc(("rc=%Rrc, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
594 rc, cbWrittenTotal, objData.mObjectSize, cbToRead));
595
596 if ( !fCanceled
597 || RT_SUCCESS(rc))
598 {
599 /*
600 * Even if we succeeded until here make sure to check whether we really transfered
601 * everything.
602 */
603 if ( objData.mObjectSize > 0
604 && cbWrittenTotal == 0)
605 {
606 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
607 * to the destination -> access denied. */
608 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
609 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
610 mSource.c_str(), mDest.c_str()));
611 rc = VERR_GENERAL_FAILURE; /* Fudge. */
612 }
613 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
614 {
615 /* If we did not copy all let the user know. */
616 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
617 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RI64 bytes transfered)"),
618 mSource.c_str(), cbWrittenTotal, objData.mObjectSize));
619 rc = VERR_GENERAL_FAILURE; /* Fudge. */
620 }
621 else
622 {
623 ProcessStatus_T procStatus;
624 LONG exitCode;
625 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
626 && procStatus != ProcessStatus_TerminatedNormally)
627 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
628 && exitCode != 0)
629 )
630 {
631 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
632 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %d"),
633 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
634 rc = VERR_GENERAL_FAILURE; /* Fudge. */
635 }
636 else /* Yay, success! */
637 rc = setProgressSuccess();
638 }
639 }
640
641 if (!pProcess.isNull())
642 pProcess->uninit();
643 }
644
645 RTFileClose(fileDest);
646 }
647 }
648
649 LogFlowFuncLeaveRC(rc);
650 return rc;
651}
652
653int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
654{
655 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
656 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
657
658 mDesc = strDesc;
659 mProgress = pProgress;
660
661 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
662 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
663 "gctlCpyFrom");
664 LogFlowFuncLeaveRC(rc);
665 return rc;
666}
667
668/* static */
669int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
670{
671 std::auto_ptr<SessionTaskCopyFrom> task(static_cast<SessionTaskCopyFrom*>(pvUser));
672 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
673
674 LogFlowFunc(("pTask=%p\n", task.get()));
675 return task->Run();
676}
677
678SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
679 const Utf8Str &strSource, uint32_t uFlags)
680 : GuestSessionTask(pSession)
681{
682 mSource = strSource;
683 mFlags = uFlags;
684}
685
686SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
687{
688
689}
690
691int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
692 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
693 bool fOptional, uint32_t *pcbSize)
694{
695 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
696 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
697 /* pcbSize is optional. */
698
699 uint32_t cbOffset;
700 size_t cbSize;
701
702 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
703 if (RT_FAILURE(rc))
704 {
705 if (fOptional)
706 return VINF_SUCCESS;
707
708 return rc;
709 }
710
711 Assert(cbOffset);
712 Assert(cbSize);
713 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
714
715 /* Copy over the Guest Additions file to the guest. */
716 if (RT_SUCCESS(rc))
717 {
718 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
719 strFileSource.c_str(), strFileDest.c_str()));
720
721 if (RT_SUCCESS(rc))
722 {
723 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
724 &pISO->file, cbOffset, cbSize,
725 strFileDest, CopyFileFlag_None);
726 AssertPtrReturn(pTask, VERR_NO_MEMORY);
727
728 ComObjPtr<Progress> pProgressCopyTo;
729 rc = pSession->startTaskAsync(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
730 mSource.c_str(), strFileDest.c_str()),
731 pTask, pProgressCopyTo);
732 if (RT_SUCCESS(rc))
733 {
734 BOOL fCanceled = FALSE;
735 HRESULT hr = pProgressCopyTo->WaitForCompletion(-1);
736 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
737 && fCanceled)
738 {
739 rc = VERR_GENERAL_FAILURE; /* Fudge. */
740 }
741 else if (FAILED(hr))
742 {
743 Assert(FAILED(hr));
744 rc = VERR_GENERAL_FAILURE; /* Fudge. */
745 }
746 }
747 }
748 }
749
750 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
751 * between finished copying, the verification and the actual execution. */
752
753 /* Determine where the installer image ended up and if it has the correct size. */
754 if (RT_SUCCESS(rc))
755 {
756 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n", strFileDest.c_str()));
757
758 GuestFsObjData objData;
759 int64_t cbSizeOnGuest;
760 rc = pSession->fileQuerySizeInternal(strFileDest, &cbSizeOnGuest);
761#ifdef VBOX_SERVICE_ENVARG_BUG
762 if (RT_FAILURE(rc))
763 {
764 /* Ugly hack: Because older Guest Additions have problems with environment variable
765 expansion in parameters we have to check an alternative location on Windows.
766 So check for "%TEMP%\" being "C:\\Windows\\system32\\EMP" actually. */
767 if (strFileDest.startsWith("%TEMP%\\", RTCString::CaseSensitive))
768 {
769 Utf8Str strFileDestBug = "C:\\Windows\\system32\\EMP" + strFileDest.substr(sizeof("%TEMP%\\") - sizeof(char));
770 rc = pSession->fileQuerySizeInternal(strFileDestBug, &cbSizeOnGuest);
771 }
772 }
773#endif
774 if ( RT_SUCCESS(rc)
775 && cbSize == (uint64_t)cbSizeOnGuest)
776 {
777 LogRel(("Guest Additions installer file \"%s\" successfully verified\n",
778 strFileDest.c_str()));
779 }
780 else
781 {
782 if (RT_SUCCESS(rc)) /* Size does not match. */
783 rc = VERR_BROKEN_PIPE; /** @todo FInd a better error. */
784 }
785
786 if (RT_SUCCESS(rc))
787 {
788 if (pcbSize)
789 *pcbSize = cbSizeOnGuest;
790 }
791 }
792
793 return rc;
794}
795
796int SessionTaskUpdateAdditions::runFile(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
797{
798 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
799
800#ifdef VBOX_SERVICE_ENVARG_BUG
801 GuestFsObjData objData;
802 int rc = pSession->fileQueryInfoInternal(procInfo.mCommand, objData);
803 if (RT_FAILURE(rc))
804 procInfo.mCommand = "C:\\Windows\\system32\\EMP" + procInfo.mCommand.substr(sizeof("%TEMP%\\") - sizeof(char));
805#endif
806
807 ComObjPtr<GuestProcess> pProcess;
808 rc = pSession->processCreateExInteral(procInfo, pProcess);
809 if (RT_SUCCESS(rc))
810 rc = pProcess->startProcess();
811
812 if (RT_SUCCESS(rc))
813 {
814 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
815
816 GuestProcessWaitResult waitRes;
817 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
818 10 * 60 * 1000 /* 10 mins Timeout */, waitRes);
819 if (waitRes.mResult == ProcessWaitResult_Terminate)
820 {
821 ProcessStatus_T procStatus;
822 LONG exitCode;
823 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
824 && procStatus != ProcessStatus_TerminatedNormally)
825 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
826 && exitCode != 0)
827 )
828 {
829 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
830 Utf8StrFmt(GuestSession::tr("Running %s failed with status %ld, exit code %ld"),
831 procInfo.mName.c_str(), procStatus, exitCode));
832 rc = VERR_GENERAL_FAILURE; /* Fudge. */
833 }
834 else /* Yay, success! */
835 {
836 LogRel(("%s successfully completed\n", procInfo.mName.c_str()));
837 }
838 }
839 else
840 {
841 if (RT_FAILURE(rc))
842 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
843 Utf8StrFmt(GuestSession::tr("Error while waiting running %s: %Rrc"),
844 procInfo.mName.c_str(), rc));
845 else
846 {
847 setProgressErrorMsg(VBOX_E_IPRT_ERROR, pProcess->errorMsg());
848 rc = VERR_GENERAL_FAILURE; /* Fudge. */
849 }
850 }
851 }
852
853 if (!pProcess.isNull())
854 pProcess->uninit();
855
856 return rc;
857}
858
859int SessionTaskUpdateAdditions::Run(void)
860{
861 LogFlowThisFuncEnter();
862
863 ComObjPtr<GuestSession> pSession = mSession;
864 Assert(!pSession.isNull());
865
866 AutoCaller autoCaller(pSession);
867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
868
869 int rc = setProgress(10);
870 if (RT_FAILURE(rc))
871 return rc;
872
873 HRESULT hr = S_OK;
874
875 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
876
877 /*
878 * Determine guest OS type and the required installer image.
879 * At the moment only Windows guests are supported.
880 */
881 Utf8Str strInstallerImage;
882
883 ComObjPtr<Guest> pGuest(mSession->getParent());
884 Bstr osTypeId;
885 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))
886 && !osTypeId.isEmpty())
887 {
888 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */
889 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)
890 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))
891 {
892 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))
893 strInstallerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
894 else
895 strInstallerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
896 /* Since the installers are located in the root directory,
897 * no further path processing needs to be done (yet). */
898 }
899 else /* Everything else is not supported (yet). */
900 {
901 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
902 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
903 osTypeIdUtf8.c_str()));
904 rc = VERR_GENERAL_FAILURE; /* Fudge. */
905 }
906 }
907 else
908 {
909 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
910 Utf8StrFmt(GuestSession::tr("Could not detected guest OS type/version, please update manually")));
911 rc = VERR_GENERAL_FAILURE; /* Fudge. */
912 }
913
914 RTISOFSFILE iso;
915 if (RT_SUCCESS(rc))
916 {
917 Assert(!strInstallerImage.isEmpty());
918
919 /*
920 * Try to open the .ISO file and locate the specified installer.
921 */
922 rc = RTIsoFsOpen(&iso, mSource.c_str());
923 if (RT_FAILURE(rc))
924 {
925 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
926 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
927 mSource.c_str(), rc));
928 }
929 else
930 {
931 rc = setProgress(5);
932
933 /** @todo Add support for non-Windows as well! */
934 Utf8Str strInstallerDest = "%TEMP%\\VBoxWindowsAdditions.exe";
935 bool fInstallCertificates = false;
936
937 if (RT_SUCCESS(rc))
938 {
939 /*
940 * Copy over main installer to the guest.
941 */
942 rc = copyFileToGuest(pSession, &iso, strInstallerImage, strInstallerDest,
943 false /* File is not optional */, NULL /* cbSize */);
944 if (RT_SUCCESS(rc))
945 rc = setProgress(20);
946
947 /*
948 * Install needed certificates for the WHQL crap.
949 ** @todo Only for Windows!
950 */
951 if (RT_SUCCESS(rc))
952 {
953 rc = copyFileToGuest(pSession, &iso, "CERT/ORACLE_VBOX.CER", "%TEMP%\\oracle-vbox.cer",
954 true /* File is optional */, NULL /* cbSize */);
955 if (RT_SUCCESS(rc))
956 {
957 rc = setProgress(30);
958 if (RT_SUCCESS(rc))
959 {
960 rc = copyFileToGuest(pSession, &iso, "CERT/VBOXCERTUTIL.EXE", "%TEMP%\\VBoxCertUtil.exe",
961 true /* File is optional */, NULL /* cbSize */);
962 if (RT_SUCCESS(rc))
963 {
964 fInstallCertificates = true;
965 rc = setProgress(40);
966 }
967 else
968 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
969 Utf8StrFmt(GuestSession::tr("Error while copying certificate installation tool to the guest: %Rrc"), rc));
970 }
971 }
972 else
973 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
974 Utf8StrFmt(GuestSession::tr("Error while copying certificate to the guest: %Rrc"), rc));
975 }
976 }
977
978 /*
979 * Run VBoxCertUtil.exe to install the Oracle certificate.
980 */
981 if ( RT_SUCCESS(rc)
982 && fInstallCertificates)
983 {
984 LogRel(("Installing certificates on the guest ...\n"));
985
986 GuestProcessStartupInfo procInfo;
987 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Certificate Utility"));
988 procInfo.mCommand = Utf8Str("%TEMP%\\VBoxCertUtil.exe");
989 procInfo.mFlags = ProcessCreateFlag_Hidden;
990
991 /* Construct arguments. */
992 /** @todo Remove hardcoded paths. */
993 procInfo.mArguments.push_back(Utf8Str("add-trusted-publisher"));
994 /* Ugly hack: Because older Guest Additions have problems with environment variable
995 expansion in parameters we have to check an alternative location on Windows.
996 So check for "%TEMP%\VBoxWindowsAdditions.exe" in a screwed up way. */
997#ifdef VBOX_SERVICE_ENVARG_BUG
998 GuestFsObjData objData;
999 rc = pSession->fileQueryInfoInternal("%TEMP%\\oracle-vbox.cer", objData);
1000 if (RT_SUCCESS(rc))
1001#endif
1002 procInfo.mArguments.push_back(Utf8Str("%TEMP%\\oracle-vbox.cer"));
1003#ifdef VBOX_SERVICE_ENVARG_BUG
1004 else
1005 procInfo.mArguments.push_back(Utf8Str("C:\\Windows\\system32\\EMPoracle-vbox.cer"));
1006#endif
1007 /* Overwrite rc in any case. */
1008 rc = runFile(pSession, procInfo);
1009 }
1010
1011 if (RT_SUCCESS(rc))
1012 rc = setProgress(60);
1013
1014 if (RT_SUCCESS(rc))
1015 {
1016 LogRel(("Updating Guest Additions ...\n"));
1017
1018 GuestProcessStartupInfo procInfo;
1019 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Setup"));
1020 procInfo.mCommand = Utf8Str(strInstallerDest);
1021 procInfo.mFlags = ProcessCreateFlag_Hidden;
1022 /* If the caller does not want to wait for out guest update process to end,
1023 * complete the progress object now so that the caller can do other work. */
1024 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1025 procInfo.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1026
1027 /* Construct arguments. */
1028 procInfo.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1029 procInfo.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1030 /* Don't quit VBoxService during upgrade because it still is used for this
1031 * piece of code we're in right now (that is, here!) ... */
1032 procInfo.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1033 /* Tell the installer to report its current installation status
1034 * using a running VBoxTray instance via balloon messages in the
1035 * Windows taskbar. */
1036 procInfo.mArguments.push_back(Utf8Str("/post_installstatus"));
1037
1038 rc = runFile(pSession, procInfo);
1039 if (RT_SUCCESS(rc))
1040 hr = setProgressSuccess();
1041 }
1042 RTIsoFsClose(&iso);
1043 }
1044 }
1045
1046 LogFlowFuncLeaveRC(rc);
1047 return rc;
1048}
1049
1050int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1051{
1052 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1053 strDesc.c_str(), mSource.c_str(), mFlags));
1054
1055 mDesc = strDesc;
1056 mProgress = pProgress;
1057
1058 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1059 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1060 "gctlUpGA");
1061 LogFlowFuncLeaveRC(rc);
1062 return rc;
1063}
1064
1065/* static */
1066int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1067{
1068 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1069 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1070
1071 LogFlowFunc(("pTask=%p\n", task.get()));
1072 return task->Run();
1073}
1074
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