VirtualBox

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

Last change on this file since 71314 was 71299, checked in by vboxsync, 7 years ago

Guest Control: Renaming (guestRc -> rcGuest).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.4 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 71299 2018-03-12 11:30:08Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session tasks.
4 */
5
6/*
7 * Copyright (C) 2012-2018 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#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "GuestCtrlImplPrivate.h"
31
32#include "Global.h"
33#include "AutoCaller.h"
34#include "ConsoleImpl.h"
35#include "ProgressImpl.h"
36
37#include <memory> /* For auto_ptr. */
38
39#include <iprt/env.h>
40#include <iprt/file.h> /* For CopyTo/From. */
41#include <iprt/dir.h>
42#include <iprt/path.h>
43
44
45/*********************************************************************************************************************************
46* Defines *
47*********************************************************************************************************************************/
48
49/**
50 * Update file flags.
51 */
52#define UPDATEFILE_FLAG_NONE 0
53/** Copy over the file from host to the
54 * guest. */
55#define UPDATEFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
56/** Execute file on the guest after it has
57 * been successfully transfered. */
58#define UPDATEFILE_FLAG_EXECUTE RT_BIT(7)
59/** File is optional, does not have to be
60 * existent on the .ISO. */
61#define UPDATEFILE_FLAG_OPTIONAL RT_BIT(8)
62
63
64// session task classes
65/////////////////////////////////////////////////////////////////////////////
66
67GuestSessionTask::GuestSessionTask(GuestSession *pSession)
68 : ThreadTask("GenericGuestSessionTask")
69{
70 mSession = pSession;
71}
72
73GuestSessionTask::~GuestSessionTask(void)
74{
75}
76
77HRESULT GuestSessionTask::createAndSetProgressObject()
78{
79 LogFlowThisFunc(("Task Description = %s, pTask=%p\n", mDesc.c_str(), this));
80
81 ComObjPtr<Progress> pProgress;
82 HRESULT hr = S_OK;
83 /* Create the progress object. */
84 hr = pProgress.createObject();
85 if (FAILED(hr))
86 return VERR_COM_UNEXPECTED;
87
88 hr = pProgress->init(static_cast<IGuestSession*>(mSession),
89 Bstr(mDesc).raw(),
90 TRUE /* aCancelable */);
91 if (FAILED(hr))
92 return VERR_COM_UNEXPECTED;
93
94 mProgress = pProgress;
95
96 LogFlowFuncLeave();
97
98 return hr;
99}
100
101int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
102{
103 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
104
105 mDesc = strDesc;
106 mProgress = pProgress;
107 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
108
109 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
110 return Global::vboxStatusCodeToCOM(hrc);
111}
112
113
114int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
115 const Utf8Str &strPath, Utf8Str &strValue)
116{
117 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
118 const ComPtr<IMachine> pMachine = pConsole->i_machine();
119
120 Assert(!pMachine.isNull());
121 Bstr strTemp, strFlags;
122 LONG64 i64Timestamp;
123 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
124 strTemp.asOutParam(),
125 &i64Timestamp, strFlags.asOutParam());
126 if (SUCCEEDED(hr))
127 {
128 strValue = strTemp;
129 return VINF_SUCCESS;
130 }
131 return VERR_NOT_FOUND;
132}
133
134int GuestSessionTask::setProgress(ULONG uPercent)
135{
136 if (mProgress.isNull()) /* Progress is optional. */
137 return VINF_SUCCESS;
138
139 BOOL fCanceled;
140 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
141 && fCanceled)
142 return VERR_CANCELLED;
143 BOOL fCompleted;
144 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
145 && fCompleted)
146 {
147 AssertMsgFailed(("Setting value of an already completed progress\n"));
148 return VINF_SUCCESS;
149 }
150 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
151 if (FAILED(hr))
152 return VERR_COM_UNEXPECTED;
153
154 return VINF_SUCCESS;
155}
156
157int GuestSessionTask::setProgressSuccess(void)
158{
159 if (mProgress.isNull()) /* Progress is optional. */
160 return VINF_SUCCESS;
161
162 BOOL fCanceled;
163 BOOL fCompleted;
164 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
165 && !fCanceled
166 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
167 && !fCompleted)
168 {
169 HRESULT hr = mProgress->i_notifyComplete(S_OK);
170 if (FAILED(hr))
171 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
172 }
173
174 return VINF_SUCCESS;
175}
176
177HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
178{
179 LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
180 hr, strMsg.c_str()));
181
182 if (mProgress.isNull()) /* Progress is optional. */
183 return hr; /* Return original rc. */
184
185 BOOL fCanceled;
186 BOOL fCompleted;
187 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
188 && !fCanceled
189 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
190 && !fCompleted)
191 {
192 HRESULT hr2 = mProgress->i_notifyComplete(hr,
193 COM_IIDOF(IGuestSession),
194 GuestSession::getStaticComponentName(),
195 strMsg.c_str());
196 if (FAILED(hr2))
197 return hr2;
198 }
199 return hr; /* Return original rc. */
200}
201
202/**
203 * Creates a directory on the guest.
204 *
205 * @return VBox status code. VWRN_ALREADY_EXISTS if directory on the guest already exists.
206 * @param strPath Absolute path to directory on the guest (guest style path) to create.
207 * @param enmDirecotryCreateFlags Directory creation flags.
208 * @param uMode Directory mode to use for creation.
209 * @param fFollowSymlinks Whether to follow symlinks on the guest or not.
210 */
211int GuestSessionTask::directoryCreate(const com::Utf8Str &strPath,
212 DirectoryCreateFlag_T enmDirecotryCreateFlags, uint32_t uMode, bool fFollowSymlinks)
213{
214 LogFlowFunc(("strPath=%s, fFlags=0x%x, uMode=%RU32, fFollowSymlinks=%RTbool\n",
215 strPath.c_str(), enmDirecotryCreateFlags, uMode, fFollowSymlinks));
216
217 GuestFsObjData objData; int rcGuest;
218
219 int rc = mSession->i_directoryQueryInfoInternal(strPath, fFollowSymlinks, objData, &rcGuest);
220 if (RT_SUCCESS(rc))
221 {
222 return VWRN_ALREADY_EXISTS;
223 }
224 else
225 {
226 switch (rc)
227 {
228 case VERR_GSTCTL_GUEST_ERROR:
229 {
230 switch (rcGuest)
231 {
232 case VERR_FILE_NOT_FOUND:
233 case VERR_PATH_NOT_FOUND:
234 rc = mSession->i_directoryCreateInternal(strPath.c_str(), uMode, enmDirecotryCreateFlags, &rcGuest);
235 break;
236 default:
237 break;
238 }
239 break;
240 }
241
242 default:
243 break;
244 }
245 }
246
247 if (RT_FAILURE(rc))
248 {
249 if (rc == VERR_GSTCTL_GUEST_ERROR)
250 {
251 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
252 }
253 else
254 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
255 Utf8StrFmt(GuestSession::tr("Error creating directory on the guest: %Rrc"), strPath.c_str(), rc));
256 }
257
258 LogFlowFuncLeaveRC(rc);
259 return rc;
260}
261
262/**
263 * Copies a file from the guest to the host, extended version.
264 *
265 * @return VBox status code.
266 * @param strSource Full path of source file on the guest to copy.
267 * @param strDest Full destination path and file name (host style) to copy file to.
268 * @param enmFileCopyFlags File copy flags. Currently not used.
269 * @param pFile Destination file handle to use for accessing the host file.
270 * The caller is responsible of opening / closing the file accordingly.
271 * @param cbOffset Offset (in bytes) where to start copying the source file.
272 * Currently unused, must be 0.
273 * @param cbSize Size (in bytes) to copy from the source file.
274 * Currently unused, must be 0.
275 */
276int GuestSessionTask::fileCopyFromEx(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags,
277 PRTFILE pFile, uint64_t cbOffset, uint64_t cbSize)
278{
279 RT_NOREF(enmFileCopyFlags, cbOffset, cbSize);
280
281 AssertReturn(cbOffset == 0, VERR_NOT_IMPLEMENTED);
282 AssertReturn(cbSize == 0, VERR_NOT_IMPLEMENTED);
283
284 RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
285
286 /*
287 * Note: There will be races between querying file size + reading the guest file's
288 * content because we currently *do not* lock down the guest file when doing the
289 * actual operations.
290 ** @todo Use the IGuestFile API for locking down the file on the guest!
291 */
292 GuestFsObjData objData;
293 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
294 int rc = mSession->i_fileQueryInfoInternal(strSource, false /*fFollowSymlinks*/, objData, &rcGuest);
295 if (RT_FAILURE(rc))
296 {
297 switch (rc)
298 {
299 case VERR_GSTCTL_GUEST_ERROR:
300 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
301 break;
302
303 default:
304 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
305 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
306 strSource.c_str(), rc));
307 break;
308 }
309 }
310 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
311 {
312 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
313 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), strSource.c_str()));
314 rc = VERR_NOT_A_FILE;
315 }
316
317 if (RT_FAILURE(rc))
318 return rc;
319
320 GuestProcessStartupInfo procInfo;
321 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
322 strSource.c_str(), strDest.c_str(), objData.mObjectSize);
323 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_CAT);
324 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
325
326 /* Set arguments.*/
327 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
328 procInfo.mArguments.push_back(strSource); /* Which file to output? */
329
330 /* Startup process. */
331 ComObjPtr<GuestProcess> pProcess;
332 rc = mSession->i_processCreateExInternal(procInfo, pProcess);
333 if (RT_SUCCESS(rc))
334 rc = pProcess->i_startProcess(msTimeout, &rcGuest);
335
336 if (RT_FAILURE(rc))
337 {
338 switch (rc)
339 {
340 case VERR_GSTCTL_GUEST_ERROR:
341 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
342 break;
343
344 default:
345 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
346 Utf8StrFmt(GuestSession::tr(
347 "Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
348 strSource.c_str(), rc));
349 break;
350 }
351
352 return rc;
353 }
354
355 ProcessWaitResult_T waitRes;
356 BYTE byBuf[_64K];
357
358 BOOL fCanceled = FALSE;
359 uint64_t cbWrittenTotal = 0;
360 uint64_t cbToRead = objData.mObjectSize;
361
362 for (;;)
363 {
364 rc = pProcess->i_waitFor(ProcessWaitForFlag_StdOut, msTimeout, waitRes, &rcGuest);
365 if (RT_FAILURE(rc))
366 {
367 switch (rc)
368 {
369 case VERR_GSTCTL_GUEST_ERROR:
370 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
371 break;
372
373 default:
374 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
375 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
376 strSource.c_str(), rc));
377 break;
378 }
379
380 break;
381 }
382
383 if ( waitRes == ProcessWaitResult_StdOut
384 || waitRes == ProcessWaitResult_WaitFlagNotSupported)
385 {
386 /* If the guest does not support waiting for stdin, we now yield in
387 * order to reduce the CPU load due to busy waiting. */
388 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
389 RTThreadYield(); /* Optional, don't check rc. */
390
391 uint32_t cbRead = 0; /* readData can return with VWRN_GSTCTL_OBJECTSTATE_CHANGED. */
392 rc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
393 msTimeout, byBuf, sizeof(byBuf),
394 &cbRead, &rcGuest);
395 if (RT_FAILURE(rc))
396 {
397 switch (rc)
398 {
399 case VERR_GSTCTL_GUEST_ERROR:
400 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
401 break;
402
403 default:
404 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
405 Utf8StrFmt(GuestSession::tr("Reading from guest file \"%s\" (offset %RU64) failed: %Rrc"),
406 strSource.c_str(), cbWrittenTotal, rc));
407 break;
408 }
409
410 break;
411 }
412
413 if (cbRead)
414 {
415 rc = RTFileWrite(*pFile, byBuf, cbRead, NULL /* No partial writes */);
416 if (RT_FAILURE(rc))
417 {
418 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
419 Utf8StrFmt(GuestSession::tr("Writing to host file \"%s\" (%RU64 bytes left) failed: %Rrc"),
420 strDest.c_str(), cbToRead, rc));
421 break;
422 }
423
424 /* Only subtract bytes reported written by the guest. */
425 Assert(cbToRead >= cbRead);
426 cbToRead -= cbRead;
427
428 /* Update total bytes written to the guest. */
429 cbWrittenTotal += cbRead;
430 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
431
432 /* Did the user cancel the operation above? */
433 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
434 && fCanceled)
435 break;
436
437 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
438 if (RT_FAILURE(rc))
439 break;
440 }
441 }
442 else
443 break;
444
445 } /* for */
446
447 LogFlowThisFunc(("rc=%Rrc, rcGuest=%Rrc, waitRes=%ld, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
448 rc, rcGuest, waitRes, cbWrittenTotal, objData.mObjectSize, cbToRead));
449
450 /*
451 * Wait on termination of guest process until it completed all operations.
452 */
453 if ( !fCanceled
454 || RT_SUCCESS(rc))
455 {
456 rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &rcGuest);
457 if ( RT_FAILURE(rc)
458 || waitRes != ProcessWaitResult_Terminate)
459 {
460 if (RT_FAILURE(rc))
461 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
462 Utf8StrFmt(
463 GuestSession::tr("Waiting on termination for copying file \"%s\" from guest failed: %Rrc"),
464 strSource.c_str(), rc));
465 else
466 {
467 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
468 Utf8StrFmt(GuestSession::tr(
469 "Waiting on termination for copying file \"%s\" from guest failed with wait result %ld"),
470 strSource.c_str(), waitRes));
471 rc = VERR_GENERAL_FAILURE; /* Fudge. */
472 }
473 }
474 }
475
476 if (RT_FAILURE(rc))
477 return rc;
478
479 /** @todo this code sequence is duplicated in CopyTo */
480 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
481 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
482 if (!SUCCEEDED(hrc))
483 procStatus = ProcessStatus_TerminatedAbnormally;
484
485 LONG exitCode = 42424242;
486 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
487 if (!SUCCEEDED(hrc))
488 exitCode = 42424242;
489
490 if ( procStatus != ProcessStatus_TerminatedNormally
491 || exitCode != 0)
492 {
493 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
494 if (procStatus == ProcessStatus_TerminatedNormally)
495 rc = GuestProcessTool::exitCodeToRc(procInfo, exitCode);
496 else
497 rc = VERR_GENERAL_FAILURE;
498 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
499 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to host failed: %Rrc"),
500 strSource.c_str(), rc));
501 }
502 /*
503 * Even if we succeeded until here make sure to check whether we really transfered
504 * everything.
505 */
506 else if ( objData.mObjectSize > 0
507 && cbWrittenTotal == 0)
508 {
509 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
510 * to the destination -> access denied. */
511 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
512 Utf8StrFmt(GuestSession::tr("Writing guest file \"%s\" to host to \"%s\" failed: Access denied"),
513 strSource.c_str(), strDest.c_str()));
514 rc = VERR_ACCESS_DENIED;
515 }
516 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
517 {
518 /* If we did not copy all let the user know. */
519 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
520 Utf8StrFmt(GuestSession::tr("Copying guest file \"%s\" to host to \"%s\" failed (%RU64/%RI64 bytes transfered)"),
521 strSource.c_str(), strDest.c_str(), cbWrittenTotal, objData.mObjectSize));
522 rc = VERR_INTERRUPTED;
523 }
524
525 return rc;
526}
527
528/**
529 * Copies a file from the guest to the host.
530 *
531 * @return VBox status code.
532 * @param strSource Full path of source file on the guest to copy.
533 * @param strDest Full destination path and file name (host style) to copy file to.
534 * @param enmFileCopyFlags File copy flags.
535 */
536int GuestSessionTask::fileCopyFrom(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
537{
538 RTFILE hFile;
539 int rc = RTFileOpen(&hFile, strDest.c_str(),
540 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
541 if (RT_FAILURE(rc))
542 {
543 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
544 Utf8StrFmt(GuestSession::tr("Opening/creating destination file on host \"%s\" failed: %Rrc"),
545 strDest.c_str(), rc));
546 return rc;
547 }
548
549 rc = fileCopyFromEx(strSource, strDest, enmFileCopyFlags, &hFile, 0 /* Offset, unused */, 0 /* Size, unused */);
550
551 int rc2 = RTFileClose(hFile);
552 AssertRC(rc2);
553
554 return rc;
555}
556
557/**
558 * Copies a file from the host to the guest, extended version.
559 *
560 * @return VBox status code.
561 * @param strSource Full path of source file on the host to copy.
562 * @param strDest Full destination path and file name (guest style) to copy file to.
563 * @param enmFileCopyFlags File copy flags.
564 * @param pFile Source file handle to use for accessing the host file.
565 * The caller is responsible of opening / closing the file accordingly.
566 * @param cbOffset Offset (in bytes) where to start copying the source file.
567 * @param cbSize Size (in bytes) to copy from the source file.
568 */
569int GuestSessionTask::fileCopyToEx(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags,
570 PRTFILE pFile, uint64_t cbOffset, uint64_t cbSize)
571{
572 /** @todo Implement sparse file support? */
573
574 if ( enmFileCopyFlags & FileCopyFlag_NoReplace
575 || enmFileCopyFlags & FileCopyFlag_FollowLinks
576 || enmFileCopyFlags & FileCopyFlag_Update)
577 {
578 return VERR_NOT_IMPLEMENTED;
579 }
580
581 RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
582
583 /*
584 * Start the actual copying process by cat'ing the source file to the
585 * destination file on the guest.
586 */
587 GuestProcessStartupInfo procInfo;
588 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_CAT);
589 procInfo.mFlags = ProcessCreateFlag_Hidden;
590
591 /* Set arguments.*/
592 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
593 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", strDest.c_str()));
594
595 /* Startup process. */
596 ComObjPtr<GuestProcess> pProcess;
597 int rc = mSession->i_processCreateExInternal(procInfo, pProcess);
598
599 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
600 if (RT_SUCCESS(rc))
601 {
602 Assert(!pProcess.isNull());
603 rc = pProcess->i_startProcess(msTimeout, &rcGuest);
604 }
605 if (RT_FAILURE(rc))
606 {
607 switch (rc)
608 {
609 case VERR_GSTCTL_GUEST_ERROR:
610 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
611 break;
612
613 default:
614 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
615 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
616 strSource.c_str(), rc));
617 break;
618 }
619 return rc;
620 }
621
622 ProcessWaitResult_T waitRes;
623 BYTE byBuf[_64K];
624
625 BOOL fCanceled = FALSE;
626 uint64_t cbWrittenTotal = 0;
627 uint64_t cbToRead = cbSize;
628
629 for (;;)
630 {
631 rc = pProcess->i_waitFor(ProcessWaitForFlag_StdIn, msTimeout, waitRes, &rcGuest);
632 if ( RT_FAILURE(rc)
633 || ( waitRes != ProcessWaitResult_StdIn
634 && waitRes != ProcessWaitResult_WaitFlagNotSupported))
635 {
636 break;
637 }
638
639 /* If the guest does not support waiting for stdin, we now yield in
640 * order to reduce the CPU load due to busy waiting. */
641 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
642 RTThreadYield(); /* Optional, don't check rc. */
643
644 size_t cbRead = 0;
645 if (cbSize) /* If we have nothing to write, take a shortcut. */
646 {
647 /** @todo Not very efficient, but works for now. */
648 rc = RTFileSeek(*pFile, cbOffset + cbWrittenTotal,
649 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
650 if (RT_SUCCESS(rc))
651 {
652 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
653 RT_MIN((size_t)cbToRead, sizeof(byBuf)), &cbRead);
654 /*
655 * Some other error occured? There might be a chance that RTFileRead
656 * could not resolve/map the native error code to an IPRT code, so just
657 * print a generic error.
658 */
659 if (RT_FAILURE(rc))
660 {
661 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
662 Utf8StrFmt(GuestSession::tr("Could not read from host file \"%s\" (%Rrc)"),
663 strSource.c_str(), rc));
664 break;
665 }
666 }
667 else
668 {
669 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
670 Utf8StrFmt(GuestSession::tr("Seeking host file \"%s\" to offset %RU64 failed: %Rrc"),
671 strSource.c_str(), cbWrittenTotal, rc));
672 break;
673 }
674 }
675
676 uint32_t fFlags = ProcessInputFlag_None;
677
678 /* Did we reach the end of the content we want to transfer (last chunk)? */
679 if ( (cbRead < sizeof(byBuf))
680 /* Did we reach the last block which is exactly _64K? */
681 || (cbToRead - cbRead == 0)
682 /* ... or does the user want to cancel? */
683 || ( !mProgress.isNull()
684 && SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
685 && fCanceled)
686 )
687 {
688 LogFlowThisFunc(("Writing last chunk cbRead=%RU64\n", cbRead));
689 fFlags |= ProcessInputFlag_EndOfFile;
690 }
691
692 uint32_t cbWritten;
693 Assert(sizeof(byBuf) >= cbRead);
694 rc = pProcess->i_writeData(0 /* StdIn */, fFlags, byBuf, cbRead, msTimeout, &cbWritten, &rcGuest);
695 if (RT_FAILURE(rc))
696 {
697 switch (rc)
698 {
699 case VERR_GSTCTL_GUEST_ERROR:
700 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
701 break;
702
703 default:
704 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
705 Utf8StrFmt(GuestSession::tr("Writing to guest file \"%s\" (offset %RU64) failed: %Rrc"),
706 strDest.c_str(), cbWrittenTotal, rc));
707 break;
708 }
709
710 break;
711 }
712
713 /* Only subtract bytes reported written by the guest. */
714 Assert(cbToRead >= cbWritten);
715 cbToRead -= cbWritten;
716
717 /* Update total bytes written to the guest. */
718 cbWrittenTotal += cbWritten;
719 Assert(cbWrittenTotal <= cbSize);
720
721 LogFlowThisFunc(("rc=%Rrc, cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
722 rc, cbWritten, cbToRead, cbWrittenTotal, cbSize));
723
724 /* Did the user cancel the operation above? */
725 if (fCanceled)
726 break;
727
728 /* Update the progress.
729 * Watch out for division by zero. */
730 cbSize > 0
731 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / cbSize))
732 : rc = setProgress(100);
733 if (RT_FAILURE(rc))
734 break;
735
736 /* End of file reached? */
737 if (!cbToRead)
738 break;
739 } /* for */
740
741 LogFlowThisFunc(("Copy loop ended with rc=%Rrc, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
742 rc, cbToRead, cbWrittenTotal, cbSize));
743
744 /*
745 * Wait on termination of guest process until it completed all operations.
746 */
747 if ( !fCanceled
748 || RT_SUCCESS(rc))
749 {
750 rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &rcGuest);
751 if ( RT_FAILURE(rc)
752 || waitRes != ProcessWaitResult_Terminate)
753 {
754 if (RT_FAILURE(rc))
755 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
756 Utf8StrFmt(
757 GuestSession::tr("Waiting on termination for copying file \"%s\" to guest failed: %Rrc"),
758 strSource.c_str(), rc));
759 else
760 {
761 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
762 Utf8StrFmt(GuestSession::tr(
763 "Waiting on termination for copying file \"%s\" to guest failed with wait result %ld"),
764 strSource.c_str(), waitRes));
765 rc = VERR_GENERAL_FAILURE; /* Fudge. */
766 }
767 }
768 }
769
770 if (RT_SUCCESS(rc))
771 {
772 /*
773 * Newer VBoxService toolbox versions report what went wrong via exit code.
774 * So handle this first.
775 */
776 /** @todo This code sequence is duplicated in CopyFrom... */
777 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
778 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
779 if (!SUCCEEDED(hrc))
780 procStatus = ProcessStatus_TerminatedAbnormally;
781
782 LONG exitCode = 42424242;
783 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
784 if (!SUCCEEDED(hrc))
785 exitCode = 42424242;
786
787 if ( procStatus != ProcessStatus_TerminatedNormally
788 || exitCode != 0)
789 {
790 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
791 if (procStatus == ProcessStatus_TerminatedNormally)
792 rc = GuestProcessTool::exitCodeToRc(procInfo, exitCode);
793 else
794 rc = VERR_GENERAL_FAILURE;
795 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
796 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed: %Rrc"),
797 strSource.c_str(), rc));
798 }
799 /*
800 * Even if we succeeded until here make sure to check whether we really transfered
801 * everything.
802 */
803 else if ( cbSize > 0
804 && cbWrittenTotal == 0)
805 {
806 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
807 * to the destination -> access denied. */
808 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
809 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to guest \"%s\""),
810 strSource.c_str(), strDest.c_str()));
811 rc = VERR_ACCESS_DENIED;
812 }
813 else if (cbWrittenTotal < cbSize)
814 {
815 /* If we did not copy all let the user know. */
816 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
817 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed (%RU64/%RU64 bytes transfered)"),
818 strSource.c_str(), cbWrittenTotal, cbSize));
819 rc = VERR_INTERRUPTED;
820 }
821 }
822
823 return rc;
824}
825
826/**
827 * Copies a file from the host to the guest.
828 *
829 * @return VBox status code.
830 * @param strSource Full path of source file on the host to copy.
831 * @param strDest Full destination path and file name (guest style) to copy file to.
832 * @param enmFileCopyFlags File copy flags.
833 */
834int GuestSessionTask::fileCopyTo(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
835{
836 int rc;
837
838 /* Does our source file exist? */
839 if (!RTFileExists(strSource.c_str()))
840 {
841 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
842 Utf8StrFmt(GuestSession::tr("Host file \"%s\" does not exist or is not a file"),
843 strSource.c_str()));
844 rc = VERR_FILE_NOT_FOUND;
845 }
846 else
847 {
848 RTFILE hFile;
849 rc = RTFileOpen(&hFile, strSource.c_str(),
850 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
851 if (RT_FAILURE(rc))
852 {
853 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
854 Utf8StrFmt(GuestSession::tr("Could not open host file \"%s\" for reading: %Rrc"),
855 strSource.c_str(), rc));
856 }
857 else
858 {
859 uint64_t cbSize;
860 rc = RTFileGetSize(hFile, &cbSize);
861 if (RT_FAILURE(rc))
862 {
863 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
864 Utf8StrFmt(GuestSession::tr("Could not query host file size of \"%s\": %Rrc"),
865 strSource.c_str(), rc));
866 }
867 else
868 rc = fileCopyToEx(strSource, strDest, enmFileCopyFlags, &hFile, 0 /* Offset */, cbSize);
869
870 RTFileClose(hFile);
871 }
872 }
873
874 return rc;
875}
876
877/**
878 * Translates a source path to a destination path (can be both sides,
879 * either host or guest). The source root is needed to determine the start
880 * of the relative source path which also needs to present in the destination
881 * path.
882 *
883 * @return IPRT status code.
884 * @param strSourceRoot Source root path. No trailing directory slash!
885 * @param strSource Actual source to transform. Must begin with
886 * the source root path!
887 * @param strDest Destination path.
888 * @param strOut where to store the output path.
889 */
890int GuestSessionTask::pathConstructOnGuest(const Utf8Str &strSourceRoot, const Utf8Str &strSource,
891 const Utf8Str &strDest, Utf8Str &strOut)
892{
893 RT_NOREF(strSourceRoot, strSource, strDest, strOut);
894 return VINF_SUCCESS;
895}
896
897SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
898 uint32_t uFlags,
899 uint32_t uTimeoutMS)
900 : GuestSessionTask(pSession),
901 mFlags(uFlags),
902 mTimeoutMS(uTimeoutMS)
903{
904 m_strTaskName = "gctlSesOpen";
905}
906
907SessionTaskOpen::~SessionTaskOpen(void)
908{
909
910}
911
912int SessionTaskOpen::Run(void)
913{
914 LogFlowThisFuncEnter();
915
916 AutoCaller autoCaller(mSession);
917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
918
919 int vrc = mSession->i_startSessionInternal(NULL /*pvrcGuest*/);
920 /* Nothing to do here anymore. */
921
922 LogFlowFuncLeaveRC(vrc);
923 return vrc;
924}
925
926SessionTaskCopyDirFrom::SessionTaskCopyDirFrom(GuestSession *pSession, const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter,
927 DirectoryCopyFlag_T enmDirCopyFlags)
928 : GuestSessionTask(pSession)
929 , mSource(strSource)
930 , mDest(strDest)
931 , mFilter(strFilter)
932 , mDirCopyFlags(enmDirCopyFlags)
933{
934 m_strTaskName = "gctlCpyDirFrm";
935}
936
937SessionTaskCopyDirFrom::~SessionTaskCopyDirFrom(void)
938{
939
940}
941
942/**
943 * Copys a directory (tree) from guest to the host.
944 *
945 * @return IPRT status code.
946 * @param strSource Source directory on the guest to copy to the host.
947 * @param strFilter DOS-style wildcard filter (?, *). Optional.
948 * @param strDest Destination directory on the host.
949 * @param fRecursive Whether to recursively copy the directory contents or not.
950 * @param fFollowSymlinks Whether to follow symlinks or not.
951 * @param strSubDir Current sub directory to handle. Needs to NULL and only
952 * is needed for recursion.
953 */
954int SessionTaskCopyDirFrom::directoryCopyToHost(const Utf8Str &strSource, const Utf8Str &strFilter,
955 const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
956 const Utf8Str &strSubDir /* For recursion. */)
957{
958 Utf8Str strSrcDir = strSource;
959 Utf8Str strDstDir = strDest;
960 Utf8Str strSrcSubDir = strSubDir;
961
962 /* Validation and sanity. */
963 if ( !strSrcDir.endsWith("/")
964 && !strSrcDir.endsWith("\\"))
965 strSrcDir += "/";
966
967 if ( !strDstDir.endsWith("/")
968 && !strDstDir.endsWith("\\"))
969 strDstDir+= "/";
970
971 if ( strSrcSubDir.isNotEmpty() /* Optional, for recursion. */
972 && !strSrcSubDir.endsWith("/")
973 && !strSrcSubDir.endsWith("\\"))
974 strSrcSubDir += "/";
975
976 Utf8Str strSrcCur = strSrcDir + strSrcSubDir;
977
978 LogFlowFunc(("Entering '%s'\n", strSrcCur.c_str()));
979
980 int rc;
981
982 if (strSrcSubDir.isNotEmpty())
983 {
984 const uint32_t fMode = 0700; /** @todo */
985
986 rc = RTDirCreate(Utf8Str(strDstDir + strSrcSubDir).c_str(), fMode, 0);
987 if (RT_FAILURE(rc))
988 return rc;
989 }
990
991 GuestDirectoryOpenInfo dirOpenInfo;
992 dirOpenInfo.mFilter = strFilter;
993 dirOpenInfo.mPath = strSrcCur;
994 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
995
996 ComObjPtr <GuestDirectory> pDir; int rcGuest;
997 rc = mSession->i_directoryOpenInternal(dirOpenInfo, pDir, &rcGuest);
998
999 if (RT_FAILURE(rc))
1000 {
1001 switch (rc)
1002 {
1003 case VERR_INVALID_PARAMETER:
1004 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1005 Utf8StrFmt(GuestSession::tr("Opening directory \"%s\" failed: Invalid parameter"), dirOpenInfo.mPath.c_str()));
1006 break;
1007
1008 case VERR_GSTCTL_GUEST_ERROR:
1009 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1010 break;
1011
1012 default:
1013 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1014 Utf8StrFmt(GuestSession::tr("Opening directory \"%s\" failed: %Rrc"), dirOpenInfo.mPath.c_str(), rc));
1015 break;
1016 }
1017
1018 return rc;
1019 }
1020
1021 ComObjPtr<GuestFsObjInfo> fsObjInfo;
1022 while (RT_SUCCESS(rc = pDir->i_readInternal(fsObjInfo, &rcGuest)))
1023 {
1024 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
1025 HRESULT hr2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1026 AssertComRC(hr2);
1027
1028 com::Bstr bstrName;
1029 hr2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1030 AssertComRC(hr2);
1031
1032 Utf8Str strName(bstrName);
1033
1034 switch (enmObjType)
1035 {
1036 case FsObjType_Directory:
1037 {
1038 bool fSkip = false;
1039
1040 if ( strName.equals(".")
1041 || strName.equals(".."))
1042 {
1043 fSkip = true;
1044 }
1045
1046 if ( strFilter.isNotEmpty()
1047 && !RTStrSimplePatternMatch(strFilter.c_str(), strName.c_str()))
1048 fSkip = true;
1049
1050 if ( fRecursive
1051 && !fSkip)
1052 rc = directoryCopyToHost(strSrcDir, strFilter, strDstDir, fRecursive, fFollowSymlinks,
1053 strSrcSubDir + strName);
1054 break;
1055 }
1056
1057 case FsObjType_Symlink:
1058 {
1059 if (fFollowSymlinks)
1060 { /* Fall through to next case is intentional. */ }
1061 else
1062 break;
1063 RT_FALL_THRU();
1064 }
1065
1066 case FsObjType_File:
1067 {
1068 if ( strFilter.isNotEmpty()
1069 && !RTStrSimplePatternMatch(strFilter.c_str(), strName.c_str()))
1070 {
1071 break; /* Filter does not match. */
1072 }
1073
1074 if (RT_SUCCESS(rc))
1075 {
1076 Utf8Str strSrcFile = strSrcDir + strSrcSubDir + strName;
1077 Utf8Str strDstFile = strDstDir + strSrcSubDir + strName;
1078 rc = fileCopyFrom(strSrcFile, strDstFile, FileCopyFlag_None);
1079 }
1080 break;
1081 }
1082
1083 default:
1084 break;
1085 }
1086 }
1087
1088 if (rc == VERR_NO_MORE_FILES) /* Reading done? */
1089 rc = VINF_SUCCESS;
1090
1091 pDir->i_closeInternal(&rcGuest);
1092
1093 LogFlowFunc(("Leaving '%s', rc=%Rrc\n", strSrcCur.c_str(), rc));
1094 return rc;
1095}
1096
1097int SessionTaskCopyDirFrom::Run(void)
1098{
1099 LogFlowThisFuncEnter();
1100
1101 AutoCaller autoCaller(mSession);
1102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1103
1104 const bool fRecursive = true; /** @todo Make this configurable. */
1105 const bool fFollowSymlinks = true; /** @todo Make this configurable. */
1106 const uint32_t fDirMode = 0700; /* Play safe by default. */
1107
1108 int rc = VINF_SUCCESS;
1109
1110 /* Create the root target directory on the host.
1111 * The target directory might already exist on the host (based on mDirCopyFlags). */
1112 const bool fExists = RTDirExists(mDest.c_str());
1113 if ( fExists
1114 && !(mDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting))
1115 {
1116 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1117 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" exists when it must not"), mDest.c_str()));
1118 rc = VERR_ALREADY_EXISTS;
1119 }
1120 else if (!fExists)
1121 {
1122 rc = RTDirCreate(mDest.c_str(), fDirMode, 0);
1123 if (RT_FAILURE(rc))
1124 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1125 Utf8StrFmt(GuestSession::tr("Error creating destination directory \"%s\", rc=%Rrc"),
1126 mDest.c_str(), rc));
1127 }
1128
1129 if (RT_FAILURE(rc))
1130 return rc;
1131
1132 RTDIR hDir;
1133 rc = RTDirOpen(&hDir, mDest.c_str());
1134 if (RT_FAILURE(rc))
1135 {
1136 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1137 Utf8StrFmt(GuestSession::tr("Error opening destination directory \"%s\", rc=%Rrc"),
1138 mDest.c_str(), rc));
1139 return rc;
1140 }
1141
1142 /* At this point the directory on the host was created and (hopefully) is ready
1143 * to receive further content. */
1144 rc = directoryCopyToHost(mSource, mFilter, mDest, fRecursive, fFollowSymlinks,
1145 "" /* strSubDir; for recursion */);
1146 if (RT_SUCCESS(rc))
1147 rc = setProgressSuccess();
1148
1149 RTDirClose(hDir);
1150
1151 LogFlowFuncLeaveRC(rc);
1152 return rc;
1153}
1154
1155SessionTaskCopyDirTo::SessionTaskCopyDirTo(GuestSession *pSession,
1156 const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter,
1157 DirectoryCopyFlag_T enmDirCopyFlags)
1158 : GuestSessionTask(pSession)
1159 , mSource(strSource)
1160 , mDest(strDest)
1161 , mFilter(strFilter)
1162 , mDirCopyFlags(enmDirCopyFlags)
1163{
1164 m_strTaskName = "gctlCpyDirTo";
1165}
1166
1167SessionTaskCopyDirTo::~SessionTaskCopyDirTo(void)
1168{
1169
1170}
1171
1172/**
1173 * Copys a directory (tree) from host to the guest.
1174 *
1175 * @return IPRT status code.
1176 * @param strSource Source directory on the host to copy to the guest.
1177 * @param strFilter DOS-style wildcard filter (?, *). Optional.
1178 * @param strDest Destination directory on the guest.
1179 * @param fRecursive Whether to recursively copy the directory contents or not.
1180 * @param fFollowSymlinks Whether to follow symlinks or not.
1181 * @param strSubDir Current sub directory to handle. Needs to NULL and only
1182 * is needed for recursion.
1183 */
1184int SessionTaskCopyDirTo::directoryCopyToGuest(const Utf8Str &strSource, const Utf8Str &strFilter,
1185 const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
1186 const Utf8Str &strSubDir /* For recursion. */)
1187{
1188 Utf8Str strSrcDir = strSource;
1189 Utf8Str strDstDir = strDest;
1190 Utf8Str strSrcSubDir = strSubDir;
1191
1192 /* Validation and sanity. */
1193 if ( !strSrcDir.endsWith("/")
1194 && !strSrcDir.endsWith("\\"))
1195 strSrcDir += "/";
1196
1197 if ( !strDstDir.endsWith("/")
1198 && !strDstDir.endsWith("\\"))
1199 strDstDir+= "/";
1200
1201 if ( strSrcSubDir.isNotEmpty() /* Optional, for recursion. */
1202 && !strSrcSubDir.endsWith("/")
1203 && !strSrcSubDir.endsWith("\\"))
1204 strSrcSubDir += "/";
1205
1206 Utf8Str strSrcCur = strSrcDir + strSrcSubDir;
1207
1208 LogFlowFunc(("Entering '%s'\n", strSrcCur.c_str()));
1209
1210 int rc;
1211
1212 if (strSrcSubDir.isNotEmpty())
1213 {
1214 const uint32_t uMode = 0700; /** @todo */
1215 rc = directoryCreate(strDstDir + strSrcSubDir, DirectoryCreateFlag_Parents, uMode, fFollowSymlinks);
1216 if (RT_FAILURE(rc))
1217 return rc;
1218 }
1219
1220 /*
1221 * Open directory without a filter - RTDirOpenFiltered unfortunately
1222 * cannot handle sub directories so we have to do the filtering ourselves.
1223 */
1224 RTDIR hDir;
1225 rc = RTDirOpen(&hDir, strSrcCur.c_str());
1226 if (RT_SUCCESS(rc))
1227 {
1228 /*
1229 * Enumerate the directory tree.
1230 */
1231 size_t cbDirEntry = 0;
1232 PRTDIRENTRYEX pDirEntry = NULL;
1233 while (RT_SUCCESS(rc))
1234 {
1235 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1236 if (RT_FAILURE(rc))
1237 {
1238 if (rc == VERR_NO_MORE_FILES)
1239 rc = VINF_SUCCESS;
1240 break;
1241 }
1242
1243#ifdef LOG_ENABLED
1244 Utf8Str strDbgCurEntry = strSrcCur + Utf8Str(pDirEntry->szName);
1245 LogFlowFunc(("Handling '%s' (fMode=0x%x)\n", strDbgCurEntry.c_str(), pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK));
1246#endif
1247 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1248 {
1249 case RTFS_TYPE_DIRECTORY:
1250 {
1251 /* Skip "." and ".." entries. */
1252 if (RTDirEntryExIsStdDotLink(pDirEntry))
1253 break;
1254
1255 bool fSkip = false;
1256
1257 if ( strFilter.isNotEmpty()
1258 && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
1259 fSkip = true;
1260
1261 if ( fRecursive
1262 && !fSkip)
1263 rc = directoryCopyToGuest(strSrcDir, strFilter, strDstDir, fRecursive, fFollowSymlinks,
1264 strSrcSubDir + Utf8Str(pDirEntry->szName));
1265 break;
1266 }
1267
1268 case RTFS_TYPE_SYMLINK:
1269 if (fFollowSymlinks)
1270 { /* Fall through to next case is intentional. */ }
1271 else
1272 break;
1273 RT_FALL_THRU();
1274
1275 case RTFS_TYPE_FILE:
1276 {
1277 if ( strFilter.isNotEmpty()
1278 && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
1279 {
1280 break; /* Filter does not match. */
1281 }
1282
1283 if (RT_SUCCESS(rc))
1284 {
1285 Utf8Str strSrcFile = strSrcDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
1286 Utf8Str strDstFile = strDstDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
1287 rc = fileCopyTo(strSrcFile, strDstFile, FileCopyFlag_None);
1288 }
1289 break;
1290 }
1291
1292 default:
1293 break;
1294 }
1295 if (RT_FAILURE(rc))
1296 break;
1297 }
1298
1299 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1300 RTDirClose(hDir);
1301 }
1302
1303 LogFlowFunc(("Leaving '%s', rc=%Rrc\n", strSrcCur.c_str(), rc));
1304 return rc;
1305}
1306
1307int SessionTaskCopyDirTo::Run(void)
1308{
1309 LogFlowThisFuncEnter();
1310
1311 AutoCaller autoCaller(mSession);
1312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1313
1314 const bool fRecursive = true; /** @todo Make this configurable. */
1315 const bool fFollowSymlinks = true; /** @todo Make this configurable. */
1316 const uint32_t uDirMode = 0700; /* Play safe by default. */
1317
1318 /* Create the root target directory on the guest.
1319 * The target directory might already exist on the guest (based on mDirCopyFlags). */
1320 int rc = directoryCreate(mDest, DirectoryCreateFlag_None, uDirMode, fFollowSymlinks);
1321 if ( rc == VWRN_ALREADY_EXISTS
1322 && !(mDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting))
1323 {
1324 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1325 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" exists when it must not"), mDest.c_str()));
1326 }
1327
1328 if (RT_FAILURE(rc))
1329 return rc;
1330
1331 /* At this point the directory on the guest was created and (hopefully) is ready
1332 * to receive further content. */
1333 rc = directoryCopyToGuest(mSource, mFilter, mDest, fRecursive, fFollowSymlinks,
1334 "" /* strSubDir; for recursion */);
1335 if (RT_SUCCESS(rc))
1336 rc = setProgressSuccess();
1337
1338 LogFlowFuncLeaveRC(rc);
1339 return rc;
1340}
1341
1342SessionTaskCopyFileTo::SessionTaskCopyFileTo(GuestSession *pSession,
1343 const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
1344 : GuestSessionTask(pSession)
1345 , mSource(strSource)
1346 , mSourceFile(NULL)
1347 , mSourceOffset(0)
1348 , mSourceSize(0)
1349 , mDest(strDest)
1350 , mFileCopyFlags(enmFileCopyFlags)
1351{
1352 m_strTaskName = "gctlCpyFileTo";
1353}
1354
1355SessionTaskCopyFileTo::SessionTaskCopyFileTo(GuestSession *pSession,
1356 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
1357 const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
1358 : GuestSessionTask(pSession)
1359 , mSourceFile(pSourceFile)
1360 , mSourceOffset(cbSourceOffset)
1361 , mSourceSize(cbSourceSize)
1362 , mDest(strDest)
1363 , mFileCopyFlags(enmFileCopyFlags)
1364{
1365 m_strTaskName = "gctlCpyFileToH";
1366}
1367
1368SessionTaskCopyFileTo::~SessionTaskCopyFileTo(void)
1369{
1370
1371}
1372
1373int SessionTaskCopyFileTo::Run(void)
1374{
1375 LogFlowThisFuncEnter();
1376
1377 AutoCaller autoCaller(mSession);
1378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1379
1380 if (mSource.isEmpty())
1381 {
1382 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No source file specified")));
1383 return VERR_INVALID_PARAMETER;
1384 }
1385
1386 if (mDest.isEmpty())
1387 {
1388 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No destintation file specified")));
1389 return VERR_INVALID_PARAMETER;
1390 }
1391
1392 const char *pszFileName = RTPathFilename(mSource.c_str());
1393
1394 if ( !pszFileName
1395 || !RTFileExists(pszFileName))
1396 {
1397 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("Source file not valid or does not exist")));
1398 return VERR_FILE_NOT_FOUND;
1399 }
1400
1401 if (mFileCopyFlags)
1402 {
1403 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1404 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
1405 mFileCopyFlags));
1406 return VERR_NOT_IMPLEMENTED;
1407 }
1408
1409 /** @todo Try to lock the destination directory on the guest here first? */
1410
1411 int rc = VINF_SUCCESS;
1412
1413 /*
1414 * Query information about our destination first.
1415 */
1416 if ( mDest.endsWith("/")
1417 || mDest.endsWith("\\"))
1418 {
1419 int rcGuest;
1420 GuestFsObjData objData;
1421 rc = mSession->i_fsQueryInfoInternal(mDest, true /* fFollowSymlinks */, objData, &rcGuest);
1422 if (RT_SUCCESS(rc))
1423 {
1424 if (objData.mType != FsObjType_Directory)
1425 {
1426 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1427 Utf8StrFmt(GuestSession::tr("Path \"%s\" is not a directory on guest"), mDest.c_str()));
1428 rc = VERR_NOT_A_DIRECTORY;
1429 }
1430 else
1431 {
1432 AssertPtr(pszFileName);
1433 mDest += pszFileName;
1434 }
1435 }
1436 else
1437 {
1438 switch (rc)
1439 {
1440 case VERR_GSTCTL_GUEST_ERROR:
1441 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1442 GuestProcess::i_guestErrorToString(rcGuest));
1443 break;
1444
1445 default:
1446 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1447 Utf8StrFmt(GuestSession::tr("Unable to query information for directory \"%s\" on the guest: %Rrc"),
1448 mDest.c_str(), rc));
1449 break;
1450 }
1451 }
1452 }
1453
1454 if (RT_SUCCESS(rc))
1455 {
1456 if (mSourceFile) /* Use existing file handle. */
1457 rc = fileCopyToEx(mSource, mDest, (FileCopyFlag_T)mFileCopyFlags, mSourceFile, mSourceOffset, mSourceSize);
1458 else
1459 rc = fileCopyTo(mSource, mDest, (FileCopyFlag_T)mFileCopyFlags);
1460
1461 if (RT_SUCCESS(rc))
1462 rc = setProgressSuccess();
1463 }
1464
1465 LogFlowFuncLeaveRC(rc);
1466 return rc;
1467}
1468
1469SessionTaskCopyFileFrom::SessionTaskCopyFileFrom(GuestSession *pSession,
1470 const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
1471 : GuestSessionTask(pSession)
1472 , mSource(strSource)
1473 , mDest(strDest)
1474 , mFileCopyFlags(enmFileCopyFlags)
1475{
1476 m_strTaskName = "gctlCpyFileFrm";
1477}
1478
1479SessionTaskCopyFileFrom::~SessionTaskCopyFileFrom(void)
1480{
1481
1482}
1483
1484int SessionTaskCopyFileFrom::Run(void)
1485{
1486 LogFlowThisFuncEnter();
1487
1488 AutoCaller autoCaller(mSession);
1489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1490
1491 int rc = fileCopyFrom(mSource, mDest, mFileCopyFlags);
1492 if (RT_SUCCESS(rc))
1493 rc = setProgressSuccess();
1494
1495 LogFlowFuncLeaveRC(rc);
1496 return rc;
1497}
1498
1499SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
1500 const Utf8Str &strSource,
1501 const ProcessArguments &aArguments,
1502 uint32_t uFlags)
1503 : GuestSessionTask(pSession)
1504{
1505 mSource = strSource;
1506 mArguments = aArguments;
1507 mFlags = uFlags;
1508 m_strTaskName = "gctlUpGA";
1509}
1510
1511SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
1512{
1513
1514}
1515
1516int SessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
1517{
1518 int rc = VINF_SUCCESS;
1519
1520 try
1521 {
1522 /* Filter out arguments which already are in the destination to
1523 * not end up having them specified twice. Not the fastest method on the
1524 * planet but does the job. */
1525 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
1526 while (itSource != aArgumentsSource.end())
1527 {
1528 bool fFound = false;
1529 ProcessArguments::iterator itDest = aArgumentsDest.begin();
1530 while (itDest != aArgumentsDest.end())
1531 {
1532 if ((*itDest).equalsIgnoreCase((*itSource)))
1533 {
1534 fFound = true;
1535 break;
1536 }
1537 ++itDest;
1538 }
1539
1540 if (!fFound)
1541 aArgumentsDest.push_back((*itSource));
1542
1543 ++itSource;
1544 }
1545 }
1546 catch(std::bad_alloc &)
1547 {
1548 return VERR_NO_MEMORY;
1549 }
1550
1551 return rc;
1552}
1553
1554int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
1555 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
1556 bool fOptional, uint32_t *pcbSize)
1557{
1558 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1559 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
1560 /* pcbSize is optional. */
1561
1562 uint32_t cbOffset;
1563 size_t cbSize;
1564
1565 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
1566 if (RT_FAILURE(rc))
1567 {
1568 if (fOptional)
1569 return VINF_SUCCESS;
1570
1571 return rc;
1572 }
1573
1574 Assert(cbOffset);
1575 Assert(cbSize);
1576 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
1577
1578 HRESULT hr = S_OK;
1579 /* Copy over the Guest Additions file to the guest. */
1580 if (RT_SUCCESS(rc))
1581 {
1582 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
1583 strFileSource.c_str(), strFileDest.c_str()));
1584
1585 if (RT_SUCCESS(rc))
1586 {
1587 SessionTaskCopyFileTo *pTask = NULL;
1588 ComObjPtr<Progress> pProgressCopyTo;
1589 try
1590 {
1591 try
1592 {
1593 pTask = new SessionTaskCopyFileTo(pSession /* GuestSession */,
1594 &pISO->file, cbOffset, cbSize,
1595 strFileDest, FileCopyFlag_None);
1596 }
1597 catch(...)
1598 {
1599 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1600 GuestSession::tr("Failed to create SessionTaskCopyTo object "));
1601 throw;
1602 }
1603
1604 hr = pTask->Init(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
1605 mSource.c_str(), strFileDest.c_str()));
1606 if (FAILED(hr))
1607 {
1608 delete pTask;
1609 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1610 GuestSession::tr("Creating progress object for SessionTaskCopyTo object failed"));
1611 throw hr;
1612 }
1613
1614 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
1615
1616 if (SUCCEEDED(hr))
1617 {
1618 /* Return progress to the caller. */
1619 pProgressCopyTo = pTask->GetProgressObject();
1620 }
1621 else
1622 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1623 GuestSession::tr("Starting thread for updating additions failed "));
1624 }
1625 catch(std::bad_alloc &)
1626 {
1627 hr = E_OUTOFMEMORY;
1628 }
1629 catch(HRESULT eHR)
1630 {
1631 hr = eHR;
1632 LogFlowThisFunc(("Exception was caught in the function \n"));
1633 }
1634
1635 if (SUCCEEDED(hr))
1636 {
1637 BOOL fCanceled = FALSE;
1638 hr = pProgressCopyTo->WaitForCompletion(-1);
1639 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
1640 && fCanceled)
1641 {
1642 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1643 }
1644 else if (FAILED(hr))
1645 {
1646 Assert(FAILED(hr));
1647 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1648 }
1649 }
1650 }
1651 }
1652
1653 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
1654 * between finished copying, the verification and the actual execution. */
1655
1656 /* Determine where the installer image ended up and if it has the correct size. */
1657 if (RT_SUCCESS(rc))
1658 {
1659 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n",
1660 strFileDest.c_str()));
1661
1662 GuestFsObjData objData;
1663 int64_t cbSizeOnGuest;
1664 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1665 rc = pSession->i_fileQuerySizeInternal(strFileDest, false /*fFollowSymlinks*/, &cbSizeOnGuest, &rcGuest);
1666 if ( RT_SUCCESS(rc)
1667 && cbSize == (uint64_t)cbSizeOnGuest)
1668 {
1669 LogFlowThisFunc(("Guest Additions installer file \"%s\" successfully verified\n",
1670 strFileDest.c_str()));
1671 }
1672 else
1673 {
1674 if (RT_SUCCESS(rc)) /* Size does not match. */
1675 {
1676 LogRel(("Size of Guest Additions installer file \"%s\" does not match: %RI64 bytes copied, %RU64 bytes expected\n",
1677 strFileDest.c_str(), cbSizeOnGuest, cbSize));
1678 rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
1679 }
1680 else
1681 {
1682 switch (rc)
1683 {
1684 case VERR_GSTCTL_GUEST_ERROR:
1685 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1686 break;
1687
1688 default:
1689 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1690 Utf8StrFmt(GuestSession::tr("Error while querying size for file \"%s\": %Rrc"),
1691 strFileDest.c_str(), rc));
1692 break;
1693 }
1694 }
1695 }
1696
1697 if (RT_SUCCESS(rc))
1698 {
1699 if (pcbSize)
1700 *pcbSize = (uint32_t)cbSizeOnGuest;
1701 }
1702 }
1703
1704 return rc;
1705}
1706
1707int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
1708{
1709 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1710
1711 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
1712
1713 GuestProcessTool procTool;
1714 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1715 int vrc = procTool.init(pSession, procInfo, false /* Async */, &rcGuest);
1716 if (RT_SUCCESS(vrc))
1717 {
1718 if (RT_SUCCESS(rcGuest))
1719 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &rcGuest);
1720 if (RT_SUCCESS(vrc))
1721 vrc = procTool.terminatedOk();
1722 }
1723
1724 if (RT_FAILURE(vrc))
1725 {
1726 switch (vrc)
1727 {
1728 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
1729 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1730 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
1731 procInfo.mExecutable.c_str(), procTool.getRc()));
1732 break;
1733
1734 case VERR_GSTCTL_GUEST_ERROR:
1735 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1736 break;
1737
1738 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
1739 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1740 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
1741 procInfo.mExecutable.c_str()));
1742 break;
1743
1744 default:
1745 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1746 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
1747 procInfo.mExecutable.c_str(), vrc));
1748 break;
1749 }
1750 }
1751
1752 return vrc;
1753}
1754
1755int SessionTaskUpdateAdditions::Run(void)
1756{
1757 LogFlowThisFuncEnter();
1758
1759 ComObjPtr<GuestSession> pSession = mSession;
1760 Assert(!pSession.isNull());
1761
1762 AutoCaller autoCaller(pSession);
1763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1764
1765 int rc = setProgress(10);
1766 if (RT_FAILURE(rc))
1767 return rc;
1768
1769 HRESULT hr = S_OK;
1770
1771 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
1772
1773 ComObjPtr<Guest> pGuest(mSession->i_getParent());
1774#if 0
1775 /*
1776 * Wait for the guest being ready within 30 seconds.
1777 */
1778 AdditionsRunLevelType_T addsRunLevel;
1779 uint64_t tsStart = RTTimeSystemMilliTS();
1780 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1781 && ( addsRunLevel != AdditionsRunLevelType_Userland
1782 && addsRunLevel != AdditionsRunLevelType_Desktop))
1783 {
1784 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
1785 {
1786 rc = VERR_TIMEOUT;
1787 break;
1788 }
1789
1790 RTThreadSleep(100); /* Wait a bit. */
1791 }
1792
1793 if (FAILED(hr)) rc = VERR_TIMEOUT;
1794 if (rc == VERR_TIMEOUT)
1795 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1796 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
1797#else
1798 /*
1799 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
1800 * can continue.
1801 */
1802 AdditionsRunLevelType_T addsRunLevel;
1803 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1804 || ( addsRunLevel != AdditionsRunLevelType_Userland
1805 && addsRunLevel != AdditionsRunLevelType_Desktop))
1806 {
1807 if (addsRunLevel == AdditionsRunLevelType_System)
1808 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1809 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1810 else
1811 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1812 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1813 rc = VERR_NOT_SUPPORTED;
1814 }
1815#endif
1816
1817 if (RT_SUCCESS(rc))
1818 {
1819 /*
1820 * Determine if we are able to update automatically. This only works
1821 * if there are recent Guest Additions installed already.
1822 */
1823 Utf8Str strAddsVer;
1824 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1825 if ( RT_SUCCESS(rc)
1826 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1827 {
1828 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1829 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1830 strAddsVer.c_str()));
1831 rc = VERR_NOT_SUPPORTED;
1832 }
1833 }
1834
1835 Utf8Str strOSVer;
1836 eOSType osType = eOSType_Unknown;
1837 if (RT_SUCCESS(rc))
1838 {
1839 /*
1840 * Determine guest OS type and the required installer image.
1841 */
1842 Utf8Str strOSType;
1843 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1844 if (RT_SUCCESS(rc))
1845 {
1846 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1847 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1848 {
1849 osType = eOSType_Windows;
1850
1851 /*
1852 * Determine guest OS version.
1853 */
1854 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1855 if (RT_FAILURE(rc))
1856 {
1857 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1858 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1859 rc = VERR_NOT_SUPPORTED;
1860 }
1861
1862 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1863 * can't do automated updates here. */
1864 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1865 if ( RT_SUCCESS(rc)
1866 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1867 {
1868 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1869 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
1870 {
1871 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1872 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1873 * flag is set this update routine ends successfully as soon as the installer was started
1874 * (and the user has to deal with it in the guest). */
1875 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1876 {
1877 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1878 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1879 rc = VERR_NOT_SUPPORTED;
1880 }
1881 }
1882 }
1883 else
1884 {
1885 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1886 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
1887 strOSType.c_str(), strOSVer.c_str()));
1888 rc = VERR_NOT_SUPPORTED;
1889 }
1890 }
1891 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1892 {
1893 osType = eOSType_Solaris;
1894 }
1895 else /* Everything else hopefully means Linux :-). */
1896 osType = eOSType_Linux;
1897
1898#if 1 /* Only Windows is supported (and tested) at the moment. */
1899 if ( RT_SUCCESS(rc)
1900 && osType != eOSType_Windows)
1901 {
1902 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1903 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1904 strOSType.c_str()));
1905 rc = VERR_NOT_SUPPORTED;
1906 }
1907#endif
1908 }
1909 }
1910
1911 RTISOFSFILE iso;
1912 if (RT_SUCCESS(rc))
1913 {
1914 /*
1915 * Try to open the .ISO file to extract all needed files.
1916 */
1917 rc = RTIsoFsOpen(&iso, mSource.c_str());
1918 if (RT_FAILURE(rc))
1919 {
1920 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1921 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1922 mSource.c_str(), rc));
1923 }
1924 else
1925 {
1926 /* Set default installation directories. */
1927 Utf8Str strUpdateDir = "/tmp/";
1928 if (osType == eOSType_Windows)
1929 strUpdateDir = "C:\\Temp\\";
1930
1931 rc = setProgress(5);
1932
1933 /* Try looking up the Guest Additions installation directory. */
1934 if (RT_SUCCESS(rc))
1935 {
1936 /* Try getting the installed Guest Additions version to know whether we
1937 * can install our temporary Guest Addition data into the original installation
1938 * directory.
1939 *
1940 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1941 * a different location then.
1942 */
1943 bool fUseInstallDir = false;
1944
1945 Utf8Str strAddsVer;
1946 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1947 if ( RT_SUCCESS(rc)
1948 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1949 {
1950 fUseInstallDir = true;
1951 }
1952
1953 if (fUseInstallDir)
1954 {
1955 if (RT_SUCCESS(rc))
1956 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1957 if (RT_SUCCESS(rc))
1958 {
1959 if (osType == eOSType_Windows)
1960 {
1961 strUpdateDir.findReplace('/', '\\');
1962 strUpdateDir.append("\\Update\\");
1963 }
1964 else
1965 strUpdateDir.append("/update/");
1966 }
1967 }
1968 }
1969
1970 if (RT_SUCCESS(rc))
1971 LogRel(("Guest Additions update directory is: %s\n",
1972 strUpdateDir.c_str()));
1973
1974 /* Create the installation directory. */
1975 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1976 rc = pSession->i_directoryCreateInternal(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &rcGuest);
1977 if (RT_FAILURE(rc))
1978 {
1979 switch (rc)
1980 {
1981 case VERR_GSTCTL_GUEST_ERROR:
1982 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1983 break;
1984
1985 default:
1986 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1987 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1988 strUpdateDir.c_str(), rc));
1989 break;
1990 }
1991 }
1992 if (RT_SUCCESS(rc))
1993 rc = setProgress(10);
1994
1995 if (RT_SUCCESS(rc))
1996 {
1997 /* Prepare the file(s) we want to copy over to the guest and
1998 * (maybe) want to run. */
1999 switch (osType)
2000 {
2001 case eOSType_Windows:
2002 {
2003 /* Do we need to install our certificates? We do this for W2K and up. */
2004 bool fInstallCert = false;
2005
2006 /* Only Windows 2000 and up need certificates to be installed. */
2007 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
2008 {
2009 fInstallCert = true;
2010 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
2011 }
2012 else
2013 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
2014
2015 if (fInstallCert)
2016 {
2017 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
2018 {
2019 { "vbox.cer", "CERT/VBOX.CER" },
2020 { "vbox-sha1.cer", "CERT/VBOX_SHA1.CER" },
2021 { "vbox-sha256.cer", "CERT/VBOX_SHA256.CER" },
2022 { "vbox-sha256-r3.cer", "CERT/VBOX_SHA256_R3.CER" },
2023 { "oracle-vbox.cer", "CERT/ORACLE_VBOX.CER" },
2024 };
2025 uint32_t fCopyCertUtil = UPDATEFILE_FLAG_COPY_FROM_ISO;
2026 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
2027 {
2028 /* Skip if not present on the ISO. */
2029 uint32_t offIgn;
2030 size_t cbIgn;
2031 rc = RTIsoFsGetFileInfo(&iso, s_aCertFiles[i].pszIso, &offIgn, &cbIgn);
2032 if (RT_FAILURE(rc))
2033 continue;
2034
2035 /* Copy the certificate certificate. */
2036 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
2037 mFiles.push_back(InstallerFile(s_aCertFiles[i].pszIso,
2038 strDstCert,
2039 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
2040
2041 /* Out certificate installation utility. */
2042 /* First pass: Copy over the file (first time only) + execute it to remove any
2043 * existing VBox certificates. */
2044 GuestProcessStartupInfo siCertUtilRem;
2045 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
2046 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
2047 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2048 siCertUtilRem.mArguments.push_back(strDstCert);
2049 siCertUtilRem.mArguments.push_back(strDstCert);
2050 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
2051 strUpdateDir + "VBoxCertUtil.exe",
2052 fCopyCertUtil | UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
2053 siCertUtilRem));
2054 fCopyCertUtil = 0;
2055 /* Second pass: Only execute (but don't copy) again, this time installng the
2056 * recent certificates just copied over. */
2057 GuestProcessStartupInfo siCertUtilAdd;
2058 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
2059 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
2060 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2061 siCertUtilAdd.mArguments.push_back(strDstCert);
2062 siCertUtilAdd.mArguments.push_back(strDstCert);
2063 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
2064 strUpdateDir + "VBoxCertUtil.exe",
2065 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
2066 siCertUtilAdd));
2067 }
2068 }
2069 /* The installers in different flavors, as we don't know (and can't assume)
2070 * the guest's bitness. */
2071 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
2072 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
2073 UPDATEFILE_FLAG_COPY_FROM_ISO));
2074 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
2075 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
2076 UPDATEFILE_FLAG_COPY_FROM_ISO));
2077 /* The stub loader which decides which flavor to run. */
2078 GuestProcessStartupInfo siInstaller;
2079 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
2080 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
2081 * setup can take quite a while, so be on the safe side. */
2082 siInstaller.mTimeoutMS = 5 * 60 * 1000;
2083 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
2084 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
2085 /* Don't quit VBoxService during upgrade because it still is used for this
2086 * piece of code we're in right now (that is, here!) ... */
2087 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
2088 /* Tell the installer to report its current installation status
2089 * using a running VBoxTray instance via balloon messages in the
2090 * Windows taskbar. */
2091 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
2092 /* Add optional installer command line arguments from the API to the
2093 * installer's startup info. */
2094 rc = addProcessArguments(siInstaller.mArguments, mArguments);
2095 AssertRC(rc);
2096 /* If the caller does not want to wait for out guest update process to end,
2097 * complete the progress object now so that the caller can do other work. */
2098 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
2099 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
2100 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
2101 strUpdateDir + "VBoxWindowsAdditions.exe",
2102 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
2103 break;
2104 }
2105 case eOSType_Linux:
2106 /** @todo Add Linux support. */
2107 break;
2108 case eOSType_Solaris:
2109 /** @todo Add Solaris support. */
2110 break;
2111 default:
2112 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
2113 break;
2114 }
2115 }
2116
2117 if (RT_SUCCESS(rc))
2118 {
2119 /* We want to spend 40% total for all copying operations. So roughly
2120 * calculate the specific percentage step of each copied file. */
2121 uint8_t uOffset = 20; /* Start at 20%. */
2122 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2123
2124 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
2125
2126 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
2127 while (itFiles != mFiles.end())
2128 {
2129 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
2130 {
2131 bool fOptional = false;
2132 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
2133 fOptional = true;
2134 rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
2135 fOptional, NULL /* cbSize */);
2136 if (RT_FAILURE(rc))
2137 {
2138 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2139 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
2140 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
2141 break;
2142 }
2143 }
2144
2145 rc = setProgress(uOffset);
2146 if (RT_FAILURE(rc))
2147 break;
2148 uOffset += uStep;
2149
2150 ++itFiles;
2151 }
2152 }
2153
2154 /* Done copying, close .ISO file. */
2155 RTIsoFsClose(&iso);
2156
2157 if (RT_SUCCESS(rc))
2158 {
2159 /* We want to spend 35% total for all copying operations. So roughly
2160 * calculate the specific percentage step of each copied file. */
2161 uint8_t uOffset = 60; /* Start at 60%. */
2162 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2163
2164 LogRel(("Executing Guest Additions update files ...\n"));
2165
2166 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
2167 while (itFiles != mFiles.end())
2168 {
2169 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
2170 {
2171 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
2172 if (RT_FAILURE(rc))
2173 break;
2174 }
2175
2176 rc = setProgress(uOffset);
2177 if (RT_FAILURE(rc))
2178 break;
2179 uOffset += uStep;
2180
2181 ++itFiles;
2182 }
2183 }
2184
2185 if (RT_SUCCESS(rc))
2186 {
2187 LogRel(("Automatic update of Guest Additions succeeded\n"));
2188 rc = setProgressSuccess();
2189 }
2190 }
2191 }
2192
2193 if (RT_FAILURE(rc))
2194 {
2195 if (rc == VERR_CANCELLED)
2196 {
2197 LogRel(("Automatic update of Guest Additions was canceled\n"));
2198
2199 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2200 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
2201 }
2202 else
2203 {
2204 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
2205 if (!mProgress.isNull()) /* Progress object is optional. */
2206 {
2207 com::ProgressErrorInfo errorInfo(mProgress);
2208 if ( errorInfo.isFullAvailable()
2209 || errorInfo.isBasicAvailable())
2210 {
2211 strError = errorInfo.getText();
2212 }
2213 }
2214
2215 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
2216 strError.c_str(), hr));
2217 }
2218
2219 LogRel(("Please install Guest Additions manually\n"));
2220 }
2221
2222 /** @todo Clean up copied / left over installation files. */
2223
2224 LogFlowFuncLeaveRC(rc);
2225 return rc;
2226}
2227
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