VirtualBox

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

Last change on this file since 71636 was 71562, checked in by vboxsync, 7 years ago

Guest Control/Main: Logging.

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