VirtualBox

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

Last change on this file since 103244 was 103244, checked in by vboxsync, 3 months ago

Guest Control/GuestSessionImplTasks: Fixed implicit fallthrough for copying files to guest and the destination file already exists (found by Parfait). bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 133.8 KB
RevLine 
[42084]1/* $Id: GuestSessionImplTasks.cpp 103244 2024-02-07 11:52:35Z vboxsync $ */
2/** @file
[44869]3 * VirtualBox Main - Guest session tasks.
[42084]4 */
5
6/*
[98103]7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[42084]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[42084]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[71213]32#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
[67914]33#include "LoggingNew.h"
34
[42105]35#include "GuestImpl.h"
[55644]36#ifndef VBOX_WITH_GUEST_CONTROL
37# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
38#endif
[42084]39#include "GuestSessionImpl.h"
[71648]40#include "GuestSessionImplTasks.h"
[42478]41#include "GuestCtrlImplPrivate.h"
[42084]42
43#include "Global.h"
44#include "AutoCaller.h"
[42923]45#include "ConsoleImpl.h"
[42566]46#include "ProgressImpl.h"
[42084]47
[42567]48#include <memory> /* For auto_ptr. */
49
[42171]50#include <iprt/env.h>
[42573]51#include <iprt/file.h> /* For CopyTo/From. */
[71220]52#include <iprt/dir.h>
[60629]53#include <iprt/path.h>
[72958]54#include <iprt/fsvfs.h>
[42171]55
[42084]56
[57358]57/*********************************************************************************************************************************
58* Defines *
59*********************************************************************************************************************************/
[42923]60
61/**
[71649]62 * (Guest Additions) ISO file flags.
63 * Needed for handling Guest Additions updates.
[42808]64 */
[71649]65#define ISOFILE_FLAG_NONE 0
[43001]66/** Copy over the file from host to the
67 * guest. */
[71649]68#define ISOFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
[43001]69/** Execute file on the guest after it has
[97137]70 * been successfully transferred. */
[71649]71#define ISOFILE_FLAG_EXECUTE RT_BIT(7)
[42923]72/** File is optional, does not have to be
73 * existent on the .ISO. */
[71649]74#define ISOFILE_FLAG_OPTIONAL RT_BIT(8)
[42749]75
[42923]76
[42566]77// session task classes
78/////////////////////////////////////////////////////////////////////////////
79
[63186]80GuestSessionTask::GuestSessionTask(GuestSession *pSession)
81 : ThreadTask("GenericGuestSessionTask")
[42566]82{
83 mSession = pSession;
[71847]84
[97306]85 switch (mSession->i_getGuestPathStyle())
[71976]86 {
87 case PathStyle_DOS:
[97344]88 mstrGuestPathStyle = "\\";
[71976]89 break;
90
91 default:
[97344]92 mstrGuestPathStyle = "/";
[71976]93 break;
94 }
[42566]95}
96
97GuestSessionTask::~GuestSessionTask(void)
98{
99}
100
[92897]101/**
102 * Creates (and initializes / sets) the progress objects of a guest session task.
103 *
104 * @returns VBox status code.
105 * @param cOperations Number of operation the task wants to perform.
106 */
[71976]107int GuestSessionTask::createAndSetProgressObject(ULONG cOperations /* = 1 */)
[58521]108{
[71976]109 LogFlowThisFunc(("cOperations=%ld\n", cOperations));
[58521]110
[71976]111 /* Create the progress object. */
[58521]112 ComObjPtr<Progress> pProgress;
[98272]113 HRESULT hrc = pProgress.createObject();
114 if (FAILED(hrc))
[58521]115 return VERR_COM_UNEXPECTED;
116
[98272]117 hrc = pProgress->init(static_cast<IGuestSession*>(mSession),
118 Bstr(mDesc).raw(),
119 TRUE /* aCancelable */, cOperations, Bstr(mDesc).raw());
120 if (FAILED(hrc))
[58521]121 return VERR_COM_UNEXPECTED;
122
123 mProgress = pProgress;
124
125 LogFlowFuncLeave();
[71976]126 return VINF_SUCCESS;
[58521]127}
128
[97137]129#if 0 /* unused */
[78764]130/** @note The task object is owned by the thread after this returns, regardless of the result. */
[63186]131int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
132{
133 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
134
135 mDesc = strDesc;
136 mProgress = pProgress;
137 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
138
139 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
140 return Global::vboxStatusCodeToCOM(hrc);
141}
[78764]142#endif
[63186]143
[92897]144/**
145 * Gets a guest property from the VM.
146 *
147 * @returns VBox status code.
148 * @param pGuest Guest object of VM to get guest property from.
149 * @param strPath Guest property to path to get.
150 * @param strValue Where to store the guest property value on success.
151 */
[42923]152int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
153 const Utf8Str &strPath, Utf8Str &strValue)
154{
[52082]155 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
[51612]156 const ComPtr<IMachine> pMachine = pConsole->i_machine();
[42923]157
158 Assert(!pMachine.isNull());
159 Bstr strTemp, strFlags;
160 LONG64 i64Timestamp;
[98272]161 HRESULT hrc = pMachine->GetGuestProperty(Bstr(strPath).raw(), strTemp.asOutParam(), &i64Timestamp, strFlags.asOutParam());
162 if (SUCCEEDED(hrc))
[42923]163 {
164 strValue = strTemp;
165 return VINF_SUCCESS;
166 }
167 return VERR_NOT_FOUND;
168}
169
[92897]170/**
171 * Sets the percentage of a guest session task progress.
172 *
173 * @returns VBox status code.
174 * @param uPercent Percentage (0-100) to set.
175 */
[42634]176int GuestSessionTask::setProgress(ULONG uPercent)
[42566]177{
[42634]178 if (mProgress.isNull()) /* Progress is optional. */
179 return VINF_SUCCESS;
180
[42566]181 BOOL fCanceled;
[42634]182 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
183 && fCanceled)
[42566]184 return VERR_CANCELLED;
[42634]185 BOOL fCompleted;
186 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
[42937]187 && fCompleted)
188 {
189 AssertMsgFailed(("Setting value of an already completed progress\n"));
[42634]190 return VINF_SUCCESS;
[42937]191 }
[98272]192 HRESULT hrc = mProgress->SetCurrentOperationProgress(uPercent);
193 if (FAILED(hrc))
[42636]194 return VERR_COM_UNEXPECTED;
[42566]195
196 return VINF_SUCCESS;
197}
198
[92897]199/**
200 * Sets the task's progress object to succeeded.
201 *
202 * @returns VBox status code.
203 */
[42566]204int GuestSessionTask::setProgressSuccess(void)
205{
[42634]206 if (mProgress.isNull()) /* Progress is optional. */
207 return VINF_SUCCESS;
208
[42566]209 BOOL fCompleted;
[71849]210 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
[42566]211 && !fCompleted)
212 {
[77038]213#ifdef VBOX_STRICT
214 ULONG uCurOp; mProgress->COMGETTER(Operation(&uCurOp));
215 ULONG cOps; mProgress->COMGETTER(OperationCount(&cOps));
216 AssertMsg(uCurOp + 1 /* Zero-based */ == cOps, ("Not all operations done yet (%u/%u)\n", uCurOp + 1, cOps));
217#endif
[98272]218 HRESULT hrc = mProgress->i_notifyComplete(S_OK);
219 if (FAILED(hrc))
220 return VERR_COM_UNEXPECTED; /** @todo Find a better vrc. */
[42566]221 }
222
223 return VINF_SUCCESS;
224}
225
[84648]226/**
227 * Sets the task's progress object to an error using a string message.
228 *
[98272]229 * @returns Returns \a hrc for convenience.
230 * @param hrc Progress operation result to set.
[84648]231 * @param strMsg Message to set.
232 */
[98272]233HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hrc, const Utf8Str &strMsg)
[42566]234{
[98272]235 LogFlowFunc(("hrc=%Rhrc, strMsg=%s\n", hrc, strMsg.c_str()));
[44863]236
[42634]237 if (mProgress.isNull()) /* Progress is optional. */
[98272]238 return hrc; /* Return original status. */
[42634]239
[42566]240 BOOL fCanceled;
241 BOOL fCompleted;
242 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
243 && !fCanceled
244 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
245 && !fCompleted)
246 {
[98272]247 HRESULT hrc2 = mProgress->i_notifyComplete(hrc,
248 COM_IIDOF(IGuestSession),
249 GuestSession::getStaticComponentName(),
250 /* Make sure to hand-in the message via format string to avoid problems
251 * with (file) paths which e.g. contain "%s" and friends. Can happen with
252 * randomly generated Validation Kit stuff. */
253 "%s", strMsg.c_str());
254 if (FAILED(hrc2))
255 return hrc2;
[42566]256 }
[98272]257 return hrc; /* Return original status. */
[42566]258}
259
[84648]260/**
261 * Sets the task's progress object to an error using a string message and a guest error info object.
262 *
[98272]263 * @returns Returns \a hrc for convenience.
264 * @param hrc Progress operation result to set.
[84648]265 * @param strMsg Message to set.
266 * @param guestErrorInfo Guest error info to use.
267 */
[98272]268HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hrc, const Utf8Str &strMsg, const GuestErrorInfo &guestErrorInfo)
[78666]269{
[98272]270 return setProgressErrorMsg(hrc, strMsg + Utf8Str(": ") + GuestBase::getErrorAsString(guestErrorInfo));
[78666]271}
272
[71213]273/**
274 * Creates a directory on the guest.
275 *
[83336]276 * @return VBox status code.
277 * VINF_ALREADY_EXISTS if directory on the guest already exists (\a fCanExist is \c true).
278 * VWRN_ALREADY_EXISTS if directory on the guest already exists but must not exist (\a fCanExist is \c false).
[71213]279 * @param strPath Absolute path to directory on the guest (guest style path) to create.
[97395]280 * @param fMode Directory mode to use for creation.
[71976]281 * @param enmDirectoryCreateFlags Directory creation flags.
[71213]282 * @param fFollowSymlinks Whether to follow symlinks on the guest or not.
[77495]283 * @param fCanExist Whether the directory to create is allowed to exist already.
[71213]284 */
[77495]285int GuestSessionTask::directoryCreateOnGuest(const com::Utf8Str &strPath,
[97395]286 uint32_t fMode, DirectoryCreateFlag_T enmDirectoryCreateFlags,
[77495]287 bool fFollowSymlinks, bool fCanExist)
[44863]288{
[77531]289 LogFlowFunc(("strPath=%s, enmDirectoryCreateFlags=0x%x, fMode=%RU32, fFollowSymlinks=%RTbool, fCanExist=%RTbool\n",
290 strPath.c_str(), enmDirectoryCreateFlags, fMode, fFollowSymlinks, fCanExist));
[44863]291
[79189]292 GuestFsObjData objData;
[94935]293 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
294 int vrc = mSession->i_directoryQueryInfo(strPath, fFollowSymlinks, objData, &vrcGuest);
295 if (RT_SUCCESS(vrc))
[42693]296 {
[77495]297 if (!fCanExist)
298 {
299 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]300 Utf8StrFmt(tr("Guest directory \"%s\" already exists"), strPath.c_str()));
[94935]301 vrc = VERR_ALREADY_EXISTS;
[77495]302 }
[83336]303 else
[94935]304 vrc = VWRN_ALREADY_EXISTS;
[42693]305 }
[71213]306 else
[42573]307 {
[94935]308 switch (vrc)
[42573]309 {
[71213]310 case VERR_GSTCTL_GUEST_ERROR:
[42573]311 {
[94935]312 switch (vrcGuest)
[42573]313 {
[71213]314 case VERR_FILE_NOT_FOUND:
[83336]315 RT_FALL_THROUGH();
[71213]316 case VERR_PATH_NOT_FOUND:
[94935]317 vrc = mSession->i_directoryCreate(strPath.c_str(), fMode, enmDirectoryCreateFlags, &vrcGuest);
[71213]318 break;
319 default:
320 break;
[42573]321 }
[83336]322
[94935]323 if (RT_FAILURE(vrc))
[83336]324 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]325 Utf8StrFmt(tr("Guest error creating directory \"%s\" on the guest: %Rrc"),
[94935]326 strPath.c_str(), vrcGuest));
[71213]327 break;
[42693]328 }
[71213]329
330 default:
[83336]331 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]332 Utf8StrFmt(tr("Host error creating directory \"%s\" on the guest: %Rrc"),
[94935]333 strPath.c_str(), vrc));
[71213]334 break;
[42693]335 }
336 }
[42573]337
[94935]338 LogFlowFuncLeaveRC(vrc);
339 return vrc;
[71213]340}
341
342/**
[77495]343 * Creates a directory on the host.
344 *
345 * @return VBox status code. VERR_ALREADY_EXISTS if directory on the guest already exists.
346 * @param strPath Absolute path to directory on the host (host style path) to create.
[97395]347 * @param fMode Directory mode to use for creation.
[77495]348 * @param fCreate Directory creation flags.
349 * @param fCanExist Whether the directory to create is allowed to exist already.
350 */
[97395]351int GuestSessionTask::directoryCreateOnHost(const com::Utf8Str &strPath, uint32_t fMode, uint32_t fCreate, bool fCanExist)
[77495]352{
[97395]353 LogFlowFunc(("strPath=%s, fMode=%RU32, fCreate=0x%x, fCanExist=%RTbool\n", strPath.c_str(), fMode, fCreate, fCanExist));
[77495]354
[97655]355 LogRel2(("Guest Control: Creating host directory \"%s\" ...\n", strPath.c_str()));
[97395]356
[94935]357 int vrc = RTDirCreate(strPath.c_str(), fMode, fCreate);
358 if (RT_FAILURE(vrc))
[77495]359 {
[94935]360 if (vrc == VERR_ALREADY_EXISTS)
[77495]361 {
362 if (!fCanExist)
363 {
364 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]365 Utf8StrFmt(tr("Host directory \"%s\" already exists"), strPath.c_str()));
[77495]366 }
367 else
[94935]368 vrc = VINF_SUCCESS;
[77495]369 }
370 else
371 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]372 Utf8StrFmt(tr("Could not create host directory \"%s\": %Rrc"),
[94935]373 strPath.c_str(), vrc));
[77495]374 }
375
[94935]376 LogFlowFuncLeaveRC(vrc);
377 return vrc;
[77495]378}
379
380/**
[71817]381 * Main function for copying a file from guest to the host.
[71251]382 *
383 * @return VBox status code.
[84648]384 * @param strSrcFile Full path of source file on the host to copy.
[71796]385 * @param srcFile Guest file (source) to copy to the host. Must be in opened and ready state already.
[84648]386 * @param strDstFile Full destination path and file name (guest style) to copy file to.
[71796]387 * @param phDstFile Pointer to host file handle (destination) to copy to. Must be in opened and ready state already.
[71817]388 * @param fFileCopyFlags File copy flags.
389 * @param offCopy Offset (in bytes) where to start copying the source file.
[71796]390 * @param cbSize Size (in bytes) to copy from the source file.
391 */
[84648]392int GuestSessionTask::fileCopyFromGuestInner(const Utf8Str &strSrcFile, ComObjPtr<GuestFile> &srcFile,
393 const Utf8Str &strDstFile, PRTFILE phDstFile,
394 FileCopyFlag_T fFileCopyFlags, uint64_t offCopy, uint64_t cbSize)
[71796]395{
[71817]396 RT_NOREF(fFileCopyFlags);
[71796]397
[97656]398 if (!cbSize) /* Nothing to copy, i.e. empty file? Bail out. */
399 return VINF_SUCCESS;
400
401 BOOL fCanceled = FALSE;
[71796]402 uint64_t cbWrittenTotal = 0;
403 uint64_t cbToRead = cbSize;
404
405 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
406
[94935]407 int vrc = VINF_SUCCESS;
[71796]408
[71817]409 if (offCopy)
[71796]410 {
[71817]411 uint64_t offActual;
[94935]412 vrc = srcFile->i_seekAt(offCopy, GUEST_FILE_SEEKTYPE_BEGIN, uTimeoutMs, &offActual);
413 if (RT_FAILURE(vrc))
[71796]414 {
415 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]416 Utf8StrFmt(tr("Seeking to offset %RU64 of guest file \"%s\" failed: %Rrc"),
[94935]417 offCopy, strSrcFile.c_str(), vrc));
418 return vrc;
[71796]419 }
420 }
421
[84864]422 BYTE byBuf[_64K]; /** @todo Can we do better here? */
[71796]423 while (cbToRead)
424 {
425 uint32_t cbRead;
426 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
[94935]427 vrc = srcFile->i_readData(cbChunk, uTimeoutMs, byBuf, sizeof(byBuf), &cbRead);
428 if (RT_FAILURE(vrc))
[71796]429 {
430 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[91718]431 Utf8StrFmt(tr("Reading %RU32 bytes @ %RU64 from guest \"%s\" failed: %Rrc", "", cbChunk),
[94935]432 cbChunk, cbWrittenTotal, strSrcFile.c_str(), vrc));
[71796]433 break;
434 }
435
[94935]436 vrc = RTFileWrite(*phDstFile, byBuf, cbRead, NULL /* No partial writes */);
437 if (RT_FAILURE(vrc))
[71796]438 {
439 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[91718]440 Utf8StrFmt(tr("Writing %RU32 bytes to host file \"%s\" failed: %Rrc", "", cbRead),
[94935]441 cbRead, strDstFile.c_str(), vrc));
[71796]442 break;
443 }
444
[84864]445 AssertBreak(cbToRead >= cbRead);
[71796]446 cbToRead -= cbRead;
447
448 /* Update total bytes written to the guest. */
449 cbWrittenTotal += cbRead;
[84864]450 AssertBreak(cbWrittenTotal <= cbSize);
[71796]451
452 /* Did the user cancel the operation above? */
453 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
454 && fCanceled)
455 break;
456
[97656]457 AssertBreakStmt(cbSize, vrc = VERR_INTERNAL_ERROR);
458 vrc = setProgress(((double)cbWrittenTotal / (double)cbSize) * 100);
[94935]459 if (RT_FAILURE(vrc))
[71796]460 break;
461 }
462
[71849]463 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
464 && fCanceled)
465 return VINF_SUCCESS;
466
[94935]467 if (RT_FAILURE(vrc))
468 return vrc;
[71796]469
470 /*
[97137]471 * Even if we succeeded until here make sure to check whether we really transferred
[71796]472 * everything.
473 */
[97656]474 if (cbWrittenTotal == 0)
[71796]475 {
[97137]476 /* If nothing was transferred but the file size was > 0 then "vbox_cat" wasn't able to write
[71796]477 * to the destination -> access denied. */
478 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]479 Utf8StrFmt(tr("Writing guest file \"%s\" to host file \"%s\" failed: Access denied"),
[84648]480 strSrcFile.c_str(), strDstFile.c_str()));
[94935]481 vrc = VERR_ACCESS_DENIED;
[71796]482 }
483 else if (cbWrittenTotal < cbSize)
484 {
485 /* If we did not copy all let the user know. */
486 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97137]487 Utf8StrFmt(tr("Copying guest file \"%s\" to host file \"%s\" failed (%RU64/%RU64 bytes transferred)"),
[84648]488 strSrcFile.c_str(), strDstFile.c_str(), cbWrittenTotal, cbSize));
[94935]489 vrc = VERR_INTERRUPTED;
[71796]490 }
491
[94935]492 LogFlowFuncLeaveRC(vrc);
493 return vrc;
[71796]494}
495
496/**
[97626]497 * Closes a formerly opened guest file.
498 *
499 * @returns VBox status code.
500 * @param file Guest file to close.
501 *
502 * @note Set a progress error message on error.
503 */
504int GuestSessionTask::fileClose(const ComObjPtr<GuestFile> &file)
505{
506 int vrcGuest;
507 int vrc = file->i_closeFile(&vrcGuest);
508 if (RT_FAILURE(vrc))
509 {
510 Utf8Str strFilename;
511 HRESULT const hrc = file->getFilename(strFilename);
512 AssertComRCReturn(hrc, VERR_OBJECT_DESTROYED);
513 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Error closing guest file \"%s\": %Rrc"),
514 strFilename.c_str(), vrc == VERR_GSTCTL_GUEST_ERROR ? vrcGuest : vrc));
515 if (RT_SUCCESS(vrc))
516 vrc = vrc == VERR_GSTCTL_GUEST_ERROR ? vrcGuest : vrc;
517 }
518
519 return vrc;
520}
521
522/**
[71796]523 * Copies a file from the guest to the host.
524 *
[97448]525 * @return VBox status code.
526 * @retval VWRN_ALREADY_EXISTS if the file already exists and FileCopyFlag_NoReplace is specified,
527 * *or * the file at the destination has the same (or newer) modification time
528 * and FileCopyFlag_Update is specified.
[84649]529 * @param strSrc Full path of source file on the guest to copy.
530 * @param strDst Full destination path and file name (host style) to copy file to.
[71817]531 * @param fFileCopyFlags File copy flags.
[71251]532 */
[84649]533int GuestSessionTask::fileCopyFromGuest(const Utf8Str &strSrc, const Utf8Str &strDst, FileCopyFlag_T fFileCopyFlags)
[71251]534{
[84864]535 LogFlowThisFunc(("strSource=%s, strDest=%s, enmFileCopyFlags=%#x\n", strSrc.c_str(), strDst.c_str(), fFileCopyFlags));
[71251]536
[71796]537 GuestFileOpenInfo srcOpenInfo;
[84648]538 srcOpenInfo.mFilename = strSrc;
[71796]539 srcOpenInfo.mOpenAction = FileOpenAction_OpenExisting;
540 srcOpenInfo.mAccessMode = FileAccessMode_ReadOnly;
541 srcOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71251]542
[71796]543 ComObjPtr<GuestFile> srcFile;
[71251]544
[71796]545 GuestFsObjData srcObjData;
[94935]546 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[99252]547 int vrc = mSession->i_fsObjQueryInfo(strSrc, TRUE /* fFollowSymlinks */, srcObjData, &vrcGuest);
[94935]548 if (RT_FAILURE(vrc))
[71251]549 {
[94935]550 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[90828]551 setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Guest file lookup failed"),
[98526]552 GuestErrorInfo(GuestErrorInfo::Type_Fs, vrcGuest, strSrc.c_str()));
[84648]553 else
554 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[94935]555 Utf8StrFmt(tr("Guest file lookup for \"%s\" failed: %Rrc"), strSrc.c_str(), vrc));
[71251]556 }
[71796]557 else
[71251]558 {
[71796]559 switch (srcObjData.mType)
560 {
561 case FsObjType_File:
562 break;
563
564 case FsObjType_Symlink:
[71817]565 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
[71796]566 {
567 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]568 Utf8StrFmt(tr("Guest file \"%s\" is a symbolic link"),
[84648]569 strSrc.c_str()));
[94935]570 vrc = VERR_IS_A_SYMLINK;
[71796]571 }
572 break;
573
574 default:
575 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]576 Utf8StrFmt(tr("Guest object \"%s\" is not a file (is type %#x)"),
[84864]577 strSrc.c_str(), srcObjData.mType));
[94935]578 vrc = VERR_NOT_A_FILE;
[71796]579 break;
580 }
[71251]581 }
582
[94935]583 if (RT_FAILURE(vrc))
584 return vrc;
[71251]585
[94935]586 vrc = mSession->i_fileOpen(srcOpenInfo, srcFile, &vrcGuest);
587 if (RT_FAILURE(vrc))
[71251]588 {
[94935]589 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[90828]590 setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Guest file could not be opened"),
[94935]591 GuestErrorInfo(GuestErrorInfo::Type_File, vrcGuest, strSrc.c_str()));
[84648]592 else
593 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[94935]594 Utf8StrFmt(tr("Guest file \"%s\" could not be opened: %Rrc"), strSrc.c_str(), vrc));
[71796]595 }
[71251]596
[94935]597 if (RT_FAILURE(vrc))
598 return vrc;
[71251]599
[71796]600 RTFSOBJINFO dstObjInfo;
601 RT_ZERO(dstObjInfo);
[71251]602
[71796]603 bool fSkip = false; /* Whether to skip handling the file. */
[71251]604
[94935]605 if (RT_SUCCESS(vrc))
[71251]606 {
[94935]607 vrc = RTPathQueryInfo(strDst.c_str(), &dstObjInfo, RTFSOBJATTRADD_NOTHING);
608 if (RT_SUCCESS(vrc))
[71251]609 {
[71817]610 if (fFileCopyFlags & FileCopyFlag_NoReplace)
[71251]611 {
[97655]612 LogRel2(("Guest Control: Host file \"%s\" already exists, skipping\n", strDst.c_str()));
[97448]613 vrc = VWRN_ALREADY_EXISTS;
614 fSkip = true;
[71251]615 }
616
[97448]617 if ( !fSkip
618 && fFileCopyFlags & FileCopyFlag_Update)
[71251]619 {
[71796]620 RTTIMESPEC srcModificationTimeTS;
621 RTTimeSpecSetSeconds(&srcModificationTimeTS, srcObjData.mModificationTime);
622 if (RTTimeSpecCompare(&srcModificationTimeTS, &dstObjInfo.ModificationTime) <= 0)
[71251]623 {
[97655]624 LogRel2(("Guest Control: Host file \"%s\" has same or newer modification date, skipping\n", strDst.c_str()));
[97448]625 vrc = VWRN_ALREADY_EXISTS;
[71796]626 fSkip = true;
[71251]627 }
628 }
629 }
630 else
631 {
[97344]632 if (vrc == VERR_PATH_NOT_FOUND) /* Destination file does not exist (yet)? */
633 vrc = VERR_FILE_NOT_FOUND; /* Needed in next block further down. */
634 else if (vrc != VERR_FILE_NOT_FOUND) /* Ditto. */
[71251]635 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97448]636 Utf8StrFmt(tr("Host file lookup for \"%s\" failed: %Rrc"), strDst.c_str(), vrc));
[71251]637 }
638 }
639
[71796]640 if (fSkip)
[71251]641 {
[97626]642 int vrc2 = fileClose(srcFile);
643 if (RT_SUCCESS(vrc))
644 vrc = vrc2;
645
[97448]646 return vrc;
[71251]647 }
648
[94935]649 if (RT_SUCCESS(vrc))
[71667]650 {
[71796]651 if (RTFS_IS_FILE(dstObjInfo.Attr.fMode))
[71667]652 {
[71817]653 if (fFileCopyFlags & FileCopyFlag_NoReplace)
[71667]654 {
[97395]655 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Host file \"%s\" already exists"), strDst.c_str()));
[94935]656 vrc = VERR_ALREADY_EXISTS;
[71667]657 }
658 }
[71796]659 else if (RTFS_IS_DIRECTORY(dstObjInfo.Attr.fMode))
[71667]660 {
[97395]661 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Host destination \"%s\" is a directory"), strDst.c_str()));
662 vrc = VERR_IS_A_DIRECTORY;
[71667]663 }
[71796]664 else if (RTFS_IS_SYMLINK(dstObjInfo.Attr.fMode))
[71667]665 {
[71817]666 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
[71667]667 {
[97395]668 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(tr("Host destination \"%s\" is a symbolic link"), strDst.c_str()));
[94935]669 vrc = VERR_IS_A_SYMLINK;
[71667]670 }
671 }
[71796]672 else
673 {
[97395]674 LogFlowThisFunc(("Host file system type %#x not supported\n", dstObjInfo.Attr.fMode & RTFS_TYPE_MASK));
675 vrc = VERR_NOT_SUPPORTED;
[71796]676 }
[71667]677 }
678
[97395]679 LogFlowFunc(("vrc=%Rrc, dstFsType=%#x, pszDstFile=%s\n", vrc, dstObjInfo.Attr.fMode & RTFS_TYPE_MASK, strDst.c_str()));
680
[94935]681 if ( RT_SUCCESS(vrc)
682 || vrc == VERR_FILE_NOT_FOUND)
[71667]683 {
[97655]684 LogRel2(("Guest Control: Copying file \"%s\" from guest to \"%s\" on host ...\n", strSrc.c_str(), strDst.c_str()));
[97395]685
686 RTFILE hDstFile;
687 vrc = RTFileOpen(&hDstFile, strDst.c_str(),
688 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
689 if (RT_SUCCESS(vrc))
[71796]690 {
[97655]691 LogFlowThisFunc(("Copying \"%s\" to \"%s\" (%RI64 bytes) ...\n",
[97395]692 strSrc.c_str(), strDst.c_str(), srcObjData.mObjectSize));
[71667]693
[97395]694 vrc = fileCopyFromGuestInner(strSrc, srcFile, strDst, &hDstFile, fFileCopyFlags,
695 0 /* Offset, unused */, (uint64_t)srcObjData.mObjectSize);
[71796]696
[97395]697 int vrc2 = RTFileClose(hDstFile);
698 AssertRC(vrc2);
[71796]699 }
[97395]700 else
701 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
702 Utf8StrFmt(tr("Opening/creating host file \"%s\" failed: %Rrc"), strDst.c_str(), vrc));
[71251]703 }
704
[97626]705 int vrc2 = fileClose(srcFile);
706 if (RT_SUCCESS(vrc))
707 vrc = vrc2;
[71251]708
[94935]709 LogFlowFuncLeaveRC(vrc);
710 return vrc;
[71251]711}
712
713/**
[71817]714 * Main function for copying a file from host to the guest.
[71213]715 *
716 * @return VBox status code.
[84648]717 * @param strSrcFile Full path of source file on the host to copy.
[72961]718 * @param hVfsFile The VFS file handle to read from.
[84648]719 * @param strDstFile Full destination path and file name (guest style) to copy file to.
720 * @param fileDst Guest file (destination) to copy to the guest. Must be in opened and ready state already.
[71817]721 * @param fFileCopyFlags File copy flags.
722 * @param offCopy Offset (in bytes) where to start copying the source file.
723 * @param cbSize Size (in bytes) to copy from the source file.
[71213]724 */
[84648]725int GuestSessionTask::fileCopyToGuestInner(const Utf8Str &strSrcFile, RTVFSFILE hVfsFile,
726 const Utf8Str &strDstFile, ComObjPtr<GuestFile> &fileDst,
727 FileCopyFlag_T fFileCopyFlags, uint64_t offCopy, uint64_t cbSize)
[71213]728{
[71817]729 RT_NOREF(fFileCopyFlags);
[60622]730
[97656]731 if (!cbSize) /* Nothing to copy, i.e. empty file? Bail out. */
732 return VINF_SUCCESS;
733
734 BOOL fCanceled = FALSE;
[71817]735 uint64_t cbWrittenTotal = 0;
736 uint64_t cbToRead = cbSize;
737
738 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
739
[94935]740 int vrc = VINF_SUCCESS;
[71817]741
742 if (offCopy)
[71213]743 {
[71817]744 uint64_t offActual;
[94935]745 vrc = RTVfsFileSeek(hVfsFile, offCopy, RTFILE_SEEK_END, &offActual);
746 if (RT_FAILURE(vrc))
[71817]747 {
748 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]749 Utf8StrFmt(tr("Seeking to offset %RU64 of host file \"%s\" failed: %Rrc"),
[94935]750 offCopy, strSrcFile.c_str(), vrc));
751 return vrc;
[71817]752 }
[71213]753 }
754
[71817]755 BYTE byBuf[_64K];
756 while (cbToRead)
757 {
758 size_t cbRead;
759 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
[94935]760 vrc = RTVfsFileRead(hVfsFile, byBuf, cbChunk, &cbRead);
761 if (RT_FAILURE(vrc))
[71817]762 {
763 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97655]764 Utf8StrFmt(tr("Reading %RU32 bytes @ %RU64 from host file \"%s\" failed: %Rrc"),
[94935]765 cbChunk, cbWrittenTotal, strSrcFile.c_str(), vrc));
[71817]766 break;
767 }
[71213]768
[94935]769 vrc = fileDst->i_writeData(uTimeoutMs, byBuf, (uint32_t)cbRead, NULL /* No partial writes */);
770 if (RT_FAILURE(vrc))
[71817]771 {
772 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97655]773 Utf8StrFmt(tr("Writing %zu bytes to guest file \"%s\" failed: %Rrc"),
[94935]774 cbRead, strDstFile.c_str(), vrc));
[71817]775 break;
776 }
777
778 Assert(cbToRead >= cbRead);
779 cbToRead -= cbRead;
780
781 /* Update total bytes written to the guest. */
782 cbWrittenTotal += cbRead;
783 Assert(cbWrittenTotal <= cbSize);
784
785 /* Did the user cancel the operation above? */
786 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
787 && fCanceled)
788 break;
789
[97656]790 AssertBreakStmt(cbSize, vrc = VERR_INTERNAL_ERROR);
791 vrc = setProgress(((double)cbWrittenTotal / (double)cbSize) * 100);
[94935]792 if (RT_FAILURE(vrc))
[71817]793 break;
794 }
795
[94935]796 if (RT_FAILURE(vrc))
797 return vrc;
[71817]798
[60622]799 /*
[97137]800 * Even if we succeeded until here make sure to check whether we really transferred
[71817]801 * everything.
[60622]802 */
[97656]803 if (cbWrittenTotal == 0)
[71817]804 {
[97137]805 /* If nothing was transferred but the file size was > 0 then "vbox_cat" wasn't able to write
[71817]806 * to the destination -> access denied. */
807 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]808 Utf8StrFmt(tr("Writing to guest file \"%s\" failed: Access denied"),
[84648]809 strDstFile.c_str()));
[94935]810 vrc = VERR_ACCESS_DENIED;
[71817]811 }
812 else if (cbWrittenTotal < cbSize)
813 {
814 /* If we did not copy all let the user know. */
815 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97137]816 Utf8StrFmt(tr("Copying to guest file \"%s\" failed (%RU64/%RU64 bytes transferred)"),
[84648]817 strDstFile.c_str(), cbWrittenTotal, cbSize));
[94935]818 vrc = VERR_INTERRUPTED;
[71817]819 }
[42573]820
[94935]821 LogFlowFuncLeaveRC(vrc);
822 return vrc;
[71817]823}
[42693]824
[71817]825/**
[97425]826 * Copies a file from the host to the guest.
[71817]827 *
[97448]828 * @return VBox status code.
829 * @retval VWRN_ALREADY_EXISTS if the file already exists and FileCopyFlag_NoReplace is specified,
830 * *or * the file at the destination has the same (or newer) modification time
831 * and FileCopyFlag_Update is specified.
[97425]832 * @param strSrc Full path of source file on the host.
833 * @param strDst Full destination path and file name (guest style) to copy file to. Guest-path style.
[71817]834 * @param fFileCopyFlags File copy flags.
835 */
[83336]836int GuestSessionTask::fileCopyToGuest(const Utf8Str &strSrc, const Utf8Str &strDst, FileCopyFlag_T fFileCopyFlags)
[71817]837{
[97448]838 LogFlowThisFunc(("strSource=%s, strDst=%s, fFileCopyFlags=%#x\n", strSrc.c_str(), strDst.c_str(), fFileCopyFlags));
[71213]839
[71817]840 GuestFileOpenInfo dstOpenInfo;
[97395]841 dstOpenInfo.mFilename = strDst;
[71817]842 if (fFileCopyFlags & FileCopyFlag_NoReplace)
843 dstOpenInfo.mOpenAction = FileOpenAction_CreateNew;
844 else
845 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
846 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
847 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71213]848
[71817]849 ComObjPtr<GuestFile> dstFile;
[94935]850 int vrcGuest;
851 int vrc = mSession->i_fileOpen(dstOpenInfo, dstFile, &vrcGuest);
852 if (RT_FAILURE(vrc))
[71817]853 {
[94935]854 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[97655]855 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
856 Utf8StrFmt(tr("Guest file \"%s\" could not be created or replaced"), strDst.c_str()),
[97425]857 GuestErrorInfo(GuestErrorInfo::Type_File, vrcGuest, strDst.c_str()));
[84648]858 else
859 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97425]860 Utf8StrFmt(tr("Guest file \"%s\" could not be created or replaced: %Rrc"), strDst.c_str(), vrc));
[94935]861 return vrc;
[71817]862 }
[71213]863
[71976]864 char szSrcReal[RTPATH_MAX];
865
[71817]866 RTFSOBJINFO srcObjInfo;
867 RT_ZERO(srcObjInfo);
[71213]868
[71817]869 bool fSkip = false; /* Whether to skip handling the file. */
[71213]870
[94935]871 if (RT_SUCCESS(vrc))
[71213]872 {
[94935]873 vrc = RTPathReal(strSrc.c_str(), szSrcReal, sizeof(szSrcReal));
874 if (RT_FAILURE(vrc))
[71213]875 {
[71976]876 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]877 Utf8StrFmt(tr("Host path lookup for file \"%s\" failed: %Rrc"),
[94935]878 strSrc.c_str(), vrc));
[71976]879 }
880 else
881 {
[94935]882 vrc = RTPathQueryInfo(szSrcReal, &srcObjInfo, RTFSOBJATTRADD_NOTHING);
883 if (RT_SUCCESS(vrc))
[42693]884 {
[97448]885 /* Only perform a remote file query when needed. */
886 if ( (fFileCopyFlags & FileCopyFlag_Update)
887 || (fFileCopyFlags & FileCopyFlag_NoReplace))
[71817]888 {
[83336]889 GuestFsObjData dstObjData;
[97395]890 vrc = mSession->i_fileQueryInfo(strDst, RT_BOOL(fFileCopyFlags & FileCopyFlag_FollowLinks), dstObjData,
[94935]891 &vrcGuest);
892 if (RT_SUCCESS(vrc))
[71976]893 {
[97448]894 if (fFileCopyFlags & FileCopyFlag_NoReplace)
[83336]895 {
[97655]896 LogRel2(("Guest Control: Guest file \"%s\" already exists, skipping\n", strDst.c_str()));
[97448]897 vrc = VWRN_ALREADY_EXISTS;
[83336]898 fSkip = true;
899 }
[97448]900
901 if ( !fSkip
902 && fFileCopyFlags & FileCopyFlag_Update)
903 {
904 RTTIMESPEC dstModificationTimeTS;
905 RTTimeSpecSetSeconds(&dstModificationTimeTS, dstObjData.mModificationTime);
906 if (RTTimeSpecCompare(&dstModificationTimeTS, &srcObjInfo.ModificationTime) <= 0)
907 {
[97655]908 LogRel2(("Guest Control: Guest file \"%s\" has same or newer modification date, skipping\n",
[97448]909 strDst.c_str()));
910 vrc = VWRN_ALREADY_EXISTS;
911 fSkip = true;
912 }
913 }
[71976]914 }
[83336]915 else
916 {
[94935]917 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[83336]918 {
[94935]919 switch (vrcGuest)
[83336]920 {
921 case VERR_FILE_NOT_FOUND:
[94935]922 vrc = VINF_SUCCESS;
[83336]923 break;
924
925 default:
926 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]927 Utf8StrFmt(tr("Guest error while determining object data for guest file \"%s\": %Rrc"),
[97395]928 strDst.c_str(), vrcGuest));
[83336]929 break;
930 }
931 }
932 else
933 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]934 Utf8StrFmt(tr("Host error while determining object data for guest file \"%s\": %Rrc"),
[97395]935 strDst.c_str(), vrc));
[83336]936 }
[71817]937 }
[42693]938 }
[71976]939 else
940 {
941 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97344]942 Utf8StrFmt(tr("Host source file lookup for \"%s\" failed: %Rrc"),
[94935]943 szSrcReal, vrc));
[71976]944 }
[71213]945 }
946 }
947
[71817]948 if (fSkip)
[71213]949 {
[97626]950 int vrc2 = fileClose(dstFile);
951 if (RT_SUCCESS(vrc))
952 vrc = vrc2;
953
[97448]954 return vrc;
[71213]955 }
[71817]956
[94935]957 if (RT_SUCCESS(vrc))
[71213]958 {
[97655]959 LogRel2(("Guest Control: Copying file \"%s\" from host to \"%s\" on guest ...\n", strSrc.c_str(), strDst.c_str()));
[97395]960
[72958]961 RTVFSFILE hSrcFile;
[94935]962 vrc = RTVfsFileOpenNormal(szSrcReal, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hSrcFile);
963 if (RT_SUCCESS(vrc))
[71213]964 {
[97655]965 LogFlowThisFunc(("Copying \"%s\" to \"%s\" (%RI64 bytes) ...\n",
[97395]966 szSrcReal, strDst.c_str(), srcObjInfo.cbObject));
[71817]967
[97395]968 vrc = fileCopyToGuestInner(szSrcReal, hSrcFile, strDst, dstFile,
[94935]969 fFileCopyFlags, 0 /* Offset, unused */, srcObjInfo.cbObject);
[71817]970
[94935]971 int vrc2 = RTVfsFileRelease(hSrcFile);
972 AssertRC(vrc2);
[71817]973 }
974 else
[71213]975 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]976 Utf8StrFmt(tr("Opening host file \"%s\" failed: %Rrc"),
[94935]977 szSrcReal, vrc));
[71213]978 }
[43162]979
[97626]980 int vrc2 = fileClose(dstFile);
981 if (RT_SUCCESS(vrc))
982 vrc = vrc2;
[71817]983
[94935]984 LogFlowFuncLeaveRC(vrc);
985 return vrc;
[71213]986}
[42758]987
[71213]988/**
[71976]989 * Adds a guest file system entry to a given list.
[71213]990 *
[71976]991 * @return VBox status code.
992 * @param strFile Path to file system entry to add.
993 * @param fsObjData Guest file system information of entry to add.
[71213]994 */
[71976]995int FsList::AddEntryFromGuest(const Utf8Str &strFile, const GuestFsObjData &fsObjData)
[71213]996{
[97655]997 LogFlowFunc(("Adding \"%s\"\n", strFile.c_str()));
[71976]998
999 FsEntry *pEntry = NULL;
1000 try
1001 {
1002 pEntry = new FsEntry();
1003 pEntry->fMode = fsObjData.GetFileMode();
1004 pEntry->strPath = strFile;
1005
1006 mVecEntries.push_back(pEntry);
1007 }
[83335]1008 catch (std::bad_alloc &)
[71976]1009 {
1010 if (pEntry)
1011 delete pEntry;
1012 return VERR_NO_MEMORY;
1013 }
1014
[71213]1015 return VINF_SUCCESS;
1016}
[42573]1017
[71976]1018/**
1019 * Adds a host file system entry to a given list.
1020 *
1021 * @return VBox status code.
1022 * @param strFile Path to file system entry to add.
1023 * @param pcObjInfo File system information of entry to add.
1024 */
1025int FsList::AddEntryFromHost(const Utf8Str &strFile, PCRTFSOBJINFO pcObjInfo)
[71213]1026{
[97655]1027 LogFlowFunc(("Adding \"%s\"\n", strFile.c_str()));
[71976]1028
1029 FsEntry *pEntry = NULL;
1030 try
1031 {
1032 pEntry = new FsEntry();
[97395]1033 pEntry->fMode = pcObjInfo->Attr.fMode;
[71976]1034 pEntry->strPath = strFile;
1035
1036 mVecEntries.push_back(pEntry);
1037 }
[83335]1038 catch (std::bad_alloc &)
[71976]1039 {
1040 if (pEntry)
1041 delete pEntry;
1042 return VERR_NO_MEMORY;
1043 }
1044
1045 return VINF_SUCCESS;
1046}
1047
1048FsList::FsList(const GuestSessionTask &Task)
1049 : mTask(Task)
1050{
1051}
1052
1053FsList::~FsList()
1054{
1055 Destroy();
1056}
1057
1058/**
1059 * Initializes a file list.
1060 *
1061 * @return VBox status code.
1062 * @param strSrcRootAbs Source root path (absolute) for this file list.
1063 * @param strDstRootAbs Destination root path (absolute) for this file list.
1064 * @param SourceSpec Source specification to use.
1065 */
1066int FsList::Init(const Utf8Str &strSrcRootAbs, const Utf8Str &strDstRootAbs,
1067 const GuestSessionFsSourceSpec &SourceSpec)
1068{
1069 mSrcRootAbs = strSrcRootAbs;
1070 mDstRootAbs = strDstRootAbs;
1071 mSourceSpec = SourceSpec;
1072
[92544]1073 /* Note: Leave the source and dest roots unmodified -- how paths will be treated
1074 * will be done directly when working on those. See @bugref{10139}. */
[83336]1075
[97395]1076 LogFlowFunc(("mSrcRootAbs=%s, mDstRootAbs=%s, fDirCopyFlags=%#x, fFileCopyFlags=%#x\n",
1077 mSrcRootAbs.c_str(), mDstRootAbs.c_str(), mSourceSpec.fDirCopyFlags, mSourceSpec.fFileCopyFlags));
[71976]1078
1079 return VINF_SUCCESS;
1080}
1081
1082/**
1083 * Destroys a file list.
1084 */
1085void FsList::Destroy(void)
1086{
1087 LogFlowFuncEnter();
1088
1089 FsEntries::iterator itEntry = mVecEntries.begin();
1090 while (itEntry != mVecEntries.end())
1091 {
1092 FsEntry *pEntry = *itEntry;
1093 delete pEntry;
1094 mVecEntries.erase(itEntry);
1095 itEntry = mVecEntries.begin();
1096 }
1097
1098 Assert(mVecEntries.empty());
1099
1100 LogFlowFuncLeave();
1101}
1102
[97446]1103#ifdef DEBUG
[71976]1104/**
[97395]1105 * Dumps a FsList to the debug log.
1106 */
1107void FsList::DumpToLog(void)
1108{
[97446]1109 LogFlowFunc(("strSrcRootAbs=%s, strDstRootAbs=%s\n", mSrcRootAbs.c_str(), mDstRootAbs.c_str()));
[97395]1110
1111 FsEntries::iterator itEntry = mVecEntries.begin();
1112 while (itEntry != mVecEntries.end())
1113 {
1114 FsEntry *pEntry = *itEntry;
[97446]1115 LogFlowFunc(("\tstrPath=%s (fMode %#x)\n", pEntry->strPath.c_str(), pEntry->fMode));
[97395]1116 ++itEntry;
1117 }
1118
1119 LogFlowFuncLeave();
1120}
[97446]1121#endif /* DEBUG */
[97395]1122
1123/**
[71976]1124 * Builds a guest file list from a given path (and optional filter).
1125 *
1126 * @return VBox status code.
1127 * @param strPath Directory on the guest to build list from.
1128 * @param strSubDir Current sub directory path; needed for recursion.
1129 * Set to an empty path.
1130 */
1131int FsList::AddDirFromGuest(const Utf8Str &strPath, const Utf8Str &strSubDir /* = "" */)
1132{
1133 Utf8Str strPathAbs = strPath;
[97344]1134 if (!strPathAbs.endsWith(PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle)))
1135 strPathAbs += PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle);
[71976]1136
1137 Utf8Str strPathSub = strSubDir;
1138 if ( strPathSub.isNotEmpty()
[97344]1139 && !strPathSub.endsWith(PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle)))
1140 strPathSub += PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle);
[71976]1141
1142 strPathAbs += strPathSub;
1143
[97655]1144 LogFlowFunc(("Entering \"%s\" (sub \"%s\")\n", strPathAbs.c_str(), strPathSub.c_str()));
[71976]1145
[97655]1146 LogRel2(("Guest Control: Handling directory \"%s\" on guest ...\n", strPathAbs.c_str()));
[92715]1147
[71976]1148 GuestDirectoryOpenInfo dirOpenInfo;
1149 dirOpenInfo.mFilter = "";
1150 dirOpenInfo.mPath = strPathAbs;
1151 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
1152
1153 const ComObjPtr<GuestSession> &pSession = mTask.GetSession();
1154
[79189]1155 ComObjPtr <GuestDirectory> pDir;
[94935]1156 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1157 int vrc = pSession->i_directoryOpen(dirOpenInfo, pDir, &vrcGuest);
1158 if (RT_FAILURE(vrc))
[71976]1159 {
[94935]1160 switch (vrc)
[71976]1161 {
1162 case VERR_INVALID_PARAMETER:
1163 break;
1164
1165 case VERR_GSTCTL_GUEST_ERROR:
1166 break;
1167
1168 default:
1169 break;
1170 }
1171
[94935]1172 return vrc;
[71976]1173 }
1174
1175 if (strPathSub.isNotEmpty())
1176 {
1177 GuestFsObjData fsObjData;
1178 fsObjData.mType = FsObjType_Directory;
1179
[94935]1180 vrc = AddEntryFromGuest(strPathSub, fsObjData);
[71976]1181 }
1182
[94935]1183 if (RT_SUCCESS(vrc))
[71976]1184 {
1185 ComObjPtr<GuestFsObjInfo> fsObjInfo;
[94935]1186 while (RT_SUCCESS(vrc = pDir->i_read(fsObjInfo, &vrcGuest)))
[71976]1187 {
1188 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
[94935]1189 HRESULT hrc2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1190 AssertComRC(hrc2);
[71976]1191
1192 com::Bstr bstrName;
[94935]1193 hrc2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1194 AssertComRC(hrc2);
[71976]1195
1196 Utf8Str strEntry = strPathSub + Utf8Str(bstrName);
1197
[97655]1198 LogFlowFunc(("Entry \"%s\"\n", strEntry.c_str()));
[71976]1199
1200 switch (enmObjType)
1201 {
1202 case FsObjType_Directory:
1203 {
1204 if ( bstrName.equals(".")
1205 || bstrName.equals(".."))
1206 {
1207 break;
1208 }
1209
[97655]1210 LogRel2(("Guest Control: Directory \"%s\"\n", strEntry.c_str()));
[92715]1211
[97395]1212 if (!(mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_Recursive))
[71976]1213 break;
1214
[94935]1215 vrc = AddDirFromGuest(strPath, strEntry);
[71976]1216 break;
1217 }
1218
1219 case FsObjType_Symlink:
1220 {
[97395]1221 if ( mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks
1222 || mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks)
[71976]1223 {
[92715]1224 /** @todo Symlink handling from guest is not implemented yet.
[71976]1225 * See IGuestSession::symlinkRead(). */
[97655]1226 LogRel2(("Guest Control: Warning: Symlink support on guest side not available, skipping \"%s\"\n",
[73037]1227 strEntry.c_str()));
[71976]1228 }
1229 break;
1230 }
1231
1232 case FsObjType_File:
1233 {
[97655]1234 LogRel2(("Guest Control: File \"%s\"\n", strEntry.c_str()));
[92715]1235
[94935]1236 vrc = AddEntryFromGuest(strEntry, fsObjInfo->i_getData());
[71976]1237 break;
1238 }
1239
1240 default:
1241 break;
1242 }
1243 }
[72045]1244
[99251]1245 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1246 && vrcGuest == VERR_NO_MORE_FILES) /* End of listing reached? */
[94935]1247 vrc = VINF_SUCCESS;
[71976]1248 }
1249
[98526]1250 int vrc2 = pDir->i_close(&vrcGuest);
[94935]1251 if (RT_SUCCESS(vrc))
1252 vrc = vrc2;
[71976]1253
[94935]1254 return vrc;
[71976]1255}
1256
1257/**
[97139]1258 * Builds a host file list from a given path.
[71976]1259 *
1260 * @return VBox status code.
1261 * @param strPath Directory on the host to build list from.
1262 * @param strSubDir Current sub directory path; needed for recursion.
1263 * Set to an empty path.
[97139]1264 * @param pszPathReal Scratch buffer for holding the resolved real path.
1265 * Needed for recursion.
1266 * @param cbPathReal Size (in bytes) of \a pszPathReal.
[97152]1267 * @param pDirEntry Where to store looked up directory information for handled paths.
1268 * Needed for recursion.
[71976]1269 */
[97139]1270int FsList::AddDirFromHost(const Utf8Str &strPath, const Utf8Str &strSubDir,
[97152]1271 char *pszPathReal, size_t cbPathReal, PRTDIRENTRYEX pDirEntry)
[71976]1272{
1273 Utf8Str strPathAbs = strPath;
[97344]1274 if (!strPathAbs.endsWith(RTPATH_SLASH_STR))
1275 strPathAbs += RTPATH_SLASH_STR;
[71976]1276
1277 Utf8Str strPathSub = strSubDir;
1278 if ( strPathSub.isNotEmpty()
[97344]1279 && !strPathSub.endsWith(RTPATH_SLASH_STR))
1280 strPathSub += RTPATH_SLASH_STR;
[71976]1281
1282 strPathAbs += strPathSub;
1283
[97655]1284 LogFlowFunc(("Entering \"%s\" (sub \"%s\")\n", strPathAbs.c_str(), strPathSub.c_str()));
[71976]1285
[97655]1286 LogRel2(("Guest Control: Handling directory \"%s\" on host ...\n", strPathAbs.c_str()));
[92715]1287
[71976]1288 RTFSOBJINFO objInfo;
[94935]1289 int vrc = RTPathQueryInfo(strPathAbs.c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
1290 if (RT_SUCCESS(vrc))
[71976]1291 {
1292 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1293 {
1294 if (strPathSub.isNotEmpty())
[94935]1295 vrc = AddEntryFromHost(strPathSub, &objInfo);
[71976]1296
[94935]1297 if (RT_SUCCESS(vrc))
[71976]1298 {
1299 RTDIR hDir;
[94935]1300 vrc = RTDirOpen(&hDir, strPathAbs.c_str());
1301 if (RT_SUCCESS(vrc))
[71976]1302 {
1303 do
1304 {
1305 /* Retrieve the next directory entry. */
[97152]1306 vrc = RTDirReadEx(hDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
[94935]1307 if (RT_FAILURE(vrc))
[71976]1308 {
[94935]1309 if (vrc == VERR_NO_MORE_FILES)
1310 vrc = VINF_SUCCESS;
[71976]1311 break;
1312 }
1313
[97152]1314 Utf8Str strEntry = strPathSub + Utf8Str(pDirEntry->szName);
[71976]1315
[97655]1316 LogFlowFunc(("Entry \"%s\"\n", strEntry.c_str()));
[71976]1317
[97152]1318 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
[71976]1319 {
1320 case RTFS_TYPE_DIRECTORY:
1321 {
1322 /* Skip "." and ".." entries. */
[97152]1323 if (RTDirEntryExIsStdDotLink(pDirEntry))
[71976]1324 break;
1325
[97655]1326 LogRel2(("Guest Control: Directory \"%s\"\n", strEntry.c_str()));
[92715]1327
[97395]1328 if (!(mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_Recursive))
[71976]1329 break;
1330
[97152]1331 vrc = AddDirFromHost(strPath, strEntry, pszPathReal, cbPathReal, pDirEntry);
[71976]1332 break;
1333 }
1334
1335 case RTFS_TYPE_FILE:
1336 {
[97655]1337 LogRel2(("Guest Control: File \"%s\"\n", strEntry.c_str()));
[92715]1338
[97152]1339 vrc = AddEntryFromHost(strEntry, &pDirEntry->Info);
[71976]1340 break;
1341 }
1342
1343 case RTFS_TYPE_SYMLINK:
1344 {
[97395]1345 Utf8Str strEntryAbs = strPathAbs + (const char *)pDirEntry->szName;
1346
1347 vrc = RTPathReal(strEntryAbs.c_str(), pszPathReal, cbPathReal);
1348 if (RT_SUCCESS(vrc))
[71976]1349 {
[97395]1350 vrc = RTPathQueryInfo(pszPathReal, &objInfo, RTFSOBJATTRADD_NOTHING);
[94935]1351 if (RT_SUCCESS(vrc))
[71976]1352 {
[97395]1353 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
[71976]1354 {
[97655]1355 LogRel2(("Guest Control: Symbolic link \"%s\" -> \"%s\" (directory)\n",
[97395]1356 strEntryAbs.c_str(), pszPathReal));
1357 if (mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks)
[97152]1358 vrc = AddDirFromHost(strPath, strEntry, pszPathReal, cbPathReal, pDirEntry);
[97395]1359 }
1360 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1361 {
[97655]1362 LogRel2(("Guest Control: Symbolic link \"%s\" -> \"%s\" (file)\n",
[97395]1363 strEntryAbs.c_str(), pszPathReal));
[97592]1364 if (mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks)
[94935]1365 vrc = AddEntryFromHost(strEntry, &objInfo);
[71976]1366 }
[97395]1367 else
1368 vrc = VERR_NOT_SUPPORTED;
1369 }
[92715]1370
[97395]1371 if (RT_FAILURE(vrc))
[98272]1372 LogRel2(("Guest Control: Unable to query symbolic link info for \"%s\", vrc=%Rrc\n",
[97395]1373 pszPathReal, vrc));
[71976]1374 }
[92715]1375 else
[97395]1376 {
[98272]1377 LogRel2(("Guest Control: Unable to resolve symlink for \"%s\", vrc=%Rrc\n",
1378 strPathAbs.c_str(), vrc));
[97395]1379 if (vrc == VERR_FILE_NOT_FOUND) /* Broken symlink, skip. */
1380 vrc = VINF_SUCCESS;
1381 }
[71976]1382 break;
1383 }
1384
1385 default:
1386 break;
1387 }
1388
[94935]1389 } while (RT_SUCCESS(vrc));
[71976]1390
1391 RTDirClose(hDir);
1392 }
1393 }
1394 }
1395 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
[94935]1396 vrc = VERR_IS_A_FILE;
[71976]1397 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
[94935]1398 vrc = VERR_IS_A_SYMLINK;
[71976]1399 else
[94935]1400 vrc = VERR_NOT_SUPPORTED;
[71976]1401 }
1402 else
[98272]1403 LogFlowFunc(("Unable to query \"%s\", vrc=%Rrc\n", strPathAbs.c_str(), vrc));
[71976]1404
[94935]1405 LogFlowFuncLeaveRC(vrc);
1406 return vrc;
[71976]1407}
1408
1409GuestSessionTaskOpen::GuestSessionTaskOpen(GuestSession *pSession, uint32_t uFlags, uint32_t uTimeoutMS)
1410 : GuestSessionTask(pSession)
1411 , mFlags(uFlags)
1412 , mTimeoutMS(uTimeoutMS)
1413{
[71213]1414 m_strTaskName = "gctlSesOpen";
1415}
[42573]1416
[71976]1417GuestSessionTaskOpen::~GuestSessionTaskOpen(void)
[71213]1418{
[43162]1419
[71213]1420}
[42573]1421
[92897]1422/** @copydoc GuestSessionTask::Run */
[71976]1423int GuestSessionTaskOpen::Run(void)
[71213]1424{
1425 LogFlowThisFuncEnter();
[42573]1426
[71213]1427 AutoCaller autoCaller(mSession);
[98262]1428 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[42573]1429
[71345]1430 int vrc = mSession->i_startSession(NULL /*pvrcGuest*/);
[71213]1431 /* Nothing to do here anymore. */
[43162]1432
[71213]1433 LogFlowFuncLeaveRC(vrc);
1434 return vrc;
1435}
1436
[71976]1437GuestSessionCopyTask::GuestSessionCopyTask(GuestSession *pSession)
1438 : GuestSessionTask(pSession)
[71251]1439{
[71976]1440}
[71817]1441
[71976]1442GuestSessionCopyTask::~GuestSessionCopyTask()
1443{
1444 FsLists::iterator itList = mVecLists.begin();
1445 while (itList != mVecLists.end())
1446 {
1447 FsList *pFsList = (*itList);
1448 pFsList->Destroy();
1449 delete pFsList;
1450 mVecLists.erase(itList);
1451 itList = mVecLists.begin();
1452 }
[71817]1453
[71976]1454 Assert(mVecLists.empty());
[71251]1455}
1456
[78666]1457GuestSessionTaskCopyFrom::GuestSessionTaskCopyFrom(GuestSession *pSession, GuestSessionFsSourceSet const &vecSrc,
1458 const Utf8Str &strDest)
1459 : GuestSessionCopyTask(pSession)
[71251]1460{
[71976]1461 m_strTaskName = "gctlCpyFrm";
1462
1463 mSources = vecSrc;
1464 mDest = strDest;
[71251]1465}
1466
[71976]1467GuestSessionTaskCopyFrom::~GuestSessionTaskCopyFrom(void)
1468{
1469}
1470
[92897]1471/**
1472 * Initializes a copy-from-guest task.
1473 *
1474 * @returns HRESULT
1475 * @param strTaskDesc Friendly task description.
1476 */
[71976]1477HRESULT GuestSessionTaskCopyFrom::Init(const Utf8Str &strTaskDesc)
[71251]1478{
[71976]1479 setTaskDesc(strTaskDesc);
[71251]1480
[71976]1481 /* Create the progress object. */
1482 ComObjPtr<Progress> pProgress;
[79418]1483 HRESULT hrc = pProgress.createObject();
1484 if (FAILED(hrc))
1485 return hrc;
[71251]1486
[71976]1487 mProgress = pProgress;
[71251]1488
[79418]1489 int vrc = VINF_SUCCESS;
[71251]1490
[71976]1491 ULONG cOperations = 0;
[72045]1492 Utf8Str strErrorInfo;
[71847]1493
[72045]1494 /**
1495 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyFrom::Run
1496 * because the caller expects a ready-for-operation progress object on return.
1497 * The progress object will have a variable operation count, based on the elements to
1498 * be processed.
1499 */
1500
[97485]1501 if (mSources.empty())
[71817]1502 {
[97485]1503 strErrorInfo.printf(tr("No guest sources specified"));
1504 vrc = VERR_INVALID_PARAMETER;
1505 }
1506 else if (mDest.isEmpty())
1507 {
[97151]1508 strErrorInfo.printf(tr("Host destination must not be empty"));
[83607]1509 vrc = VERR_INVALID_PARAMETER;
1510 }
1511 else
1512 {
1513 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1514 while (itSrc != mSources.end())
1515 {
1516 Utf8Str strSrc = itSrc->strSource;
1517 Utf8Str strDst = mDest;
[71976]1518
[83607]1519 bool fFollowSymlinks;
[73037]1520
[83607]1521 if (strSrc.isEmpty())
[71976]1522 {
[97151]1523 strErrorInfo.printf(tr("Guest source entry must not be empty"));
[83607]1524 vrc = VERR_INVALID_PARAMETER;
1525 break;
[71976]1526 }
[73037]1527
[83607]1528 if (itSrc->enmType == FsObjType_Directory)
1529 {
[97395]1530 fFollowSymlinks = itSrc->fDirCopyFlags & DirectoryCopyFlag_FollowLinks;
[83607]1531 }
1532 else
[71976]1533 {
[97395]1534 fFollowSymlinks = RT_BOOL(itSrc->fFileCopyFlags & FileCopyFlag_FollowLinks);
[71976]1535 }
[83607]1536
[97395]1537 LogFlowFunc(("strSrc=%s (path style is %s), strDst=%s, fFollowSymlinks=%RTbool\n",
1538 strSrc.c_str(), GuestBase::pathStyleToStr(itSrc->enmPathStyle), strDst.c_str(), fFollowSymlinks));
[83607]1539
1540 GuestFsObjData srcObjData;
[94935]1541 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[99252]1542 vrc = mSession->i_fsObjQueryInfo(strSrc, fFollowSymlinks, srcObjData, &vrcGuest);
[83607]1543 if (RT_FAILURE(vrc))
[71976]1544 {
[84864]1545 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[97395]1546 strErrorInfo = GuestBase::getErrorAsString(tr("Guest source lookup failed"),
[98526]1547 GuestErrorInfo(GuestErrorInfo::Type_Fs, vrcGuest, strSrc.c_str()));
[84864]1548 else
[97395]1549 strErrorInfo.printf(tr("Guest source lookup for \"%s\" failed: %Rrc"),
[97151]1550 strSrc.c_str(), vrc);
[71976]1551 break;
1552 }
[71847]1553
[83607]1554 if (srcObjData.mType == FsObjType_Directory)
[71976]1555 {
[83607]1556 if (itSrc->enmType != FsObjType_Directory)
1557 {
[97151]1558 strErrorInfo.printf(tr("Guest source is not a file: %s"), strSrc.c_str());
[83607]1559 vrc = VERR_NOT_A_FILE;
1560 break;
1561 }
[71976]1562 }
[83607]1563 else
1564 {
1565 if (itSrc->enmType != FsObjType_File)
1566 {
[97151]1567 strErrorInfo.printf(tr("Guest source is not a directory: %s"), strSrc.c_str());
[83607]1568 vrc = VERR_NOT_A_DIRECTORY;
1569 break;
1570 }
1571 }
[71976]1572
[83607]1573 FsList *pFsList = NULL;
1574 try
[71976]1575 {
[83607]1576 pFsList = new FsList(*this);
1577 vrc = pFsList->Init(strSrc, strDst, *itSrc);
1578 if (RT_SUCCESS(vrc))
1579 {
[97395]1580 switch (itSrc->enmType)
1581 {
1582 case FsObjType_Directory:
1583 {
1584 vrc = pFsList->AddDirFromGuest(strSrc);
1585 break;
1586 }
1587
1588 case FsObjType_File:
1589 /* The file name is already part of the actual list's source root (strSrc). */
1590 break;
1591
1592 default:
[97446]1593 LogRel2(("Guest Control: Warning: Unknown guest file system type %#x for source \"%s\", skipping\n",
[97395]1594 itSrc->enmType, strSrc.c_str()));
1595 break;
1596 }
[83607]1597 }
1598
1599 if (RT_FAILURE(vrc))
1600 {
1601 delete pFsList;
[97655]1602 strErrorInfo.printf(tr("Error adding guest source \"%s\" to list: %Rrc"),
[97151]1603 strSrc.c_str(), vrc);
[83607]1604 break;
1605 }
[97446]1606#ifdef DEBUG
[97395]1607 pFsList->DumpToLog();
[97446]1608#endif
[83607]1609 mVecLists.push_back(pFsList);
1610 }
1611 catch (std::bad_alloc &)
1612 {
1613 vrc = VERR_NO_MEMORY;
[71976]1614 break;
1615 }
1616
[83607]1617 AssertPtr(pFsList);
1618 cOperations += (ULONG)pFsList->mVecEntries.size();
1619
1620 itSrc++;
[71976]1621 }
[71847]1622 }
1623
[97485]1624 if (RT_SUCCESS(vrc))
[71251]1625 {
[97485]1626 /* When there are no entries in the first source list, this means the source only contains a single file
1627 * (see \a mSrcRootAbs of FsList). So use \a mSrcRootAbs directly. */
1628 Utf8Str const &strFirstOp = mVecLists[0]->mVecEntries.size() > 0
1629 ? mVecLists[0]->mVecEntries[0]->strPath : mVecLists[0]->mSrcRootAbs;
[71976]1630
[97481]1631 /* Now that we know how many objects we're handling, tweak the progress description so that it
1632 * reflects more accurately what the progress is actually doing. */
1633 if (cOperations > 1)
1634 {
1635 mDesc.printf(tr("Copying \"%s\" [and %zu %s] from guest to \"%s\" on the host ..."),
1636 strFirstOp.c_str(), cOperations - 1, cOperations > 2 ? tr("others") : tr("other"), mDest.c_str());
1637 }
1638 else
1639 mDesc.printf(tr("Copying \"%s\" from guest to \"%s\" on the host ..."), strFirstOp.c_str(), mDest.c_str());
1640
[79418]1641 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1642 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */, Bstr(strFirstOp).raw());
[71251]1643 }
[97485]1644 else /* On error we go with an "empty" progress object when will be used for error handling. */
[79418]1645 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1646 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
[71976]1647
[97485]1648 if (FAILED(hrc)) /* Progress object creation failed -- we're doomed. */
1649 return hrc;
1650
[79418]1651 if (RT_FAILURE(vrc))
[72045]1652 {
[83607]1653 if (strErrorInfo.isEmpty())
[97151]1654 strErrorInfo.printf(tr("Failed with %Rrc"), vrc);
[97553]1655 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
[72045]1656 }
1657
[79418]1658 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hrc, vrc));
1659 return hrc;
[71976]1660}
1661
[92897]1662/** @copydoc GuestSessionTask::Run */
[71976]1663int GuestSessionTaskCopyFrom::Run(void)
1664{
1665 LogFlowThisFuncEnter();
1666
1667 AutoCaller autoCaller(mSession);
[98262]1668 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[71976]1669
[94935]1670 int vrc = VINF_SUCCESS;
[71976]1671
1672 FsLists::const_iterator itList = mVecLists.begin();
1673 while (itList != mVecLists.end())
[71251]1674 {
[71976]1675 FsList *pList = *itList;
1676 AssertPtr(pList);
1677
[97344]1678 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1679
1680 Utf8Str strSrcRootAbs = pList->mSrcRootAbs;
1681 Utf8Str strDstRootAbs = pList->mDstRootAbs;
1682
[97531]1683 vrc = GuestPath::BuildDestinationPath(strSrcRootAbs, mSession->i_getGuestPathStyle() /* Source */,
1684 strDstRootAbs, PATH_STYLE_NATIVE /* Dest */);
1685 if (RT_FAILURE(vrc))
1686 {
1687 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1688 Utf8StrFmt(tr("Building host destination root path \"%s\" failed: %Rrc"),
1689 strDstRootAbs.c_str(), vrc));
1690 break;
1691 }
[71976]1692
[97395]1693 bool fCopyIntoExisting;
1694 bool fFollowSymlinks;
[77495]1695
[97395]1696 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[97344]1697 {
[97395]1698 fCopyIntoExisting = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting);
1699 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks);
[97344]1700 }
[97395]1701 else if (pList->mSourceSpec.enmType == FsObjType_File)
1702 {
1703 fCopyIntoExisting = !RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_NoReplace);
1704 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks);
1705 }
[97344]1706 else
[97395]1707 AssertFailedBreakStmt(vrc = VERR_NOT_IMPLEMENTED);
[71976]1708
[97395]1709 uint32_t const fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
1710 uint32_t fDirCreate = 0;
[97344]1711
[97395]1712 bool fDstExists = true;
1713
[97555]1714 RTFSOBJINFO dstFsObjInfo;
1715 RT_ZERO(dstFsObjInfo);
1716 vrc = RTPathQueryInfoEx(strDstRootAbs.c_str(), &dstFsObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK /* fFlags */);
1717 if (RT_SUCCESS(vrc))
[97344]1718 {
[97555]1719 char szPathReal[RTPATH_MAX];
1720 vrc = RTPathReal(strDstRootAbs.c_str(), szPathReal, sizeof(szPathReal));
1721 if (RT_SUCCESS(vrc))
1722 {
1723 vrc = RTPathQueryInfoEx(szPathReal, &dstFsObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK /* fFlags */);
1724 if (RT_SUCCESS(vrc))
1725 {
[97655]1726 LogRel2(("Guest Control: Host destination is a symbolic link \"%s\" -> \"%s\" (%s)\n",
[97555]1727 strDstRootAbs.c_str(), szPathReal,
1728 GuestBase::fsObjTypeToStr(GuestBase::fileModeToFsObjType(dstFsObjInfo.Attr.fMode))));
1729 }
1730
1731 strDstRootAbs = szPathReal;
1732 }
1733 }
1734 else
1735 {
[97395]1736 if ( vrc == VERR_FILE_NOT_FOUND
1737 || vrc == VERR_PATH_NOT_FOUND)
[97344]1738 {
[97395]1739 fDstExists = false;
1740 vrc = VINF_SUCCESS;
1741 }
1742 else
1743 {
[97344]1744 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97395]1745 Utf8StrFmt(tr("Host path lookup for \"%s\" failed: %Rrc"), strDstRootAbs.c_str(), vrc));
[97344]1746 break;
1747 }
1748 }
1749
[97395]1750 /* Create the root directory. */
1751 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71976]1752 {
[97474]1753 LogFlowFunc(("Directory: fDirCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool -> fDstExist=%RTbool (%s)\n",
1754 pList->mSourceSpec.fDirCopyFlags, fCopyIntoExisting, fFollowSymlinks,
[97555]1755 fDstExists, GuestBase::fsObjTypeToStr(GuestBase::fileModeToFsObjType(dstFsObjInfo.Attr.fMode))));
[97395]1756
1757 if (fDstExists)
1758 {
[97555]1759 switch (dstFsObjInfo.Attr.fMode & RTFS_TYPE_MASK)
[97395]1760 {
1761 case RTFS_TYPE_DIRECTORY:
1762 {
1763 if (!fCopyIntoExisting)
1764 {
1765 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1766 Utf8StrFmt(tr("Host root directory \"%s\" already exists"), strDstRootAbs.c_str()));
1767 vrc = VERR_ALREADY_EXISTS;
1768 break;
1769 }
1770 break;
1771 }
1772
1773 case RTFS_TYPE_FILE:
1774 {
1775 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1776 Utf8StrFmt(tr("Destination \"%s\" on the host already exists and is a file"), strDstRootAbs.c_str()));
1777 vrc = VERR_IS_A_FILE;
1778 break;
1779 }
1780
1781 default:
1782 {
1783 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1784 Utf8StrFmt(tr("Unknown object type (%#x) on host for \"%s\""),
[97555]1785 dstFsObjInfo.Attr.fMode & RTFS_TYPE_MASK, strDstRootAbs.c_str()));
[97395]1786 vrc = VERR_NOT_SUPPORTED;
1787 break;
1788 }
1789 }
1790 }
1791
[94935]1792 if (RT_FAILURE(vrc))
[71976]1793 break;
1794
[97395]1795 /* Make sure the destination root directory exists. */
1796 if (pList->mSourceSpec.fDryRun == false)
1797 {
1798 vrc = directoryCreateOnHost(strDstRootAbs, fDirMode, 0 /* fCreate */, true /* fCanExist */);
1799 if (RT_FAILURE(vrc))
1800 break;
1801 }
[92544]1802
[97395]1803 AssertBreakStmt(pList->mSourceSpec.enmType == FsObjType_Directory, vrc = VERR_NOT_SUPPORTED);
1804
1805 /* Walk the entries. */
1806 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1807 while (itEntry != pList->mVecEntries.end())
[71976]1808 {
[97395]1809 FsEntry *pEntry = *itEntry;
1810 AssertPtr(pEntry);
1811
1812 Utf8Str strSrcAbs = strSrcRootAbs;
1813 Utf8Str strDstAbs = strDstRootAbs;
1814
[97344]1815 strSrcAbs += PATH_STYLE_SEP_STR(pList->mSourceSpec.enmPathStyle);
1816 strSrcAbs += pEntry->strPath;
[96617]1817
[97344]1818 strDstAbs += PATH_STYLE_SEP_STR(PATH_STYLE_NATIVE);
1819 strDstAbs += pEntry->strPath;
[71976]1820
[97531]1821 /* Clean up the final guest source path. */
[97426]1822 vrc = GuestPath::Translate(strSrcAbs, pList->mSourceSpec.enmPathStyle /* Source */,
1823 pList->mSourceSpec.enmPathStyle /* Dest */);
[97395]1824 if (RT_FAILURE(vrc))
1825 {
1826 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97399]1827 Utf8StrFmt(tr("Translating guest source path \"%s\" failed: %Rrc"),
1828 strSrcAbs.c_str(), vrc));
[97395]1829 break;
1830 }
[92544]1831
[97538]1832 /* Translate the final host desitnation path. */
1833 vrc = GuestPath::Translate(strDstAbs, mSession->i_getGuestPathStyle() /* Source */, PATH_STYLE_NATIVE /* Dest */);
[97395]1834 if (RT_FAILURE(vrc))
1835 {
1836 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97399]1837 Utf8StrFmt(tr("Translating host destination path \"%s\" failed: %Rrc"),
1838 strDstAbs.c_str(), vrc));
[97395]1839 break;
1840 }
[97344]1841
[97395]1842 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
[77038]1843
[97395]1844 switch (pEntry->fMode & RTFS_TYPE_MASK)
1845 {
1846 case RTFS_TYPE_DIRECTORY:
1847 if (!pList->mSourceSpec.fDryRun)
1848 vrc = directoryCreateOnHost(strDstAbs, fDirMode, fDirCreate, fCopyIntoExisting);
1849 break;
[92544]1850
[97395]1851 case RTFS_TYPE_FILE:
1852 RT_FALL_THROUGH();
1853 case RTFS_TYPE_SYMLINK:
1854 if (!pList->mSourceSpec.fDryRun)
1855 vrc = fileCopyFromGuest(strSrcAbs, strDstAbs, pList->mSourceSpec.fFileCopyFlags);
1856 break;
[71976]1857
[97395]1858 default:
1859 AssertFailed(); /* Should never happen (we already have a filtered list). */
1860 break;
1861 }
1862
1863 if (RT_FAILURE(vrc))
[71976]1864 break;
1865
[97395]1866 ++itEntry;
[71976]1867 }
[97395]1868 }
1869 else if (pList->mSourceSpec.enmType == FsObjType_File)
1870 {
[97474]1871 LogFlowFunc(("File: fFileCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool -> fDstExist=%RTbool (%s)\n",
1872 pList->mSourceSpec.fFileCopyFlags, fCopyIntoExisting, fFollowSymlinks,
[97555]1873 fDstExists, GuestBase::fsObjTypeToStr(GuestBase::fileModeToFsObjType(dstFsObjInfo.Attr.fMode))));
[71976]1874
[97395]1875 if (fDstExists)
1876 {
[97555]1877 switch (dstFsObjInfo.Attr.fMode & RTFS_TYPE_MASK)
[97395]1878 {
1879 case RTFS_TYPE_DIRECTORY:
1880 {
[97474]1881 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1882 Utf8StrFmt(tr("Destination \"%s\" on the host already exists and is a directory"),
1883 strDstRootAbs.c_str()));
1884 vrc = VERR_IS_A_DIRECTORY;
[97395]1885 break;
1886 }
[71976]1887
[97395]1888 case RTFS_TYPE_FILE:
1889 {
1890 if (!fCopyIntoExisting)
1891 {
1892 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1893 Utf8StrFmt(tr("Host file \"%s\" already exists"), strDstRootAbs.c_str()));
1894 vrc = VERR_ALREADY_EXISTS;
1895 }
1896 break;
1897 }
1898
1899 default:
1900 {
[97476]1901 /** @todo Resolve symlinks? */
[97395]1902 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1903 Utf8StrFmt(tr("Unknown object type (%#x) on host for \"%s\""),
[97555]1904 dstFsObjInfo.Attr.fMode & RTFS_TYPE_MASK, strDstRootAbs.c_str()));
[97395]1905 vrc = VERR_NOT_SUPPORTED;
1906 break;
1907 }
1908 }
1909 }
1910
[97541]1911 if (RT_SUCCESS(vrc))
[97538]1912 {
[97541]1913 /* Translate the final host destination file path. */
1914 vrc = GuestPath::Translate(strDstRootAbs,
1915 mSession->i_getGuestPathStyle() /* Dest */, PATH_STYLE_NATIVE /* Source */);
1916 if (RT_FAILURE(vrc))
1917 {
1918 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1919 Utf8StrFmt(tr("Translating host destination path \"%s\" failed: %Rrc"),
1920 strDstRootAbs.c_str(), vrc));
1921 break;
1922 }
1923
1924 if (!pList->mSourceSpec.fDryRun)
1925 vrc = fileCopyFromGuest(strSrcRootAbs, strDstRootAbs, pList->mSourceSpec.fFileCopyFlags);
[97538]1926 }
[71976]1927 }
[97395]1928 else
1929 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
[71976]1930
[94935]1931 if (RT_FAILURE(vrc))
[71976]1932 break;
[71251]1933
[71976]1934 ++itList;
[71251]1935 }
1936
[94935]1937 if (RT_SUCCESS(vrc))
1938 vrc = setProgressSuccess();
[71251]1939
[94935]1940 LogFlowFuncLeaveRC(vrc);
1941 return vrc;
[71251]1942}
1943
[78666]1944GuestSessionTaskCopyTo::GuestSessionTaskCopyTo(GuestSession *pSession, GuestSessionFsSourceSet const &vecSrc,
1945 const Utf8Str &strDest)
1946 : GuestSessionCopyTask(pSession)
[71213]1947{
[71976]1948 m_strTaskName = "gctlCpyTo";
[71817]1949
[71976]1950 mSources = vecSrc;
1951 mDest = strDest;
[71213]1952}
1953
[71976]1954GuestSessionTaskCopyTo::~GuestSessionTaskCopyTo(void)
[71213]1955{
1956}
1957
[92897]1958/**
1959 * Initializes a copy-to-guest task.
1960 *
1961 * @returns HRESULT
1962 * @param strTaskDesc Friendly task description.
1963 */
[71976]1964HRESULT GuestSessionTaskCopyTo::Init(const Utf8Str &strTaskDesc)
[71213]1965{
[71976]1966 LogFlowFuncEnter();
[71213]1967
[71976]1968 setTaskDesc(strTaskDesc);
[71213]1969
[71976]1970 /* Create the progress object. */
1971 ComObjPtr<Progress> pProgress;
[94935]1972 HRESULT hrc = pProgress.createObject();
1973 if (FAILED(hrc))
1974 return hrc;
[71213]1975
[71976]1976 mProgress = pProgress;
[71213]1977
[94935]1978 int vrc = VINF_SUCCESS;
[71213]1979
[71976]1980 ULONG cOperations = 0;
[72045]1981 Utf8Str strErrorInfo;
[71213]1982
[96617]1983 /*
[72045]1984 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyTo::Run
1985 * because the caller expects a ready-for-operation progress object on return.
1986 * The progress object will have a variable operation count, based on the elements to
1987 * be processed.
1988 */
1989
[97485]1990 if (mSources.empty())
[71976]1991 {
[97485]1992 strErrorInfo.printf(tr("No host sources specified"));
1993 vrc = VERR_INVALID_PARAMETER;
1994 }
1995 else if (mDest.isEmpty())
1996 {
[97151]1997 strErrorInfo.printf(tr("Guest destination must not be empty"));
[94935]1998 vrc = VERR_INVALID_PARAMETER;
[83607]1999 }
2000 else
2001 {
2002 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
2003 while (itSrc != mSources.end())
[83606]2004 {
[83607]2005 Utf8Str strSrc = itSrc->strSource;
2006 Utf8Str strDst = mDest;
[83606]2007
[97395]2008 bool fFollowSymlinks;
[71213]2009
[83607]2010 if (strSrc.isEmpty())
[60622]2011 {
[97151]2012 strErrorInfo.printf(tr("Host source entry must not be empty"));
[94935]2013 vrc = VERR_INVALID_PARAMETER;
[71213]2014 break;
2015 }
[83607]2016
[97395]2017 if (itSrc->enmType == FsObjType_Directory)
2018 {
2019 fFollowSymlinks = itSrc->fDirCopyFlags & DirectoryCopyFlag_FollowLinks;
2020 }
2021 else
2022 {
2023 fFollowSymlinks = RT_BOOL(itSrc->fFileCopyFlags & FileCopyFlag_FollowLinks);
2024 }
2025
2026 LogFlowFunc(("strSrc=%s (path style is %s), strDst=%s\n",
2027 strSrc.c_str(), GuestBase::pathStyleToStr(itSrc->enmPathStyle), strDst.c_str()));
2028
[83607]2029 RTFSOBJINFO srcFsObjInfo;
[97553]2030 vrc = RTPathQueryInfoEx(strSrc.c_str(), &srcFsObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK /* fFlags */);
[94935]2031 if (RT_FAILURE(vrc))
[71976]2032 {
[97151]2033 strErrorInfo.printf(tr("No such host file/directory: %s"), strSrc.c_str());
[71976]2034 break;
2035 }
[71213]2036
[97553]2037 switch (srcFsObjInfo.Attr.fMode & RTFS_TYPE_MASK)
[71213]2038 {
[97553]2039 case RTFS_TYPE_DIRECTORY:
[83607]2040 {
[97553]2041 if (itSrc->enmType != FsObjType_Directory)
2042 {
2043 strErrorInfo.printf(tr("Host source \"%s\" is not a file (is a directory)"), strSrc.c_str());
2044 vrc = VERR_NOT_A_FILE;
2045 }
[83607]2046 break;
2047 }
[97553]2048
2049 case RTFS_TYPE_FILE:
[60622]2050 {
[97553]2051 if (itSrc->enmType == FsObjType_Directory)
2052 {
2053 strErrorInfo.printf(tr("Host source \"%s\" is not a directory (is a file)"), strSrc.c_str());
2054 vrc = VERR_NOT_A_DIRECTORY;
2055 }
[83607]2056 break;
[71976]2057 }
[97553]2058
2059 case RTFS_TYPE_SYMLINK:
2060 {
2061 if (!fFollowSymlinks)
2062 {
2063 strErrorInfo.printf(tr("Host source \"%s\" is a symbolic link"), strSrc.c_str());
2064 vrc = VERR_IS_A_SYMLINK;
2065 break;
2066 }
2067
2068 char szPathReal[RTPATH_MAX];
2069 vrc = RTPathReal(strSrc.c_str(), szPathReal, sizeof(szPathReal));
2070 if (RT_SUCCESS(vrc))
2071 {
2072 vrc = RTPathQueryInfoEx(szPathReal, &srcFsObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
2073 if (RT_SUCCESS(vrc))
2074 {
[97655]2075 LogRel2(("Guest Control: Host source is a symbolic link \"%s\" -> \"%s\" (%s)\n",
[97553]2076 strSrc.c_str(), szPathReal,
2077 GuestBase::fsObjTypeToStr(GuestBase::fileModeToFsObjType(srcFsObjInfo.Attr.fMode))));
2078
2079 /* We want to keep the symbolic link name of the source instead of the target pointing to,
2080 * so don't touch the source's name here. */
2081 itSrc->enmType = GuestBase::fileModeToFsObjType(srcFsObjInfo.Attr.fMode);
2082 }
2083 else
2084 {
2085 strErrorInfo.printf(tr("Querying symbolic link info for host source \"%s\" failed"), strSrc.c_str());
2086 break;
2087 }
2088 }
2089 else
2090 {
2091 strErrorInfo.printf(tr("Resolving symbolic link for host source \"%s\" failed"), strSrc.c_str());
2092 break;
2093 }
2094 break;
2095 }
2096
2097 default:
2098 LogRel2(("Guest Control: Warning: Unknown host file system type %#x for source \"%s\", skipping\n",
2099 srcFsObjInfo.Attr.fMode & RTFS_TYPE_MASK, strSrc.c_str()));
2100 break;
[71976]2101 }
[71213]2102
[97553]2103 if (RT_FAILURE(vrc))
2104 break;
2105
[83607]2106 FsList *pFsList = NULL;
2107 try
[72045]2108 {
[83607]2109 pFsList = new FsList(*this);
[94935]2110 vrc = pFsList->Init(strSrc, strDst, *itSrc);
2111 if (RT_SUCCESS(vrc))
[83607]2112 {
[97395]2113 switch (itSrc->enmType)
[97139]2114 {
[97395]2115 case FsObjType_Directory:
2116 {
2117 char szPathReal[RTPATH_MAX];
2118 RTDIRENTRYEX DirEntry;
2119 vrc = pFsList->AddDirFromHost(strSrc /* strPath */, "" /* strSubDir */,
2120 szPathReal, sizeof(szPathReal), &DirEntry);
[97485]2121 break;
[97395]2122 }
2123
2124 case FsObjType_File:
2125 /* The file name is already part of the actual list's source root (strSrc). */
2126 break;
2127
[97553]2128 case FsObjType_Symlink:
2129 AssertFailed(); /* Should never get here, as we do the resolving above. */
2130 break;
2131
[97395]2132 default:
[97553]2133 LogRel2(("Guest Control: Warning: Unknown source type %#x for host source \"%s\", skipping\n",
[97395]2134 itSrc->enmType, strSrc.c_str()));
2135 break;
[97139]2136 }
[83607]2137 }
2138
[94935]2139 if (RT_FAILURE(vrc))
[83607]2140 {
2141 delete pFsList;
[97655]2142 strErrorInfo.printf(tr("Error adding host source \"%s\" to list: %Rrc"),
[97151]2143 strSrc.c_str(), vrc);
[83607]2144 break;
2145 }
[97446]2146#ifdef DEBUG
[97395]2147 pFsList->DumpToLog();
[97446]2148#endif
[83607]2149 mVecLists.push_back(pFsList);
2150 }
2151 catch (std::bad_alloc &)
2152 {
[94935]2153 vrc = VERR_NO_MEMORY;
[71976]2154 break;
[72045]2155 }
[71213]2156
[83607]2157 AssertPtr(pFsList);
2158 cOperations += (ULONG)pFsList->mVecEntries.size();
2159
2160 itSrc++;
[71976]2161 }
2162 }
[71213]2163
[97485]2164 if (RT_SUCCESS(vrc))
[71976]2165 {
[97485]2166 /* When there are no entries in the first source list, this means the source only contains a single file
2167 * (see \a mSrcRootAbs of FsList). So use \a mSrcRootAbs directly. */
2168 Utf8Str const &strFirstOp = mVecLists[0]->mVecEntries.size() > 0
2169 ? mVecLists[0]->mVecEntries[0]->strPath : mVecLists[0]->mSrcRootAbs;
[71213]2170
[97481]2171 /* Now that we know how many objects we're handling, tweak the progress description so that it
2172 * reflects more accurately what the progress is actually doing. */
2173 if (cOperations > 1)
2174 {
2175 mDesc.printf(tr("Copying \"%s\" [and %zu %s] from host to \"%s\" on the guest ..."),
2176 strFirstOp.c_str(), cOperations - 1, cOperations > 2 ? tr("others") : tr("other"), mDest.c_str());
2177 }
2178 else
2179 mDesc.printf(tr("Copying \"%s\" from host to \"%s\" on the guest ..."), strFirstOp.c_str(), mDest.c_str());
2180
[94935]2181 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
[97485]2182 TRUE /* aCancelable */, cOperations + 1/* Number of operations */,
[97481]2183 Bstr(strFirstOp).raw());
[71213]2184 }
[97485]2185 else /* On error we go with an "empty" progress object when will be used for error handling. */
[94935]2186 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
2187 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
[71213]2188
[97485]2189 if (FAILED(hrc)) /* Progress object creation failed -- we're doomed. */
2190 return hrc;
2191
[94935]2192 if (RT_FAILURE(vrc))
[72045]2193 {
[83607]2194 if (strErrorInfo.isEmpty())
[97151]2195 strErrorInfo.printf(tr("Failed with %Rrc"), vrc);
[97553]2196 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
[72045]2197 }
2198
[94935]2199 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hrc, vrc));
2200 return hrc;
[71213]2201}
2202
[92897]2203/** @copydoc GuestSessionTask::Run */
[71976]2204int GuestSessionTaskCopyTo::Run(void)
[71213]2205{
2206 LogFlowThisFuncEnter();
2207
2208 AutoCaller autoCaller(mSession);
[98262]2209 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[71213]2210
[94935]2211 int vrc = VINF_SUCCESS;
[71213]2212
[71976]2213 FsLists::const_iterator itList = mVecLists.begin();
2214 while (itList != mVecLists.end())
[71817]2215 {
[71976]2216 FsList *pList = *itList;
2217 AssertPtr(pList);
2218
[97344]2219 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
2220
[83336]2221 Utf8Str strSrcRootAbs = pList->mSrcRootAbs;
2222 Utf8Str strDstRootAbs = pList->mDstRootAbs;
2223
[97531]2224 vrc = GuestPath::BuildDestinationPath(strSrcRootAbs, PATH_STYLE_NATIVE /* Source */,
2225 strDstRootAbs, mSession->i_getGuestPathStyle() /* Dest */);
[97344]2226 if (RT_FAILURE(vrc))
[97395]2227 {
2228 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97531]2229 Utf8StrFmt(tr("Building guest destination root path \"%s\" failed: %Rrc"),
[97399]2230 strDstRootAbs.c_str(), vrc));
[97344]2231 break;
[97395]2232 }
[97344]2233
[97395]2234 bool fCopyIntoExisting;
2235 bool fFollowSymlinks;
[71976]2236
[97395]2237 if (pList->mSourceSpec.enmType == FsObjType_Directory)
2238 {
2239 fCopyIntoExisting = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting);
2240 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks);
2241 }
2242 else if (pList->mSourceSpec.enmType == FsObjType_File)
2243 {
2244 fCopyIntoExisting = !RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_NoReplace);
2245 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks);
2246 }
2247 else
2248 AssertFailedBreakStmt(vrc = VERR_NOT_IMPLEMENTED);
2249
2250 uint32_t const fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
2251
2252 bool fDstExists = true;
2253
[83336]2254 GuestFsObjData dstObjData;
[94935]2255 int vrcGuest;
[99252]2256 vrc = mSession->i_fsObjQueryInfo(strDstRootAbs, fFollowSymlinks, dstObjData, &vrcGuest);
[94935]2257 if (RT_FAILURE(vrc))
[83336]2258 {
[94935]2259 if (vrc == VERR_GSTCTL_GUEST_ERROR)
[83336]2260 {
[94935]2261 switch (vrcGuest)
[83336]2262 {
2263 case VERR_PATH_NOT_FOUND:
2264 RT_FALL_THROUGH();
2265 case VERR_FILE_NOT_FOUND:
[97395]2266 {
2267 fDstExists = false;
2268 vrc = VINF_SUCCESS;
[83336]2269 break;
[97395]2270 }
[83336]2271 default:
2272 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97655]2273 Utf8StrFmt(tr("Querying information on guest for \"%s\" failed: %Rrc"),
[94935]2274 strDstRootAbs.c_str(), vrcGuest));
[83336]2275 break;
2276 }
2277 }
2278 else
2279 {
2280 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97655]2281 Utf8StrFmt(tr("Querying information on guest for \"%s\" failed: %Rrc"),
[94935]2282 strDstRootAbs.c_str(), vrc));
[83336]2283 break;
2284 }
2285 }
[71976]2286
[72001]2287 if (pList->mSourceSpec.enmType == FsObjType_Directory)
[71827]2288 {
[97474]2289 LogFlowFunc(("Directory: fDirCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool -> fDstExist=%RTbool (%s)\n",
2290 pList->mSourceSpec.fDirCopyFlags, fCopyIntoExisting, fFollowSymlinks,
2291 fDstExists, GuestBase::fsObjTypeToStr(dstObjData.mType)));
[83336]2292
[97395]2293 if (fDstExists)
[77500]2294 {
[97395]2295 switch (dstObjData.mType)
[83336]2296 {
[97395]2297 case FsObjType_Directory:
[92544]2298 {
[97395]2299 if (!fCopyIntoExisting)
2300 {
2301 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2302 Utf8StrFmt(tr("Guest root directory \"%s\" already exists"),
2303 strDstRootAbs.c_str()));
2304 vrc = VERR_ALREADY_EXISTS;
2305 }
2306 break;
[92544]2307 }
[97395]2308
2309 case FsObjType_File:
[92544]2310 {
2311 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97395]2312 Utf8StrFmt(tr("Destination \"%s\" on guest already exists and is a file"),
[92544]2313 strDstRootAbs.c_str()));
[97395]2314 vrc = VERR_IS_A_FILE;
[103244]2315 break;
[92544]2316 }
[97395]2317
2318 case FsObjType_Symlink:
[97476]2319 /** @todo Resolve symlinks? */
[97395]2320 break;
2321
2322 default:
2323 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2324 Utf8StrFmt(tr("Unknown object type (%#x) on guest for \"%s\""),
2325 dstObjData.mType, strDstRootAbs.c_str()));
2326 vrc = VERR_NOT_SUPPORTED;
2327 break;
[83336]2328 }
[97395]2329 }
[92544]2330
[97395]2331 if (RT_FAILURE(vrc))
2332 break;
[92544]2333
[83336]2334 /* Make sure the destination root directory exists. */
[97395]2335 if (pList->mSourceSpec.fDryRun == false)
[83336]2336 {
[97395]2337 vrc = directoryCreateOnGuest(strDstRootAbs, fDirMode, DirectoryCreateFlag_None,
2338 fFollowSymlinks, fCopyIntoExisting);
2339 if (RT_FAILURE(vrc))
2340 break;
[83336]2341 }
2342
[97395]2343 /* Walk the entries. */
2344 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
2345 while ( RT_SUCCESS(vrc)
2346 && itEntry != pList->mVecEntries.end())
2347 {
2348 FsEntry *pEntry = *itEntry;
2349 AssertPtr(pEntry);
[83336]2350
[97395]2351 Utf8Str strSrcAbs = strSrcRootAbs;
2352 Utf8Str strDstAbs = strDstRootAbs;
[83336]2353
[97344]2354 strSrcAbs += PATH_STYLE_SEP_STR(PATH_STYLE_NATIVE);
2355 strSrcAbs += pEntry->strPath;
[83336]2356
[97344]2357 strDstAbs += PATH_STYLE_SEP_STR(mSession->i_getGuestPathStyle());
2358 strDstAbs += pEntry->strPath;
[71213]2359
[97531]2360 /* Clean up the final host source path. */
[97538]2361 vrc = GuestPath::Translate(strSrcAbs, pList->mSourceSpec.enmPathStyle /* Source */,
2362 pList->mSourceSpec.enmPathStyle /* Dest */);
[97395]2363 if (RT_FAILURE(vrc))
[83336]2364 {
[97395]2365 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97399]2366 Utf8StrFmt(tr("Translating host source path\"%s\" failed: %Rrc"),
2367 strSrcAbs.c_str(), vrc));
[71976]2368 break;
[83336]2369 }
[71213]2370
[97538]2371 /* Translate final guest destination path. */
[97395]2372 vrc = GuestPath::Translate(strDstAbs,
[97538]2373 PATH_STYLE_NATIVE /* Source */, mSession->i_getGuestPathStyle() /* Dest */);
[97395]2374 if (RT_FAILURE(vrc))
[83336]2375 {
[97395]2376 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[97399]2377 Utf8StrFmt(tr("Translating guest destination path \"%s\" failed: %Rrc"),
2378 strDstAbs.c_str(), vrc));
[71976]2379 break;
[83336]2380 }
[71213]2381
[97395]2382 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
2383
2384 switch (pEntry->fMode & RTFS_TYPE_MASK)
2385 {
2386 case RTFS_TYPE_DIRECTORY:
2387 {
[97655]2388 LogRel2(("Guest Control: Copying directory \"%s\" from host to \"%s\" on guest ...\n",
2389 strSrcAbs.c_str(), strDstAbs.c_str()));
[97395]2390 if (!pList->mSourceSpec.fDryRun)
2391 vrc = directoryCreateOnGuest(strDstAbs, fDirMode, DirectoryCreateFlag_None,
2392 fFollowSymlinks, fCopyIntoExisting);
2393 break;
2394 }
2395
2396 case RTFS_TYPE_FILE:
2397 {
2398 if (!pList->mSourceSpec.fDryRun)
2399 vrc = fileCopyToGuest(strSrcAbs, strDstAbs, pList->mSourceSpec.fFileCopyFlags);
2400 break;
2401 }
2402
2403 default:
[97655]2404 LogRel2(("Guest Control: Warning: Host file system type 0x%x for \"%s\" is not supported, skipping\n",
[97395]2405 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
2406 break;
2407 }
2408
2409 if (RT_FAILURE(vrc))
[71976]2410 break;
[97395]2411
2412 ++itEntry;
[71976]2413 }
[97395]2414 }
2415 else if (pList->mSourceSpec.enmType == FsObjType_File)
2416 {
[97474]2417 LogFlowFunc(("File: fFileCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool -> fDstExist=%RTbool (%s)\n",
2418 pList->mSourceSpec.fFileCopyFlags, fCopyIntoExisting, fFollowSymlinks,
2419 fDstExists, GuestBase::fsObjTypeToStr(dstObjData.mType)));
[71817]2420
[97395]2421 if (fDstExists)
2422 {
2423 switch (dstObjData.mType)
2424 {
2425 case FsObjType_Directory:
2426 {
[97474]2427 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2428 Utf8StrFmt(tr("Destination \"%s\" on the guest already exists and is a directory"),
2429 strDstRootAbs.c_str()));
2430 vrc = VERR_IS_A_DIRECTORY;
[97395]2431 break;
2432 }
2433
2434 case FsObjType_File:
2435 {
2436 if (!fCopyIntoExisting)
2437 {
2438 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2439 Utf8StrFmt(tr("Guest file \"%s\" already exists"), strDstRootAbs.c_str()));
2440 vrc = VERR_ALREADY_EXISTS;
2441 }
2442 break;
2443 }
2444
2445 default:
2446 {
2447 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2448 Utf8StrFmt(tr("Unsupported guest file system type (%#x) for \"%s\""),
2449 dstObjData.mType, strDstRootAbs.c_str()));
2450 vrc = VERR_NOT_SUPPORTED;
2451 break;
2452 }
2453 }
2454 }
2455
[97541]2456 if (RT_SUCCESS(vrc))
[97395]2457 {
[97541]2458 /* Translate the final guest destination file path. */
2459 vrc = GuestPath::Translate(strDstRootAbs,
2460 PATH_STYLE_NATIVE /* Source */, mSession->i_getGuestPathStyle() /* Dest */);
2461 if (RT_FAILURE(vrc))
2462 {
2463 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2464 Utf8StrFmt(tr("Translating guest destination path \"%s\" failed: %Rrc"),
2465 strDstRootAbs.c_str(), vrc));
2466 break;
2467 }
2468
2469 if (!pList->mSourceSpec.fDryRun)
2470 vrc = fileCopyToGuest(strSrcRootAbs, strDstRootAbs, pList->mSourceSpec.fFileCopyFlags);
[97395]2471 }
[71976]2472 }
[97395]2473 else
2474 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
[71213]2475
[94935]2476 if (RT_FAILURE(vrc))
[71976]2477 break;
[71213]2478
[71976]2479 ++itList;
2480 }
[71213]2481
[94935]2482 if (RT_SUCCESS(vrc))
2483 vrc = setProgressSuccess();
[71533]2484
[94935]2485 LogFlowFuncLeaveRC(vrc);
2486 return vrc;
[42566]2487}
2488
[71976]2489GuestSessionTaskUpdateAdditions::GuestSessionTaskUpdateAdditions(GuestSession *pSession,
2490 const Utf8Str &strSource,
2491 const ProcessArguments &aArguments,
2492 uint32_t fFlags)
2493 : GuestSessionTask(pSession)
[42693]2494{
[71817]2495 m_strTaskName = "gctlUpGA";
2496
2497 mSource = strSource;
[46524]2498 mArguments = aArguments;
[71817]2499 mFlags = fFlags;
[42693]2500}
2501
[71976]2502GuestSessionTaskUpdateAdditions::~GuestSessionTaskUpdateAdditions(void)
[42693]2503{
2504
2505}
2506
[92897]2507/**
2508 * Adds arguments to existing process arguments.
2509 * Identical / already existing arguments will be filtered out.
2510 *
2511 * @returns VBox status code.
2512 * @param aArgumentsDest Destination to add arguments to.
2513 * @param aArgumentsSource Arguments to add.
2514 */
[71976]2515int GuestSessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
[46524]2516{
2517 try
2518 {
2519 /* Filter out arguments which already are in the destination to
2520 * not end up having them specified twice. Not the fastest method on the
2521 * planet but does the job. */
2522 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
2523 while (itSource != aArgumentsSource.end())
2524 {
2525 bool fFound = false;
2526 ProcessArguments::iterator itDest = aArgumentsDest.begin();
2527 while (itDest != aArgumentsDest.end())
2528 {
2529 if ((*itDest).equalsIgnoreCase((*itSource)))
2530 {
2531 fFound = true;
2532 break;
2533 }
[56030]2534 ++itDest;
[46524]2535 }
2536
2537 if (!fFound)
2538 aArgumentsDest.push_back((*itSource));
2539
[56030]2540 ++itSource;
[46524]2541 }
2542 }
2543 catch(std::bad_alloc &)
2544 {
2545 return VERR_NO_MEMORY;
2546 }
2547
[94935]2548 return VINF_SUCCESS;
[46524]2549}
2550
[92897]2551/**
2552 * Helper function to copy a file from a VISO to the guest.
2553 *
2554 * @returns VBox status code.
2555 * @param pSession Guest session to use.
2556 * @param hVfsIso VISO handle to use.
2557 * @param strFileSrc Source file path on VISO to copy.
2558 * @param strFileDst Destination file path on guest.
2559 * @param fOptional When set to \c true, the file is optional, i.e. can be skipped
2560 * when not found, \c false if not.
2561 */
[72958]2562int GuestSessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, RTVFS hVfsIso,
[91745]2563 Utf8Str const &strFileSrc, const Utf8Str &strFileDst, bool fOptional)
[42749]2564{
2565 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
[72958]2566 AssertReturn(hVfsIso != NIL_RTVFS, VERR_INVALID_POINTER);
[42749]2567
[72958]2568 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
[94935]2569 int vrc = RTVfsFileOpen(hVfsIso, strFileSrc.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFile);
2570 if (RT_SUCCESS(vrc))
[42749]2571 {
[72960]2572 uint64_t cbSrcSize = 0;
[94935]2573 vrc = RTVfsFileQuerySize(hVfsFile, &cbSrcSize);
2574 if (RT_SUCCESS(vrc))
[72958]2575 {
2576 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
[84648]2577 strFileSrc.c_str(), strFileDst.c_str()));
[42749]2578
[72958]2579 GuestFileOpenInfo dstOpenInfo;
[84648]2580 dstOpenInfo.mFilename = strFileDst;
[72958]2581 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
2582 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
2583 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
[71817]2584
[79189]2585 ComObjPtr<GuestFile> dstFile;
[94935]2586 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2587 vrc = mSession->i_fileOpen(dstOpenInfo, dstFile, &vrcGuest);
2588 if (RT_FAILURE(vrc))
[42749]2589 {
[94935]2590 switch (vrc)
[72958]2591 {
2592 case VERR_GSTCTL_GUEST_ERROR:
[94935]2593 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(vrcGuest, strFileDst.c_str()));
[72958]2594 break;
[58521]2595
[72958]2596 default:
2597 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]2598 Utf8StrFmt(tr("Guest file \"%s\" could not be opened: %Rrc"),
[94935]2599 strFileDst.c_str(), vrc));
[72958]2600 break;
2601 }
[58521]2602 }
[72958]2603 else
2604 {
[94935]2605 vrc = fileCopyToGuestInner(strFileSrc, hVfsFile, strFileDst, dstFile, FileCopyFlag_None, 0 /*offCopy*/, cbSrcSize);
[43162]2606
[97626]2607 int vrc2 = fileClose(dstFile);
2608 if (RT_SUCCESS(vrc))
2609 vrc = vrc2;
[72958]2610 }
[42749]2611 }
2612
[72958]2613 RTVfsFileRelease(hVfsFile);
[42749]2614 }
[91745]2615 else if (fOptional)
[94935]2616 vrc = VINF_SUCCESS;
[42749]2617
[94935]2618 return vrc;
[42749]2619}
2620
[92897]2621/**
2622 * Helper function to run (start) a file on the guest.
2623 *
2624 * @returns VBox status code.
2625 * @param pSession Guest session to use.
2626 * @param procInfo Guest process startup info to use.
[98729]2627 * @param fSilent Whether to set progress into failure state in case of error.
[92897]2628 */
[98729]2629int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo, bool fSilent)
[42749]2630{
2631 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2632
[43162]2633 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
[42749]2634
[98526]2635 GuestProcessToolbox procToRun;
[94935]2636 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[98526]2637 int vrc = procToRun.init(pSession, procInfo, false /* Async */, &vrcGuest);
[43162]2638 if (RT_SUCCESS(vrc))
[42749]2639 {
[94935]2640 if (RT_SUCCESS(vrcGuest))
[98526]2641 vrc = procToRun.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &vrcGuest);
[43162]2642 if (RT_SUCCESS(vrc))
[98526]2643 vrc = procToRun.getTerminationStatus();
[43162]2644 }
[42749]2645
[98729]2646 if ( RT_FAILURE(vrc)
2647 && !fSilent)
[43162]2648 {
2649 switch (vrc)
[42749]2650 {
[73036]2651 case VERR_GSTCTL_PROCESS_EXIT_CODE:
[42749]2652 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]2653 Utf8StrFmt(tr("Running update file \"%s\" on guest failed: %Rrc"),
[98526]2654 procInfo.mExecutable.c_str(), procToRun.getRc()));
[43162]2655 break;
2656
[45078]2657 case VERR_GSTCTL_GUEST_ERROR:
[90828]2658 setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Running update file on guest failed"),
[94935]2659 GuestErrorInfo(GuestErrorInfo::Type_Process, vrcGuest, procInfo.mExecutable.c_str()));
[43162]2660 break;
2661
[98272]2662 case VERR_INVALID_STATE: /** @todo Special guest control vrc needed! */
[43299]2663 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]2664 Utf8StrFmt(tr("Update file \"%s\" reported invalid running state"),
[55535]2665 procInfo.mExecutable.c_str()));
[43299]2666 break;
2667
[43162]2668 default:
2669 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
[90828]2670 Utf8StrFmt(tr("Error while running update file \"%s\" on guest: %Rrc"),
[55535]2671 procInfo.mExecutable.c_str(), vrc));
[43162]2672 break;
[42749]2673 }
2674 }
2675
[43162]2676 return vrc;
[42749]2677}
2678
[97540]2679/**
[98690]2680 * Helper function which checks Guest Additions installation status.
2681 *
2682 * @returns IPRT status code.
2683 * @param pSession Guest session to use.
2684 * @param osType Guest type.
2685 */
2686int GuestSessionTaskUpdateAdditions::checkGuestAdditionsStatus(GuestSession *pSession, eOSType osType)
2687{
2688 int vrc = VINF_SUCCESS;
[98692]2689 HRESULT hrc;
[98690]2690
2691 if (osType == eOSType_Linux)
2692 {
[98729]2693 const Utf8Str ksStatusScript = Utf8Str("/sbin/rcvboxadd");
[98690]2694
2695 /* Check if Guest Additions kernel modules were loaded. */
2696 GuestProcessStartupInfo procInfo;
2697 procInfo.mFlags = ProcessCreateFlag_None;
2698 procInfo.mExecutable = Utf8Str("/bin/sh");;
2699 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
2700 procInfo.mArguments.push_back(ksStatusScript);
2701 procInfo.mArguments.push_back("status-kernel");
2702
[98729]2703 vrc = runFileOnGuest(pSession, procInfo, true /* fSilent */);
[98690]2704 if (RT_SUCCESS(vrc))
2705 {
2706 /* Replace the last argument with corresponding value and check
2707 * if Guest Additions user services were started. */
2708 procInfo.mArguments.pop_back();
2709 procInfo.mArguments.push_back("status-user");
2710
[98729]2711 vrc = runFileOnGuest(pSession, procInfo, true /* fSilent */);
[98690]2712 if (RT_FAILURE(vrc))
2713 hrc = setProgressErrorMsg(VBOX_E_GSTCTL_GUEST_ERROR,
2714 Utf8StrFmt(tr("Automatic update of Guest Additions has failed: "
2715 "files were installed, but user services were not reloaded automatically. "
2716 "Please consider rebooting the guest")));
2717 }
2718 else
2719 hrc = setProgressErrorMsg(VBOX_E_GSTCTL_GUEST_ERROR,
2720 Utf8StrFmt(tr("Automatic update of Guest Additions has failed: "
2721 "files were installed, but kernel modules were not reloaded automatically. "
2722 "Please consider rebooting the guest")));
2723 }
[98780]2724
[98690]2725 return vrc;
2726}
2727
2728/**
[97540]2729 * Helper function which waits until Guest Additions services started.
2730 *
2731 * @returns 0 on success or VERR_TIMEOUT if guest services were not
2732 * started on time.
2733 * @param pGuest Guest interface to use.
[98690]2734 * @param osType Guest type.
[97540]2735 */
[98690]2736int GuestSessionTaskUpdateAdditions::waitForGuestSession(ComObjPtr<Guest> pGuest, eOSType osType)
[97540]2737{
2738 int vrc = VERR_GSTCTL_GUEST_ERROR;
[98272]2739 int vrcRet = VERR_TIMEOUT;
[97540]2740
2741 uint64_t tsStart = RTTimeSystemMilliTS();
[98272]2742 const uint64_t cMsTimeout = 10 * RT_MS_1MIN;
[97540]2743
2744 AssertReturn(!pGuest.isNull(), VERR_TIMEOUT);
2745
2746 do
2747 {
2748 ComObjPtr<GuestSession> pSession;
2749 GuestCredentials guestCreds;
2750 GuestSessionStartupInfo startupInfo;
2751
2752 startupInfo.mName = "Guest Additions connection checker";
2753 startupInfo.mOpenTimeoutMS = 100;
2754
2755 vrc = pGuest->i_sessionCreate(startupInfo, guestCreds, pSession);
2756 if (RT_SUCCESS(vrc))
2757 {
2758 Assert(!pSession.isNull());
2759
[98272]2760 int vrcGuest = VERR_GSTCTL_GUEST_ERROR; /* unused. */
[97540]2761 vrc = pSession->i_startSession(&vrcGuest);
2762 if (RT_SUCCESS(vrc))
2763 {
[98272]2764 /* Wait for VBoxService to start. */
[97540]2765 GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None;
[98272]2766 int vrcGuest2 = VINF_SUCCESS; /* unused. */
2767 vrc = pSession->i_waitFor(GuestSessionWaitForFlag_Start, 100 /* timeout, ms */, enmWaitResult, &vrcGuest2);
[97540]2768 if (RT_SUCCESS(vrc))
2769 {
[98690]2770 /* Make sure Guest Additions were reloaded on the guest side. */
2771 vrc = checkGuestAdditionsStatus(pSession, osType);
2772 if (RT_SUCCESS(vrc))
2773 LogRel(("Guest Additions were successfully reloaded after installation\n"));
2774 else
2775 LogRel(("Guest Additions were failed to reload after installation, please consider rebooting the guest\n"));
2776
[97540]2777 vrc = pSession->Close();
[98272]2778 vrcRet = VINF_SUCCESS;
[97540]2779 break;
2780 }
2781 }
2782
2783 vrc = pSession->Close();
2784 }
2785
2786 RTThreadSleep(100);
2787
[98272]2788 } while ((RTTimeSystemMilliTS() - tsStart) < cMsTimeout);
[97540]2789
[98272]2790 return vrcRet;
[97540]2791}
2792
[92897]2793/** @copydoc GuestSessionTask::Run */
[71976]2794int GuestSessionTaskUpdateAdditions::Run(void)
[42693]2795{
2796 LogFlowThisFuncEnter();
2797
2798 ComObjPtr<GuestSession> pSession = mSession;
2799 Assert(!pSession.isNull());
2800
2801 AutoCaller autoCaller(pSession);
[98262]2802 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[42693]2803
[94935]2804 int vrc = setProgress(10);
2805 if (RT_FAILURE(vrc))
2806 return vrc;
[42693]2807
[94935]2808 HRESULT hrc = S_OK;
[42693]2809
2810 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
2811
[50727]2812 ComObjPtr<Guest> pGuest(mSession->i_getParent());
[43030]2813#if 0
[42693]2814 /*
[43001]2815 * Wait for the guest being ready within 30 seconds.
[42693]2816 */
[43001]2817 AdditionsRunLevelType_T addsRunLevel;
2818 uint64_t tsStart = RTTimeSystemMilliTS();
[94935]2819 while ( SUCCEEDED(hrc = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
[43001]2820 && ( addsRunLevel != AdditionsRunLevelType_Userland
2821 && addsRunLevel != AdditionsRunLevelType_Desktop))
2822 {
2823 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
2824 {
[94935]2825 vrc = VERR_TIMEOUT;
[43001]2826 break;
2827 }
[42693]2828
[43001]2829 RTThreadSleep(100); /* Wait a bit. */
2830 }
2831
[94935]2832 if (FAILED(hrc)) vrc = VERR_TIMEOUT;
2833 if (vrc == VERR_TIMEOUT)
2834 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2835 Utf8StrFmt(tr("Guest Additions were not ready within time, giving up")));
[43030]2836#else
2837 /*
2838 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
2839 * can continue.
2840 */
2841 AdditionsRunLevelType_T addsRunLevel;
[94935]2842 if ( FAILED(hrc = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
[43030]2843 || ( addsRunLevel != AdditionsRunLevelType_Userland
2844 && addsRunLevel != AdditionsRunLevelType_Desktop))
2845 {
[43034]2846 if (addsRunLevel == AdditionsRunLevelType_System)
[94935]2847 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2848 Utf8StrFmt(tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
[43034]2849 else
[94935]2850 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2851 Utf8StrFmt(tr("Guest Additions not installed or ready, aborting automatic update")));
2852 vrc = VERR_NOT_SUPPORTED;
[43030]2853 }
2854#endif
[43001]2855
[94935]2856 if (RT_SUCCESS(vrc))
[42693]2857 {
[43001]2858 /*
2859 * Determine if we are able to update automatically. This only works
2860 * if there are recent Guest Additions installed already.
2861 */
2862 Utf8Str strAddsVer;
[94935]2863 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2864 if ( RT_SUCCESS(vrc)
[43001]2865 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
2866 {
[94935]2867 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2868 Utf8StrFmt(tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
2869 strAddsVer.c_str()));
2870 vrc = VERR_NOT_SUPPORTED;
[43001]2871 }
[43030]2872 }
[42923]2873
[43053]2874 Utf8Str strOSVer;
[43493]2875 eOSType osType = eOSType_Unknown;
[94935]2876 if (RT_SUCCESS(vrc))
[43030]2877 {
[43001]2878 /*
2879 * Determine guest OS type and the required installer image.
2880 */
[101381]2881/** @todo r=bird: Why are we using the guest properties for this instead of the
2882 * reported guest VBOXOSTYPE/ID? Since we've got guest properties, we must
2883 * have GAs, so the guest additions must've reported the guest OS type. That
2884 * would allow proper OS categorization by family ID instead of this ridiculous
2885 * naive code assuming anything that isn't windows or solaris must be linux. */
[43001]2886 Utf8Str strOSType;
[94935]2887 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
2888 if (RT_SUCCESS(vrc))
[43001]2889 {
[101381]2890 if ( strOSType.contains(GUEST_OS_ID_STR_PARTIAL("Microsoft"), Utf8Str::CaseInsensitive)
2891 || strOSType.contains(GUEST_OS_ID_STR_PARTIAL("Windows"), Utf8Str::CaseInsensitive))
[43001]2892 {
[43002]2893 osType = eOSType_Windows;
[43053]2894
2895 /*
2896 * Determine guest OS version.
2897 */
[94935]2898 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
2899 if (RT_FAILURE(vrc))
[43053]2900 {
[94935]2901 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2902 Utf8StrFmt(tr("Unable to detected guest OS version, please update manually")));
2903 vrc = VERR_NOT_SUPPORTED;
[43053]2904 }
2905
2906 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
2907 * can't do automated updates here. */
[43060]2908 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
[94935]2909 if ( RT_SUCCESS(vrc)
[45572]2910 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
[43053]2911 {
[45572]2912 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
2913 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
[43060]2914 {
[45572]2915 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
2916 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
2917 * flag is set this update routine ends successfully as soon as the installer was started
2918 * (and the user has to deal with it in the guest). */
2919 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2920 {
[94935]2921 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2922 Utf8StrFmt(tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
2923 vrc = VERR_NOT_SUPPORTED;
[45572]2924 }
[43060]2925 }
[43053]2926 }
[45572]2927 else
2928 {
[94935]2929 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2930 Utf8StrFmt(tr("%s (%s) not supported for automatic updating, please update manually"),
2931 strOSType.c_str(), strOSVer.c_str()));
2932 vrc = VERR_NOT_SUPPORTED;
[45572]2933 }
[43001]2934 }
[101381]2935 else if (strOSType.contains(GUEST_OS_ID_STR_PARTIAL("Solaris"), Utf8Str::CaseInsensitive))
[43001]2936 {
[43002]2937 osType = eOSType_Solaris;
[43001]2938 }
2939 else /* Everything else hopefully means Linux :-). */
[43002]2940 osType = eOSType_Linux;
[43001]2941
[94935]2942 if ( RT_SUCCESS(vrc)
[83870]2943 && ( osType != eOSType_Windows
2944 && osType != eOSType_Linux))
[84551]2945 /** @todo Support Solaris. */
[43001]2946 {
[94935]2947 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2948 Utf8StrFmt(tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
2949 strOSType.c_str()));
2950 vrc = VERR_NOT_SUPPORTED;
[43001]2951 }
2952 }
[42693]2953 }
2954
[94935]2955 if (RT_SUCCESS(vrc))
[42693]2956 {
2957 /*
[42923]2958 * Try to open the .ISO file to extract all needed files.
[42693]2959 */
[72958]2960 RTVFSFILE hVfsFileIso;
[94935]2961 vrc = RTVfsFileOpenNormal(mSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFileIso);
2962 if (RT_FAILURE(vrc))
[42693]2963 {
[94935]2964 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2965 Utf8StrFmt(tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
2966 mSource.c_str(), vrc));
[83250]2967 }
2968 else
2969 {
[72958]2970 RTVFS hVfsIso;
[94935]2971 vrc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, NULL);
2972 if (RT_FAILURE(vrc))
[72958]2973 {
[94935]2974 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2975 Utf8StrFmt(tr("Unable to open file as ISO 9660 file system volume: %Rrc"), vrc));
[72958]2976 }
2977 else
2978 {
[83870]2979 Utf8Str strUpdateDir;
[42923]2980
[94935]2981 vrc = setProgress(5);
2982 if (RT_SUCCESS(vrc))
[42923]2983 {
[72958]2984 /* Try getting the installed Guest Additions version to know whether we
2985 * can install our temporary Guest Addition data into the original installation
2986 * directory.
2987 *
2988 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
2989 * a different location then.
2990 */
2991 bool fUseInstallDir = false;
[43001]2992
[72958]2993 Utf8Str strAddsVer;
[94935]2994 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2995 if ( RT_SUCCESS(vrc)
[72958]2996 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
[42693]2997 {
[72958]2998 fUseInstallDir = true;
2999 }
3000
3001 if (fUseInstallDir)
3002 {
[94935]3003 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
3004 if (RT_SUCCESS(vrc))
[43001]3005 {
[83870]3006 if (strUpdateDir.isNotEmpty())
[72958]3007 {
[83870]3008 if (osType == eOSType_Windows)
3009 {
3010 strUpdateDir.findReplace('/', '\\');
3011 strUpdateDir.append("\\Update\\");
3012 }
3013 else
3014 strUpdateDir.append("/update/");
[72958]3015 }
[83870]3016 /* else Older Guest Additions might not handle this property correctly. */
[43001]3017 }
[83870]3018 /* Ditto. */
[42693]3019 }
[83870]3020
3021 /** @todo Set fallback installation directory. Make this a *lot* smarter. Later. */
3022 if (strUpdateDir.isEmpty())
3023 {
3024 if (osType == eOSType_Windows)
3025 strUpdateDir = "C:\\Temp\\";
3026 else
3027 strUpdateDir = "/tmp/";
3028 }
[42693]3029 }
3030
[72958]3031 /* Create the installation directory. */
[94935]3032 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3033 if (RT_SUCCESS(vrc))
[43162]3034 {
[83870]3035 LogRel(("Guest Additions update directory is: %s\n", strUpdateDir.c_str()));
3036
[94935]3037 vrc = pSession->i_directoryCreate(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &vrcGuest);
3038 if (RT_FAILURE(vrc))
[72958]3039 {
[94935]3040 switch (vrc)
[83870]3041 {
3042 case VERR_GSTCTL_GUEST_ERROR:
[94935]3043 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Creating installation directory on guest failed"),
3044 GuestErrorInfo(GuestErrorInfo::Type_Directory, vrcGuest, strUpdateDir.c_str()));
[83870]3045 break;
[43162]3046
[83870]3047 default:
[94935]3048 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3049 Utf8StrFmt(tr("Creating installation directory \"%s\" on guest failed: %Rrc"),
3050 strUpdateDir.c_str(), vrc));
[83870]3051 break;
3052 }
[72958]3053 }
[43162]3054 }
[83870]3055
[94935]3056 if (RT_SUCCESS(vrc))
3057 vrc = setProgress(10);
[42937]3058
[94935]3059 if (RT_SUCCESS(vrc))
[42923]3060 {
[72958]3061 /* Prepare the file(s) we want to copy over to the guest and
3062 * (maybe) want to run. */
3063 switch (osType)
[42937]3064 {
[72958]3065 case eOSType_Windows:
[43001]3066 {
[72958]3067 /* Do we need to install our certificates? We do this for W2K and up. */
3068 bool fInstallCert = false;
[43001]3069
[72958]3070 /* Only Windows 2000 and up need certificates to be installed. */
3071 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
[64939]3072 {
[72958]3073 fInstallCert = true;
3074 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
3075 }
3076 else
3077 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
3078
3079 if (fInstallCert)
[64939]3080 {
[72958]3081 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
3082 {
[81453]3083 { "vbox.cer", "/CERT/VBOX.CER" },
3084 { "vbox-sha1.cer", "/CERT/VBOX-SHA1.CER" },
3085 { "vbox-sha256.cer", "/CERT/VBOX-SHA256.CER" },
3086 { "vbox-sha256-r3.cer", "/CERT/VBOX-SHA256-R3.CER" },
3087 { "oracle-vbox.cer", "/CERT/ORACLE-VBOX.CER" },
[72958]3088 };
3089 uint32_t fCopyCertUtil = ISOFILE_FLAG_COPY_FROM_ISO;
3090 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
3091 {
3092 /* Skip if not present on the ISO. */
3093 RTFSOBJINFO ObjInfo;
[94935]3094 vrc = RTVfsQueryPathInfo(hVfsIso, s_aCertFiles[i].pszIso, &ObjInfo, RTFSOBJATTRADD_NOTHING,
3095 RTPATH_F_ON_LINK);
3096 if (RT_FAILURE(vrc))
[72958]3097 continue;
[64939]3098
[72958]3099 /* Copy the certificate certificate. */
3100 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
3101 mFiles.push_back(ISOFile(s_aCertFiles[i].pszIso,
3102 strDstCert,
3103 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_OPTIONAL));
[64939]3104
[72958]3105 /* Out certificate installation utility. */
3106 /* First pass: Copy over the file (first time only) + execute it to remove any
3107 * existing VBox certificates. */
3108 GuestProcessStartupInfo siCertUtilRem;
3109 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
[81453]3110 /* The argv[0] should contain full path to the executable module */
3111 siCertUtilRem.mArguments.push_back(strUpdateDir + "VBoxCertUtil.exe");
[72958]3112 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
3113 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
3114 siCertUtilRem.mArguments.push_back(strDstCert);
3115 siCertUtilRem.mArguments.push_back(strDstCert);
3116 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
3117 strUpdateDir + "VBoxCertUtil.exe",
3118 fCopyCertUtil | ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
3119 siCertUtilRem));
3120 fCopyCertUtil = 0;
3121 /* Second pass: Only execute (but don't copy) again, this time installng the
3122 * recent certificates just copied over. */
3123 GuestProcessStartupInfo siCertUtilAdd;
3124 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
[81453]3125 /* The argv[0] should contain full path to the executable module */
3126 siCertUtilAdd.mArguments.push_back(strUpdateDir + "VBoxCertUtil.exe");
[72958]3127 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
3128 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
3129 siCertUtilAdd.mArguments.push_back(strDstCert);
3130 siCertUtilAdd.mArguments.push_back(strDstCert);
3131 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
3132 strUpdateDir + "VBoxCertUtil.exe",
3133 ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
3134 siCertUtilAdd));
3135 }
[64939]3136 }
[72958]3137 /* The installers in different flavors, as we don't know (and can't assume)
3138 * the guest's bitness. */
[81453]3139 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS-X86.EXE",
[72958]3140 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
3141 ISOFILE_FLAG_COPY_FROM_ISO));
[81453]3142 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS-AMD64.EXE",
[72958]3143 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
3144 ISOFILE_FLAG_COPY_FROM_ISO));
3145 /* The stub loader which decides which flavor to run. */
3146 GuestProcessStartupInfo siInstaller;
3147 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
3148 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
3149 * setup can take quite a while, so be on the safe side. */
3150 siInstaller.mTimeoutMS = 5 * 60 * 1000;
[81453]3151
3152 /* The argv[0] should contain full path to the executable module */
3153 siInstaller.mArguments.push_back(strUpdateDir + "VBoxWindowsAdditions.exe");
[72958]3154 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
3155 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
3156 /* Don't quit VBoxService during upgrade because it still is used for this
3157 * piece of code we're in right now (that is, here!) ... */
3158 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
3159 /* Tell the installer to report its current installation status
3160 * using a running VBoxTray instance via balloon messages in the
3161 * Windows taskbar. */
3162 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
3163 /* Add optional installer command line arguments from the API to the
3164 * installer's startup info. */
[94935]3165 vrc = addProcessArguments(siInstaller.mArguments, mArguments);
3166 AssertRC(vrc);
[72958]3167 /* If the caller does not want to wait for out guest update process to end,
3168 * complete the progress object now so that the caller can do other work. */
3169 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
3170 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
3171 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS.EXE",
3172 strUpdateDir + "VBoxWindowsAdditions.exe",
3173 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_EXECUTE, siInstaller));
3174 break;
[43001]3175 }
[72958]3176 case eOSType_Linux:
[83870]3177 {
3178 /* Copy over the installer to the guest but don't execute it.
3179 * Execution will be done by the shell instead. */
3180 mFiles.push_back(ISOFile("VBOXLINUXADDITIONS.RUN",
3181 strUpdateDir + "VBoxLinuxAdditions.run", ISOFILE_FLAG_COPY_FROM_ISO));
3182
3183 GuestProcessStartupInfo siInstaller;
3184 siInstaller.mName = "VirtualBox Linux Guest Additions Installer";
3185 /* Set a running timeout of 5 minutes -- compiling modules and stuff for the Linux Guest Additions
3186 * setup can take quite a while, so be on the safe side. */
3187 siInstaller.mTimeoutMS = 5 * 60 * 1000;
3188 /* The argv[0] should contain full path to the shell we're using to execute the installer. */
3189 siInstaller.mArguments.push_back("/bin/sh");
3190 /* Now add the stuff we need in order to execute the installer. */
3191 siInstaller.mArguments.push_back(strUpdateDir + "VBoxLinuxAdditions.run");
3192 /* Make sure to add "--nox11" to the makeself wrapper in order to not getting any blocking xterm
3193 * window spawned when doing any unattended Linux GA installations. */
3194 siInstaller.mArguments.push_back("--nox11");
3195 siInstaller.mArguments.push_back("--");
3196 /* Force the upgrade. Needed in order to skip the confirmation dialog about warning to upgrade. */
3197 siInstaller.mArguments.push_back("--force"); /** @todo We might want a dedicated "--silent" switch here. */
3198 /* If the caller does not want to wait for out guest update process to end,
3199 * complete the progress object now so that the caller can do other work. */
3200 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
3201 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
3202 mFiles.push_back(ISOFile("/bin/sh" /* Source */, "/bin/sh" /* Dest */,
3203 ISOFILE_FLAG_EXECUTE, siInstaller));
[72958]3204 break;
[83870]3205 }
[72958]3206 case eOSType_Solaris:
3207 /** @todo Add Solaris support. */
3208 break;
3209 default:
3210 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
3211 break;
[42937]3212 }
[42923]3213 }
[42693]3214
[94935]3215 if (RT_SUCCESS(vrc))
[72958]3216 {
3217 /* We want to spend 40% total for all copying operations. So roughly
3218 * calculate the specific percentage step of each copied file. */
3219 uint8_t uOffset = 20; /* Start at 20%. */
3220 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
[42937]3221
[72958]3222 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
[42937]3223
[72958]3224 std::vector<ISOFile>::const_iterator itFiles = mFiles.begin();
3225 while (itFiles != mFiles.end())
[42923]3226 {
[72958]3227 if (itFiles->fFlags & ISOFILE_FLAG_COPY_FROM_ISO)
[43001]3228 {
[72958]3229 bool fOptional = false;
3230 if (itFiles->fFlags & ISOFILE_FLAG_OPTIONAL)
3231 fOptional = true;
[94935]3232 vrc = copyFileToGuest(pSession, hVfsIso, itFiles->strSource, itFiles->strDest, fOptional);
3233 if (RT_FAILURE(vrc))
[72958]3234 {
[94935]3235 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3236 Utf8StrFmt(tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
3237 itFiles->strSource.c_str(), itFiles->strDest.c_str(), vrc));
[72958]3238 break;
3239 }
[43001]3240 }
[42937]3241
[94935]3242 vrc = setProgress(uOffset);
3243 if (RT_FAILURE(vrc))
[72958]3244 break;
3245 uOffset += uStep;
[42937]3246
[72958]3247 ++itFiles;
3248 }
[42923]3249 }
[42693]3250
[72958]3251 /* Done copying, close .ISO file. */
3252 RTVfsRelease(hVfsIso);
[43001]3253
[94935]3254 if (RT_SUCCESS(vrc))
[72958]3255 {
3256 /* We want to spend 35% total for all copying operations. So roughly
3257 * calculate the specific percentage step of each copied file. */
3258 uint8_t uOffset = 60; /* Start at 60%. */
3259 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
[42937]3260
[72958]3261 LogRel(("Executing Guest Additions update files ...\n"));
[42937]3262
[72958]3263 std::vector<ISOFile>::iterator itFiles = mFiles.begin();
3264 while (itFiles != mFiles.end())
[42937]3265 {
[72958]3266 if (itFiles->fFlags & ISOFILE_FLAG_EXECUTE)
3267 {
[94935]3268 vrc = runFileOnGuest(pSession, itFiles->mProcInfo);
3269 if (RT_FAILURE(vrc))
[72958]3270 break;
3271 }
3272
[94935]3273 vrc = setProgress(uOffset);
3274 if (RT_FAILURE(vrc))
[42937]3275 break;
[72958]3276 uOffset += uStep;
3277
3278 ++itFiles;
[42937]3279 }
[72958]3280 }
[42937]3281
[94935]3282 if (RT_SUCCESS(vrc))
[72958]3283 {
[97540]3284 /* Linux Guest Additions will restart VBoxService during installation process.
3285 * In this case, connection to the guest will be temporary lost until new
3286 * kernel modules will be rebuilt, loaded and new VBoxService restarted.
3287 * Handle this case here: check if old connection was terminated and
3288 * new one has started. */
3289 if (osType == eOSType_Linux)
3290 {
3291 if (pSession->i_isTerminated())
3292 {
3293 LogRel(("Old guest session has terminated, waiting updated guest services to start\n"));
3294
3295 /* Wait for VBoxService to restart. */
[98690]3296 vrc = waitForGuestSession(pSession->i_getParent(), osType);
[97540]3297 if (RT_FAILURE(vrc))
3298 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3299 Utf8StrFmt(tr("Automatic update of Guest Additions has failed: "
[98729]3300 "guest services were not restarted, please reinstall Guest Additions manually")));
[97540]3301 }
3302 else
3303 {
3304 vrc = VERR_TRY_AGAIN;
3305 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3306 Utf8StrFmt(tr("Old guest session is still active, guest services were not restarted "
[98729]3307 "after installation, please reinstall Guest Additions manually")));
[97540]3308 }
3309 }
3310
3311 if (RT_SUCCESS(vrc))
3312 {
3313 LogRel(("Automatic update of Guest Additions succeeded\n"));
3314 hrc = setProgressSuccess();
3315 }
[42923]3316 }
3317 }
[42716]3318
[72958]3319 RTVfsFileRelease(hVfsFileIso);
[43034]3320 }
3321 }
3322
[94935]3323 if (RT_FAILURE(vrc))
[43034]3324 {
[94935]3325 if (vrc == VERR_CANCELLED)
[43034]3326 {
3327 LogRel(("Automatic update of Guest Additions was canceled\n"));
3328
[94935]3329 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3330 Utf8StrFmt(tr("Installation was canceled")));
[43034]3331 }
[97540]3332 else if (vrc == VERR_TIMEOUT)
3333 {
3334 LogRel(("Automatic update of Guest Additions has timed out\n"));
3335
3336 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3337 Utf8StrFmt(tr("Installation has timed out")));
3338 }
[43034]3339 else
3340 {
[94935]3341 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", vrc);
[43034]3342 if (!mProgress.isNull()) /* Progress object is optional. */
[42937]3343 {
[83250]3344#ifdef VBOX_STRICT
3345 /* If we forgot to set the progress object accordingly, let us know. */
[83251]3346 LONG rcProgress;
[83250]3347 AssertMsg( SUCCEEDED(mProgress->COMGETTER(ResultCode(&rcProgress)))
3348 && FAILED(rcProgress), ("Task indicated an error (%Rrc), but progress did not indicate this (%Rhrc)\n",
[94935]3349 vrc, rcProgress));
[83250]3350#endif
[44863]3351 com::ProgressErrorInfo errorInfo(mProgress);
3352 if ( errorInfo.isFullAvailable()
3353 || errorInfo.isBasicAvailable())
[42937]3354 {
[44863]3355 strError = errorInfo.getText();
[42937]3356 }
[43034]3357 }
[42693]3358
[44863]3359 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
[94935]3360 strError.c_str(), hrc));
[42693]3361 }
[43034]3362
3363 LogRel(("Please install Guest Additions manually\n"));
[42693]3364 }
3365
[44863]3366 /** @todo Clean up copied / left over installation files. */
3367
[94935]3368 LogFlowFuncLeaveRC(vrc);
3369 return vrc;
[42693]3370}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use