VirtualBox

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

Last change on this file since 97438 was 97438, checked in by vboxsync, 3 years ago

Guest Control/Main: Addendum to r154431 (missed on fForce = true) + logging fix. ​​bugref:10286

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette