VirtualBox

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

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

Guest Control/Main: More (temporary) logging. ​bugref:10286

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.3 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 97428 2022-11-07 11:18:45Z 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 \"%s\" 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 LogFlowFunc(("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 LogFlowFunc(("\tstrPath=%s (fMode %#x)\n", pEntry->strPath.c_str(), pEntry->fMode));
1055 ++itEntry;
1056 }
1057
1058 LogFlowFuncLeave();
1059}
1060
1061/**
1062 * Builds a guest file list from a given path (and optional filter).
1063 *
1064 * @return VBox status code.
1065 * @param strPath Directory on the guest to build list from.
1066 * @param strSubDir Current sub directory path; needed for recursion.
1067 * Set to an empty path.
1068 */
1069int FsList::AddDirFromGuest(const Utf8Str &strPath, const Utf8Str &strSubDir /* = "" */)
1070{
1071 Utf8Str strPathAbs = strPath;
1072 if (!strPathAbs.endsWith(PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle)))
1073 strPathAbs += PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle);
1074
1075 Utf8Str strPathSub = strSubDir;
1076 if ( strPathSub.isNotEmpty()
1077 && !strPathSub.endsWith(PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle)))
1078 strPathSub += PATH_STYLE_SEP_STR(mSourceSpec.enmPathStyle);
1079
1080 strPathAbs += strPathSub;
1081
1082 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1083
1084 LogRel(("Guest Control: Handling directory '%s' on guest ...\n", strPathAbs.c_str()));
1085
1086 GuestDirectoryOpenInfo dirOpenInfo;
1087 dirOpenInfo.mFilter = "";
1088 dirOpenInfo.mPath = strPathAbs;
1089 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
1090
1091 const ComObjPtr<GuestSession> &pSession = mTask.GetSession();
1092
1093 ComObjPtr <GuestDirectory> pDir;
1094 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1095 int vrc = pSession->i_directoryOpen(dirOpenInfo, pDir, &vrcGuest);
1096 if (RT_FAILURE(vrc))
1097 {
1098 switch (vrc)
1099 {
1100 case VERR_INVALID_PARAMETER:
1101 break;
1102
1103 case VERR_GSTCTL_GUEST_ERROR:
1104 break;
1105
1106 default:
1107 break;
1108 }
1109
1110 return vrc;
1111 }
1112
1113 if (strPathSub.isNotEmpty())
1114 {
1115 GuestFsObjData fsObjData;
1116 fsObjData.mType = FsObjType_Directory;
1117
1118 vrc = AddEntryFromGuest(strPathSub, fsObjData);
1119 }
1120
1121 if (RT_SUCCESS(vrc))
1122 {
1123 ComObjPtr<GuestFsObjInfo> fsObjInfo;
1124 while (RT_SUCCESS(vrc = pDir->i_read(fsObjInfo, &vrcGuest)))
1125 {
1126 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
1127 HRESULT hrc2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1128 AssertComRC(hrc2);
1129
1130 com::Bstr bstrName;
1131 hrc2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1132 AssertComRC(hrc2);
1133
1134 Utf8Str strEntry = strPathSub + Utf8Str(bstrName);
1135
1136 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1137
1138 switch (enmObjType)
1139 {
1140 case FsObjType_Directory:
1141 {
1142 if ( bstrName.equals(".")
1143 || bstrName.equals(".."))
1144 {
1145 break;
1146 }
1147
1148 LogRel(("Guest Control: Directory '%s'\n", strEntry.c_str()));
1149
1150 if (!(mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_Recursive))
1151 break;
1152
1153 vrc = AddDirFromGuest(strPath, strEntry);
1154 break;
1155 }
1156
1157 case FsObjType_Symlink:
1158 {
1159 if ( mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks
1160 || mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks)
1161 {
1162 /** @todo Symlink handling from guest is not implemented yet.
1163 * See IGuestSession::symlinkRead(). */
1164 LogRel(("Guest Control: Warning: Symlink support on guest side not available, skipping '%s'",
1165 strEntry.c_str()));
1166 }
1167 break;
1168 }
1169
1170 case FsObjType_File:
1171 {
1172 LogRel(("Guest Control: File '%s'\n", strEntry.c_str()));
1173
1174 vrc = AddEntryFromGuest(strEntry, fsObjInfo->i_getData());
1175 break;
1176 }
1177
1178 default:
1179 break;
1180 }
1181 }
1182
1183 if (vrc == VERR_NO_MORE_FILES) /* End of listing reached? */
1184 vrc = VINF_SUCCESS;
1185 }
1186
1187 int vrc2 = pDir->i_closeInternal(&vrcGuest);
1188 if (RT_SUCCESS(vrc))
1189 vrc = vrc2;
1190
1191 return vrc;
1192}
1193
1194/**
1195 * Builds a host file list from a given path.
1196 *
1197 * @return VBox status code.
1198 * @param strPath Directory on the host to build list from.
1199 * @param strSubDir Current sub directory path; needed for recursion.
1200 * Set to an empty path.
1201 * @param pszPathReal Scratch buffer for holding the resolved real path.
1202 * Needed for recursion.
1203 * @param cbPathReal Size (in bytes) of \a pszPathReal.
1204 * @param pDirEntry Where to store looked up directory information for handled paths.
1205 * Needed for recursion.
1206 */
1207int FsList::AddDirFromHost(const Utf8Str &strPath, const Utf8Str &strSubDir,
1208 char *pszPathReal, size_t cbPathReal, PRTDIRENTRYEX pDirEntry)
1209{
1210 Utf8Str strPathAbs = strPath;
1211 if (!strPathAbs.endsWith(RTPATH_SLASH_STR))
1212 strPathAbs += RTPATH_SLASH_STR;
1213
1214 Utf8Str strPathSub = strSubDir;
1215 if ( strPathSub.isNotEmpty()
1216 && !strPathSub.endsWith(RTPATH_SLASH_STR))
1217 strPathSub += RTPATH_SLASH_STR;
1218
1219 strPathAbs += strPathSub;
1220
1221 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1222
1223 LogRel(("Guest Control: Handling directory '%s' on host ...\n", strPathAbs.c_str()));
1224
1225 RTFSOBJINFO objInfo;
1226 int vrc = RTPathQueryInfo(strPathAbs.c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
1227 if (RT_SUCCESS(vrc))
1228 {
1229 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1230 {
1231 if (strPathSub.isNotEmpty())
1232 vrc = AddEntryFromHost(strPathSub, &objInfo);
1233
1234 if (RT_SUCCESS(vrc))
1235 {
1236 RTDIR hDir;
1237 vrc = RTDirOpen(&hDir, strPathAbs.c_str());
1238 if (RT_SUCCESS(vrc))
1239 {
1240 do
1241 {
1242 /* Retrieve the next directory entry. */
1243 vrc = RTDirReadEx(hDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1244 if (RT_FAILURE(vrc))
1245 {
1246 if (vrc == VERR_NO_MORE_FILES)
1247 vrc = VINF_SUCCESS;
1248 break;
1249 }
1250
1251 Utf8Str strEntry = strPathSub + Utf8Str(pDirEntry->szName);
1252
1253 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1254
1255 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1256 {
1257 case RTFS_TYPE_DIRECTORY:
1258 {
1259 /* Skip "." and ".." entries. */
1260 if (RTDirEntryExIsStdDotLink(pDirEntry))
1261 break;
1262
1263 LogRel(("Guest Control: Directory '%s'\n", strEntry.c_str()));
1264
1265 if (!(mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_Recursive))
1266 break;
1267
1268 vrc = AddDirFromHost(strPath, strEntry, pszPathReal, cbPathReal, pDirEntry);
1269 break;
1270 }
1271
1272 case RTFS_TYPE_FILE:
1273 {
1274 LogRel(("Guest Control: File '%s'\n", strEntry.c_str()));
1275
1276 vrc = AddEntryFromHost(strEntry, &pDirEntry->Info);
1277 break;
1278 }
1279
1280 case RTFS_TYPE_SYMLINK:
1281 {
1282 Utf8Str strEntryAbs = strPathAbs + (const char *)pDirEntry->szName;
1283
1284 vrc = RTPathReal(strEntryAbs.c_str(), pszPathReal, cbPathReal);
1285 if (RT_SUCCESS(vrc))
1286 {
1287 vrc = RTPathQueryInfo(pszPathReal, &objInfo, RTFSOBJATTRADD_NOTHING);
1288 if (RT_SUCCESS(vrc))
1289 {
1290 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1291 {
1292 LogRel(("Guest Control: Symbolic link '%s' -> '%s' (directory)\n",
1293 strEntryAbs.c_str(), pszPathReal));
1294 if (mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks)
1295 vrc = AddDirFromHost(strPath, strEntry, pszPathReal, cbPathReal, pDirEntry);
1296 }
1297 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1298 {
1299 LogRel(("Guest Control: Symbolic link '%s' -> '%s' (file)\n",
1300 strEntryAbs.c_str(), pszPathReal));
1301 if (mSourceSpec.fFileCopyFlags & DirectoryCopyFlag_FollowLinks)
1302 vrc = AddEntryFromHost(strEntry, &objInfo);
1303 }
1304 else
1305 vrc = VERR_NOT_SUPPORTED;
1306 }
1307
1308 if (RT_FAILURE(vrc))
1309 LogRel(("Guest Control: Unable to query symbolic link info for '%s', rc=%Rrc\n",
1310 pszPathReal, vrc));
1311 }
1312 else
1313 {
1314 LogRel(("Guest Control: Unable to resolve symlink for '%s', rc=%Rrc\n", strPathAbs.c_str(), vrc));
1315 if (vrc == VERR_FILE_NOT_FOUND) /* Broken symlink, skip. */
1316 vrc = VINF_SUCCESS;
1317 }
1318 break;
1319 }
1320
1321 default:
1322 break;
1323 }
1324
1325 } while (RT_SUCCESS(vrc));
1326
1327 RTDirClose(hDir);
1328 }
1329 }
1330 }
1331 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1332 vrc = VERR_IS_A_FILE;
1333 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1334 vrc = VERR_IS_A_SYMLINK;
1335 else
1336 vrc = VERR_NOT_SUPPORTED;
1337 }
1338 else
1339 LogFlowFunc(("Unable to query '%s', rc=%Rrc\n", strPathAbs.c_str(), vrc));
1340
1341 LogFlowFuncLeaveRC(vrc);
1342 return vrc;
1343}
1344
1345GuestSessionTaskOpen::GuestSessionTaskOpen(GuestSession *pSession, uint32_t uFlags, uint32_t uTimeoutMS)
1346 : GuestSessionTask(pSession)
1347 , mFlags(uFlags)
1348 , mTimeoutMS(uTimeoutMS)
1349{
1350 m_strTaskName = "gctlSesOpen";
1351}
1352
1353GuestSessionTaskOpen::~GuestSessionTaskOpen(void)
1354{
1355
1356}
1357
1358/** @copydoc GuestSessionTask::Run */
1359int GuestSessionTaskOpen::Run(void)
1360{
1361 LogFlowThisFuncEnter();
1362
1363 AutoCaller autoCaller(mSession);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 int vrc = mSession->i_startSession(NULL /*pvrcGuest*/);
1367 /* Nothing to do here anymore. */
1368
1369 LogFlowFuncLeaveRC(vrc);
1370 return vrc;
1371}
1372
1373GuestSessionCopyTask::GuestSessionCopyTask(GuestSession *pSession)
1374 : GuestSessionTask(pSession)
1375{
1376}
1377
1378GuestSessionCopyTask::~GuestSessionCopyTask()
1379{
1380 FsLists::iterator itList = mVecLists.begin();
1381 while (itList != mVecLists.end())
1382 {
1383 FsList *pFsList = (*itList);
1384 pFsList->Destroy();
1385 delete pFsList;
1386 mVecLists.erase(itList);
1387 itList = mVecLists.begin();
1388 }
1389
1390 Assert(mVecLists.empty());
1391}
1392
1393GuestSessionTaskCopyFrom::GuestSessionTaskCopyFrom(GuestSession *pSession, GuestSessionFsSourceSet const &vecSrc,
1394 const Utf8Str &strDest)
1395 : GuestSessionCopyTask(pSession)
1396{
1397 m_strTaskName = "gctlCpyFrm";
1398
1399 mSources = vecSrc;
1400 mDest = strDest;
1401}
1402
1403GuestSessionTaskCopyFrom::~GuestSessionTaskCopyFrom(void)
1404{
1405}
1406
1407/**
1408 * Initializes a copy-from-guest task.
1409 *
1410 * @returns HRESULT
1411 * @param strTaskDesc Friendly task description.
1412 */
1413HRESULT GuestSessionTaskCopyFrom::Init(const Utf8Str &strTaskDesc)
1414{
1415 setTaskDesc(strTaskDesc);
1416
1417 /* Create the progress object. */
1418 ComObjPtr<Progress> pProgress;
1419 HRESULT hrc = pProgress.createObject();
1420 if (FAILED(hrc))
1421 return hrc;
1422
1423 mProgress = pProgress;
1424
1425 int vrc = VINF_SUCCESS;
1426
1427 ULONG cOperations = 0;
1428 Utf8Str strErrorInfo;
1429
1430 /**
1431 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyFrom::Run
1432 * because the caller expects a ready-for-operation progress object on return.
1433 * The progress object will have a variable operation count, based on the elements to
1434 * be processed.
1435 */
1436
1437 if (mDest.isEmpty())
1438 {
1439 strErrorInfo.printf(tr("Host destination must not be empty"));
1440 vrc = VERR_INVALID_PARAMETER;
1441 }
1442 else
1443 {
1444 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1445 while (itSrc != mSources.end())
1446 {
1447 Utf8Str strSrc = itSrc->strSource;
1448 Utf8Str strDst = mDest;
1449
1450 bool fFollowSymlinks;
1451
1452 if (strSrc.isEmpty())
1453 {
1454 strErrorInfo.printf(tr("Guest source entry must not be empty"));
1455 vrc = VERR_INVALID_PARAMETER;
1456 break;
1457 }
1458
1459 if (itSrc->enmType == FsObjType_Directory)
1460 {
1461 fFollowSymlinks = itSrc->fDirCopyFlags & DirectoryCopyFlag_FollowLinks;
1462 }
1463 else
1464 {
1465 fFollowSymlinks = RT_BOOL(itSrc->fFileCopyFlags & FileCopyFlag_FollowLinks);
1466 }
1467
1468 LogFlowFunc(("strSrc=%s (path style is %s), strDst=%s, fFollowSymlinks=%RTbool\n",
1469 strSrc.c_str(), GuestBase::pathStyleToStr(itSrc->enmPathStyle), strDst.c_str(), fFollowSymlinks));
1470
1471 GuestFsObjData srcObjData;
1472 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1473 vrc = mSession->i_fsQueryInfo(strSrc, fFollowSymlinks, srcObjData, &vrcGuest);
1474 if (RT_FAILURE(vrc))
1475 {
1476 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1477 strErrorInfo = GuestBase::getErrorAsString(tr("Guest source lookup failed"),
1478 GuestErrorInfo(GuestErrorInfo::Type_ToolStat, vrcGuest, strSrc.c_str()));
1479 else
1480 strErrorInfo.printf(tr("Guest source lookup for \"%s\" failed: %Rrc"),
1481 strSrc.c_str(), vrc);
1482 break;
1483 }
1484
1485 if (srcObjData.mType == FsObjType_Directory)
1486 {
1487 if (itSrc->enmType != FsObjType_Directory)
1488 {
1489 strErrorInfo.printf(tr("Guest source is not a file: %s"), strSrc.c_str());
1490 vrc = VERR_NOT_A_FILE;
1491 break;
1492 }
1493 }
1494 else
1495 {
1496 if (itSrc->enmType != FsObjType_File)
1497 {
1498 strErrorInfo.printf(tr("Guest source is not a directory: %s"), strSrc.c_str());
1499 vrc = VERR_NOT_A_DIRECTORY;
1500 break;
1501 }
1502 }
1503
1504 FsList *pFsList = NULL;
1505 try
1506 {
1507 pFsList = new FsList(*this);
1508 vrc = pFsList->Init(strSrc, strDst, *itSrc);
1509 if (RT_SUCCESS(vrc))
1510 {
1511 switch (itSrc->enmType)
1512 {
1513 case FsObjType_Directory:
1514 {
1515 vrc = pFsList->AddDirFromGuest(strSrc);
1516 break;
1517 }
1518
1519 case FsObjType_File:
1520 /* The file name is already part of the actual list's source root (strSrc). */
1521 break;
1522
1523 default:
1524 LogRel(("Guest Control: Warning: Unknown guest file system type %#x for source \"%s\", skipping\n",
1525 itSrc->enmType, strSrc.c_str()));
1526 break;
1527 }
1528 }
1529
1530 if (RT_FAILURE(vrc))
1531 {
1532 delete pFsList;
1533 strErrorInfo.printf(tr("Error adding guest source '%s' to list: %Rrc"),
1534 strSrc.c_str(), vrc);
1535 break;
1536 }
1537
1538 pFsList->DumpToLog();
1539
1540 mVecLists.push_back(pFsList);
1541 }
1542 catch (std::bad_alloc &)
1543 {
1544 vrc = VERR_NO_MEMORY;
1545 break;
1546 }
1547
1548 AssertPtr(pFsList);
1549 cOperations += (ULONG)pFsList->mVecEntries.size();
1550
1551 itSrc++;
1552 }
1553 }
1554
1555 if (cOperations) /* Use the first element as description (if available). */
1556 {
1557 Assert(mVecLists.size());
1558 Assert(mVecLists[0]->mVecEntries.size());
1559
1560 Utf8Str strFirstOp = mDest + mVecLists[0]->mVecEntries[0]->strPath;
1561 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1562 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */, Bstr(strFirstOp).raw());
1563 }
1564 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
1565 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1566 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
1567
1568 if (RT_FAILURE(vrc))
1569 {
1570 if (strErrorInfo.isEmpty())
1571 strErrorInfo.printf(tr("Failed with %Rrc"), vrc);
1572 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
1573 }
1574
1575 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hrc, vrc));
1576 return hrc;
1577}
1578
1579/** @copydoc GuestSessionTask::Run */
1580int GuestSessionTaskCopyFrom::Run(void)
1581{
1582 LogFlowThisFuncEnter();
1583
1584 AutoCaller autoCaller(mSession);
1585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1586
1587 int vrc = VINF_SUCCESS;
1588
1589 FsLists::const_iterator itList = mVecLists.begin();
1590 while (itList != mVecLists.end())
1591 {
1592 FsList *pList = *itList;
1593 AssertPtr(pList);
1594
1595 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1596
1597 Utf8Str strSrcRootAbs = pList->mSrcRootAbs;
1598 Utf8Str strDstRootAbs = pList->mDstRootAbs;
1599
1600 GuestPath::BuildDestinationPath(strSrcRootAbs, mSession->i_getGuestPathStyle(),
1601 strDstRootAbs, PATH_STYLE_NATIVE);
1602
1603 bool fCopyIntoExisting;
1604 bool fFollowSymlinks;
1605
1606 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1607 {
1608 fCopyIntoExisting = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting);
1609 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks);
1610 }
1611 else if (pList->mSourceSpec.enmType == FsObjType_File)
1612 {
1613 fCopyIntoExisting = !RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_NoReplace);
1614 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks);
1615 }
1616 else
1617 AssertFailedBreakStmt(vrc = VERR_NOT_IMPLEMENTED);
1618
1619 uint32_t const fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
1620 uint32_t fDirCreate = 0;
1621
1622 bool fDstExists = true;
1623
1624 RTFSOBJINFO ObjInfo;
1625 RT_ZERO(ObjInfo);
1626 vrc = RTPathQueryInfoEx(strDstRootAbs.c_str(), &ObjInfo, RTFSOBJATTRADD_NOTHING,
1627 fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK /* fFlags */);
1628 if (RT_FAILURE(vrc))
1629 {
1630 if ( vrc == VERR_FILE_NOT_FOUND
1631 || vrc == VERR_PATH_NOT_FOUND)
1632 {
1633 fDstExists = false;
1634 vrc = VINF_SUCCESS;
1635 }
1636 else
1637 {
1638 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1639 Utf8StrFmt(tr("Host path lookup for \"%s\" failed: %Rrc"), strDstRootAbs.c_str(), vrc));
1640 break;
1641 }
1642 }
1643
1644 /* Create the root directory. */
1645 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1646 {
1647 LogRel(("Guest Control: Directory: fDstExists=%RTbool, fDirCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool\n",
1648 fDstExists, pList->mSourceSpec.fDirCopyFlags, fCopyIntoExisting, fFollowSymlinks));
1649
1650 if (fDstExists)
1651 {
1652 switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
1653 {
1654 case RTFS_TYPE_DIRECTORY:
1655 {
1656 if (!fCopyIntoExisting)
1657 {
1658 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1659 Utf8StrFmt(tr("Host root directory \"%s\" already exists"), strDstRootAbs.c_str()));
1660 vrc = VERR_ALREADY_EXISTS;
1661 break;
1662 }
1663 break;
1664 }
1665
1666 case RTFS_TYPE_FILE:
1667 {
1668 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1669 Utf8StrFmt(tr("Destination \"%s\" on the host already exists and is a file"), strDstRootAbs.c_str()));
1670 vrc = VERR_IS_A_FILE;
1671 break;
1672 }
1673
1674 default:
1675 {
1676 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1677 Utf8StrFmt(tr("Unknown object type (%#x) on host for \"%s\""),
1678 ObjInfo.Attr.fMode & RTFS_TYPE_MASK, strDstRootAbs.c_str()));
1679 vrc = VERR_NOT_SUPPORTED;
1680 break;
1681 }
1682 }
1683 }
1684
1685 if (RT_FAILURE(vrc))
1686 break;
1687
1688 /* Clean up the final host destination root path (for cleaning up mixed path separators). */
1689 vrc = GuestPath::Translate(strDstRootAbs,
1690 PATH_STYLE_NATIVE /* Source */, PATH_STYLE_NATIVE /* Dest */, true /* fForce */);
1691 if (RT_FAILURE(vrc))
1692 {
1693 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1694 Utf8StrFmt(tr("Translating host destination root path \"%s\" failed: %Rrc"),
1695 strDstRootAbs.c_str(), vrc));
1696 break;
1697 }
1698
1699 /* Make sure the destination root directory exists. */
1700 if (pList->mSourceSpec.fDryRun == false)
1701 {
1702 vrc = directoryCreateOnHost(strDstRootAbs, fDirMode, 0 /* fCreate */, true /* fCanExist */);
1703 if (RT_FAILURE(vrc))
1704 break;
1705 }
1706
1707 AssertBreakStmt(pList->mSourceSpec.enmType == FsObjType_Directory, vrc = VERR_NOT_SUPPORTED);
1708
1709 /* Walk the entries. */
1710 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1711 while (itEntry != pList->mVecEntries.end())
1712 {
1713 FsEntry *pEntry = *itEntry;
1714 AssertPtr(pEntry);
1715
1716 Utf8Str strSrcAbs = strSrcRootAbs;
1717 Utf8Str strDstAbs = strDstRootAbs;
1718
1719 strSrcAbs += PATH_STYLE_SEP_STR(pList->mSourceSpec.enmPathStyle);
1720 strSrcAbs += pEntry->strPath;
1721
1722 strDstAbs += PATH_STYLE_SEP_STR(PATH_STYLE_NATIVE);
1723 strDstAbs += pEntry->strPath;
1724
1725 /* Clean up the final guest source path (for cleaning up mixed path separators). */
1726 vrc = GuestPath::Translate(strSrcAbs, pList->mSourceSpec.enmPathStyle /* Source */,
1727 pList->mSourceSpec.enmPathStyle /* Dest */);
1728 if (RT_FAILURE(vrc))
1729 {
1730 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1731 Utf8StrFmt(tr("Translating guest source path \"%s\" failed: %Rrc"),
1732 strSrcAbs.c_str(), vrc));
1733 break;
1734 }
1735
1736 /* Translate the final host destination path. */
1737 vrc = GuestPath::Translate(strDstAbs, PATH_STYLE_NATIVE /* Source */, PATH_STYLE_NATIVE /* Dest */);
1738 if (RT_FAILURE(vrc))
1739 {
1740 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1741 Utf8StrFmt(tr("Translating host destination path \"%s\" failed: %Rrc"),
1742 strDstAbs.c_str(), vrc));
1743 break;
1744 }
1745
1746 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
1747
1748 switch (pEntry->fMode & RTFS_TYPE_MASK)
1749 {
1750 case RTFS_TYPE_DIRECTORY:
1751 if (!pList->mSourceSpec.fDryRun)
1752 vrc = directoryCreateOnHost(strDstAbs, fDirMode, fDirCreate, fCopyIntoExisting);
1753 break;
1754
1755 case RTFS_TYPE_FILE:
1756 RT_FALL_THROUGH();
1757 case RTFS_TYPE_SYMLINK:
1758 if (!pList->mSourceSpec.fDryRun)
1759 vrc = fileCopyFromGuest(strSrcAbs, strDstAbs, pList->mSourceSpec.fFileCopyFlags);
1760 break;
1761
1762 default:
1763 AssertFailed(); /* Should never happen (we already have a filtered list). */
1764 break;
1765 }
1766
1767 if (RT_FAILURE(vrc))
1768 break;
1769
1770 ++itEntry;
1771 }
1772 }
1773 else if (pList->mSourceSpec.enmType == FsObjType_File)
1774 {
1775 LogRel(("Guest Control: File: fDstExists=%RTbool, fFileCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool\n",
1776 fDstExists, pList->mSourceSpec.fFileCopyFlags, fCopyIntoExisting, fFollowSymlinks));
1777
1778 if (fDstExists)
1779 {
1780 switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
1781 {
1782 case RTFS_TYPE_DIRECTORY:
1783 {
1784 if (!fCopyIntoExisting)
1785 {
1786 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1787 Utf8StrFmt(tr("Destination \"%s\" on the host already exists and is a directory"),
1788 strDstRootAbs.c_str()));
1789 vrc = VERR_IS_A_DIRECTORY;
1790 break;
1791 }
1792 break;
1793 }
1794
1795 case RTFS_TYPE_FILE:
1796 {
1797 if (!fCopyIntoExisting)
1798 {
1799 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1800 Utf8StrFmt(tr("Host file \"%s\" already exists"), strDstRootAbs.c_str()));
1801 vrc = VERR_ALREADY_EXISTS;
1802 }
1803 break;
1804 }
1805
1806 default:
1807 {
1808 /** @ŧodo Resolve symlinks? */
1809 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1810 Utf8StrFmt(tr("Unknown object type (%#x) on host for \"%s\""),
1811 ObjInfo.Attr.fMode & RTFS_TYPE_MASK, strDstRootAbs.c_str()));
1812 vrc = VERR_NOT_SUPPORTED;
1813 break;
1814 }
1815 }
1816 }
1817
1818 if (!pList->mSourceSpec.fDryRun)
1819 vrc = fileCopyFromGuest(strSrcRootAbs, strDstRootAbs, pList->mSourceSpec.fFileCopyFlags);
1820 }
1821 else
1822 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
1823
1824 if (RT_FAILURE(vrc))
1825 break;
1826
1827 ++itList;
1828 }
1829
1830 if (RT_SUCCESS(vrc))
1831 vrc = setProgressSuccess();
1832
1833 LogFlowFuncLeaveRC(vrc);
1834 return vrc;
1835}
1836
1837GuestSessionTaskCopyTo::GuestSessionTaskCopyTo(GuestSession *pSession, GuestSessionFsSourceSet const &vecSrc,
1838 const Utf8Str &strDest)
1839 : GuestSessionCopyTask(pSession)
1840{
1841 m_strTaskName = "gctlCpyTo";
1842
1843 mSources = vecSrc;
1844 mDest = strDest;
1845}
1846
1847GuestSessionTaskCopyTo::~GuestSessionTaskCopyTo(void)
1848{
1849}
1850
1851/**
1852 * Initializes a copy-to-guest task.
1853 *
1854 * @returns HRESULT
1855 * @param strTaskDesc Friendly task description.
1856 */
1857HRESULT GuestSessionTaskCopyTo::Init(const Utf8Str &strTaskDesc)
1858{
1859 LogFlowFuncEnter();
1860
1861 setTaskDesc(strTaskDesc);
1862
1863 /* Create the progress object. */
1864 ComObjPtr<Progress> pProgress;
1865 HRESULT hrc = pProgress.createObject();
1866 if (FAILED(hrc))
1867 return hrc;
1868
1869 mProgress = pProgress;
1870
1871 int vrc = VINF_SUCCESS;
1872
1873 ULONG cOperations = 0;
1874 Utf8Str strErrorInfo;
1875
1876 /*
1877 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyTo::Run
1878 * because the caller expects a ready-for-operation progress object on return.
1879 * The progress object will have a variable operation count, based on the elements to
1880 * be processed.
1881 */
1882
1883 if (mDest.isEmpty())
1884 {
1885 strErrorInfo.printf(tr("Guest destination must not be empty"));
1886 vrc = VERR_INVALID_PARAMETER;
1887 }
1888 else
1889 {
1890 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1891 while (itSrc != mSources.end())
1892 {
1893 Utf8Str strSrc = itSrc->strSource;
1894 Utf8Str strDst = mDest;
1895
1896 bool fFollowSymlinks;
1897
1898 if (strSrc.isEmpty())
1899 {
1900 strErrorInfo.printf(tr("Host source entry must not be empty"));
1901 vrc = VERR_INVALID_PARAMETER;
1902 break;
1903 }
1904
1905 if (itSrc->enmType == FsObjType_Directory)
1906 {
1907 fFollowSymlinks = itSrc->fDirCopyFlags & DirectoryCopyFlag_FollowLinks;
1908 }
1909 else
1910 {
1911 fFollowSymlinks = RT_BOOL(itSrc->fFileCopyFlags & FileCopyFlag_FollowLinks);
1912 }
1913
1914 LogFlowFunc(("strSrc=%s (path style is %s), strDst=%s\n",
1915 strSrc.c_str(), GuestBase::pathStyleToStr(itSrc->enmPathStyle), strDst.c_str()));
1916
1917 RTFSOBJINFO srcFsObjInfo;
1918 vrc = RTPathQueryInfoEx(strSrc.c_str(), &srcFsObjInfo, RTFSOBJATTRADD_NOTHING,
1919 fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK /* fFlags */);
1920 if (RT_FAILURE(vrc))
1921 {
1922 strErrorInfo.printf(tr("No such host file/directory: %s"), strSrc.c_str());
1923 break;
1924 }
1925
1926 if (RTFS_IS_DIRECTORY(srcFsObjInfo.Attr.fMode))
1927 {
1928 if (itSrc->enmType != FsObjType_Directory)
1929 {
1930 strErrorInfo.printf(tr("Host source is not a file: %s"), strSrc.c_str());
1931 vrc = VERR_NOT_A_FILE;
1932 break;
1933 }
1934 }
1935 else
1936 {
1937 if (itSrc->enmType == FsObjType_Directory)
1938 {
1939 strErrorInfo.printf(tr("Host source is not a directory: %s"), strSrc.c_str());
1940 vrc = VERR_NOT_A_DIRECTORY;
1941 break;
1942 }
1943 }
1944
1945 FsList *pFsList = NULL;
1946 try
1947 {
1948 pFsList = new FsList(*this);
1949 vrc = pFsList->Init(strSrc, strDst, *itSrc);
1950 if (RT_SUCCESS(vrc))
1951 {
1952 switch (itSrc->enmType)
1953 {
1954 case FsObjType_Directory:
1955 {
1956 char szPathReal[RTPATH_MAX];
1957 RTDIRENTRYEX DirEntry;
1958 vrc = pFsList->AddDirFromHost(strSrc /* strPath */, "" /* strSubDir */,
1959 szPathReal, sizeof(szPathReal), &DirEntry);
1960 }
1961
1962 case FsObjType_File:
1963 /* The file name is already part of the actual list's source root (strSrc). */
1964 break;
1965
1966 default:
1967 LogRel(("Guest Control: Warning: Unknown guest host system type %#x for source \"%s\", skipping\n",
1968 itSrc->enmType, strSrc.c_str()));
1969 break;
1970 }
1971 }
1972
1973 if (RT_FAILURE(vrc))
1974 {
1975 delete pFsList;
1976 strErrorInfo.printf(tr("Error adding host source '%s' to list: %Rrc"),
1977 strSrc.c_str(), vrc);
1978 break;
1979 }
1980
1981 pFsList->DumpToLog();
1982
1983 mVecLists.push_back(pFsList);
1984 }
1985 catch (std::bad_alloc &)
1986 {
1987 vrc = VERR_NO_MEMORY;
1988 break;
1989 }
1990
1991 AssertPtr(pFsList);
1992 cOperations += (ULONG)pFsList->mVecEntries.size();
1993
1994 itSrc++;
1995 }
1996 }
1997
1998 if (cOperations) /* Use the first element as description (if available). */
1999 {
2000 Assert(mVecLists.size());
2001 Assert(mVecLists[0]->mVecEntries.size());
2002
2003 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
2004 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */,
2005 Bstr(mDesc).raw());
2006 }
2007 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
2008 hrc = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
2009 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
2010
2011 if (RT_FAILURE(vrc))
2012 {
2013 if (strErrorInfo.isEmpty())
2014 strErrorInfo.printf(tr("Failed with %Rrc"), vrc);
2015 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
2016 }
2017
2018 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hrc, vrc));
2019 return hrc;
2020}
2021
2022/** @copydoc GuestSessionTask::Run */
2023int GuestSessionTaskCopyTo::Run(void)
2024{
2025 LogFlowThisFuncEnter();
2026
2027 AutoCaller autoCaller(mSession);
2028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2029
2030 int vrc = VINF_SUCCESS;
2031
2032 FsLists::const_iterator itList = mVecLists.begin();
2033 while (itList != mVecLists.end())
2034 {
2035 FsList *pList = *itList;
2036 AssertPtr(pList);
2037
2038 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
2039
2040 Utf8Str strSrcRootAbs = pList->mSrcRootAbs;
2041 Utf8Str strDstRootAbs = pList->mDstRootAbs;
2042
2043 /* Translate the destination path to a path compatible with the guest.
2044 * Note: This needs to be done *before* GuestPath::BuildDestinationPath() below! */
2045 vrc = GuestPath::Translate(strDstRootAbs, mSession->i_getGuestPathStyle() /* Source */,
2046 mSession->i_getGuestPathStyle() /* Dest */);
2047
2048 GuestPath::BuildDestinationPath(strSrcRootAbs, PATH_STYLE_NATIVE,
2049 strDstRootAbs, mSession->i_getGuestPathStyle());
2050
2051
2052 if (RT_FAILURE(vrc))
2053 {
2054 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2055 Utf8StrFmt(tr("Translating guest destination path \"%s\" failed: %Rrc"),
2056 strDstRootAbs.c_str(), vrc));
2057 break;
2058 }
2059
2060 bool fCopyIntoExisting;
2061 bool fFollowSymlinks;
2062
2063 if (pList->mSourceSpec.enmType == FsObjType_Directory)
2064 {
2065 fCopyIntoExisting = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_CopyIntoExisting);
2066 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fDirCopyFlags & DirectoryCopyFlag_FollowLinks);
2067 }
2068 else if (pList->mSourceSpec.enmType == FsObjType_File)
2069 {
2070 fCopyIntoExisting = !RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_NoReplace);
2071 fFollowSymlinks = RT_BOOL(pList->mSourceSpec.fFileCopyFlags & FileCopyFlag_FollowLinks);
2072 }
2073 else
2074 AssertFailedBreakStmt(vrc = VERR_NOT_IMPLEMENTED);
2075
2076 uint32_t const fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
2077
2078 bool fDstExists = true;
2079
2080 GuestFsObjData dstObjData;
2081 int vrcGuest;
2082 vrc = mSession->i_fsQueryInfo(strDstRootAbs, fFollowSymlinks, dstObjData, &vrcGuest);
2083 if (RT_FAILURE(vrc))
2084 {
2085 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2086 {
2087 switch (vrcGuest)
2088 {
2089 case VERR_PATH_NOT_FOUND:
2090 RT_FALL_THROUGH();
2091 case VERR_FILE_NOT_FOUND:
2092 {
2093 fDstExists = false;
2094 vrc = VINF_SUCCESS;
2095 break;
2096 }
2097 default:
2098 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2099 Utf8StrFmt(tr("Querying information on guest for '%s' failed: %Rrc"),
2100 strDstRootAbs.c_str(), vrcGuest));
2101 break;
2102 }
2103 }
2104 else
2105 {
2106 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2107 Utf8StrFmt(tr("Querying information on guest for '%s' failed: %Rrc"),
2108 strDstRootAbs.c_str(), vrc));
2109 break;
2110 }
2111 }
2112
2113 if (pList->mSourceSpec.enmType == FsObjType_Directory)
2114 {
2115 LogFlowFunc(("Directory: fDirCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool\n",
2116 pList->mSourceSpec.fDirCopyFlags, fCopyIntoExisting, fFollowSymlinks));
2117
2118 if (fDstExists)
2119 {
2120 switch (dstObjData.mType)
2121 {
2122 case FsObjType_Directory:
2123 {
2124 if (!fCopyIntoExisting)
2125 {
2126 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2127 Utf8StrFmt(tr("Guest root directory \"%s\" already exists"),
2128 strDstRootAbs.c_str()));
2129 vrc = VERR_ALREADY_EXISTS;
2130 }
2131 break;
2132 }
2133
2134 case FsObjType_File:
2135 {
2136 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2137 Utf8StrFmt(tr("Destination \"%s\" on guest already exists and is a file"),
2138 strDstRootAbs.c_str()));
2139 vrc = VERR_IS_A_FILE;
2140 }
2141
2142 case FsObjType_Symlink:
2143 /** @ŧodo Resolve symlinks? */
2144 break;
2145
2146 default:
2147 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2148 Utf8StrFmt(tr("Unknown object type (%#x) on guest for \"%s\""),
2149 dstObjData.mType, strDstRootAbs.c_str()));
2150 vrc = VERR_NOT_SUPPORTED;
2151 break;
2152 }
2153 }
2154
2155 if (RT_FAILURE(vrc))
2156 break;
2157
2158 /* Clean up the final guest destination root path (for cleaning up mixed path separators). */
2159 vrc = GuestPath::Translate(strDstRootAbs,
2160 mSession->i_getGuestPathStyle() /* Source */, mSession->i_getGuestPathStyle() /* Dest */);
2161 if (RT_FAILURE(vrc))
2162 {
2163 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2164 Utf8StrFmt(tr("Translating guest destination root path \"%s\" failed: %Rrc"),
2165 strDstRootAbs.c_str(), vrc));
2166 break;
2167 }
2168
2169 /* Make sure the destination root directory exists. */
2170 if (pList->mSourceSpec.fDryRun == false)
2171 {
2172 vrc = directoryCreateOnGuest(strDstRootAbs, fDirMode, DirectoryCreateFlag_None,
2173 fFollowSymlinks, fCopyIntoExisting);
2174 if (RT_FAILURE(vrc))
2175 break;
2176 }
2177
2178 /* Walk the entries. */
2179 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
2180 while ( RT_SUCCESS(vrc)
2181 && itEntry != pList->mVecEntries.end())
2182 {
2183 FsEntry *pEntry = *itEntry;
2184 AssertPtr(pEntry);
2185
2186 Utf8Str strSrcAbs = strSrcRootAbs;
2187 Utf8Str strDstAbs = strDstRootAbs;
2188
2189 strSrcAbs += PATH_STYLE_SEP_STR(PATH_STYLE_NATIVE);
2190 strSrcAbs += pEntry->strPath;
2191
2192 strDstAbs += PATH_STYLE_SEP_STR(mSession->i_getGuestPathStyle());
2193 strDstAbs += pEntry->strPath;
2194
2195 /* Clean up the final host source path (for cleaning up mixed path separators). */
2196 vrc = GuestPath::Translate(strSrcAbs, PATH_STYLE_NATIVE /* Source */, PATH_STYLE_NATIVE /* Dest */);
2197 if (RT_FAILURE(vrc))
2198 {
2199 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2200 Utf8StrFmt(tr("Translating host source path\"%s\" failed: %Rrc"),
2201 strSrcAbs.c_str(), vrc));
2202 break;
2203 }
2204
2205 /* Translate the final guest destination path. */
2206 vrc = GuestPath::Translate(strDstAbs,
2207 mSession->i_getGuestPathStyle() /* Source */, mSession->i_getGuestPathStyle() /* Dest */);
2208 if (RT_FAILURE(vrc))
2209 {
2210 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2211 Utf8StrFmt(tr("Translating guest destination path \"%s\" failed: %Rrc"),
2212 strDstAbs.c_str(), vrc));
2213 break;
2214 }
2215
2216 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
2217
2218 switch (pEntry->fMode & RTFS_TYPE_MASK)
2219 {
2220 case RTFS_TYPE_DIRECTORY:
2221 {
2222 LogRel(("Guest Control: Copying directory '%s' from host to '%s' on guest ...\n", strSrcAbs.c_str(), strDstAbs.c_str()));
2223 if (!pList->mSourceSpec.fDryRun)
2224 vrc = directoryCreateOnGuest(strDstAbs, fDirMode, DirectoryCreateFlag_None,
2225 fFollowSymlinks, fCopyIntoExisting);
2226 break;
2227 }
2228
2229 case RTFS_TYPE_FILE:
2230 {
2231 if (!pList->mSourceSpec.fDryRun)
2232 vrc = fileCopyToGuest(strSrcAbs, strDstAbs, pList->mSourceSpec.fFileCopyFlags);
2233 break;
2234 }
2235
2236 default:
2237 LogRel(("Guest Control: Warning: Host file system type 0x%x for '%s' is not supported, skipping\n",
2238 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
2239 break;
2240 }
2241
2242 if (RT_FAILURE(vrc))
2243 break;
2244
2245 ++itEntry;
2246 }
2247 }
2248 else if (pList->mSourceSpec.enmType == FsObjType_File)
2249 {
2250 LogFlowFunc(("File: fFileCopyFlags=%#x, fCopyIntoExisting=%RTbool, fFollowSymlinks=%RTbool\n",
2251 pList->mSourceSpec.fFileCopyFlags, fCopyIntoExisting, fFollowSymlinks));
2252
2253 if (fDstExists)
2254 {
2255 switch (dstObjData.mType)
2256 {
2257 case FsObjType_Directory:
2258 {
2259 if (!fCopyIntoExisting)
2260 {
2261 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2262 Utf8StrFmt(tr("Destination \"%s\" on the guest already exists and is a directory"),
2263 strDstRootAbs.c_str()));
2264 vrc = VERR_IS_A_DIRECTORY;
2265 break;
2266 }
2267
2268 /* Append the actual file name to the destination. */
2269 strDstRootAbs += PATH_STYLE_SEP_STR(mSession->i_getGuestPathStyle());
2270 strDstRootAbs += RTPathFilename(strSrcRootAbs.c_str());
2271 break;
2272 }
2273
2274 case FsObjType_File:
2275 {
2276 if (!fCopyIntoExisting)
2277 {
2278 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2279 Utf8StrFmt(tr("Guest file \"%s\" already exists"), strDstRootAbs.c_str()));
2280 vrc = VERR_ALREADY_EXISTS;
2281 }
2282 break;
2283 }
2284
2285 default:
2286 {
2287 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2288 Utf8StrFmt(tr("Unsupported guest file system type (%#x) for \"%s\""),
2289 dstObjData.mType, strDstRootAbs.c_str()));
2290 vrc = VERR_NOT_SUPPORTED;
2291 break;
2292 }
2293 }
2294 }
2295
2296 /* Cleanup the destination path. */
2297 vrc = GuestPath::Translate(strDstRootAbs,
2298 mSession->i_getGuestPathStyle() /* Source */, mSession->i_getGuestPathStyle() /* Dest */);
2299 if (RT_FAILURE(vrc))
2300 {
2301 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2302 Utf8StrFmt(tr("Translating guest destination path \"%s\" failed: %Rrc"),
2303 strDstRootAbs.c_str(), vrc));
2304 break;
2305 }
2306
2307 if (!pList->mSourceSpec.fDryRun)
2308 vrc = fileCopyToGuest(strSrcRootAbs, strDstRootAbs, pList->mSourceSpec.fFileCopyFlags);
2309 }
2310 else
2311 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
2312
2313 if (RT_FAILURE(vrc))
2314 break;
2315
2316 ++itList;
2317 }
2318
2319 if (RT_SUCCESS(vrc))
2320 vrc = setProgressSuccess();
2321
2322 LogFlowFuncLeaveRC(vrc);
2323 return vrc;
2324}
2325
2326GuestSessionTaskUpdateAdditions::GuestSessionTaskUpdateAdditions(GuestSession *pSession,
2327 const Utf8Str &strSource,
2328 const ProcessArguments &aArguments,
2329 uint32_t fFlags)
2330 : GuestSessionTask(pSession)
2331{
2332 m_strTaskName = "gctlUpGA";
2333
2334 mSource = strSource;
2335 mArguments = aArguments;
2336 mFlags = fFlags;
2337}
2338
2339GuestSessionTaskUpdateAdditions::~GuestSessionTaskUpdateAdditions(void)
2340{
2341
2342}
2343
2344/**
2345 * Adds arguments to existing process arguments.
2346 * Identical / already existing arguments will be filtered out.
2347 *
2348 * @returns VBox status code.
2349 * @param aArgumentsDest Destination to add arguments to.
2350 * @param aArgumentsSource Arguments to add.
2351 */
2352int GuestSessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
2353{
2354 try
2355 {
2356 /* Filter out arguments which already are in the destination to
2357 * not end up having them specified twice. Not the fastest method on the
2358 * planet but does the job. */
2359 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
2360 while (itSource != aArgumentsSource.end())
2361 {
2362 bool fFound = false;
2363 ProcessArguments::iterator itDest = aArgumentsDest.begin();
2364 while (itDest != aArgumentsDest.end())
2365 {
2366 if ((*itDest).equalsIgnoreCase((*itSource)))
2367 {
2368 fFound = true;
2369 break;
2370 }
2371 ++itDest;
2372 }
2373
2374 if (!fFound)
2375 aArgumentsDest.push_back((*itSource));
2376
2377 ++itSource;
2378 }
2379 }
2380 catch(std::bad_alloc &)
2381 {
2382 return VERR_NO_MEMORY;
2383 }
2384
2385 return VINF_SUCCESS;
2386}
2387
2388/**
2389 * Helper function to copy a file from a VISO to the guest.
2390 *
2391 * @returns VBox status code.
2392 * @param pSession Guest session to use.
2393 * @param hVfsIso VISO handle to use.
2394 * @param strFileSrc Source file path on VISO to copy.
2395 * @param strFileDst Destination file path on guest.
2396 * @param fOptional When set to \c true, the file is optional, i.e. can be skipped
2397 * when not found, \c false if not.
2398 */
2399int GuestSessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, RTVFS hVfsIso,
2400 Utf8Str const &strFileSrc, const Utf8Str &strFileDst, bool fOptional)
2401{
2402 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2403 AssertReturn(hVfsIso != NIL_RTVFS, VERR_INVALID_POINTER);
2404
2405 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
2406 int vrc = RTVfsFileOpen(hVfsIso, strFileSrc.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFile);
2407 if (RT_SUCCESS(vrc))
2408 {
2409 uint64_t cbSrcSize = 0;
2410 vrc = RTVfsFileQuerySize(hVfsFile, &cbSrcSize);
2411 if (RT_SUCCESS(vrc))
2412 {
2413 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
2414 strFileSrc.c_str(), strFileDst.c_str()));
2415
2416 GuestFileOpenInfo dstOpenInfo;
2417 dstOpenInfo.mFilename = strFileDst;
2418 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
2419 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
2420 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
2421
2422 ComObjPtr<GuestFile> dstFile;
2423 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2424 vrc = mSession->i_fileOpen(dstOpenInfo, dstFile, &vrcGuest);
2425 if (RT_FAILURE(vrc))
2426 {
2427 switch (vrc)
2428 {
2429 case VERR_GSTCTL_GUEST_ERROR:
2430 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(vrcGuest, strFileDst.c_str()));
2431 break;
2432
2433 default:
2434 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2435 Utf8StrFmt(tr("Guest file \"%s\" could not be opened: %Rrc"),
2436 strFileDst.c_str(), vrc));
2437 break;
2438 }
2439 }
2440 else
2441 {
2442 vrc = fileCopyToGuestInner(strFileSrc, hVfsFile, strFileDst, dstFile, FileCopyFlag_None, 0 /*offCopy*/, cbSrcSize);
2443
2444 int vrc2 = dstFile->i_closeFile(&vrcGuest);
2445 AssertRC(vrc2);
2446 }
2447 }
2448
2449 RTVfsFileRelease(hVfsFile);
2450 }
2451 else if (fOptional)
2452 vrc = VINF_SUCCESS;
2453
2454 return vrc;
2455}
2456
2457/**
2458 * Helper function to run (start) a file on the guest.
2459 *
2460 * @returns VBox status code.
2461 * @param pSession Guest session to use.
2462 * @param procInfo Guest process startup info to use.
2463 */
2464int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
2465{
2466 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2467
2468 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
2469
2470 GuestProcessTool procTool;
2471 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2472 int vrc = procTool.init(pSession, procInfo, false /* Async */, &vrcGuest);
2473 if (RT_SUCCESS(vrc))
2474 {
2475 if (RT_SUCCESS(vrcGuest))
2476 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &vrcGuest);
2477 if (RT_SUCCESS(vrc))
2478 vrc = procTool.getTerminationStatus();
2479 }
2480
2481 if (RT_FAILURE(vrc))
2482 {
2483 switch (vrc)
2484 {
2485 case VERR_GSTCTL_PROCESS_EXIT_CODE:
2486 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2487 Utf8StrFmt(tr("Running update file \"%s\" on guest failed: %Rrc"),
2488 procInfo.mExecutable.c_str(), procTool.getRc()));
2489 break;
2490
2491 case VERR_GSTCTL_GUEST_ERROR:
2492 setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Running update file on guest failed"),
2493 GuestErrorInfo(GuestErrorInfo::Type_Process, vrcGuest, procInfo.mExecutable.c_str()));
2494 break;
2495
2496 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
2497 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2498 Utf8StrFmt(tr("Update file \"%s\" reported invalid running state"),
2499 procInfo.mExecutable.c_str()));
2500 break;
2501
2502 default:
2503 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2504 Utf8StrFmt(tr("Error while running update file \"%s\" on guest: %Rrc"),
2505 procInfo.mExecutable.c_str(), vrc));
2506 break;
2507 }
2508 }
2509
2510 return vrc;
2511}
2512
2513/** @copydoc GuestSessionTask::Run */
2514int GuestSessionTaskUpdateAdditions::Run(void)
2515{
2516 LogFlowThisFuncEnter();
2517
2518 ComObjPtr<GuestSession> pSession = mSession;
2519 Assert(!pSession.isNull());
2520
2521 AutoCaller autoCaller(pSession);
2522 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2523
2524 int vrc = setProgress(10);
2525 if (RT_FAILURE(vrc))
2526 return vrc;
2527
2528 HRESULT hrc = S_OK;
2529
2530 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
2531
2532 ComObjPtr<Guest> pGuest(mSession->i_getParent());
2533#if 0
2534 /*
2535 * Wait for the guest being ready within 30 seconds.
2536 */
2537 AdditionsRunLevelType_T addsRunLevel;
2538 uint64_t tsStart = RTTimeSystemMilliTS();
2539 while ( SUCCEEDED(hrc = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
2540 && ( addsRunLevel != AdditionsRunLevelType_Userland
2541 && addsRunLevel != AdditionsRunLevelType_Desktop))
2542 {
2543 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
2544 {
2545 vrc = VERR_TIMEOUT;
2546 break;
2547 }
2548
2549 RTThreadSleep(100); /* Wait a bit. */
2550 }
2551
2552 if (FAILED(hrc)) vrc = VERR_TIMEOUT;
2553 if (vrc == VERR_TIMEOUT)
2554 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2555 Utf8StrFmt(tr("Guest Additions were not ready within time, giving up")));
2556#else
2557 /*
2558 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
2559 * can continue.
2560 */
2561 AdditionsRunLevelType_T addsRunLevel;
2562 if ( FAILED(hrc = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
2563 || ( addsRunLevel != AdditionsRunLevelType_Userland
2564 && addsRunLevel != AdditionsRunLevelType_Desktop))
2565 {
2566 if (addsRunLevel == AdditionsRunLevelType_System)
2567 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2568 Utf8StrFmt(tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
2569 else
2570 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2571 Utf8StrFmt(tr("Guest Additions not installed or ready, aborting automatic update")));
2572 vrc = VERR_NOT_SUPPORTED;
2573 }
2574#endif
2575
2576 if (RT_SUCCESS(vrc))
2577 {
2578 /*
2579 * Determine if we are able to update automatically. This only works
2580 * if there are recent Guest Additions installed already.
2581 */
2582 Utf8Str strAddsVer;
2583 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2584 if ( RT_SUCCESS(vrc)
2585 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
2586 {
2587 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2588 Utf8StrFmt(tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
2589 strAddsVer.c_str()));
2590 vrc = VERR_NOT_SUPPORTED;
2591 }
2592 }
2593
2594 Utf8Str strOSVer;
2595 eOSType osType = eOSType_Unknown;
2596 if (RT_SUCCESS(vrc))
2597 {
2598 /*
2599 * Determine guest OS type and the required installer image.
2600 */
2601 Utf8Str strOSType;
2602 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
2603 if (RT_SUCCESS(vrc))
2604 {
2605 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
2606 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
2607 {
2608 osType = eOSType_Windows;
2609
2610 /*
2611 * Determine guest OS version.
2612 */
2613 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
2614 if (RT_FAILURE(vrc))
2615 {
2616 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2617 Utf8StrFmt(tr("Unable to detected guest OS version, please update manually")));
2618 vrc = VERR_NOT_SUPPORTED;
2619 }
2620
2621 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
2622 * can't do automated updates here. */
2623 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
2624 if ( RT_SUCCESS(vrc)
2625 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
2626 {
2627 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
2628 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
2629 {
2630 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
2631 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
2632 * flag is set this update routine ends successfully as soon as the installer was started
2633 * (and the user has to deal with it in the guest). */
2634 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2635 {
2636 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2637 Utf8StrFmt(tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
2638 vrc = VERR_NOT_SUPPORTED;
2639 }
2640 }
2641 }
2642 else
2643 {
2644 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2645 Utf8StrFmt(tr("%s (%s) not supported for automatic updating, please update manually"),
2646 strOSType.c_str(), strOSVer.c_str()));
2647 vrc = VERR_NOT_SUPPORTED;
2648 }
2649 }
2650 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
2651 {
2652 osType = eOSType_Solaris;
2653 }
2654 else /* Everything else hopefully means Linux :-). */
2655 osType = eOSType_Linux;
2656
2657 if ( RT_SUCCESS(vrc)
2658 && ( osType != eOSType_Windows
2659 && osType != eOSType_Linux))
2660 /** @todo Support Solaris. */
2661 {
2662 hrc = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2663 Utf8StrFmt(tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
2664 strOSType.c_str()));
2665 vrc = VERR_NOT_SUPPORTED;
2666 }
2667 }
2668 }
2669
2670 if (RT_SUCCESS(vrc))
2671 {
2672 /*
2673 * Try to open the .ISO file to extract all needed files.
2674 */
2675 RTVFSFILE hVfsFileIso;
2676 vrc = RTVfsFileOpenNormal(mSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, &hVfsFileIso);
2677 if (RT_FAILURE(vrc))
2678 {
2679 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2680 Utf8StrFmt(tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
2681 mSource.c_str(), vrc));
2682 }
2683 else
2684 {
2685 RTVFS hVfsIso;
2686 vrc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, NULL);
2687 if (RT_FAILURE(vrc))
2688 {
2689 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2690 Utf8StrFmt(tr("Unable to open file as ISO 9660 file system volume: %Rrc"), vrc));
2691 }
2692 else
2693 {
2694 Utf8Str strUpdateDir;
2695
2696 vrc = setProgress(5);
2697 if (RT_SUCCESS(vrc))
2698 {
2699 /* Try getting the installed Guest Additions version to know whether we
2700 * can install our temporary Guest Addition data into the original installation
2701 * directory.
2702 *
2703 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
2704 * a different location then.
2705 */
2706 bool fUseInstallDir = false;
2707
2708 Utf8Str strAddsVer;
2709 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2710 if ( RT_SUCCESS(vrc)
2711 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
2712 {
2713 fUseInstallDir = true;
2714 }
2715
2716 if (fUseInstallDir)
2717 {
2718 vrc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
2719 if (RT_SUCCESS(vrc))
2720 {
2721 if (strUpdateDir.isNotEmpty())
2722 {
2723 if (osType == eOSType_Windows)
2724 {
2725 strUpdateDir.findReplace('/', '\\');
2726 strUpdateDir.append("\\Update\\");
2727 }
2728 else
2729 strUpdateDir.append("/update/");
2730 }
2731 /* else Older Guest Additions might not handle this property correctly. */
2732 }
2733 /* Ditto. */
2734 }
2735
2736 /** @todo Set fallback installation directory. Make this a *lot* smarter. Later. */
2737 if (strUpdateDir.isEmpty())
2738 {
2739 if (osType == eOSType_Windows)
2740 strUpdateDir = "C:\\Temp\\";
2741 else
2742 strUpdateDir = "/tmp/";
2743 }
2744 }
2745
2746 /* Create the installation directory. */
2747 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2748 if (RT_SUCCESS(vrc))
2749 {
2750 LogRel(("Guest Additions update directory is: %s\n", strUpdateDir.c_str()));
2751
2752 vrc = pSession->i_directoryCreate(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &vrcGuest);
2753 if (RT_FAILURE(vrc))
2754 {
2755 switch (vrc)
2756 {
2757 case VERR_GSTCTL_GUEST_ERROR:
2758 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR, tr("Creating installation directory on guest failed"),
2759 GuestErrorInfo(GuestErrorInfo::Type_Directory, vrcGuest, strUpdateDir.c_str()));
2760 break;
2761
2762 default:
2763 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2764 Utf8StrFmt(tr("Creating installation directory \"%s\" on guest failed: %Rrc"),
2765 strUpdateDir.c_str(), vrc));
2766 break;
2767 }
2768 }
2769 }
2770
2771 if (RT_SUCCESS(vrc))
2772 vrc = setProgress(10);
2773
2774 if (RT_SUCCESS(vrc))
2775 {
2776 /* Prepare the file(s) we want to copy over to the guest and
2777 * (maybe) want to run. */
2778 switch (osType)
2779 {
2780 case eOSType_Windows:
2781 {
2782 /* Do we need to install our certificates? We do this for W2K and up. */
2783 bool fInstallCert = false;
2784
2785 /* Only Windows 2000 and up need certificates to be installed. */
2786 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
2787 {
2788 fInstallCert = true;
2789 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
2790 }
2791 else
2792 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
2793
2794 if (fInstallCert)
2795 {
2796 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
2797 {
2798 { "vbox.cer", "/CERT/VBOX.CER" },
2799 { "vbox-sha1.cer", "/CERT/VBOX-SHA1.CER" },
2800 { "vbox-sha256.cer", "/CERT/VBOX-SHA256.CER" },
2801 { "vbox-sha256-r3.cer", "/CERT/VBOX-SHA256-R3.CER" },
2802 { "oracle-vbox.cer", "/CERT/ORACLE-VBOX.CER" },
2803 };
2804 uint32_t fCopyCertUtil = ISOFILE_FLAG_COPY_FROM_ISO;
2805 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
2806 {
2807 /* Skip if not present on the ISO. */
2808 RTFSOBJINFO ObjInfo;
2809 vrc = RTVfsQueryPathInfo(hVfsIso, s_aCertFiles[i].pszIso, &ObjInfo, RTFSOBJATTRADD_NOTHING,
2810 RTPATH_F_ON_LINK);
2811 if (RT_FAILURE(vrc))
2812 continue;
2813
2814 /* Copy the certificate certificate. */
2815 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
2816 mFiles.push_back(ISOFile(s_aCertFiles[i].pszIso,
2817 strDstCert,
2818 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_OPTIONAL));
2819
2820 /* Out certificate installation utility. */
2821 /* First pass: Copy over the file (first time only) + execute it to remove any
2822 * existing VBox certificates. */
2823 GuestProcessStartupInfo siCertUtilRem;
2824 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
2825 /* The argv[0] should contain full path to the executable module */
2826 siCertUtilRem.mArguments.push_back(strUpdateDir + "VBoxCertUtil.exe");
2827 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
2828 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2829 siCertUtilRem.mArguments.push_back(strDstCert);
2830 siCertUtilRem.mArguments.push_back(strDstCert);
2831 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2832 strUpdateDir + "VBoxCertUtil.exe",
2833 fCopyCertUtil | ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2834 siCertUtilRem));
2835 fCopyCertUtil = 0;
2836 /* Second pass: Only execute (but don't copy) again, this time installng the
2837 * recent certificates just copied over. */
2838 GuestProcessStartupInfo siCertUtilAdd;
2839 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
2840 /* The argv[0] should contain full path to the executable module */
2841 siCertUtilAdd.mArguments.push_back(strUpdateDir + "VBoxCertUtil.exe");
2842 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
2843 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2844 siCertUtilAdd.mArguments.push_back(strDstCert);
2845 siCertUtilAdd.mArguments.push_back(strDstCert);
2846 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2847 strUpdateDir + "VBoxCertUtil.exe",
2848 ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2849 siCertUtilAdd));
2850 }
2851 }
2852 /* The installers in different flavors, as we don't know (and can't assume)
2853 * the guest's bitness. */
2854 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS-X86.EXE",
2855 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
2856 ISOFILE_FLAG_COPY_FROM_ISO));
2857 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS-AMD64.EXE",
2858 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
2859 ISOFILE_FLAG_COPY_FROM_ISO));
2860 /* The stub loader which decides which flavor to run. */
2861 GuestProcessStartupInfo siInstaller;
2862 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
2863 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
2864 * setup can take quite a while, so be on the safe side. */
2865 siInstaller.mTimeoutMS = 5 * 60 * 1000;
2866
2867 /* The argv[0] should contain full path to the executable module */
2868 siInstaller.mArguments.push_back(strUpdateDir + "VBoxWindowsAdditions.exe");
2869 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
2870 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
2871 /* Don't quit VBoxService during upgrade because it still is used for this
2872 * piece of code we're in right now (that is, here!) ... */
2873 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
2874 /* Tell the installer to report its current installation status
2875 * using a running VBoxTray instance via balloon messages in the
2876 * Windows taskbar. */
2877 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
2878 /* Add optional installer command line arguments from the API to the
2879 * installer's startup info. */
2880 vrc = addProcessArguments(siInstaller.mArguments, mArguments);
2881 AssertRC(vrc);
2882 /* If the caller does not want to wait for out guest update process to end,
2883 * complete the progress object now so that the caller can do other work. */
2884 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
2885 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
2886 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS.EXE",
2887 strUpdateDir + "VBoxWindowsAdditions.exe",
2888 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_EXECUTE, siInstaller));
2889 break;
2890 }
2891 case eOSType_Linux:
2892 {
2893 /* Copy over the installer to the guest but don't execute it.
2894 * Execution will be done by the shell instead. */
2895 mFiles.push_back(ISOFile("VBOXLINUXADDITIONS.RUN",
2896 strUpdateDir + "VBoxLinuxAdditions.run", ISOFILE_FLAG_COPY_FROM_ISO));
2897
2898 GuestProcessStartupInfo siInstaller;
2899 siInstaller.mName = "VirtualBox Linux Guest Additions Installer";
2900 /* Set a running timeout of 5 minutes -- compiling modules and stuff for the Linux Guest Additions
2901 * setup can take quite a while, so be on the safe side. */
2902 siInstaller.mTimeoutMS = 5 * 60 * 1000;
2903 /* The argv[0] should contain full path to the shell we're using to execute the installer. */
2904 siInstaller.mArguments.push_back("/bin/sh");
2905 /* Now add the stuff we need in order to execute the installer. */
2906 siInstaller.mArguments.push_back(strUpdateDir + "VBoxLinuxAdditions.run");
2907 /* Make sure to add "--nox11" to the makeself wrapper in order to not getting any blocking xterm
2908 * window spawned when doing any unattended Linux GA installations. */
2909 siInstaller.mArguments.push_back("--nox11");
2910 siInstaller.mArguments.push_back("--");
2911 /* Force the upgrade. Needed in order to skip the confirmation dialog about warning to upgrade. */
2912 siInstaller.mArguments.push_back("--force"); /** @todo We might want a dedicated "--silent" switch here. */
2913 /* If the caller does not want to wait for out guest update process to end,
2914 * complete the progress object now so that the caller can do other work. */
2915 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
2916 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
2917 mFiles.push_back(ISOFile("/bin/sh" /* Source */, "/bin/sh" /* Dest */,
2918 ISOFILE_FLAG_EXECUTE, siInstaller));
2919 break;
2920 }
2921 case eOSType_Solaris:
2922 /** @todo Add Solaris support. */
2923 break;
2924 default:
2925 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
2926 break;
2927 }
2928 }
2929
2930 if (RT_SUCCESS(vrc))
2931 {
2932 /* We want to spend 40% total for all copying operations. So roughly
2933 * calculate the specific percentage step of each copied file. */
2934 uint8_t uOffset = 20; /* Start at 20%. */
2935 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2936
2937 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
2938
2939 std::vector<ISOFile>::const_iterator itFiles = mFiles.begin();
2940 while (itFiles != mFiles.end())
2941 {
2942 if (itFiles->fFlags & ISOFILE_FLAG_COPY_FROM_ISO)
2943 {
2944 bool fOptional = false;
2945 if (itFiles->fFlags & ISOFILE_FLAG_OPTIONAL)
2946 fOptional = true;
2947 vrc = copyFileToGuest(pSession, hVfsIso, itFiles->strSource, itFiles->strDest, fOptional);
2948 if (RT_FAILURE(vrc))
2949 {
2950 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2951 Utf8StrFmt(tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
2952 itFiles->strSource.c_str(), itFiles->strDest.c_str(), vrc));
2953 break;
2954 }
2955 }
2956
2957 vrc = setProgress(uOffset);
2958 if (RT_FAILURE(vrc))
2959 break;
2960 uOffset += uStep;
2961
2962 ++itFiles;
2963 }
2964 }
2965
2966 /* Done copying, close .ISO file. */
2967 RTVfsRelease(hVfsIso);
2968
2969 if (RT_SUCCESS(vrc))
2970 {
2971 /* We want to spend 35% total for all copying operations. So roughly
2972 * calculate the specific percentage step of each copied file. */
2973 uint8_t uOffset = 60; /* Start at 60%. */
2974 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2975
2976 LogRel(("Executing Guest Additions update files ...\n"));
2977
2978 std::vector<ISOFile>::iterator itFiles = mFiles.begin();
2979 while (itFiles != mFiles.end())
2980 {
2981 if (itFiles->fFlags & ISOFILE_FLAG_EXECUTE)
2982 {
2983 vrc = runFileOnGuest(pSession, itFiles->mProcInfo);
2984 if (RT_FAILURE(vrc))
2985 break;
2986 }
2987
2988 vrc = setProgress(uOffset);
2989 if (RT_FAILURE(vrc))
2990 break;
2991 uOffset += uStep;
2992
2993 ++itFiles;
2994 }
2995 }
2996
2997 if (RT_SUCCESS(vrc))
2998 {
2999 LogRel(("Automatic update of Guest Additions succeeded\n"));
3000 vrc = setProgressSuccess();
3001 }
3002 }
3003
3004 RTVfsFileRelease(hVfsFileIso);
3005 }
3006 }
3007
3008 if (RT_FAILURE(vrc))
3009 {
3010 if (vrc == VERR_CANCELLED)
3011 {
3012 LogRel(("Automatic update of Guest Additions was canceled\n"));
3013
3014 hrc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
3015 Utf8StrFmt(tr("Installation was canceled")));
3016 }
3017 else
3018 {
3019 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", vrc);
3020 if (!mProgress.isNull()) /* Progress object is optional. */
3021 {
3022#ifdef VBOX_STRICT
3023 /* If we forgot to set the progress object accordingly, let us know. */
3024 LONG rcProgress;
3025 AssertMsg( SUCCEEDED(mProgress->COMGETTER(ResultCode(&rcProgress)))
3026 && FAILED(rcProgress), ("Task indicated an error (%Rrc), but progress did not indicate this (%Rhrc)\n",
3027 vrc, rcProgress));
3028#endif
3029 com::ProgressErrorInfo errorInfo(mProgress);
3030 if ( errorInfo.isFullAvailable()
3031 || errorInfo.isBasicAvailable())
3032 {
3033 strError = errorInfo.getText();
3034 }
3035 }
3036
3037 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
3038 strError.c_str(), hrc));
3039 }
3040
3041 LogRel(("Please install Guest Additions manually\n"));
3042 }
3043
3044 /** @todo Clean up copied / left over installation files. */
3045
3046 LogFlowFuncLeaveRC(vrc);
3047 return vrc;
3048}
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