VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 3 weeks ago

Guest Control:

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

© 2023 Oracle
ContactPrivacy policyTerms of Use