VirtualBox

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

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

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