VirtualBox

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

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

Guest Control/Main: More path building / translation fixes. ​bugref:10286

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

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