VirtualBox

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

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

Guest Control/Main: Implemented file flag handling for GuestSessionTask::fileCopyFrom().

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