VirtualBox

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

Last change on this file since 73768 was 73040, checked in by vboxsync, 6 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.8 KB
RevLine 
[42084]1/* $Id: GuestSessionImplTasks.cpp 73040 2018-07-10 16:08:51Z vboxsync $ */
2/** @file
[44869]3 * VirtualBox Main - Guest session tasks.
[42084]4 */
5
6/*
[71213]7 * Copyright (C) 2012-2018 Oracle Corporation
[42084]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
[57358]19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[71213]22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
[67914]23#include "LoggingNew.h"
24
[42105]25#include "GuestImpl.h"
[55644]26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
[42084]29#include "GuestSessionImpl.h"
[71648]30#include "GuestSessionImplTasks.h"
[42478]31#include "GuestCtrlImplPrivate.h"
[42084]32
33#include "Global.h"
34#include "AutoCaller.h"
[42923]35#include "ConsoleImpl.h"
[42566]36#include "ProgressImpl.h"
[42084]37
[42567]38#include <memory> /* For auto_ptr. */
39
[42171]40#include <iprt/env.h>
[42573]41#include <iprt/file.h> /* For CopyTo/From. */
[71220]42#include <iprt/dir.h>
[60629]43#include <iprt/path.h>
[72958]44#include <iprt/fsvfs.h>
[42171]45
[42084]46
[57358]47/*********************************************************************************************************************************
48* Defines *
49*********************************************************************************************************************************/
[42923]50
51/**
[71649]52 * (Guest Additions) ISO file flags.
53 * Needed for handling Guest Additions updates.
[42808]54 */
[71649]55#define ISOFILE_FLAG_NONE 0
[43001]56/** Copy over the file from host to the
57 * guest. */
[71649]58#define ISOFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
[43001]59/** Execute file on the guest after it has
60 * been successfully transfered. */
[71649]61#define ISOFILE_FLAG_EXECUTE RT_BIT(7)
[42923]62/** File is optional, does not have to be
63 * existent on the .ISO. */
[71649]64#define ISOFILE_FLAG_OPTIONAL RT_BIT(8)
[42749]65
[42923]66
[42566]67// session task classes
68/////////////////////////////////////////////////////////////////////////////
69
[63186]70GuestSessionTask::GuestSessionTask(GuestSession *pSession)
71 : ThreadTask("GenericGuestSessionTask")
[42566]72{
73 mSession = pSession;
[71847]74
[71976]75 switch (mSession->i_getPathStyle())
76 {
77 case PathStyle_DOS:
78 mfPathStyle = RTPATH_STR_F_STYLE_DOS;
79 mPathStyle = "\\";
80 break;
81
82 default:
83 mfPathStyle = RTPATH_STR_F_STYLE_UNIX;
84 mPathStyle = "/";
85 break;
86 }
[42566]87}
88
89GuestSessionTask::~GuestSessionTask(void)
90{
91}
92
[71976]93int GuestSessionTask::createAndSetProgressObject(ULONG cOperations /* = 1 */)
[58521]94{
[71976]95 LogFlowThisFunc(("cOperations=%ld\n", cOperations));
[58521]96
[71976]97 /* Create the progress object. */
[58521]98 ComObjPtr<Progress> pProgress;
[71976]99 HRESULT hr = pProgress.createObject();
[58521]100 if (FAILED(hr))
101 return VERR_COM_UNEXPECTED;
102
103 hr = pProgress->init(static_cast<IGuestSession*>(mSession),
104 Bstr(mDesc).raw(),
[71976]105 TRUE /* aCancelable */, cOperations, Bstr(mDesc).raw());
[58521]106 if (FAILED(hr))
107 return VERR_COM_UNEXPECTED;
108
109 mProgress = pProgress;
110
111 LogFlowFuncLeave();
[71976]112 return VINF_SUCCESS;
[58521]113}
114
[63186]115int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
116{
117 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
118
119 mDesc = strDesc;
120 mProgress = pProgress;
121 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
122
123 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
124 return Global::vboxStatusCodeToCOM(hrc);
125}
126
[42923]127int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
128 const Utf8Str &strPath, Utf8Str &strValue)
129{
[52082]130 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
[51612]131 const ComPtr<IMachine> pMachine = pConsole->i_machine();
[42923]132
133 Assert(!pMachine.isNull());
134 Bstr strTemp, strFlags;
135 LONG64 i64Timestamp;
136 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
137 strTemp.asOutParam(),
138 &i64Timestamp, strFlags.asOutParam());
139 if (SUCCEEDED(hr))
140 {
141 strValue = strTemp;
142 return VINF_SUCCESS;
143 }
144 return VERR_NOT_FOUND;
145}
146
[42634]147int GuestSessionTask::setProgress(ULONG uPercent)
[42566]148{
[42634]149 if (mProgress.isNull()) /* Progress is optional. */
150 return VINF_SUCCESS;
151
[42566]152 BOOL fCanceled;
[42634]153 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
154 && fCanceled)
[42566]155 return VERR_CANCELLED;
[42634]156 BOOL fCompleted;
157 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
[42937]158 && fCompleted)
159 {
160 AssertMsgFailed(("Setting value of an already completed progress\n"));
[42634]161 return VINF_SUCCESS;
[42937]162 }
[42634]163 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
[42636]164 if (FAILED(hr))
165 return VERR_COM_UNEXPECTED;
[42566]166
167 return VINF_SUCCESS;
168}
169
170int GuestSessionTask::setProgressSuccess(void)
171{
[42634]172 if (mProgress.isNull()) /* Progress is optional. */
173 return VINF_SUCCESS;
174
[42566]175 BOOL fCompleted;
[71849]176 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
[42566]177 && !fCompleted)
178 {
[50874]179 HRESULT hr = mProgress->i_notifyComplete(S_OK);
[42567]180 if (FAILED(hr))
181 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
[42566]182 }
183
184 return VINF_SUCCESS;
185}
186
[42693]187HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
[42566]188{
[44869]189 LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
190 hr, strMsg.c_str()));
[44863]191
[42634]192 if (mProgress.isNull()) /* Progress is optional. */
[42693]193 return hr; /* Return original rc. */
[42634]194
[42566]195 BOOL fCanceled;
196 BOOL fCompleted;
197 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
198 && !fCanceled
199 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
200 && !fCompleted)
201 {
[50874]202 HRESULT hr2 = mProgress->i_notifyComplete(hr,
203 COM_IIDOF(IGuestSession),
204 GuestSession::getStaticComponentName(),
205 strMsg.c_str());
[42567]206 if (FAILED(hr2))
[42693]207 return hr2;
[42566]208 }
[42693]209 return hr; /* Return original rc. */
[42566]210}
211
[71213]212/**
213 * Creates a directory on the guest.
214 *
[72045]215 * @return VBox status code. VERR_ALREADY_EXISTS if directory on the guest already exists.
[71213]216 * @param strPath Absolute path to directory on the guest (guest style path) to create.
[71976]217 * @param enmDirectoryCreateFlags Directory creation flags.
[71213]218 * @param uMode Directory mode to use for creation.
219 * @param fFollowSymlinks Whether to follow symlinks on the guest or not.
220 */
221int GuestSessionTask::directoryCreate(const com::Utf8Str &strPath,
[71976]222 DirectoryCreateFlag_T enmDirectoryCreateFlags, uint32_t uMode, bool fFollowSymlinks)
[44863]223{
[71213]224 LogFlowFunc(("strPath=%s, fFlags=0x%x, uMode=%RU32, fFollowSymlinks=%RTbool\n",
[71976]225 strPath.c_str(), enmDirectoryCreateFlags, uMode, fFollowSymlinks));
[44863]226
[71299]227 GuestFsObjData objData; int rcGuest;
[71345]228 int rc = mSession->i_directoryQueryInfo(strPath, fFollowSymlinks, objData, &rcGuest);
[71213]229 if (RT_SUCCESS(rc))
[42693]230 {
[72045]231 return VERR_ALREADY_EXISTS;
[42693]232 }
[71213]233 else
[42573]234 {
[71213]235 switch (rc)
[42573]236 {
[71213]237 case VERR_GSTCTL_GUEST_ERROR:
[42573]238 {
[71299]239 switch (rcGuest)
[42573]240 {
[71213]241 case VERR_FILE_NOT_FOUND:
242 case VERR_PATH_NOT_FOUND:
[71976]243 rc = mSession->i_directoryCreate(strPath.c_str(), uMode, enmDirectoryCreateFlags, &rcGuest);
[71213]244 break;
245 default:
246 break;
[42573]247 }
[71213]248 break;
[42693]249 }
[71213]250
251 default:
252 break;
[42693]253 }
254 }
[42573]255
[71213]256 if (RT_FAILURE(rc))
[60622]257 {
[71213]258 if (rc == VERR_GSTCTL_GUEST_ERROR)
[60622]259 {
[71299]260 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
[60622]261 }
[71213]262 else
263 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
264 Utf8StrFmt(GuestSession::tr("Error creating directory on the guest: %Rrc"), strPath.c_str(), rc));
[60622]265 }
266
[71213]267 LogFlowFuncLeaveRC(rc);
268 return rc;
269}
270
271/**
[71817]272 * Main function for copying a file from guest to the host.
[71251]273 *
274 * @return VBox status code.
[71796]275 * @param srcFile Guest file (source) to copy to the host. Must be in opened and ready state already.
276 * @param phDstFile Pointer to host file handle (destination) to copy to. Must be in opened and ready state already.
[71817]277 * @param fFileCopyFlags File copy flags.
278 * @param offCopy Offset (in bytes) where to start copying the source file.
[71796]279 * @param cbSize Size (in bytes) to copy from the source file.
280 */
[71817]281int GuestSessionTask::fileCopyFromGuestInner(ComObjPtr<GuestFile> &srcFile, PRTFILE phDstFile, FileCopyFlag_T fFileCopyFlags,
282 uint64_t offCopy, uint64_t cbSize)
[71796]283{
[71817]284 RT_NOREF(fFileCopyFlags);
[71796]285
286 BOOL fCanceled = FALSE;
287 uint64_t cbWrittenTotal = 0;
288 uint64_t cbToRead = cbSize;
289
290 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
291
292 int rc = VINF_SUCCESS;
293
[71817]294 if (offCopy)
[71796]295 {
[71817]296 uint64_t offActual;
297 rc = srcFile->i_seekAt(offCopy, GUEST_FILE_SEEKTYPE_BEGIN, uTimeoutMs, &offActual);
[71796]298 if (RT_FAILURE(rc))
299 {
300 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71817]301 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
[71796]302 return rc;
303 }
304 }
305
306 BYTE byBuf[_64K];
307 while (cbToRead)
308 {
309 uint32_t cbRead;
310 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
311 rc = srcFile->i_readData(cbChunk, uTimeoutMs, byBuf, sizeof(byBuf), &cbRead);
312 if (RT_FAILURE(rc))
313 {
314 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[72090]315 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64 from guest failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
[71796]316 break;
317 }
318
319 rc = RTFileWrite(*phDstFile, byBuf, cbRead, NULL /* No partial writes */);
320 if (RT_FAILURE(rc))
321 {
322 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[72090]323 Utf8StrFmt(GuestSession::tr("Writing %RU32 bytes to file on host failed: %Rrc"), cbRead, rc));
[71796]324 break;
325 }
326
327 Assert(cbToRead >= cbRead);
328 cbToRead -= cbRead;
329
330 /* Update total bytes written to the guest. */
331 cbWrittenTotal += cbRead;
332 Assert(cbWrittenTotal <= cbSize);
333
334 /* Did the user cancel the operation above? */
335 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
336 && fCanceled)
337 break;
338
339 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
340 if (RT_FAILURE(rc))
341 break;
342 }
343
[71849]344 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
345 && fCanceled)
346 return VINF_SUCCESS;
347
[71796]348 if (RT_FAILURE(rc))
349 return rc;
350
351 /*
352 * Even if we succeeded until here make sure to check whether we really transfered
353 * everything.
354 */
355 if ( cbSize > 0
356 && cbWrittenTotal == 0)
357 {
358 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
359 * to the destination -> access denied. */
360 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
361 Utf8StrFmt(GuestSession::tr("Writing guest file to host failed: Access denied")));
362 rc = VERR_ACCESS_DENIED;
363 }
364 else if (cbWrittenTotal < cbSize)
365 {
366 /* If we did not copy all let the user know. */
367 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
368 Utf8StrFmt(GuestSession::tr("Copying guest file to host to failed (%RU64/%RU64 bytes transfered)"),
369 cbWrittenTotal, cbSize));
370 rc = VERR_INTERRUPTED;
371 }
372
373 LogFlowFuncLeaveRC(rc);
374 return rc;
375}
376
377/**
378 * Copies a file from the guest to the host.
379 *
380 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
[71251]381 * @param strSource Full path of source file on the guest to copy.
382 * @param strDest Full destination path and file name (host style) to copy file to.
[71817]383 * @param fFileCopyFlags File copy flags.
[71251]384 */
[71817]385int GuestSessionTask::fileCopyFromGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
[71251]386{
[71817]387 LogFlowThisFunc(("strSource=%s, strDest=%s, enmFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
[71251]388
[71796]389 GuestFileOpenInfo srcOpenInfo;
390 RT_ZERO(srcOpenInfo);
391 srcOpenInfo.mFileName = strSource;
392 srcOpenInfo.mOpenAction = FileOpenAction_OpenExisting;
393 srcOpenInfo.mAccessMode = FileAccessMode_ReadOnly;
394 srcOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71251]395
[71796]396 ComObjPtr<GuestFile> srcFile;
[71251]397
[71796]398 GuestFsObjData srcObjData;
[71251]399 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[71817]400 int rc = mSession->i_fsQueryInfo(strSource, TRUE /* fFollowSymlinks */, srcObjData, &rcGuest);
[71251]401 if (RT_FAILURE(rc))
402 {
403 switch (rc)
404 {
405 case VERR_GSTCTL_GUEST_ERROR:
[71796]406 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
[71251]407 break;
408
409 default:
410 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]411 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
[71251]412 strSource.c_str(), rc));
413 break;
414 }
415 }
[71796]416 else
[71251]417 {
[71796]418 switch (srcObjData.mType)
419 {
420 case FsObjType_File:
421 break;
422
423 case FsObjType_Symlink:
[71817]424 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
[71796]425 {
426 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
427 Utf8StrFmt(GuestSession::tr("Source file \"%s\" is a symbolic link"),
428 strSource.c_str(), rc));
429 rc = VERR_IS_A_SYMLINK;
430 }
431 break;
432
433 default:
434 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
435 Utf8StrFmt(GuestSession::tr("Source element \"%s\" is not a file"), strSource.c_str()));
436 rc = VERR_NOT_A_FILE;
437 break;
438 }
[71251]439 }
440
441 if (RT_FAILURE(rc))
442 return rc;
443
[71796]444 rc = mSession->i_fileOpen(srcOpenInfo, srcFile, &rcGuest);
[71251]445 if (RT_FAILURE(rc))
446 {
447 switch (rc)
448 {
449 case VERR_GSTCTL_GUEST_ERROR:
[71796]450 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
[71251]451 break;
452
453 default:
454 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]455 Utf8StrFmt(GuestSession::tr("Source file \"%s\" could not be opened: %Rrc"),
456 strSource.c_str(), rc));
[71251]457 break;
458 }
[71796]459 }
[71251]460
[71796]461 if (RT_FAILURE(rc))
[71251]462 return rc;
463
[71796]464 char *pszDstFile = NULL;
465 RTFSOBJINFO dstObjInfo;
466 RT_ZERO(dstObjInfo);
[71251]467
[71796]468 bool fSkip = false; /* Whether to skip handling the file. */
[71251]469
[71796]470 if (RT_SUCCESS(rc))
[71251]471 {
[71796]472 rc = RTPathQueryInfo(strDest.c_str(), &dstObjInfo, RTFSOBJATTRADD_NOTHING);
473 if (RT_SUCCESS(rc))
[71251]474 {
[71817]475 if (fFileCopyFlags & FileCopyFlag_NoReplace)
[71251]476 {
[71796]477 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
478 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"), strDest.c_str()));
479 rc = VERR_ALREADY_EXISTS;
[71251]480 }
481
[71817]482 if (fFileCopyFlags & FileCopyFlag_Update)
[71251]483 {
[71796]484 RTTIMESPEC srcModificationTimeTS;
485 RTTimeSpecSetSeconds(&srcModificationTimeTS, srcObjData.mModificationTime);
486 if (RTTimeSpecCompare(&srcModificationTimeTS, &dstObjInfo.ModificationTime) <= 0)
[71251]487 {
488 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]489 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
490 strDest.c_str()));
491 fSkip = true;
[71251]492 }
493 }
494 }
495 else
496 {
[71796]497 if (rc != VERR_FILE_NOT_FOUND) /* Destination file does not exist (yet)? */
[71251]498 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]499 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
500 strDest.c_str(), rc));
[71251]501 }
502 }
503
[71796]504 if (fSkip)
[71251]505 {
[71796]506 int rc2 = srcFile->i_closeFile(&rcGuest);
507 AssertRC(rc2);
508 return VINF_SUCCESS;
[71251]509 }
510
[71667]511 if (RT_SUCCESS(rc))
512 {
[71796]513 if (RTFS_IS_FILE(dstObjInfo.Attr.fMode))
[71667]514 {
[71817]515 if (fFileCopyFlags & FileCopyFlag_NoReplace)
[71667]516 {
517 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]518 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
[71667]519 strDest.c_str(), rc));
[71796]520 rc = VERR_ALREADY_EXISTS;
[71667]521 }
[71796]522 else
523 pszDstFile = RTStrDup(strDest.c_str());
[71667]524 }
[71796]525 else if (RTFS_IS_DIRECTORY(dstObjInfo.Attr.fMode))
[71667]526 {
[71847]527 /* Build the final file name with destination path (on the host). */
528 char szDstPath[RTPATH_MAX];
529 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
[71667]530
[71847]531 if ( !strDest.endsWith("\\")
532 && !strDest.endsWith("/"))
533 RTPathAppend(szDstPath, sizeof(szDstPath), "/"); /* IPRT can handle / on all hosts. */
[71667]534
[71847]535 RTPathAppend(szDstPath, sizeof(szDstPath), RTPathFilenameEx(strSource.c_str(), mfPathStyle));
536
537 pszDstFile = RTStrDup(szDstPath);
[71667]538 }
[71796]539 else if (RTFS_IS_SYMLINK(dstObjInfo.Attr.fMode))
[71667]540 {
[71817]541 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
[71667]542 {
543 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71796]544 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
[71667]545 strDest.c_str(), rc));
[71796]546 rc = VERR_IS_A_SYMLINK;
[71667]547 }
[71796]548 else
549 pszDstFile = RTStrDup(strDest.c_str());
[71667]550 }
[71796]551 else
552 {
553 LogFlowThisFunc(("Object type %RU32 not implemented yet\n", dstObjInfo.Attr.fMode));
554 rc = VERR_NOT_IMPLEMENTED;
555 }
[71667]556 }
[71796]557 else if (rc == VERR_FILE_NOT_FOUND)
[71667]558 pszDstFile = RTStrDup(strDest.c_str());
559
[71796]560 if ( RT_SUCCESS(rc)
561 || rc == VERR_FILE_NOT_FOUND)
[71667]562 {
[71796]563 if (!pszDstFile)
564 {
565 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No memory to allocate destination file path")));
566 rc = VERR_NO_MEMORY;
567 }
568 else
569 {
570 RTFILE hDstFile;
571 rc = RTFileOpen(&hDstFile, pszDstFile,
572 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
573 if (RT_SUCCESS(rc))
574 {
575 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n", strSource.c_str(), pszDstFile, srcObjData.mObjectSize));
[71667]576
[71817]577 rc = fileCopyFromGuestInner(srcFile, &hDstFile, fFileCopyFlags, 0 /* Offset, unused */, (uint64_t)srcObjData.mObjectSize);
[71796]578
579 int rc2 = RTFileClose(hDstFile);
580 AssertRC(rc2);
581 }
582 else
583 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
584 Utf8StrFmt(GuestSession::tr("Opening/creating destination file \"%s\" failed: %Rrc"),
585 pszDstFile, rc));
586 }
[71251]587 }
588
[71796]589 RTStrFree(pszDstFile);
[71251]590
[71796]591 int rc2 = srcFile->i_closeFile(&rcGuest);
[71251]592 AssertRC(rc2);
593
[71667]594 LogFlowFuncLeaveRC(rc);
[71251]595 return rc;
596}
597
598/**
[71817]599 * Main function for copying a file from host to the guest.
[71213]600 *
601 * @return VBox status code.
[72961]602 * @param hVfsFile The VFS file handle to read from.
[71817]603 * @param dstFile Guest file (destination) to copy to the guest. Must be in opened and ready state already.
604 * @param fFileCopyFlags File copy flags.
605 * @param offCopy Offset (in bytes) where to start copying the source file.
606 * @param cbSize Size (in bytes) to copy from the source file.
[71213]607 */
[72958]608int GuestSessionTask::fileCopyToGuestInner(RTVFSFILE hVfsFile, ComObjPtr<GuestFile> &dstFile, FileCopyFlag_T fFileCopyFlags,
[71817]609 uint64_t offCopy, uint64_t cbSize)
[71213]610{
[71817]611 RT_NOREF(fFileCopyFlags);
[60622]612
[71817]613 BOOL fCanceled = FALSE;
614 uint64_t cbWrittenTotal = 0;
615 uint64_t cbToRead = cbSize;
616
617 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
618
619 int rc = VINF_SUCCESS;
620
621 if (offCopy)
[71213]622 {
[71817]623 uint64_t offActual;
[72958]624 rc = RTVfsFileSeek(hVfsFile, offCopy, RTFILE_SEEK_END, &offActual);
[71817]625 if (RT_FAILURE(rc))
626 {
627 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
628 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
629 return rc;
630 }
[71213]631 }
632
[71817]633 BYTE byBuf[_64K];
634 while (cbToRead)
635 {
636 size_t cbRead;
637 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
[72958]638 rc = RTVfsFileRead(hVfsFile, byBuf, cbChunk, &cbRead);
[71817]639 if (RT_FAILURE(rc))
640 {
641 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[72090]642 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64 from host failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
[71817]643 break;
644 }
[71213]645
[71819]646 rc = dstFile->i_writeData(uTimeoutMs, byBuf, (uint32_t)cbRead, NULL /* No partial writes */);
[71817]647 if (RT_FAILURE(rc))
648 {
649 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[72090]650 Utf8StrFmt(GuestSession::tr("Writing %zu bytes to file on guest failed: %Rrc"), cbRead, rc));
[71817]651 break;
652 }
653
654 Assert(cbToRead >= cbRead);
655 cbToRead -= cbRead;
656
657 /* Update total bytes written to the guest. */
658 cbWrittenTotal += cbRead;
659 Assert(cbWrittenTotal <= cbSize);
660
661 /* Did the user cancel the operation above? */
662 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
663 && fCanceled)
664 break;
665
666 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
667 if (RT_FAILURE(rc))
668 break;
669 }
670
671 if (RT_FAILURE(rc))
672 return rc;
673
[60622]674 /*
[71817]675 * Even if we succeeded until here make sure to check whether we really transfered
676 * everything.
[60622]677 */
[71817]678 if ( cbSize > 0
679 && cbWrittenTotal == 0)
680 {
681 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
682 * to the destination -> access denied. */
683 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
684 Utf8StrFmt(GuestSession::tr("Writing to destination file failed: Access denied")));
685 rc = VERR_ACCESS_DENIED;
686 }
687 else if (cbWrittenTotal < cbSize)
688 {
689 /* If we did not copy all let the user know. */
690 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
691 Utf8StrFmt(GuestSession::tr("Copying to destination failed (%RU64/%RU64 bytes transfered)"),
692 cbWrittenTotal, cbSize));
693 rc = VERR_INTERRUPTED;
694 }
[42573]695
[71817]696 LogFlowFuncLeaveRC(rc);
697 return rc;
698}
[42693]699
[71817]700/**
701 * Copies a file from the guest to the host.
702 *
703 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
704 * @param strSource Full path of source file on the host to copy.
705 * @param strDest Full destination path and file name (guest style) to copy file to.
706 * @param fFileCopyFlags File copy flags.
707 */
708int GuestSessionTask::fileCopyToGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
709{
710 LogFlowThisFunc(("strSource=%s, strDest=%s, fFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
[71213]711
[71817]712 Utf8Str strDestFinal = strDest;
713
714 GuestFsObjData dstObjData;
[71219]715 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[71817]716 int rc = mSession->i_fsQueryInfo(strDest, TRUE /* fFollowSymlinks */, dstObjData, &rcGuest);
[42693]717 if (RT_FAILURE(rc))
718 {
[43162]719 switch (rc)
720 {
[45078]721 case VERR_GSTCTL_GUEST_ERROR:
[71817]722 if (rcGuest == VERR_FILE_NOT_FOUND) /* File might not exist on the guest yet. */
723 {
724 rc = VINF_SUCCESS;
725 }
726 else
727 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
[43162]728 break;
729
730 default:
731 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71817]732 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
733 strDest.c_str(), rc));
[43162]734 break;
735 }
[42693]736 }
[71817]737 else
[42693]738 {
[71817]739 switch (dstObjData.mType)
[71213]740 {
[71817]741 case FsObjType_Directory:
742 {
[71976]743 /* Build the final file name with destination path (on the host). */
744 char szDstPath[RTPATH_MAX];
745 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
[42693]746
[71976]747 if ( !strDest.endsWith("\\")
748 && !strDest.endsWith("/"))
[72045]749 RTStrCat(szDstPath, sizeof(szDstPath), "/");
[42693]750
[71976]751 RTStrCat(szDstPath, sizeof(szDstPath), RTPathFilename(strSource.c_str()));
752
753 strDestFinal = szDstPath;
[42693]754 break;
755 }
756
[71817]757 case FsObjType_File:
758 if (fFileCopyFlags & FileCopyFlag_NoReplace)
759 {
760 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
761 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
762 strDest.c_str(), rc));
763 rc = VERR_ALREADY_EXISTS;
764 }
765 break;
[42810]766
[71817]767 case FsObjType_Symlink:
768 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
769 {
[71213]770 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71817]771 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
772 strDest.c_str(), rc));
773 rc = VERR_IS_A_SYMLINK;
774 }
775 break;
[42573]776
[71817]777 default:
778 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
779 Utf8StrFmt(GuestSession::tr("Destination element \"%s\" not supported"), strDest.c_str()));
780 rc = VERR_NOT_SUPPORTED;
781 break;
[71213]782 }
[71817]783 }
[42573]784
[71817]785 if (RT_FAILURE(rc))
786 return rc;
[71213]787
[71817]788 GuestFileOpenInfo dstOpenInfo;
789 RT_ZERO(dstOpenInfo);
790 dstOpenInfo.mFileName = strDestFinal;
791 if (fFileCopyFlags & FileCopyFlag_NoReplace)
792 dstOpenInfo.mOpenAction = FileOpenAction_CreateNew;
793 else
794 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
795 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
796 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71213]797
[71817]798 ComObjPtr<GuestFile> dstFile;
799 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
800 if (RT_FAILURE(rc))
801 {
802 switch (rc)
803 {
804 case VERR_GSTCTL_GUEST_ERROR:
805 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
806 break;
[71213]807
[71817]808 default:
809 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
810 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
[71818]811 strDestFinal.c_str(), rc));
[71817]812 break;
813 }
814 }
[71213]815
[71817]816 if (RT_FAILURE(rc))
817 return rc;
[71213]818
[71976]819 char szSrcReal[RTPATH_MAX];
820
[71817]821 RTFSOBJINFO srcObjInfo;
822 RT_ZERO(srcObjInfo);
[71213]823
[71817]824 bool fSkip = false; /* Whether to skip handling the file. */
[71213]825
[71817]826 if (RT_SUCCESS(rc))
[71213]827 {
[71976]828 rc = RTPathReal(strSource.c_str(), szSrcReal, sizeof(szSrcReal));
829 if (RT_FAILURE(rc))
[71213]830 {
[71976]831 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
832 Utf8StrFmt(GuestSession::tr("Source path lookup for \"%s\" failed: %Rrc"),
833 strSource.c_str(), rc));
834 }
835 else
836 {
837 rc = RTPathQueryInfo(szSrcReal, &srcObjInfo, RTFSOBJATTRADD_NOTHING);
838 if (RT_SUCCESS(rc))
[42693]839 {
[71976]840 if (fFileCopyFlags & FileCopyFlag_Update)
[71817]841 {
[71976]842 RTTIMESPEC dstModificationTimeTS;
843 RTTimeSpecSetSeconds(&dstModificationTimeTS, dstObjData.mModificationTime);
844 if (RTTimeSpecCompare(&dstModificationTimeTS, &srcObjInfo.ModificationTime) <= 0)
845 {
846 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
847 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
848 strDestFinal.c_str()));
849 fSkip = true;
850 }
[71817]851 }
[42693]852 }
[71976]853 else
854 {
855 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
856 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
857 szSrcReal, rc));
858 }
[71213]859 }
860 }
861
[71817]862 if (fSkip)
[71213]863 {
[71817]864 int rc2 = dstFile->i_closeFile(&rcGuest);
865 AssertRC(rc2);
866 return VINF_SUCCESS;
[71213]867 }
[71817]868
869 if (RT_SUCCESS(rc))
[71213]870 {
[72958]871 RTVFSFILE hSrcFile;
872 rc = RTVfsFileOpenNormal(szSrcReal, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hSrcFile); /** @todo Use the correct open modes! */
[71817]873 if (RT_SUCCESS(rc))
[71213]874 {
[71817]875 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n",
[71976]876 szSrcReal, strDestFinal.c_str(), srcObjInfo.cbObject));
[71817]877
[72958]878 rc = fileCopyToGuestInner(hSrcFile, dstFile, fFileCopyFlags, 0 /* Offset, unused */, srcObjInfo.cbObject);
[71817]879
[72958]880 int rc2 = RTVfsFileRelease(hSrcFile);
[71817]881 AssertRC(rc2);
882 }
883 else
[71213]884 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[71817]885 Utf8StrFmt(GuestSession::tr("Opening source file \"%s\" failed: %Rrc"),
[71976]886 szSrcReal, rc));
[71213]887 }
[43162]888
[71817]889 int rc2 = dstFile->i_closeFile(&rcGuest);
890 AssertRC(rc2);
891
892 LogFlowFuncLeaveRC(rc);
[71213]893 return rc;
894}
[42758]895
[71213]896/**
[71976]897 * Adds a guest file system entry to a given list.
[71213]898 *
[71976]899 * @return VBox status code.
900 * @param strFile Path to file system entry to add.
901 * @param fsObjData Guest file system information of entry to add.
[71213]902 */
[71976]903int FsList::AddEntryFromGuest(const Utf8Str &strFile, const GuestFsObjData &fsObjData)
[71213]904{
[71976]905 LogFlowFunc(("Adding '%s'\n", strFile.c_str()));
906
907 FsEntry *pEntry = NULL;
908 try
909 {
910 pEntry = new FsEntry();
911 pEntry->fMode = fsObjData.GetFileMode();
912 pEntry->strPath = strFile;
913
914 mVecEntries.push_back(pEntry);
915 }
916 catch (...)
917 {
918 if (pEntry)
919 delete pEntry;
920 return VERR_NO_MEMORY;
921 }
922
[71213]923 return VINF_SUCCESS;
924}
[42573]925
[71976]926/**
927 * Adds a host file system entry to a given list.
928 *
929 * @return VBox status code.
930 * @param strFile Path to file system entry to add.
931 * @param pcObjInfo File system information of entry to add.
932 */
933int FsList::AddEntryFromHost(const Utf8Str &strFile, PCRTFSOBJINFO pcObjInfo)
[71213]934{
[71976]935 LogFlowFunc(("Adding '%s'\n", strFile.c_str()));
936
937 FsEntry *pEntry = NULL;
938 try
939 {
940 pEntry = new FsEntry();
941 pEntry->fMode = pcObjInfo->Attr.fMode & RTFS_TYPE_MASK;
942 pEntry->strPath = strFile;
943
944 mVecEntries.push_back(pEntry);
945 }
946 catch (...)
947 {
948 if (pEntry)
949 delete pEntry;
950 return VERR_NO_MEMORY;
951 }
952
953 return VINF_SUCCESS;
954}
955
956FsList::FsList(const GuestSessionTask &Task)
957 : mTask(Task)
958{
959}
960
961FsList::~FsList()
962{
963 Destroy();
964}
965
966/**
967 * Initializes a file list.
968 *
969 * @return VBox status code.
970 * @param strSrcRootAbs Source root path (absolute) for this file list.
971 * @param strDstRootAbs Destination root path (absolute) for this file list.
972 * @param SourceSpec Source specification to use.
973 */
974int FsList::Init(const Utf8Str &strSrcRootAbs, const Utf8Str &strDstRootAbs,
975 const GuestSessionFsSourceSpec &SourceSpec)
976{
977 mSrcRootAbs = strSrcRootAbs;
978 mDstRootAbs = strDstRootAbs;
979 mSourceSpec = SourceSpec;
980
981 /* If the source is a directory, make sure the path is properly terminated already. */
[72001]982 if (mSourceSpec.enmType == FsObjType_Directory)
[71976]983 {
984 if ( !mSrcRootAbs.endsWith("/")
985 && !mSrcRootAbs.endsWith("\\"))
[72045]986 mSrcRootAbs += "/";
[71976]987
988 if ( !mDstRootAbs.endsWith("/")
989 && !mDstRootAbs.endsWith("\\"))
990 mDstRootAbs += "/";
991 }
992
993 return VINF_SUCCESS;
994}
995
996/**
997 * Destroys a file list.
998 */
999void FsList::Destroy(void)
1000{
1001 LogFlowFuncEnter();
1002
1003 FsEntries::iterator itEntry = mVecEntries.begin();
1004 while (itEntry != mVecEntries.end())
1005 {
1006 FsEntry *pEntry = *itEntry;
1007 delete pEntry;
1008 mVecEntries.erase(itEntry);
1009 itEntry = mVecEntries.begin();
1010 }
1011
1012 Assert(mVecEntries.empty());
1013
1014 LogFlowFuncLeave();
1015}
1016
1017/**
1018 * Builds a guest file list from a given path (and optional filter).
1019 *
1020 * @return VBox status code.
1021 * @param strPath Directory on the guest to build list from.
1022 * @param strSubDir Current sub directory path; needed for recursion.
1023 * Set to an empty path.
1024 */
1025int FsList::AddDirFromGuest(const Utf8Str &strPath, const Utf8Str &strSubDir /* = "" */)
1026{
1027 Utf8Str strPathAbs = strPath;
1028 if ( !strPathAbs.endsWith("/")
1029 && !strPathAbs.endsWith("\\"))
[72045]1030 strPathAbs += "/";
[71976]1031
1032 Utf8Str strPathSub = strSubDir;
1033 if ( strPathSub.isNotEmpty()
1034 && !strPathSub.endsWith("/")
1035 && !strPathSub.endsWith("\\"))
[72045]1036 strPathSub += "/";
[71976]1037
1038 strPathAbs += strPathSub;
1039
1040 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1041
1042 GuestDirectoryOpenInfo dirOpenInfo;
1043 dirOpenInfo.mFilter = "";
1044 dirOpenInfo.mPath = strPathAbs;
1045 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
1046
1047 const ComObjPtr<GuestSession> &pSession = mTask.GetSession();
1048
1049 ComObjPtr <GuestDirectory> pDir; int rcGuest;
1050 int rc = pSession->i_directoryOpen(dirOpenInfo, pDir, &rcGuest);
1051 if (RT_FAILURE(rc))
1052 {
1053 switch (rc)
1054 {
1055 case VERR_INVALID_PARAMETER:
1056 break;
1057
1058 case VERR_GSTCTL_GUEST_ERROR:
1059 break;
1060
1061 default:
1062 break;
1063 }
1064
1065 return rc;
1066 }
1067
1068 if (strPathSub.isNotEmpty())
1069 {
1070 GuestFsObjData fsObjData;
1071 fsObjData.mType = FsObjType_Directory;
1072
1073 rc = AddEntryFromGuest(strPathSub, fsObjData);
1074 }
1075
1076 if (RT_SUCCESS(rc))
1077 {
1078 ComObjPtr<GuestFsObjInfo> fsObjInfo;
1079 while (RT_SUCCESS(rc = pDir->i_readInternal(fsObjInfo, &rcGuest)))
1080 {
1081 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
1082 HRESULT hr2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1083 AssertComRC(hr2);
1084
1085 com::Bstr bstrName;
1086 hr2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1087 AssertComRC(hr2);
1088
1089 Utf8Str strEntry = strPathSub + Utf8Str(bstrName);
1090
1091 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1092
1093 switch (enmObjType)
1094 {
1095 case FsObjType_Directory:
1096 {
1097 if ( bstrName.equals(".")
1098 || bstrName.equals(".."))
1099 {
1100 break;
1101 }
1102
1103 if (!(mSourceSpec.Type.Dir.fRecursive))
1104 break;
1105
1106 rc = AddDirFromGuest(strPath, strEntry);
1107 break;
1108 }
1109
1110 case FsObjType_Symlink:
1111 {
[73037]1112 if (mSourceSpec.Type.Dir.fFollowSymlinks)
[71976]1113 {
1114 /** @todo Symlink handling from guest is not imlemented yet.
1115 * See IGuestSession::symlinkRead(). */
[73037]1116 LogRel2(("Guest Control: Warning: Symlink support on guest side not available, skipping \"%s\"",
1117 strEntry.c_str()));
[71976]1118 }
1119 break;
1120 }
1121
1122 case FsObjType_File:
1123 {
1124 rc = AddEntryFromGuest(strEntry, fsObjInfo->i_getData());
1125 break;
1126 }
1127
1128 default:
1129 break;
1130 }
1131 }
[72045]1132
1133 if (rc == VERR_NO_MORE_FILES) /* End of listing reached? */
1134 rc = VINF_SUCCESS;
[71976]1135 }
1136
1137 int rc2 = pDir->i_closeInternal(&rcGuest);
1138 if (RT_SUCCESS(rc))
1139 rc = rc2;
1140
1141 return rc;
1142}
1143
1144/**
1145 * Builds a host file list from a given path (and optional filter).
1146 *
1147 * @return VBox status code.
1148 * @param strPath Directory on the host to build list from.
1149 * @param strSubDir Current sub directory path; needed for recursion.
1150 * Set to an empty path.
1151 */
1152int FsList::AddDirFromHost(const Utf8Str &strPath, const Utf8Str &strSubDir)
1153{
1154 Utf8Str strPathAbs = strPath;
1155 if ( !strPathAbs.endsWith("/")
1156 && !strPathAbs.endsWith("\\"))
1157 strPathAbs += "/";
1158
1159 Utf8Str strPathSub = strSubDir;
1160 if ( strPathSub.isNotEmpty()
1161 && !strPathSub.endsWith("/")
1162 && !strPathSub.endsWith("\\"))
1163 strPathSub += "/";
1164
1165 strPathAbs += strPathSub;
1166
1167 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1168
1169 RTFSOBJINFO objInfo;
1170 int rc = RTPathQueryInfo(strPathAbs.c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
1171 if (RT_SUCCESS(rc))
1172 {
1173 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1174 {
1175 if (strPathSub.isNotEmpty())
1176 rc = AddEntryFromHost(strPathSub, &objInfo);
1177
1178 if (RT_SUCCESS(rc))
1179 {
1180 RTDIR hDir;
1181 rc = RTDirOpen(&hDir, strPathAbs.c_str());
1182 if (RT_SUCCESS(rc))
1183 {
1184 do
1185 {
1186 /* Retrieve the next directory entry. */
1187 RTDIRENTRYEX Entry;
1188 rc = RTDirReadEx(hDir, &Entry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1189 if (RT_FAILURE(rc))
1190 {
1191 if (rc == VERR_NO_MORE_FILES)
1192 rc = VINF_SUCCESS;
1193 break;
1194 }
1195
1196 Utf8Str strEntry = strPathSub + Utf8Str(Entry.szName);
1197
1198 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1199
1200 switch (Entry.Info.Attr.fMode & RTFS_TYPE_MASK)
1201 {
1202 case RTFS_TYPE_DIRECTORY:
1203 {
1204 /* Skip "." and ".." entries. */
1205 if (RTDirEntryExIsStdDotLink(&Entry))
1206 break;
1207
1208 if (!(mSourceSpec.Type.Dir.fRecursive))
1209 break;
1210
1211 rc = AddDirFromHost(strPath, strEntry);
1212 break;
1213 }
1214
1215 case RTFS_TYPE_FILE:
1216 {
1217 rc = AddEntryFromHost(strEntry, &Entry.Info);
1218 break;
1219 }
1220
1221 case RTFS_TYPE_SYMLINK:
1222 {
[73037]1223 if (mSourceSpec.Type.Dir.fFollowSymlinks)
[71976]1224 {
1225 Utf8Str strEntryAbs = strPathAbs + Utf8Str(Entry.szName);
1226
1227 char szPathReal[RTPATH_MAX];
1228 rc = RTPathReal(strEntryAbs.c_str(), szPathReal, sizeof(szPathReal));
1229 if (RT_SUCCESS(rc))
1230 {
1231 rc = RTPathQueryInfo(szPathReal, &objInfo, RTFSOBJATTRADD_NOTHING);
1232 if (RT_SUCCESS(rc))
1233 {
1234 LogFlowFunc(("Symlink '%s' -> '%s'\n", strEntryAbs.c_str(), szPathReal));
1235
1236 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1237 {
1238 LogFlowFunc(("Symlink to directory\n"));
1239 rc = AddDirFromHost(strPath, strEntry);
1240 }
1241 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1242 {
1243 LogFlowFunc(("Symlink to file\n"));
1244 rc = AddEntryFromHost(strEntry, &objInfo);
1245 }
1246 else
1247 rc = VERR_NOT_SUPPORTED;
1248 }
1249 else
1250 LogFlowFunc(("Unable to query symlink info for '%s', rc=%Rrc\n", szPathReal, rc));
1251 }
1252 else
1253 {
1254 LogFlowFunc(("Unable to resolve symlink for '%s', rc=%Rrc\n", strPathAbs.c_str(), rc));
1255 if (rc == VERR_FILE_NOT_FOUND) /* Broken symlink, skip. */
1256 rc = VINF_SUCCESS;
1257 }
1258 }
1259 break;
1260 }
1261
1262 default:
1263 break;
1264 }
1265
1266 } while (RT_SUCCESS(rc));
1267
1268 RTDirClose(hDir);
1269 }
1270 }
1271 }
1272 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1273 {
1274 rc = VERR_IS_A_FILE;
1275 }
1276 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1277 {
1278 rc = VERR_IS_A_SYMLINK;
1279 }
1280 else
1281 rc = VERR_NOT_SUPPORTED;
1282 }
1283 else
1284 LogFlowFunc(("Unable to query '%s', rc=%Rrc\n", strPathAbs.c_str(), rc));
1285
1286 LogFlowFuncLeaveRC(rc);
1287 return rc;
1288}
1289
1290GuestSessionTaskOpen::GuestSessionTaskOpen(GuestSession *pSession, uint32_t uFlags, uint32_t uTimeoutMS)
1291 : GuestSessionTask(pSession)
1292 , mFlags(uFlags)
1293 , mTimeoutMS(uTimeoutMS)
1294{
[71213]1295 m_strTaskName = "gctlSesOpen";
1296}
[42573]1297
[71976]1298GuestSessionTaskOpen::~GuestSessionTaskOpen(void)
[71213]1299{
[43162]1300
[71213]1301}
[42573]1302
[71976]1303int GuestSessionTaskOpen::Run(void)
[71213]1304{
1305 LogFlowThisFuncEnter();
[42573]1306
[71213]1307 AutoCaller autoCaller(mSession);
1308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
[42573]1309
[71345]1310 int vrc = mSession->i_startSession(NULL /*pvrcGuest*/);
[71213]1311 /* Nothing to do here anymore. */
[43162]1312
[71213]1313 LogFlowFuncLeaveRC(vrc);
1314 return vrc;
1315}
1316
[71976]1317GuestSessionCopyTask::GuestSessionCopyTask(GuestSession *pSession)
1318 : GuestSessionTask(pSession)
[71251]1319{
[71976]1320}
[71817]1321
[71976]1322GuestSessionCopyTask::~GuestSessionCopyTask()
1323{
1324 FsLists::iterator itList = mVecLists.begin();
1325 while (itList != mVecLists.end())
1326 {
1327 FsList *pFsList = (*itList);
1328 pFsList->Destroy();
1329 delete pFsList;
1330 mVecLists.erase(itList);
1331 itList = mVecLists.begin();
1332 }
[71817]1333
[71976]1334 Assert(mVecLists.empty());
[71251]1335}
1336
[71976]1337GuestSessionTaskCopyFrom::GuestSessionTaskCopyFrom(GuestSession *pSession, GuestSessionFsSourceSet vecSrc, const Utf8Str &strDest)
1338 : GuestSessionCopyTask(pSession)
[71251]1339{
[71976]1340 m_strTaskName = "gctlCpyFrm";
1341
1342 mSources = vecSrc;
1343 mDest = strDest;
[71251]1344}
1345
[71976]1346GuestSessionTaskCopyFrom::~GuestSessionTaskCopyFrom(void)
1347{
1348}
1349
1350HRESULT GuestSessionTaskCopyFrom::Init(const Utf8Str &strTaskDesc)
[71251]1351{
[71976]1352 setTaskDesc(strTaskDesc);
[71251]1353
[71976]1354 /* Create the progress object. */
1355 ComObjPtr<Progress> pProgress;
1356 HRESULT hr = pProgress.createObject();
1357 if (FAILED(hr))
1358 return hr;
[71251]1359
[71976]1360 mProgress = pProgress;
[71251]1361
1362 int rc = VINF_SUCCESS;
1363
[71976]1364 ULONG cOperations = 0;
[72045]1365 Utf8Str strErrorInfo;
[71847]1366
[72045]1367 /**
1368 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyFrom::Run
1369 * because the caller expects a ready-for-operation progress object on return.
1370 * The progress object will have a variable operation count, based on the elements to
1371 * be processed.
1372 */
1373
[71976]1374 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1375 while (itSrc != mSources.end())
[71817]1376 {
[71976]1377 Utf8Str strSrc = itSrc->strSource;
1378 Utf8Str strDst = mDest;
1379
[73037]1380 bool fFollowSymlinks;
1381
[72001]1382 if (itSrc->enmType == FsObjType_Directory)
[71827]1383 {
[71976]1384 /* If the source does not end with a slash, copy over the entire directory
1385 * (and not just its contents). */
1386 if ( !strSrc.endsWith("/")
1387 && !strSrc.endsWith("\\"))
1388 {
1389 if ( !strDst.endsWith("/")
1390 && !strDst.endsWith("\\"))
[72045]1391 strDst += "/";
[71976]1392
1393 strDst += Utf8StrFmt("%s", RTPathFilenameEx(strSrc.c_str(), mfPathStyle));
1394 }
[73037]1395
1396 fFollowSymlinks = itSrc->Type.Dir.fFollowSymlinks;
[71827]1397 }
[73037]1398 else
1399 {
[73038]1400 fFollowSymlinks = RT_BOOL(itSrc->Type.File.fCopyFlags & FileCopyFlag_FollowLinks);
[73037]1401 }
[71817]1402
[73037]1403 LogFlowFunc(("strSrc=%s, strDst=%s, fFollowSymlinks=%RTbool\n", strSrc.c_str(), strDst.c_str(), fFollowSymlinks));
[72045]1404
[71976]1405 GuestFsObjData srcObjData; int rcGuest;
[73037]1406 rc = mSession->i_fsQueryInfo(strSrc, fFollowSymlinks, srcObjData, &rcGuest);
[71976]1407 if (RT_FAILURE(rc))
1408 {
[72045]1409 strErrorInfo = Utf8StrFmt(GuestSession::tr("No such source file/directory: %s"), strSrc.c_str());
[71976]1410 break;
1411 }
[71847]1412
[71976]1413 if (srcObjData.mType == FsObjType_Directory)
1414 {
[72001]1415 if (itSrc->enmType != FsObjType_Directory)
[71976]1416 {
[72045]1417 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a file: %s"), strSrc.c_str());
[71976]1418 rc = VERR_NOT_A_FILE;
1419 break;
1420 }
1421 }
1422 else
1423 {
[72001]1424 if (itSrc->enmType != FsObjType_File)
[71976]1425 {
[72045]1426 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a directory: %s"), strSrc.c_str());
[71976]1427 rc = VERR_NOT_A_DIRECTORY;
1428 break;
1429 }
1430 }
[71847]1431
[71976]1432 FsList *pFsList = NULL;
1433 try
1434 {
1435 pFsList = new FsList(*this);
1436 rc = pFsList->Init(strSrc, strDst, *itSrc);
1437 if (RT_SUCCESS(rc))
1438 {
[72001]1439 if (itSrc->enmType == FsObjType_Directory)
[71976]1440 {
1441 rc = pFsList->AddDirFromGuest(strSrc);
1442 }
1443 else
1444 rc = pFsList->AddEntryFromGuest(RTPathFilename(strSrc.c_str()), srcObjData);
1445 }
1446
1447 if (RT_FAILURE(rc))
1448 {
1449 delete pFsList;
[72045]1450 strErrorInfo = Utf8StrFmt(GuestSession::tr("Error adding source '%s' to list: %Rrc"), strSrc.c_str(), rc);
[71976]1451 break;
1452 }
1453
1454 mVecLists.push_back(pFsList);
1455 }
1456 catch (...)
1457 {
1458 rc = VERR_NO_MEMORY;
1459 break;
1460 }
1461
1462 AssertPtr(pFsList);
1463 cOperations += (ULONG)pFsList->mVecEntries.size();
1464
1465 itSrc++;
[71847]1466 }
1467
[72045]1468 if (cOperations) /* Use the first element as description (if available). */
[71251]1469 {
[72045]1470 Assert(mVecLists.size());
1471 Assert(mVecLists[0]->mVecEntries.size());
[71976]1472
[72045]1473 Utf8Str strFirstOp = mDest + mVecLists[0]->mVecEntries[0]->strPath;
[71976]1474 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1475 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */,
1476 Bstr(strFirstOp).raw());
[71251]1477 }
[72045]1478 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
1479 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1480 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
[71976]1481
[72045]1482 if (RT_FAILURE(rc))
1483 {
1484 Assert(strErrorInfo.isNotEmpty());
1485 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
1486 }
1487
1488 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hr, rc));
[71976]1489 return rc;
1490}
1491
1492int GuestSessionTaskCopyFrom::Run(void)
1493{
1494 LogFlowThisFuncEnter();
1495
1496 AutoCaller autoCaller(mSession);
1497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1498
1499 int rc = VINF_SUCCESS;
1500
1501 FsLists::const_iterator itList = mVecLists.begin();
1502 while (itList != mVecLists.end())
[71251]1503 {
[71976]1504 FsList *pList = *itList;
1505 AssertPtr(pList);
1506
1507 const bool fCopyIntoExisting = pList->mSourceSpec.Type.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting;
1508 const uint32_t fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
1509
1510 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1511
1512 /* Create the root directory. */
[72001]1513 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71976]1514 {
1515 rc = RTDirCreate(pList->mDstRootAbs.c_str(), fDirMode, 0 /* fCreate */);
1516 if ( rc == VWRN_ALREADY_EXISTS
1517 && !fCopyIntoExisting)
1518 {
1519 break;
1520 }
1521 }
1522
1523 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1524 while (itEntry != pList->mVecEntries.end())
1525 {
1526 FsEntry *pEntry = *itEntry;
1527 AssertPtr(pEntry);
1528
1529 Utf8Str strSrcAbs = pList->mSrcRootAbs;
1530 Utf8Str strDstAbs = pList->mDstRootAbs;
[72001]1531 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71976]1532 {
1533 strSrcAbs += pEntry->strPath;
1534 strDstAbs += pEntry->strPath;
[72045]1535
1536 if (pList->mSourceSpec.enmPathStyle == PathStyle_DOS)
1537 strDstAbs.findReplace('\\', '/');
[71976]1538 }
1539
1540 switch (pEntry->fMode & RTFS_TYPE_MASK)
1541 {
1542 case RTFS_TYPE_DIRECTORY:
[72045]1543 LogFlowFunc(("Directory '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
[71976]1544 if (!pList->mSourceSpec.fDryRun)
1545 {
1546 rc = RTDirCreate(strDstAbs.c_str(), fDirMode, 0 /* fCreate */);
1547 if (rc == VERR_ALREADY_EXISTS)
1548 {
1549 if (!fCopyIntoExisting)
1550 {
[72045]1551 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1552 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
1553 strDstAbs.c_str()));
[71976]1554 break;
1555 }
1556
1557 rc = VINF_SUCCESS;
1558 }
1559
1560 if (RT_FAILURE(rc))
1561 break;
1562 }
1563 break;
1564
1565 case RTFS_TYPE_FILE:
[72045]1566 LogFlowFunc(("File '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
[71976]1567 if (!pList->mSourceSpec.fDryRun)
1568 rc = fileCopyFromGuest(strSrcAbs, strDstAbs, FileCopyFlag_None);
1569 break;
1570
1571 default:
1572 LogFlowFunc(("Warning: Type %d for '%s' is not supported\n",
1573 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
1574 break;
1575 }
1576
1577 if (RT_FAILURE(rc))
1578 break;
1579
1580 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
1581
1582 ++itEntry;
1583 }
1584
[71251]1585 if (RT_FAILURE(rc))
[71976]1586 break;
[71251]1587
[71976]1588 ++itList;
[71251]1589 }
1590
1591 if (RT_SUCCESS(rc))
1592 rc = setProgressSuccess();
1593
1594 LogFlowFuncLeaveRC(rc);
1595 return rc;
1596}
1597
[71976]1598GuestSessionTaskCopyTo::GuestSessionTaskCopyTo(GuestSession *pSession, GuestSessionFsSourceSet vecSrc, const Utf8Str &strDest)
1599 : GuestSessionCopyTask(pSession)
[71213]1600{
[71976]1601 m_strTaskName = "gctlCpyTo";
[71817]1602
[71976]1603 mSources = vecSrc;
1604 mDest = strDest;
[71213]1605}
1606
[71976]1607GuestSessionTaskCopyTo::~GuestSessionTaskCopyTo(void)
[71213]1608{
1609}
1610
[71976]1611HRESULT GuestSessionTaskCopyTo::Init(const Utf8Str &strTaskDesc)
[71213]1612{
[71976]1613 LogFlowFuncEnter();
[71213]1614
[71976]1615 setTaskDesc(strTaskDesc);
[71213]1616
[71976]1617 /* Create the progress object. */
1618 ComObjPtr<Progress> pProgress;
1619 HRESULT hr = pProgress.createObject();
1620 if (FAILED(hr))
1621 return hr;
[71213]1622
[71976]1623 mProgress = pProgress;
[71213]1624
[71976]1625 int rc = VINF_SUCCESS;
[71213]1626
[71976]1627 ULONG cOperations = 0;
[72045]1628 Utf8Str strErrorInfo;
[71213]1629
[72045]1630 /**
1631 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyTo::Run
1632 * because the caller expects a ready-for-operation progress object on return.
1633 * The progress object will have a variable operation count, based on the elements to
1634 * be processed.
1635 */
1636
[71976]1637 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1638 while (itSrc != mSources.end())
1639 {
1640 Utf8Str strSrc = itSrc->strSource;
1641 Utf8Str strDst = mDest;
[71213]1642
[72001]1643 if (itSrc->enmType == FsObjType_Directory)
[71976]1644 {
1645 /* If the source does not end with a slash, copy over the entire directory
1646 * (and not just its contents). */
1647 if ( !strSrc.endsWith("/")
1648 && !strSrc.endsWith("\\"))
1649 {
1650 if ( !strDst.endsWith("/")
1651 && !strDst.endsWith("\\"))
[72045]1652 strDst += "/";
[71976]1653
1654 strDst += Utf8StrFmt("%s", RTPathFilenameEx(strSrc.c_str(), mfPathStyle));
1655 }
1656 }
1657
[72045]1658 LogFlowFunc(("strSrc=%s, strDst=%s\n", strSrc.c_str(), strDst.c_str()));
1659
[71976]1660 RTFSOBJINFO srcFsObjInfo;
1661 rc = RTPathQueryInfo(strSrc.c_str(), &srcFsObjInfo, RTFSOBJATTRADD_NOTHING);
[71213]1662 if (RT_FAILURE(rc))
[72045]1663 {
1664 strErrorInfo = Utf8StrFmt(GuestSession::tr("No such source file/directory: %s"), strSrc.c_str());
[71976]1665 break;
[72045]1666 }
[71213]1667
[71976]1668 if (RTFS_IS_DIRECTORY(srcFsObjInfo.Attr.fMode))
[42693]1669 {
[72001]1670 if (itSrc->enmType != FsObjType_Directory)
[60622]1671 {
[72045]1672 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a file: %s"), strSrc.c_str());
[71976]1673 rc = VERR_NOT_A_FILE;
[71213]1674 break;
1675 }
[71976]1676 }
1677 else
1678 {
[72001]1679 if (itSrc->enmType == FsObjType_Directory)
[71976]1680 {
[72045]1681 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a directory: %s"), strSrc.c_str());
[71976]1682 rc = VERR_NOT_A_DIRECTORY;
1683 break;
1684 }
1685 }
[71213]1686
[71976]1687 FsList *pFsList = NULL;
1688 try
1689 {
1690 pFsList = new FsList(*this);
1691 rc = pFsList->Init(strSrc, strDst, *itSrc);
1692 if (RT_SUCCESS(rc))
[71213]1693 {
[72001]1694 if (itSrc->enmType == FsObjType_Directory)
[60622]1695 {
[71976]1696 rc = pFsList->AddDirFromHost(strSrc);
1697 }
1698 else
1699 rc = pFsList->AddEntryFromHost(RTPathFilename(strSrc.c_str()), &srcFsObjInfo);
1700 }
[71213]1701
[71976]1702 if (RT_FAILURE(rc))
[72045]1703 {
1704 delete pFsList;
1705 strErrorInfo = Utf8StrFmt(GuestSession::tr("Error adding source '%s' to list: %Rrc"), strSrc.c_str(), rc);
[71976]1706 break;
[72045]1707 }
[71213]1708
[71976]1709 mVecLists.push_back(pFsList);
1710 }
1711 catch (...)
1712 {
1713 rc = VERR_NO_MEMORY;
1714 break;
1715 }
[71213]1716
[71976]1717 AssertPtr(pFsList);
1718 cOperations += (ULONG)pFsList->mVecEntries.size();
[71213]1719
[71976]1720 itSrc++;
1721 }
[71213]1722
[72045]1723 if (cOperations) /* Use the first element as description (if available). */
[71976]1724 {
[72045]1725 Assert(mVecLists.size());
1726 Assert(mVecLists[0]->mVecEntries.size());
[71213]1727
[72045]1728 Utf8Str strFirstOp = mDest + mVecLists[0]->mVecEntries[0]->strPath;
[60622]1729
[71976]1730 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1731 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */,
1732 Bstr(strFirstOp).raw());
[71213]1733 }
[72045]1734 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
1735 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1736 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
[71213]1737
[72045]1738 if (RT_FAILURE(rc))
1739 {
1740 Assert(strErrorInfo.isNotEmpty());
1741 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
1742 }
1743
1744 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hr, rc));
[71976]1745 return hr;
[71213]1746}
1747
[71976]1748int GuestSessionTaskCopyTo::Run(void)
[71213]1749{
1750 LogFlowThisFuncEnter();
1751
1752 AutoCaller autoCaller(mSession);
1753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1754
[71976]1755 int rc = VINF_SUCCESS;
[71213]1756
[71976]1757 FsLists::const_iterator itList = mVecLists.begin();
1758 while (itList != mVecLists.end())
[71817]1759 {
[71976]1760 FsList *pList = *itList;
1761 AssertPtr(pList);
1762
[73040]1763 bool fCopyIntoExisting = false;
1764 bool fFollowSymlinks = false;
1765 uint32_t fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
[71976]1766
1767 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1768
1769 /* Create the root directory. */
[72001]1770 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71827]1771 {
[73037]1772 fCopyIntoExisting = pList->mSourceSpec.Type.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting;
1773 fFollowSymlinks = pList->mSourceSpec.Type.Dir.fFollowSymlinks;
1774
1775 rc = directoryCreate(pList->mDstRootAbs.c_str(), DirectoryCreateFlag_None, fDirMode,
1776 pList->mSourceSpec.Type.Dir.fFollowSymlinks);
[71976]1777 if ( rc == VWRN_ALREADY_EXISTS
1778 && !fCopyIntoExisting)
1779 {
1780 break;
1781 }
[71827]1782 }
[73037]1783 else if (pList->mSourceSpec.enmType == FsObjType_File)
1784 {
[73038]1785 fCopyIntoExisting = !RT_BOOL(pList->mSourceSpec.Type.File.fCopyFlags & FileCopyFlag_NoReplace);
1786 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.Type.File.fCopyFlags & FileCopyFlag_FollowLinks);
[73037]1787 }
1788 else
1789 AssertFailed();
[71817]1790
[71976]1791 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1792 while (itEntry != pList->mVecEntries.end())
1793 {
1794 FsEntry *pEntry = *itEntry;
1795 AssertPtr(pEntry);
[71213]1796
[71976]1797 Utf8Str strSrcAbs = pList->mSrcRootAbs;
1798 Utf8Str strDstAbs = pList->mDstRootAbs;
[72001]1799 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71976]1800 {
1801 strSrcAbs += pEntry->strPath;
1802 strDstAbs += pEntry->strPath;
1803 }
[71213]1804
[71976]1805 switch (pEntry->fMode & RTFS_TYPE_MASK)
1806 {
1807 case RTFS_TYPE_DIRECTORY:
[72045]1808 LogFlowFunc(("Directory '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
[71976]1809 if (!pList->mSourceSpec.fDryRun)
1810 {
1811 rc = directoryCreate(strDstAbs.c_str(), DirectoryCreateFlag_None, fDirMode, fFollowSymlinks);
[72045]1812 if ( rc == VERR_ALREADY_EXISTS
[71976]1813 && !fCopyIntoExisting)
1814 {
[72045]1815 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1816 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
1817 strDstAbs.c_str()));
[71976]1818 break;
1819 }
1820 }
1821 break;
[71213]1822
[71976]1823 case RTFS_TYPE_FILE:
[72045]1824 LogFlowFunc(("File '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
[71976]1825 if (!pList->mSourceSpec.fDryRun)
[73037]1826 rc = fileCopyToGuest(strSrcAbs, strDstAbs, pList->mSourceSpec.Type.File.fCopyFlags);
[71976]1827 break;
[71213]1828
[71976]1829 default:
1830 LogFlowFunc(("Warning: Type %d for '%s' is not supported\n",
1831 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
1832 break;
1833 }
[71817]1834
[71976]1835 if (RT_FAILURE(rc))
1836 break;
[71213]1837
[71976]1838 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
[71213]1839
[71976]1840 ++itEntry;
1841 }
[71213]1842
[71976]1843 if (RT_FAILURE(rc))
1844 break;
[71213]1845
[71976]1846 ++itList;
1847 }
[71213]1848
1849 if (RT_SUCCESS(rc))
[71817]1850 rc = setProgressSuccess();
[71533]1851
[42573]1852 LogFlowFuncLeaveRC(rc);
1853 return rc;
[42566]1854}
1855
[71976]1856GuestSessionTaskUpdateAdditions::GuestSessionTaskUpdateAdditions(GuestSession *pSession,
1857 const Utf8Str &strSource,
1858 const ProcessArguments &aArguments,
1859 uint32_t fFlags)
1860 : GuestSessionTask(pSession)
[42693]1861{
[71817]1862 m_strTaskName = "gctlUpGA";
1863
1864 mSource = strSource;
[46524]1865 mArguments = aArguments;
[71817]1866 mFlags = fFlags;
[42693]1867}
1868
[71976]1869GuestSessionTaskUpdateAdditions::~GuestSessionTaskUpdateAdditions(void)
[42693]1870{
1871
1872}
1873
[71976]1874int GuestSessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
[46524]1875{
1876 int rc = VINF_SUCCESS;
1877
1878 try
1879 {
1880 /* Filter out arguments which already are in the destination to
1881 * not end up having them specified twice. Not the fastest method on the
1882 * planet but does the job. */
1883 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
1884 while (itSource != aArgumentsSource.end())
1885 {
1886 bool fFound = false;
1887 ProcessArguments::iterator itDest = aArgumentsDest.begin();
1888 while (itDest != aArgumentsDest.end())
1889 {
1890 if ((*itDest).equalsIgnoreCase((*itSource)))
1891 {
1892 fFound = true;
1893 break;
1894 }
[56030]1895 ++itDest;
[46524]1896 }
1897
1898 if (!fFound)
1899 aArgumentsDest.push_back((*itSource));
1900
[56030]1901 ++itSource;
[46524]1902 }
1903 }
1904 catch(std::bad_alloc &)
1905 {
1906 return VERR_NO_MEMORY;
1907 }
1908
1909 return rc;
1910}
1911
[72958]1912int GuestSessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, RTVFS hVfsIso,
[71213]1913 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
[71817]1914 bool fOptional)
[42749]1915{
1916 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
[72958]1917 AssertReturn(hVfsIso != NIL_RTVFS, VERR_INVALID_POINTER);
[42749]1918
[72958]1919 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1920 int rc = RTVfsFileOpen(hVfsIso, strFileSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ, &hVfsFile);
[42749]1921 if (RT_SUCCESS(rc))
1922 {
[72960]1923 uint64_t cbSrcSize = 0;
[72958]1924 rc = RTVfsFileGetSize(hVfsFile, &cbSrcSize);
1925 if (RT_SUCCESS(rc))
1926 {
1927 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
1928 strFileSource.c_str(), strFileDest.c_str()));
[42749]1929
[72958]1930 GuestFileOpenInfo dstOpenInfo;
1931 RT_ZERO(dstOpenInfo);
1932 dstOpenInfo.mFileName = strFileDest;
1933 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
1934 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
1935 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71817]1936
[72958]1937 ComObjPtr<GuestFile> dstFile; int rcGuest;
1938 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
1939 if (RT_FAILURE(rc))
[42749]1940 {
[72958]1941 switch (rc)
1942 {
1943 case VERR_GSTCTL_GUEST_ERROR:
1944 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
1945 break;
[58521]1946
[72958]1947 default:
1948 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1949 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
1950 strFileDest.c_str(), rc));
1951 break;
1952 }
[58521]1953 }
[72958]1954 else
1955 {
1956 rc = fileCopyToGuestInner(hVfsFile, dstFile, FileCopyFlag_None, 0 /*cbOffset*/, cbSrcSize);
[43162]1957
[72958]1958 int rc2 = dstFile->i_closeFile(&rcGuest);
1959 AssertRC(rc2);
1960 }
[42749]1961 }
1962
[72958]1963 RTVfsFileRelease(hVfsFile);
[71817]1964 if (RT_FAILURE(rc))
1965 return rc;
[42749]1966 }
[72958]1967 else
1968 {
1969 if (fOptional)
1970 return VINF_SUCCESS;
[42749]1971
[72958]1972 return rc;
1973 }
1974
[42749]1975 return rc;
1976}
1977
[71976]1978int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
[42749]1979{
1980 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1981
[43162]1982 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
[42749]1983
[71219]1984 GuestProcessTool procTool;
1985 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[71272]1986 int vrc = procTool.init(pSession, procInfo, false /* Async */, &rcGuest);
[43162]1987 if (RT_SUCCESS(vrc))
[42749]1988 {
[71219]1989 if (RT_SUCCESS(rcGuest))
[71272]1990 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &rcGuest);
[43162]1991 if (RT_SUCCESS(vrc))
[73036]1992 vrc = procTool.getTerminationStatus();
[43162]1993 }
[42749]1994
[43162]1995 if (RT_FAILURE(vrc))
1996 {
1997 switch (vrc)
[42749]1998 {
[73036]1999 case VERR_GSTCTL_PROCESS_EXIT_CODE:
[42749]2000 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[60622]2001 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
[71272]2002 procInfo.mExecutable.c_str(), procTool.getRc()));
[43162]2003 break;
2004
[45078]2005 case VERR_GSTCTL_GUEST_ERROR:
[71219]2006 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
[43162]2007 break;
2008
[43299]2009 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
2010 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[51441]2011 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
[55535]2012 procInfo.mExecutable.c_str()));
[43299]2013 break;
2014
[43162]2015 default:
2016 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[51441]2017 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
[55535]2018 procInfo.mExecutable.c_str(), vrc));
[43162]2019 break;
[42749]2020 }
2021 }
2022
[43162]2023 return vrc;
[42749]2024}
2025
[71976]2026int GuestSessionTaskUpdateAdditions::Run(void)
[42693]2027{
2028 LogFlowThisFuncEnter();
2029
2030 ComObjPtr<GuestSession> pSession = mSession;
2031 Assert(!pSession.isNull());
2032
2033 AutoCaller autoCaller(pSession);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 int rc = setProgress(10);
2037 if (RT_FAILURE(rc))
2038 return rc;
2039
2040 HRESULT hr = S_OK;
2041
2042 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
2043
[50727]2044 ComObjPtr<Guest> pGuest(mSession->i_getParent());
[43030]2045#if 0
[42693]2046 /*
[43001]2047 * Wait for the guest being ready within 30 seconds.
[42693]2048 */
[43001]2049 AdditionsRunLevelType_T addsRunLevel;
2050 uint64_t tsStart = RTTimeSystemMilliTS();
2051 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
2052 && ( addsRunLevel != AdditionsRunLevelType_Userland
2053 && addsRunLevel != AdditionsRunLevelType_Desktop))
2054 {
2055 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
2056 {
2057 rc = VERR_TIMEOUT;
2058 break;
2059 }
[42693]2060
[43001]2061 RTThreadSleep(100); /* Wait a bit. */
2062 }
2063
2064 if (FAILED(hr)) rc = VERR_TIMEOUT;
2065 if (rc == VERR_TIMEOUT)
2066 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2067 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
[43030]2068#else
2069 /*
2070 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
2071 * can continue.
2072 */
2073 AdditionsRunLevelType_T addsRunLevel;
[43034]2074 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
[43030]2075 || ( addsRunLevel != AdditionsRunLevelType_Userland
2076 && addsRunLevel != AdditionsRunLevelType_Desktop))
2077 {
[43034]2078 if (addsRunLevel == AdditionsRunLevelType_System)
2079 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
[51441]2080 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
[43034]2081 else
2082 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
[51441]2083 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
[43030]2084 rc = VERR_NOT_SUPPORTED;
2085 }
2086#endif
[43001]2087
2088 if (RT_SUCCESS(rc))
[42693]2089 {
[43001]2090 /*
2091 * Determine if we are able to update automatically. This only works
2092 * if there are recent Guest Additions installed already.
2093 */
2094 Utf8Str strAddsVer;
2095 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2096 if ( RT_SUCCESS(rc)
2097 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
2098 {
2099 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2100 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
2101 strAddsVer.c_str()));
2102 rc = VERR_NOT_SUPPORTED;
2103 }
[43030]2104 }
[42923]2105
[43053]2106 Utf8Str strOSVer;
[43493]2107 eOSType osType = eOSType_Unknown;
[43030]2108 if (RT_SUCCESS(rc))
2109 {
[43001]2110 /*
2111 * Determine guest OS type and the required installer image.
2112 */
2113 Utf8Str strOSType;
2114 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
2115 if (RT_SUCCESS(rc))
2116 {
2117 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
2118 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
2119 {
[43002]2120 osType = eOSType_Windows;
[43053]2121
2122 /*
2123 * Determine guest OS version.
2124 */
2125 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
2126 if (RT_FAILURE(rc))
2127 {
2128 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
[51092]2129 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
[43053]2130 rc = VERR_NOT_SUPPORTED;
2131 }
2132
2133 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
2134 * can't do automated updates here. */
[43060]2135 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
[43053]2136 if ( RT_SUCCESS(rc)
[45572]2137 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
[43053]2138 {
[45572]2139 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
2140 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
[43060]2141 {
[45572]2142 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
2143 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
2144 * flag is set this update routine ends successfully as soon as the installer was started
2145 * (and the user has to deal with it in the guest). */
2146 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2147 {
2148 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
[51441]2149 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
[45572]2150 rc = VERR_NOT_SUPPORTED;
2151 }
[43060]2152 }
[43053]2153 }
[45572]2154 else
2155 {
2156 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
[51441]2157 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
[45572]2158 strOSType.c_str(), strOSVer.c_str()));
2159 rc = VERR_NOT_SUPPORTED;
2160 }
[43001]2161 }
2162 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
2163 {
[43002]2164 osType = eOSType_Solaris;
[43001]2165 }
2166 else /* Everything else hopefully means Linux :-). */
[43002]2167 osType = eOSType_Linux;
[43001]2168
[42923]2169#if 1 /* Only Windows is supported (and tested) at the moment. */
[45572]2170 if ( RT_SUCCESS(rc)
2171 && osType != eOSType_Windows)
[43001]2172 {
2173 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2174 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
2175 strOSType.c_str()));
2176 rc = VERR_NOT_SUPPORTED;
2177 }
2178#endif
2179 }
[42693]2180 }
2181
2182 if (RT_SUCCESS(rc))
2183 {
2184 /*
[42923]2185 * Try to open the .ISO file to extract all needed files.
[42693]2186 */
[72958]2187 RTVFSFILE hVfsFileIso;
2188 rc = RTVfsFileOpenNormal(mSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsFileIso);
2189 if (RT_SUCCESS(rc))
[42693]2190 {
[72958]2191 RTVFS hVfsIso;
2192 rc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, NULL);
2193 if (RT_FAILURE(rc))
2194 {
2195 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2196 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
2197 mSource.c_str(), rc));
2198 }
2199 else
2200 {
2201 /* Set default installation directories. */
2202 Utf8Str strUpdateDir = "/tmp/";
2203 if (osType == eOSType_Windows)
2204 strUpdateDir = "C:\\Temp\\";
[42923]2205
[72958]2206 rc = setProgress(5);
[42693]2207
[72958]2208 /* Try looking up the Guest Additions installation directory. */
2209 if (RT_SUCCESS(rc))
[42923]2210 {
[72958]2211 /* Try getting the installed Guest Additions version to know whether we
2212 * can install our temporary Guest Addition data into the original installation
2213 * directory.
2214 *
2215 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
2216 * a different location then.
2217 */
2218 bool fUseInstallDir = false;
[43001]2219
[72958]2220 Utf8Str strAddsVer;
2221 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2222 if ( RT_SUCCESS(rc)
2223 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
[42693]2224 {
[72958]2225 fUseInstallDir = true;
2226 }
2227
2228 if (fUseInstallDir)
2229 {
2230 if (RT_SUCCESS(rc))
2231 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
2232 if (RT_SUCCESS(rc))
[43001]2233 {
[72958]2234 if (osType == eOSType_Windows)
2235 {
2236 strUpdateDir.findReplace('/', '\\');
2237 strUpdateDir.append("\\Update\\");
2238 }
2239 else
2240 strUpdateDir.append("/update/");
[43001]2241 }
[42693]2242 }
2243 }
2244
[72958]2245 if (RT_SUCCESS(rc))
2246 LogRel(("Guest Additions update directory is: %s\n",
2247 strUpdateDir.c_str()));
[42923]2248
[72958]2249 /* Create the installation directory. */
2250 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2251 rc = pSession->i_directoryCreate(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &rcGuest);
2252 if (RT_FAILURE(rc))
[43162]2253 {
[72958]2254 switch (rc)
2255 {
2256 case VERR_GSTCTL_GUEST_ERROR:
2257 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
2258 break;
[43162]2259
[72958]2260 default:
2261 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2262 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
2263 strUpdateDir.c_str(), rc));
2264 break;
2265 }
[43162]2266 }
[72958]2267 if (RT_SUCCESS(rc))
2268 rc = setProgress(10);
[42937]2269
[72958]2270 if (RT_SUCCESS(rc))
[42923]2271 {
[72958]2272 /* Prepare the file(s) we want to copy over to the guest and
2273 * (maybe) want to run. */
2274 switch (osType)
[42937]2275 {
[72958]2276 case eOSType_Windows:
[43001]2277 {
[72958]2278 /* Do we need to install our certificates? We do this for W2K and up. */
2279 bool fInstallCert = false;
[43001]2280
[72958]2281 /* Only Windows 2000 and up need certificates to be installed. */
2282 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
[64939]2283 {
[72958]2284 fInstallCert = true;
2285 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
2286 }
2287 else
2288 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
2289
2290 if (fInstallCert)
[64939]2291 {
[72958]2292 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
2293 {
2294 { "vbox.cer", "CERT/VBOX.CER" },
2295 { "vbox-sha1.cer", "CERT/VBOX_SHA1.CER" },
2296 { "vbox-sha256.cer", "CERT/VBOX_SHA256.CER" },
2297 { "vbox-sha256-r3.cer", "CERT/VBOX_SHA256_R3.CER" },
2298 { "oracle-vbox.cer", "CERT/ORACLE_VBOX.CER" },
2299 };
2300 uint32_t fCopyCertUtil = ISOFILE_FLAG_COPY_FROM_ISO;
2301 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
2302 {
2303 /* Skip if not present on the ISO. */
2304 RTFSOBJINFO ObjInfo;
2305 rc = RTVfsQueryPathInfo(hVfsIso, s_aCertFiles[i].pszIso, &ObjInfo, RTFSOBJATTRADD_NOTHING,
2306 RTPATH_F_ON_LINK);
2307 if (RT_FAILURE(rc))
2308 continue;
[64939]2309
[72958]2310 /* Copy the certificate certificate. */
2311 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
2312 mFiles.push_back(ISOFile(s_aCertFiles[i].pszIso,
2313 strDstCert,
2314 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_OPTIONAL));
[64939]2315
[72958]2316 /* Out certificate installation utility. */
2317 /* First pass: Copy over the file (first time only) + execute it to remove any
2318 * existing VBox certificates. */
2319 GuestProcessStartupInfo siCertUtilRem;
2320 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
2321 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
2322 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2323 siCertUtilRem.mArguments.push_back(strDstCert);
2324 siCertUtilRem.mArguments.push_back(strDstCert);
2325 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2326 strUpdateDir + "VBoxCertUtil.exe",
2327 fCopyCertUtil | ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2328 siCertUtilRem));
2329 fCopyCertUtil = 0;
2330 /* Second pass: Only execute (but don't copy) again, this time installng the
2331 * recent certificates just copied over. */
2332 GuestProcessStartupInfo siCertUtilAdd;
2333 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
2334 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
2335 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2336 siCertUtilAdd.mArguments.push_back(strDstCert);
2337 siCertUtilAdd.mArguments.push_back(strDstCert);
2338 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2339 strUpdateDir + "VBoxCertUtil.exe",
2340 ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2341 siCertUtilAdd));
2342 }
[64939]2343 }
[72958]2344 /* The installers in different flavors, as we don't know (and can't assume)
2345 * the guest's bitness. */
2346 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_X86.EXE",
2347 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
2348 ISOFILE_FLAG_COPY_FROM_ISO));
2349 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
2350 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
2351 ISOFILE_FLAG_COPY_FROM_ISO));
2352 /* The stub loader which decides which flavor to run. */
2353 GuestProcessStartupInfo siInstaller;
2354 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
2355 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
2356 * setup can take quite a while, so be on the safe side. */
2357 siInstaller.mTimeoutMS = 5 * 60 * 1000;
2358 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
2359 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
2360 /* Don't quit VBoxService during upgrade because it still is used for this
2361 * piece of code we're in right now (that is, here!) ... */
2362 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
2363 /* Tell the installer to report its current installation status
2364 * using a running VBoxTray instance via balloon messages in the
2365 * Windows taskbar. */
2366 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
2367 /* Add optional installer command line arguments from the API to the
2368 * installer's startup info. */
2369 rc = addProcessArguments(siInstaller.mArguments, mArguments);
2370 AssertRC(rc);
2371 /* If the caller does not want to wait for out guest update process to end,
2372 * complete the progress object now so that the caller can do other work. */
2373 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
2374 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
2375 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS.EXE",
2376 strUpdateDir + "VBoxWindowsAdditions.exe",
2377 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_EXECUTE, siInstaller));
2378 break;
[43001]2379 }
[72958]2380 case eOSType_Linux:
2381 /** @todo Add Linux support. */
2382 break;
2383 case eOSType_Solaris:
2384 /** @todo Add Solaris support. */
2385 break;
2386 default:
2387 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
2388 break;
[42937]2389 }
[42923]2390 }
[42693]2391
[72958]2392 if (RT_SUCCESS(rc))
2393 {
2394 /* We want to spend 40% total for all copying operations. So roughly
2395 * calculate the specific percentage step of each copied file. */
2396 uint8_t uOffset = 20; /* Start at 20%. */
2397 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
[42937]2398
[72958]2399 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
[42937]2400
[72958]2401 std::vector<ISOFile>::const_iterator itFiles = mFiles.begin();
2402 while (itFiles != mFiles.end())
[42923]2403 {
[72958]2404 if (itFiles->fFlags & ISOFILE_FLAG_COPY_FROM_ISO)
[43001]2405 {
[72958]2406 bool fOptional = false;
2407 if (itFiles->fFlags & ISOFILE_FLAG_OPTIONAL)
2408 fOptional = true;
2409 rc = copyFileToGuest(pSession, hVfsIso, itFiles->strSource, itFiles->strDest, fOptional);
2410 if (RT_FAILURE(rc))
2411 {
2412 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2413 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
2414 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
2415 break;
2416 }
[43001]2417 }
[42937]2418
[72958]2419 rc = setProgress(uOffset);
2420 if (RT_FAILURE(rc))
2421 break;
2422 uOffset += uStep;
[42937]2423
[72958]2424 ++itFiles;
2425 }
[42923]2426 }
[42693]2427
[72958]2428 /* Done copying, close .ISO file. */
2429 RTVfsRelease(hVfsIso);
[43001]2430
[72958]2431 if (RT_SUCCESS(rc))
2432 {
2433 /* We want to spend 35% total for all copying operations. So roughly
2434 * calculate the specific percentage step of each copied file. */
2435 uint8_t uOffset = 60; /* Start at 60%. */
2436 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
[42937]2437
[72958]2438 LogRel(("Executing Guest Additions update files ...\n"));
[42937]2439
[72958]2440 std::vector<ISOFile>::iterator itFiles = mFiles.begin();
2441 while (itFiles != mFiles.end())
[42937]2442 {
[72958]2443 if (itFiles->fFlags & ISOFILE_FLAG_EXECUTE)
2444 {
2445 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
2446 if (RT_FAILURE(rc))
2447 break;
2448 }
2449
2450 rc = setProgress(uOffset);
[42937]2451 if (RT_FAILURE(rc))
2452 break;
[72958]2453 uOffset += uStep;
2454
2455 ++itFiles;
[42937]2456 }
[72958]2457 }
[42937]2458
[72958]2459 if (RT_SUCCESS(rc))
2460 {
2461 LogRel(("Automatic update of Guest Additions succeeded\n"));
2462 rc = setProgressSuccess();
[42923]2463 }
2464 }
[42716]2465
[72958]2466 RTVfsFileRelease(hVfsFileIso);
[43034]2467 }
2468 }
2469
2470 if (RT_FAILURE(rc))
2471 {
2472 if (rc == VERR_CANCELLED)
2473 {
2474 LogRel(("Automatic update of Guest Additions was canceled\n"));
2475
2476 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2477 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
2478 }
2479 else
2480 {
2481 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
2482 if (!mProgress.isNull()) /* Progress object is optional. */
[42937]2483 {
[44863]2484 com::ProgressErrorInfo errorInfo(mProgress);
2485 if ( errorInfo.isFullAvailable()
2486 || errorInfo.isBasicAvailable())
[42937]2487 {
[44863]2488 strError = errorInfo.getText();
[42937]2489 }
[43034]2490 }
[42693]2491
[44863]2492 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
2493 strError.c_str(), hr));
[42693]2494 }
[43034]2495
2496 LogRel(("Please install Guest Additions manually\n"));
[42693]2497 }
2498
[44863]2499 /** @todo Clean up copied / left over installation files. */
2500
[42693]2501 LogFlowFuncLeaveRC(rc);
2502 return rc;
2503}
2504
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use