VirtualBox

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

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

Guest Control/Main: Added a dedicated header file for guest session tasks.

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