VirtualBox

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

Last change on this file since 72958 was 72958, checked in by vboxsync, 7 years ago

Main/GuestCtrl: Move the guest additions update code from RTIsoFs* to the appropriate RTVfs* APIs (not tested because of bugref:8078)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.9 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 72958 2018-07-07 21:27:53Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session tasks.
4 */
5
6/*
7 * Copyright (C) 2012-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "GuestSessionImplTasks.h"
31#include "GuestCtrlImplPrivate.h"
32
33#include "Global.h"
34#include "AutoCaller.h"
35#include "ConsoleImpl.h"
36#include "ProgressImpl.h"
37
38#include <memory> /* For auto_ptr. */
39
40#include <iprt/env.h>
41#include <iprt/file.h> /* For CopyTo/From. */
42#include <iprt/dir.h>
43#include <iprt/path.h>
44#include <iprt/fsvfs.h>
45
46
47/*********************************************************************************************************************************
48* Defines *
49*********************************************************************************************************************************/
50
51/**
52 * (Guest Additions) ISO file flags.
53 * Needed for handling Guest Additions updates.
54 */
55#define ISOFILE_FLAG_NONE 0
56/** Copy over the file from host to the
57 * guest. */
58#define ISOFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
59/** Execute file on the guest after it has
60 * been successfully transfered. */
61#define ISOFILE_FLAG_EXECUTE RT_BIT(7)
62/** File is optional, does not have to be
63 * existent on the .ISO. */
64#define ISOFILE_FLAG_OPTIONAL RT_BIT(8)
65
66
67// session task classes
68/////////////////////////////////////////////////////////////////////////////
69
70GuestSessionTask::GuestSessionTask(GuestSession *pSession)
71 : ThreadTask("GenericGuestSessionTask")
72{
73 mSession = pSession;
74
75 switch (mSession->i_getPathStyle())
76 {
77 case PathStyle_DOS:
78 mfPathStyle = RTPATH_STR_F_STYLE_DOS;
79 mPathStyle = "\\";
80 break;
81
82 default:
83 mfPathStyle = RTPATH_STR_F_STYLE_UNIX;
84 mPathStyle = "/";
85 break;
86 }
87}
88
89GuestSessionTask::~GuestSessionTask(void)
90{
91}
92
93int GuestSessionTask::createAndSetProgressObject(ULONG cOperations /* = 1 */)
94{
95 LogFlowThisFunc(("cOperations=%ld\n", cOperations));
96
97 /* Create the progress object. */
98 ComObjPtr<Progress> pProgress;
99 HRESULT hr = pProgress.createObject();
100 if (FAILED(hr))
101 return VERR_COM_UNEXPECTED;
102
103 hr = pProgress->init(static_cast<IGuestSession*>(mSession),
104 Bstr(mDesc).raw(),
105 TRUE /* aCancelable */, cOperations, Bstr(mDesc).raw());
106 if (FAILED(hr))
107 return VERR_COM_UNEXPECTED;
108
109 mProgress = pProgress;
110
111 LogFlowFuncLeave();
112 return VINF_SUCCESS;
113}
114
115int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
116{
117 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
118
119 mDesc = strDesc;
120 mProgress = pProgress;
121 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
122
123 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
124 return Global::vboxStatusCodeToCOM(hrc);
125}
126
127int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
128 const Utf8Str &strPath, Utf8Str &strValue)
129{
130 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
131 const ComPtr<IMachine> pMachine = pConsole->i_machine();
132
133 Assert(!pMachine.isNull());
134 Bstr strTemp, strFlags;
135 LONG64 i64Timestamp;
136 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
137 strTemp.asOutParam(),
138 &i64Timestamp, strFlags.asOutParam());
139 if (SUCCEEDED(hr))
140 {
141 strValue = strTemp;
142 return VINF_SUCCESS;
143 }
144 return VERR_NOT_FOUND;
145}
146
147int GuestSessionTask::setProgress(ULONG uPercent)
148{
149 if (mProgress.isNull()) /* Progress is optional. */
150 return VINF_SUCCESS;
151
152 BOOL fCanceled;
153 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
154 && fCanceled)
155 return VERR_CANCELLED;
156 BOOL fCompleted;
157 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
158 && fCompleted)
159 {
160 AssertMsgFailed(("Setting value of an already completed progress\n"));
161 return VINF_SUCCESS;
162 }
163 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
164 if (FAILED(hr))
165 return VERR_COM_UNEXPECTED;
166
167 return VINF_SUCCESS;
168}
169
170int GuestSessionTask::setProgressSuccess(void)
171{
172 if (mProgress.isNull()) /* Progress is optional. */
173 return VINF_SUCCESS;
174
175 BOOL fCompleted;
176 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
177 && !fCompleted)
178 {
179 HRESULT hr = mProgress->i_notifyComplete(S_OK);
180 if (FAILED(hr))
181 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
182 }
183
184 return VINF_SUCCESS;
185}
186
187HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
188{
189 LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
190 hr, strMsg.c_str()));
191
192 if (mProgress.isNull()) /* Progress is optional. */
193 return hr; /* Return original rc. */
194
195 BOOL fCanceled;
196 BOOL fCompleted;
197 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
198 && !fCanceled
199 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
200 && !fCompleted)
201 {
202 HRESULT hr2 = mProgress->i_notifyComplete(hr,
203 COM_IIDOF(IGuestSession),
204 GuestSession::getStaticComponentName(),
205 strMsg.c_str());
206 if (FAILED(hr2))
207 return hr2;
208 }
209 return hr; /* Return original rc. */
210}
211
212/**
213 * Creates a directory on the guest.
214 *
215 * @return VBox status code. VERR_ALREADY_EXISTS if directory on the guest already exists.
216 * @param strPath Absolute path to directory on the guest (guest style path) to create.
217 * @param enmDirectoryCreateFlags Directory creation flags.
218 * @param uMode Directory mode to use for creation.
219 * @param fFollowSymlinks Whether to follow symlinks on the guest or not.
220 */
221int GuestSessionTask::directoryCreate(const com::Utf8Str &strPath,
222 DirectoryCreateFlag_T enmDirectoryCreateFlags, uint32_t uMode, bool fFollowSymlinks)
223{
224 LogFlowFunc(("strPath=%s, fFlags=0x%x, uMode=%RU32, fFollowSymlinks=%RTbool\n",
225 strPath.c_str(), enmDirectoryCreateFlags, uMode, fFollowSymlinks));
226
227 GuestFsObjData objData; int rcGuest;
228 int rc = mSession->i_directoryQueryInfo(strPath, fFollowSymlinks, objData, &rcGuest);
229 if (RT_SUCCESS(rc))
230 {
231 return VERR_ALREADY_EXISTS;
232 }
233 else
234 {
235 switch (rc)
236 {
237 case VERR_GSTCTL_GUEST_ERROR:
238 {
239 switch (rcGuest)
240 {
241 case VERR_FILE_NOT_FOUND:
242 case VERR_PATH_NOT_FOUND:
243 rc = mSession->i_directoryCreate(strPath.c_str(), uMode, enmDirectoryCreateFlags, &rcGuest);
244 break;
245 default:
246 break;
247 }
248 break;
249 }
250
251 default:
252 break;
253 }
254 }
255
256 if (RT_FAILURE(rc))
257 {
258 if (rc == VERR_GSTCTL_GUEST_ERROR)
259 {
260 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
261 }
262 else
263 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
264 Utf8StrFmt(GuestSession::tr("Error creating directory on the guest: %Rrc"), strPath.c_str(), rc));
265 }
266
267 LogFlowFuncLeaveRC(rc);
268 return rc;
269}
270
271/**
272 * Main function for copying a file from guest to the host.
273 *
274 * @return VBox status code.
275 * @param srcFile Guest file (source) to copy to the host. Must be in opened and ready state already.
276 * @param phDstFile Pointer to host file handle (destination) to copy to. Must be in opened and ready state already.
277 * @param fFileCopyFlags File copy flags.
278 * @param offCopy Offset (in bytes) where to start copying the source file.
279 * @param cbSize Size (in bytes) to copy from the source file.
280 */
281int GuestSessionTask::fileCopyFromGuestInner(ComObjPtr<GuestFile> &srcFile, PRTFILE phDstFile, FileCopyFlag_T fFileCopyFlags,
282 uint64_t offCopy, uint64_t cbSize)
283{
284 RT_NOREF(fFileCopyFlags);
285
286 BOOL fCanceled = FALSE;
287 uint64_t cbWrittenTotal = 0;
288 uint64_t cbToRead = cbSize;
289
290 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
291
292 int rc = VINF_SUCCESS;
293
294 if (offCopy)
295 {
296 uint64_t offActual;
297 rc = srcFile->i_seekAt(offCopy, GUEST_FILE_SEEKTYPE_BEGIN, uTimeoutMs, &offActual);
298 if (RT_FAILURE(rc))
299 {
300 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
301 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
302 return rc;
303 }
304 }
305
306 BYTE byBuf[_64K];
307 while (cbToRead)
308 {
309 uint32_t cbRead;
310 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
311 rc = srcFile->i_readData(cbChunk, uTimeoutMs, byBuf, sizeof(byBuf), &cbRead);
312 if (RT_FAILURE(rc))
313 {
314 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
315 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64 from guest failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
316 break;
317 }
318
319 rc = RTFileWrite(*phDstFile, byBuf, cbRead, NULL /* No partial writes */);
320 if (RT_FAILURE(rc))
321 {
322 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
323 Utf8StrFmt(GuestSession::tr("Writing %RU32 bytes to file on host failed: %Rrc"), cbRead, rc));
324 break;
325 }
326
327 Assert(cbToRead >= cbRead);
328 cbToRead -= cbRead;
329
330 /* Update total bytes written to the guest. */
331 cbWrittenTotal += cbRead;
332 Assert(cbWrittenTotal <= cbSize);
333
334 /* Did the user cancel the operation above? */
335 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
336 && fCanceled)
337 break;
338
339 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
340 if (RT_FAILURE(rc))
341 break;
342 }
343
344 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
345 && fCanceled)
346 return VINF_SUCCESS;
347
348 if (RT_FAILURE(rc))
349 return rc;
350
351 /*
352 * Even if we succeeded until here make sure to check whether we really transfered
353 * everything.
354 */
355 if ( cbSize > 0
356 && cbWrittenTotal == 0)
357 {
358 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
359 * to the destination -> access denied. */
360 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
361 Utf8StrFmt(GuestSession::tr("Writing guest file to host failed: Access denied")));
362 rc = VERR_ACCESS_DENIED;
363 }
364 else if (cbWrittenTotal < cbSize)
365 {
366 /* If we did not copy all let the user know. */
367 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
368 Utf8StrFmt(GuestSession::tr("Copying guest file to host to failed (%RU64/%RU64 bytes transfered)"),
369 cbWrittenTotal, cbSize));
370 rc = VERR_INTERRUPTED;
371 }
372
373 LogFlowFuncLeaveRC(rc);
374 return rc;
375}
376
377/**
378 * Copies a file from the guest to the host.
379 *
380 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
381 * @param strSource Full path of source file on the guest to copy.
382 * @param strDest Full destination path and file name (host style) to copy file to.
383 * @param fFileCopyFlags File copy flags.
384 */
385int GuestSessionTask::fileCopyFromGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
386{
387 LogFlowThisFunc(("strSource=%s, strDest=%s, enmFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
388
389 GuestFileOpenInfo srcOpenInfo;
390 RT_ZERO(srcOpenInfo);
391 srcOpenInfo.mFileName = strSource;
392 srcOpenInfo.mOpenAction = FileOpenAction_OpenExisting;
393 srcOpenInfo.mAccessMode = FileAccessMode_ReadOnly;
394 srcOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
395
396 ComObjPtr<GuestFile> srcFile;
397
398 GuestFsObjData srcObjData;
399 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
400 int rc = mSession->i_fsQueryInfo(strSource, TRUE /* fFollowSymlinks */, srcObjData, &rcGuest);
401 if (RT_FAILURE(rc))
402 {
403 switch (rc)
404 {
405 case VERR_GSTCTL_GUEST_ERROR:
406 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
407 break;
408
409 default:
410 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
411 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
412 strSource.c_str(), rc));
413 break;
414 }
415 }
416 else
417 {
418 switch (srcObjData.mType)
419 {
420 case FsObjType_File:
421 break;
422
423 case FsObjType_Symlink:
424 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
425 {
426 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
427 Utf8StrFmt(GuestSession::tr("Source file \"%s\" is a symbolic link"),
428 strSource.c_str(), rc));
429 rc = VERR_IS_A_SYMLINK;
430 }
431 break;
432
433 default:
434 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
435 Utf8StrFmt(GuestSession::tr("Source element \"%s\" is not a file"), strSource.c_str()));
436 rc = VERR_NOT_A_FILE;
437 break;
438 }
439 }
440
441 if (RT_FAILURE(rc))
442 return rc;
443
444 rc = mSession->i_fileOpen(srcOpenInfo, srcFile, &rcGuest);
445 if (RT_FAILURE(rc))
446 {
447 switch (rc)
448 {
449 case VERR_GSTCTL_GUEST_ERROR:
450 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
451 break;
452
453 default:
454 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
455 Utf8StrFmt(GuestSession::tr("Source file \"%s\" could not be opened: %Rrc"),
456 strSource.c_str(), rc));
457 break;
458 }
459 }
460
461 if (RT_FAILURE(rc))
462 return rc;
463
464 char *pszDstFile = NULL;
465 RTFSOBJINFO dstObjInfo;
466 RT_ZERO(dstObjInfo);
467
468 bool fSkip = false; /* Whether to skip handling the file. */
469
470 if (RT_SUCCESS(rc))
471 {
472 rc = RTPathQueryInfo(strDest.c_str(), &dstObjInfo, RTFSOBJATTRADD_NOTHING);
473 if (RT_SUCCESS(rc))
474 {
475 if (fFileCopyFlags & FileCopyFlag_NoReplace)
476 {
477 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
478 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"), strDest.c_str()));
479 rc = VERR_ALREADY_EXISTS;
480 }
481
482 if (fFileCopyFlags & FileCopyFlag_Update)
483 {
484 RTTIMESPEC srcModificationTimeTS;
485 RTTimeSpecSetSeconds(&srcModificationTimeTS, srcObjData.mModificationTime);
486 if (RTTimeSpecCompare(&srcModificationTimeTS, &dstObjInfo.ModificationTime) <= 0)
487 {
488 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
489 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
490 strDest.c_str()));
491 fSkip = true;
492 }
493 }
494 }
495 else
496 {
497 if (rc != VERR_FILE_NOT_FOUND) /* Destination file does not exist (yet)? */
498 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
499 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
500 strDest.c_str(), rc));
501 }
502 }
503
504 if (fSkip)
505 {
506 int rc2 = srcFile->i_closeFile(&rcGuest);
507 AssertRC(rc2);
508 return VINF_SUCCESS;
509 }
510
511 if (RT_SUCCESS(rc))
512 {
513 if (RTFS_IS_FILE(dstObjInfo.Attr.fMode))
514 {
515 if (fFileCopyFlags & FileCopyFlag_NoReplace)
516 {
517 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
518 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
519 strDest.c_str(), rc));
520 rc = VERR_ALREADY_EXISTS;
521 }
522 else
523 pszDstFile = RTStrDup(strDest.c_str());
524 }
525 else if (RTFS_IS_DIRECTORY(dstObjInfo.Attr.fMode))
526 {
527 /* Build the final file name with destination path (on the host). */
528 char szDstPath[RTPATH_MAX];
529 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
530
531 if ( !strDest.endsWith("\\")
532 && !strDest.endsWith("/"))
533 RTPathAppend(szDstPath, sizeof(szDstPath), "/"); /* IPRT can handle / on all hosts. */
534
535 RTPathAppend(szDstPath, sizeof(szDstPath), RTPathFilenameEx(strSource.c_str(), mfPathStyle));
536
537 pszDstFile = RTStrDup(szDstPath);
538 }
539 else if (RTFS_IS_SYMLINK(dstObjInfo.Attr.fMode))
540 {
541 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
542 {
543 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
544 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
545 strDest.c_str(), rc));
546 rc = VERR_IS_A_SYMLINK;
547 }
548 else
549 pszDstFile = RTStrDup(strDest.c_str());
550 }
551 else
552 {
553 LogFlowThisFunc(("Object type %RU32 not implemented yet\n", dstObjInfo.Attr.fMode));
554 rc = VERR_NOT_IMPLEMENTED;
555 }
556 }
557 else if (rc == VERR_FILE_NOT_FOUND)
558 pszDstFile = RTStrDup(strDest.c_str());
559
560 if ( RT_SUCCESS(rc)
561 || rc == VERR_FILE_NOT_FOUND)
562 {
563 if (!pszDstFile)
564 {
565 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No memory to allocate destination file path")));
566 rc = VERR_NO_MEMORY;
567 }
568 else
569 {
570 RTFILE hDstFile;
571 rc = RTFileOpen(&hDstFile, pszDstFile,
572 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
573 if (RT_SUCCESS(rc))
574 {
575 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n", strSource.c_str(), pszDstFile, srcObjData.mObjectSize));
576
577 rc = fileCopyFromGuestInner(srcFile, &hDstFile, fFileCopyFlags, 0 /* Offset, unused */, (uint64_t)srcObjData.mObjectSize);
578
579 int rc2 = RTFileClose(hDstFile);
580 AssertRC(rc2);
581 }
582 else
583 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
584 Utf8StrFmt(GuestSession::tr("Opening/creating destination file \"%s\" failed: %Rrc"),
585 pszDstFile, rc));
586 }
587 }
588
589 RTStrFree(pszDstFile);
590
591 int rc2 = srcFile->i_closeFile(&rcGuest);
592 AssertRC(rc2);
593
594 LogFlowFuncLeaveRC(rc);
595 return rc;
596}
597
598/**
599 * Main function for copying a file from host to the guest.
600 *
601 * @return VBox status code.
602 * @param hVfsFIle The VFS file handle to read from.
603 * @param dstFile Guest file (destination) to copy to the guest. Must be in opened and ready state already.
604 * @param fFileCopyFlags File copy flags.
605 * @param offCopy Offset (in bytes) where to start copying the source file.
606 * @param cbSize Size (in bytes) to copy from the source file.
607 */
608int GuestSessionTask::fileCopyToGuestInner(RTVFSFILE hVfsFile, ComObjPtr<GuestFile> &dstFile, FileCopyFlag_T fFileCopyFlags,
609 uint64_t offCopy, uint64_t cbSize)
610{
611 RT_NOREF(fFileCopyFlags);
612
613 BOOL fCanceled = FALSE;
614 uint64_t cbWrittenTotal = 0;
615 uint64_t cbToRead = cbSize;
616
617 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
618
619 int rc = VINF_SUCCESS;
620
621 if (offCopy)
622 {
623 uint64_t offActual;
624 rc = RTVfsFileSeek(hVfsFile, offCopy, RTFILE_SEEK_END, &offActual);
625 if (RT_FAILURE(rc))
626 {
627 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
628 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
629 return rc;
630 }
631 }
632
633 BYTE byBuf[_64K];
634 while (cbToRead)
635 {
636 size_t cbRead;
637 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
638 rc = RTVfsFileRead(hVfsFile, byBuf, cbChunk, &cbRead);
639 if (RT_FAILURE(rc))
640 {
641 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
642 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64 from host failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
643 break;
644 }
645
646 rc = dstFile->i_writeData(uTimeoutMs, byBuf, (uint32_t)cbRead, NULL /* No partial writes */);
647 if (RT_FAILURE(rc))
648 {
649 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
650 Utf8StrFmt(GuestSession::tr("Writing %zu bytes to file on guest failed: %Rrc"), cbRead, rc));
651 break;
652 }
653
654 Assert(cbToRead >= cbRead);
655 cbToRead -= cbRead;
656
657 /* Update total bytes written to the guest. */
658 cbWrittenTotal += cbRead;
659 Assert(cbWrittenTotal <= cbSize);
660
661 /* Did the user cancel the operation above? */
662 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
663 && fCanceled)
664 break;
665
666 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
667 if (RT_FAILURE(rc))
668 break;
669 }
670
671 if (RT_FAILURE(rc))
672 return rc;
673
674 /*
675 * Even if we succeeded until here make sure to check whether we really transfered
676 * everything.
677 */
678 if ( cbSize > 0
679 && cbWrittenTotal == 0)
680 {
681 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
682 * to the destination -> access denied. */
683 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
684 Utf8StrFmt(GuestSession::tr("Writing to destination file failed: Access denied")));
685 rc = VERR_ACCESS_DENIED;
686 }
687 else if (cbWrittenTotal < cbSize)
688 {
689 /* If we did not copy all let the user know. */
690 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
691 Utf8StrFmt(GuestSession::tr("Copying to destination failed (%RU64/%RU64 bytes transfered)"),
692 cbWrittenTotal, cbSize));
693 rc = VERR_INTERRUPTED;
694 }
695
696 LogFlowFuncLeaveRC(rc);
697 return rc;
698}
699
700/**
701 * Copies a file from the guest to the host.
702 *
703 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
704 * @param strSource Full path of source file on the host to copy.
705 * @param strDest Full destination path and file name (guest style) to copy file to.
706 * @param fFileCopyFlags File copy flags.
707 */
708int GuestSessionTask::fileCopyToGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
709{
710 LogFlowThisFunc(("strSource=%s, strDest=%s, fFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
711
712 Utf8Str strDestFinal = strDest;
713
714 GuestFsObjData dstObjData;
715 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
716 int rc = mSession->i_fsQueryInfo(strDest, TRUE /* fFollowSymlinks */, dstObjData, &rcGuest);
717 if (RT_FAILURE(rc))
718 {
719 switch (rc)
720 {
721 case VERR_GSTCTL_GUEST_ERROR:
722 if (rcGuest == VERR_FILE_NOT_FOUND) /* File might not exist on the guest yet. */
723 {
724 rc = VINF_SUCCESS;
725 }
726 else
727 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
728 break;
729
730 default:
731 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
732 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
733 strDest.c_str(), rc));
734 break;
735 }
736 }
737 else
738 {
739 switch (dstObjData.mType)
740 {
741 case FsObjType_Directory:
742 {
743 /* Build the final file name with destination path (on the host). */
744 char szDstPath[RTPATH_MAX];
745 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
746
747 if ( !strDest.endsWith("\\")
748 && !strDest.endsWith("/"))
749 RTStrCat(szDstPath, sizeof(szDstPath), "/");
750
751 RTStrCat(szDstPath, sizeof(szDstPath), RTPathFilename(strSource.c_str()));
752
753 strDestFinal = szDstPath;
754 break;
755 }
756
757 case FsObjType_File:
758 if (fFileCopyFlags & FileCopyFlag_NoReplace)
759 {
760 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
761 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
762 strDest.c_str(), rc));
763 rc = VERR_ALREADY_EXISTS;
764 }
765 break;
766
767 case FsObjType_Symlink:
768 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
769 {
770 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
771 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
772 strDest.c_str(), rc));
773 rc = VERR_IS_A_SYMLINK;
774 }
775 break;
776
777 default:
778 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
779 Utf8StrFmt(GuestSession::tr("Destination element \"%s\" not supported"), strDest.c_str()));
780 rc = VERR_NOT_SUPPORTED;
781 break;
782 }
783 }
784
785 if (RT_FAILURE(rc))
786 return rc;
787
788 GuestFileOpenInfo dstOpenInfo;
789 RT_ZERO(dstOpenInfo);
790 dstOpenInfo.mFileName = strDestFinal;
791 if (fFileCopyFlags & FileCopyFlag_NoReplace)
792 dstOpenInfo.mOpenAction = FileOpenAction_CreateNew;
793 else
794 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
795 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
796 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
797
798 ComObjPtr<GuestFile> dstFile;
799 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
800 if (RT_FAILURE(rc))
801 {
802 switch (rc)
803 {
804 case VERR_GSTCTL_GUEST_ERROR:
805 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
806 break;
807
808 default:
809 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
810 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
811 strDestFinal.c_str(), rc));
812 break;
813 }
814 }
815
816 if (RT_FAILURE(rc))
817 return rc;
818
819 char szSrcReal[RTPATH_MAX];
820
821 RTFSOBJINFO srcObjInfo;
822 RT_ZERO(srcObjInfo);
823
824 bool fSkip = false; /* Whether to skip handling the file. */
825
826 if (RT_SUCCESS(rc))
827 {
828 rc = RTPathReal(strSource.c_str(), szSrcReal, sizeof(szSrcReal));
829 if (RT_FAILURE(rc))
830 {
831 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
832 Utf8StrFmt(GuestSession::tr("Source path lookup for \"%s\" failed: %Rrc"),
833 strSource.c_str(), rc));
834 }
835 else
836 {
837 rc = RTPathQueryInfo(szSrcReal, &srcObjInfo, RTFSOBJATTRADD_NOTHING);
838 if (RT_SUCCESS(rc))
839 {
840 if (fFileCopyFlags & FileCopyFlag_Update)
841 {
842 RTTIMESPEC dstModificationTimeTS;
843 RTTimeSpecSetSeconds(&dstModificationTimeTS, dstObjData.mModificationTime);
844 if (RTTimeSpecCompare(&dstModificationTimeTS, &srcObjInfo.ModificationTime) <= 0)
845 {
846 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
847 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
848 strDestFinal.c_str()));
849 fSkip = true;
850 }
851 }
852 }
853 else
854 {
855 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
856 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
857 szSrcReal, rc));
858 }
859 }
860 }
861
862 if (fSkip)
863 {
864 int rc2 = dstFile->i_closeFile(&rcGuest);
865 AssertRC(rc2);
866 return VINF_SUCCESS;
867 }
868
869 if (RT_SUCCESS(rc))
870 {
871 RTVFSFILE hSrcFile;
872 rc = RTVfsFileOpenNormal(szSrcReal, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hSrcFile); /** @todo Use the correct open modes! */
873 if (RT_SUCCESS(rc))
874 {
875 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n",
876 szSrcReal, strDestFinal.c_str(), srcObjInfo.cbObject));
877
878 rc = fileCopyToGuestInner(hSrcFile, dstFile, fFileCopyFlags, 0 /* Offset, unused */, srcObjInfo.cbObject);
879
880 int rc2 = RTVfsFileRelease(hSrcFile);
881 AssertRC(rc2);
882 }
883 else
884 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
885 Utf8StrFmt(GuestSession::tr("Opening source file \"%s\" failed: %Rrc"),
886 szSrcReal, rc));
887 }
888
889 int rc2 = dstFile->i_closeFile(&rcGuest);
890 AssertRC(rc2);
891
892 LogFlowFuncLeaveRC(rc);
893 return rc;
894}
895
896/**
897 * Adds a guest file system entry to a given list.
898 *
899 * @return VBox status code.
900 * @param strFile Path to file system entry to add.
901 * @param fsObjData Guest file system information of entry to add.
902 */
903int FsList::AddEntryFromGuest(const Utf8Str &strFile, const GuestFsObjData &fsObjData)
904{
905 LogFlowFunc(("Adding '%s'\n", strFile.c_str()));
906
907 FsEntry *pEntry = NULL;
908 try
909 {
910 pEntry = new FsEntry();
911 pEntry->fMode = fsObjData.GetFileMode();
912 pEntry->strPath = strFile;
913
914 mVecEntries.push_back(pEntry);
915 }
916 catch (...)
917 {
918 if (pEntry)
919 delete pEntry;
920 return VERR_NO_MEMORY;
921 }
922
923 return VINF_SUCCESS;
924}
925
926/**
927 * Adds a host file system entry to a given list.
928 *
929 * @return VBox status code.
930 * @param strFile Path to file system entry to add.
931 * @param pcObjInfo File system information of entry to add.
932 */
933int FsList::AddEntryFromHost(const Utf8Str &strFile, PCRTFSOBJINFO pcObjInfo)
934{
935 LogFlowFunc(("Adding '%s'\n", strFile.c_str()));
936
937 FsEntry *pEntry = NULL;
938 try
939 {
940 pEntry = new FsEntry();
941 pEntry->fMode = pcObjInfo->Attr.fMode & RTFS_TYPE_MASK;
942 pEntry->strPath = strFile;
943
944 mVecEntries.push_back(pEntry);
945 }
946 catch (...)
947 {
948 if (pEntry)
949 delete pEntry;
950 return VERR_NO_MEMORY;
951 }
952
953 return VINF_SUCCESS;
954}
955
956FsList::FsList(const GuestSessionTask &Task)
957 : mTask(Task)
958{
959}
960
961FsList::~FsList()
962{
963 Destroy();
964}
965
966/**
967 * Initializes a file list.
968 *
969 * @return VBox status code.
970 * @param strSrcRootAbs Source root path (absolute) for this file list.
971 * @param strDstRootAbs Destination root path (absolute) for this file list.
972 * @param SourceSpec Source specification to use.
973 */
974int FsList::Init(const Utf8Str &strSrcRootAbs, const Utf8Str &strDstRootAbs,
975 const GuestSessionFsSourceSpec &SourceSpec)
976{
977 mSrcRootAbs = strSrcRootAbs;
978 mDstRootAbs = strDstRootAbs;
979 mSourceSpec = SourceSpec;
980
981 /* If the source is a directory, make sure the path is properly terminated already. */
982 if (mSourceSpec.enmType == FsObjType_Directory)
983 {
984 if ( !mSrcRootAbs.endsWith("/")
985 && !mSrcRootAbs.endsWith("\\"))
986 mSrcRootAbs += "/";
987
988 if ( !mDstRootAbs.endsWith("/")
989 && !mDstRootAbs.endsWith("\\"))
990 mDstRootAbs += "/";
991 }
992
993 return VINF_SUCCESS;
994}
995
996/**
997 * Destroys a file list.
998 */
999void FsList::Destroy(void)
1000{
1001 LogFlowFuncEnter();
1002
1003 FsEntries::iterator itEntry = mVecEntries.begin();
1004 while (itEntry != mVecEntries.end())
1005 {
1006 FsEntry *pEntry = *itEntry;
1007 delete pEntry;
1008 mVecEntries.erase(itEntry);
1009 itEntry = mVecEntries.begin();
1010 }
1011
1012 Assert(mVecEntries.empty());
1013
1014 LogFlowFuncLeave();
1015}
1016
1017/**
1018 * Builds a guest file list from a given path (and optional filter).
1019 *
1020 * @return VBox status code.
1021 * @param strPath Directory on the guest to build list from.
1022 * @param strSubDir Current sub directory path; needed for recursion.
1023 * Set to an empty path.
1024 */
1025int FsList::AddDirFromGuest(const Utf8Str &strPath, const Utf8Str &strSubDir /* = "" */)
1026{
1027 Utf8Str strPathAbs = strPath;
1028 if ( !strPathAbs.endsWith("/")
1029 && !strPathAbs.endsWith("\\"))
1030 strPathAbs += "/";
1031
1032 Utf8Str strPathSub = strSubDir;
1033 if ( strPathSub.isNotEmpty()
1034 && !strPathSub.endsWith("/")
1035 && !strPathSub.endsWith("\\"))
1036 strPathSub += "/";
1037
1038 strPathAbs += strPathSub;
1039
1040 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1041
1042 GuestDirectoryOpenInfo dirOpenInfo;
1043 dirOpenInfo.mFilter = "";
1044 dirOpenInfo.mPath = strPathAbs;
1045 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
1046
1047 const ComObjPtr<GuestSession> &pSession = mTask.GetSession();
1048
1049 ComObjPtr <GuestDirectory> pDir; int rcGuest;
1050 int rc = pSession->i_directoryOpen(dirOpenInfo, pDir, &rcGuest);
1051 if (RT_FAILURE(rc))
1052 {
1053 switch (rc)
1054 {
1055 case VERR_INVALID_PARAMETER:
1056 break;
1057
1058 case VERR_GSTCTL_GUEST_ERROR:
1059 break;
1060
1061 default:
1062 break;
1063 }
1064
1065 return rc;
1066 }
1067
1068 if (strPathSub.isNotEmpty())
1069 {
1070 GuestFsObjData fsObjData;
1071 fsObjData.mType = FsObjType_Directory;
1072
1073 rc = AddEntryFromGuest(strPathSub, fsObjData);
1074 }
1075
1076 if (RT_SUCCESS(rc))
1077 {
1078 ComObjPtr<GuestFsObjInfo> fsObjInfo;
1079 while (RT_SUCCESS(rc = pDir->i_readInternal(fsObjInfo, &rcGuest)))
1080 {
1081 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
1082 HRESULT hr2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1083 AssertComRC(hr2);
1084
1085 com::Bstr bstrName;
1086 hr2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1087 AssertComRC(hr2);
1088
1089 Utf8Str strEntry = strPathSub + Utf8Str(bstrName);
1090
1091 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1092
1093 switch (enmObjType)
1094 {
1095 case FsObjType_Directory:
1096 {
1097 if ( bstrName.equals(".")
1098 || bstrName.equals(".."))
1099 {
1100 break;
1101 }
1102
1103 if (!(mSourceSpec.Type.Dir.fRecursive))
1104 break;
1105
1106 rc = AddDirFromGuest(strPath, strEntry);
1107 break;
1108 }
1109
1110 case FsObjType_Symlink:
1111 {
1112 if (mSourceSpec.fFollowSymlinks)
1113 {
1114 /** @todo Symlink handling from guest is not imlemented yet.
1115 * See IGuestSession::symlinkRead(). */
1116 }
1117 break;
1118 }
1119
1120 case FsObjType_File:
1121 {
1122 rc = AddEntryFromGuest(strEntry, fsObjInfo->i_getData());
1123 break;
1124 }
1125
1126 default:
1127 break;
1128 }
1129 }
1130
1131 if (rc == VERR_NO_MORE_FILES) /* End of listing reached? */
1132 rc = VINF_SUCCESS;
1133 }
1134
1135 int rc2 = pDir->i_closeInternal(&rcGuest);
1136 if (RT_SUCCESS(rc))
1137 rc = rc2;
1138
1139 return rc;
1140}
1141
1142/**
1143 * Builds a host file list from a given path (and optional filter).
1144 *
1145 * @return VBox status code.
1146 * @param strPath Directory on the host to build list from.
1147 * @param strSubDir Current sub directory path; needed for recursion.
1148 * Set to an empty path.
1149 */
1150int FsList::AddDirFromHost(const Utf8Str &strPath, const Utf8Str &strSubDir)
1151{
1152 Utf8Str strPathAbs = strPath;
1153 if ( !strPathAbs.endsWith("/")
1154 && !strPathAbs.endsWith("\\"))
1155 strPathAbs += "/";
1156
1157 Utf8Str strPathSub = strSubDir;
1158 if ( strPathSub.isNotEmpty()
1159 && !strPathSub.endsWith("/")
1160 && !strPathSub.endsWith("\\"))
1161 strPathSub += "/";
1162
1163 strPathAbs += strPathSub;
1164
1165 LogFlowFunc(("Entering '%s' (sub '%s')\n", strPathAbs.c_str(), strPathSub.c_str()));
1166
1167 RTFSOBJINFO objInfo;
1168 int rc = RTPathQueryInfo(strPathAbs.c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
1169 if (RT_SUCCESS(rc))
1170 {
1171 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1172 {
1173 if (strPathSub.isNotEmpty())
1174 rc = AddEntryFromHost(strPathSub, &objInfo);
1175
1176 if (RT_SUCCESS(rc))
1177 {
1178 RTDIR hDir;
1179 rc = RTDirOpen(&hDir, strPathAbs.c_str());
1180 if (RT_SUCCESS(rc))
1181 {
1182 do
1183 {
1184 /* Retrieve the next directory entry. */
1185 RTDIRENTRYEX Entry;
1186 rc = RTDirReadEx(hDir, &Entry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1187 if (RT_FAILURE(rc))
1188 {
1189 if (rc == VERR_NO_MORE_FILES)
1190 rc = VINF_SUCCESS;
1191 break;
1192 }
1193
1194 Utf8Str strEntry = strPathSub + Utf8Str(Entry.szName);
1195
1196 LogFlowFunc(("Entry '%s'\n", strEntry.c_str()));
1197
1198 switch (Entry.Info.Attr.fMode & RTFS_TYPE_MASK)
1199 {
1200 case RTFS_TYPE_DIRECTORY:
1201 {
1202 /* Skip "." and ".." entries. */
1203 if (RTDirEntryExIsStdDotLink(&Entry))
1204 break;
1205
1206 if (!(mSourceSpec.Type.Dir.fRecursive))
1207 break;
1208
1209 rc = AddDirFromHost(strPath, strEntry);
1210 break;
1211 }
1212
1213 case RTFS_TYPE_FILE:
1214 {
1215 rc = AddEntryFromHost(strEntry, &Entry.Info);
1216 break;
1217 }
1218
1219 case RTFS_TYPE_SYMLINK:
1220 {
1221 if (mSourceSpec.fFollowSymlinks)
1222 {
1223 Utf8Str strEntryAbs = strPathAbs + Utf8Str(Entry.szName);
1224
1225 char szPathReal[RTPATH_MAX];
1226 rc = RTPathReal(strEntryAbs.c_str(), szPathReal, sizeof(szPathReal));
1227 if (RT_SUCCESS(rc))
1228 {
1229 rc = RTPathQueryInfo(szPathReal, &objInfo, RTFSOBJATTRADD_NOTHING);
1230 if (RT_SUCCESS(rc))
1231 {
1232 LogFlowFunc(("Symlink '%s' -> '%s'\n", strEntryAbs.c_str(), szPathReal));
1233
1234 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1235 {
1236 LogFlowFunc(("Symlink to directory\n"));
1237 rc = AddDirFromHost(strPath, strEntry);
1238 }
1239 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1240 {
1241 LogFlowFunc(("Symlink to file\n"));
1242 rc = AddEntryFromHost(strEntry, &objInfo);
1243 }
1244 else
1245 rc = VERR_NOT_SUPPORTED;
1246 }
1247 else
1248 LogFlowFunc(("Unable to query symlink info for '%s', rc=%Rrc\n", szPathReal, rc));
1249 }
1250 else
1251 {
1252 LogFlowFunc(("Unable to resolve symlink for '%s', rc=%Rrc\n", strPathAbs.c_str(), rc));
1253 if (rc == VERR_FILE_NOT_FOUND) /* Broken symlink, skip. */
1254 rc = VINF_SUCCESS;
1255 }
1256 }
1257 break;
1258 }
1259
1260 default:
1261 break;
1262 }
1263
1264 } while (RT_SUCCESS(rc));
1265
1266 RTDirClose(hDir);
1267 }
1268 }
1269 }
1270 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1271 {
1272 rc = VERR_IS_A_FILE;
1273 }
1274 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1275 {
1276 rc = VERR_IS_A_SYMLINK;
1277 }
1278 else
1279 rc = VERR_NOT_SUPPORTED;
1280 }
1281 else
1282 LogFlowFunc(("Unable to query '%s', rc=%Rrc\n", strPathAbs.c_str(), rc));
1283
1284 LogFlowFuncLeaveRC(rc);
1285 return rc;
1286}
1287
1288GuestSessionTaskOpen::GuestSessionTaskOpen(GuestSession *pSession, uint32_t uFlags, uint32_t uTimeoutMS)
1289 : GuestSessionTask(pSession)
1290 , mFlags(uFlags)
1291 , mTimeoutMS(uTimeoutMS)
1292{
1293 m_strTaskName = "gctlSesOpen";
1294}
1295
1296GuestSessionTaskOpen::~GuestSessionTaskOpen(void)
1297{
1298
1299}
1300
1301int GuestSessionTaskOpen::Run(void)
1302{
1303 LogFlowThisFuncEnter();
1304
1305 AutoCaller autoCaller(mSession);
1306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1307
1308 int vrc = mSession->i_startSession(NULL /*pvrcGuest*/);
1309 /* Nothing to do here anymore. */
1310
1311 LogFlowFuncLeaveRC(vrc);
1312 return vrc;
1313}
1314
1315GuestSessionCopyTask::GuestSessionCopyTask(GuestSession *pSession)
1316 : GuestSessionTask(pSession)
1317{
1318}
1319
1320GuestSessionCopyTask::~GuestSessionCopyTask()
1321{
1322 FsLists::iterator itList = mVecLists.begin();
1323 while (itList != mVecLists.end())
1324 {
1325 FsList *pFsList = (*itList);
1326 pFsList->Destroy();
1327 delete pFsList;
1328 mVecLists.erase(itList);
1329 itList = mVecLists.begin();
1330 }
1331
1332 Assert(mVecLists.empty());
1333}
1334
1335GuestSessionTaskCopyFrom::GuestSessionTaskCopyFrom(GuestSession *pSession, GuestSessionFsSourceSet vecSrc, const Utf8Str &strDest)
1336 : GuestSessionCopyTask(pSession)
1337{
1338 m_strTaskName = "gctlCpyFrm";
1339
1340 mSources = vecSrc;
1341 mDest = strDest;
1342}
1343
1344GuestSessionTaskCopyFrom::~GuestSessionTaskCopyFrom(void)
1345{
1346}
1347
1348HRESULT GuestSessionTaskCopyFrom::Init(const Utf8Str &strTaskDesc)
1349{
1350 setTaskDesc(strTaskDesc);
1351
1352 /* Create the progress object. */
1353 ComObjPtr<Progress> pProgress;
1354 HRESULT hr = pProgress.createObject();
1355 if (FAILED(hr))
1356 return hr;
1357
1358 mProgress = pProgress;
1359
1360 int rc = VINF_SUCCESS;
1361
1362 ULONG cOperations = 0;
1363 Utf8Str strErrorInfo;
1364
1365 /**
1366 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyFrom::Run
1367 * because the caller expects a ready-for-operation progress object on return.
1368 * The progress object will have a variable operation count, based on the elements to
1369 * be processed.
1370 */
1371
1372 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1373 while (itSrc != mSources.end())
1374 {
1375 Utf8Str strSrc = itSrc->strSource;
1376 Utf8Str strDst = mDest;
1377
1378 if (itSrc->enmType == FsObjType_Directory)
1379 {
1380 /* If the source does not end with a slash, copy over the entire directory
1381 * (and not just its contents). */
1382 if ( !strSrc.endsWith("/")
1383 && !strSrc.endsWith("\\"))
1384 {
1385 if ( !strDst.endsWith("/")
1386 && !strDst.endsWith("\\"))
1387 strDst += "/";
1388
1389 strDst += Utf8StrFmt("%s", RTPathFilenameEx(strSrc.c_str(), mfPathStyle));
1390 }
1391 }
1392
1393 LogFlowFunc(("strSrc=%s, strDst=%s\n", strSrc.c_str(), strDst.c_str()));
1394
1395 GuestFsObjData srcObjData; int rcGuest;
1396 rc = mSession->i_fsQueryInfo(strSrc, itSrc->fFollowSymlinks ? TRUE : FALSE,
1397 srcObjData, &rcGuest);
1398 if (RT_FAILURE(rc))
1399 {
1400 strErrorInfo = Utf8StrFmt(GuestSession::tr("No such source file/directory: %s"), strSrc.c_str());
1401 break;
1402 }
1403
1404 if (srcObjData.mType == FsObjType_Directory)
1405 {
1406 if (itSrc->enmType != FsObjType_Directory)
1407 {
1408 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a file: %s"), strSrc.c_str());
1409 rc = VERR_NOT_A_FILE;
1410 break;
1411 }
1412 }
1413 else
1414 {
1415 if (itSrc->enmType != FsObjType_File)
1416 {
1417 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a directory: %s"), strSrc.c_str());
1418 rc = VERR_NOT_A_DIRECTORY;
1419 break;
1420 }
1421 }
1422
1423 FsList *pFsList = NULL;
1424 try
1425 {
1426 pFsList = new FsList(*this);
1427 rc = pFsList->Init(strSrc, strDst, *itSrc);
1428 if (RT_SUCCESS(rc))
1429 {
1430 if (itSrc->enmType == FsObjType_Directory)
1431 {
1432 rc = pFsList->AddDirFromGuest(strSrc);
1433 }
1434 else
1435 rc = pFsList->AddEntryFromGuest(RTPathFilename(strSrc.c_str()), srcObjData);
1436 }
1437
1438 if (RT_FAILURE(rc))
1439 {
1440 delete pFsList;
1441 strErrorInfo = Utf8StrFmt(GuestSession::tr("Error adding source '%s' to list: %Rrc"), strSrc.c_str(), rc);
1442 break;
1443 }
1444
1445 mVecLists.push_back(pFsList);
1446 }
1447 catch (...)
1448 {
1449 rc = VERR_NO_MEMORY;
1450 break;
1451 }
1452
1453 AssertPtr(pFsList);
1454 cOperations += (ULONG)pFsList->mVecEntries.size();
1455
1456 itSrc++;
1457 }
1458
1459 if (cOperations) /* Use the first element as description (if available). */
1460 {
1461 Assert(mVecLists.size());
1462 Assert(mVecLists[0]->mVecEntries.size());
1463
1464 Utf8Str strFirstOp = mDest + mVecLists[0]->mVecEntries[0]->strPath;
1465 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1466 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */,
1467 Bstr(strFirstOp).raw());
1468 }
1469 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
1470 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1471 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
1472
1473 if (RT_FAILURE(rc))
1474 {
1475 Assert(strErrorInfo.isNotEmpty());
1476 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
1477 }
1478
1479 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hr, rc));
1480 return rc;
1481}
1482
1483int GuestSessionTaskCopyFrom::Run(void)
1484{
1485 LogFlowThisFuncEnter();
1486
1487 AutoCaller autoCaller(mSession);
1488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1489
1490 int rc = VINF_SUCCESS;
1491
1492 FsLists::const_iterator itList = mVecLists.begin();
1493 while (itList != mVecLists.end())
1494 {
1495 FsList *pList = *itList;
1496 AssertPtr(pList);
1497
1498 const bool fCopyIntoExisting = pList->mSourceSpec.Type.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting;
1499 const uint32_t fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
1500
1501 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1502
1503 /* Create the root directory. */
1504 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1505 {
1506 rc = RTDirCreate(pList->mDstRootAbs.c_str(), fDirMode, 0 /* fCreate */);
1507 if ( rc == VWRN_ALREADY_EXISTS
1508 && !fCopyIntoExisting)
1509 {
1510 break;
1511 }
1512 }
1513
1514 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1515 while (itEntry != pList->mVecEntries.end())
1516 {
1517 FsEntry *pEntry = *itEntry;
1518 AssertPtr(pEntry);
1519
1520 Utf8Str strSrcAbs = pList->mSrcRootAbs;
1521 Utf8Str strDstAbs = pList->mDstRootAbs;
1522 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1523 {
1524 strSrcAbs += pEntry->strPath;
1525 strDstAbs += pEntry->strPath;
1526
1527 if (pList->mSourceSpec.enmPathStyle == PathStyle_DOS)
1528 strDstAbs.findReplace('\\', '/');
1529 }
1530
1531 switch (pEntry->fMode & RTFS_TYPE_MASK)
1532 {
1533 case RTFS_TYPE_DIRECTORY:
1534 LogFlowFunc(("Directory '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
1535 if (!pList->mSourceSpec.fDryRun)
1536 {
1537 rc = RTDirCreate(strDstAbs.c_str(), fDirMode, 0 /* fCreate */);
1538 if (rc == VERR_ALREADY_EXISTS)
1539 {
1540 if (!fCopyIntoExisting)
1541 {
1542 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1543 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
1544 strDstAbs.c_str()));
1545 break;
1546 }
1547
1548 rc = VINF_SUCCESS;
1549 }
1550
1551 if (RT_FAILURE(rc))
1552 break;
1553 }
1554 break;
1555
1556 case RTFS_TYPE_FILE:
1557 LogFlowFunc(("File '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
1558 if (!pList->mSourceSpec.fDryRun)
1559 rc = fileCopyFromGuest(strSrcAbs, strDstAbs, FileCopyFlag_None);
1560 break;
1561
1562 default:
1563 LogFlowFunc(("Warning: Type %d for '%s' is not supported\n",
1564 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
1565 break;
1566 }
1567
1568 if (RT_FAILURE(rc))
1569 break;
1570
1571 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
1572
1573 ++itEntry;
1574 }
1575
1576 if (RT_FAILURE(rc))
1577 break;
1578
1579 ++itList;
1580 }
1581
1582 if (RT_SUCCESS(rc))
1583 rc = setProgressSuccess();
1584
1585 LogFlowFuncLeaveRC(rc);
1586 return rc;
1587}
1588
1589GuestSessionTaskCopyTo::GuestSessionTaskCopyTo(GuestSession *pSession, GuestSessionFsSourceSet vecSrc, const Utf8Str &strDest)
1590 : GuestSessionCopyTask(pSession)
1591{
1592 m_strTaskName = "gctlCpyTo";
1593
1594 mSources = vecSrc;
1595 mDest = strDest;
1596}
1597
1598GuestSessionTaskCopyTo::~GuestSessionTaskCopyTo(void)
1599{
1600}
1601
1602HRESULT GuestSessionTaskCopyTo::Init(const Utf8Str &strTaskDesc)
1603{
1604 LogFlowFuncEnter();
1605
1606 setTaskDesc(strTaskDesc);
1607
1608 /* Create the progress object. */
1609 ComObjPtr<Progress> pProgress;
1610 HRESULT hr = pProgress.createObject();
1611 if (FAILED(hr))
1612 return hr;
1613
1614 mProgress = pProgress;
1615
1616 int rc = VINF_SUCCESS;
1617
1618 ULONG cOperations = 0;
1619 Utf8Str strErrorInfo;
1620
1621 /**
1622 * Note: We need to build up the file/directory here instead of GuestSessionTaskCopyTo::Run
1623 * because the caller expects a ready-for-operation progress object on return.
1624 * The progress object will have a variable operation count, based on the elements to
1625 * be processed.
1626 */
1627
1628 GuestSessionFsSourceSet::iterator itSrc = mSources.begin();
1629 while (itSrc != mSources.end())
1630 {
1631 Utf8Str strSrc = itSrc->strSource;
1632 Utf8Str strDst = mDest;
1633
1634 if (itSrc->enmType == FsObjType_Directory)
1635 {
1636 /* If the source does not end with a slash, copy over the entire directory
1637 * (and not just its contents). */
1638 if ( !strSrc.endsWith("/")
1639 && !strSrc.endsWith("\\"))
1640 {
1641 if ( !strDst.endsWith("/")
1642 && !strDst.endsWith("\\"))
1643 strDst += "/";
1644
1645 strDst += Utf8StrFmt("%s", RTPathFilenameEx(strSrc.c_str(), mfPathStyle));
1646 }
1647 }
1648
1649 LogFlowFunc(("strSrc=%s, strDst=%s\n", strSrc.c_str(), strDst.c_str()));
1650
1651 RTFSOBJINFO srcFsObjInfo;
1652 rc = RTPathQueryInfo(strSrc.c_str(), &srcFsObjInfo, RTFSOBJATTRADD_NOTHING);
1653 if (RT_FAILURE(rc))
1654 {
1655 strErrorInfo = Utf8StrFmt(GuestSession::tr("No such source file/directory: %s"), strSrc.c_str());
1656 break;
1657 }
1658
1659 if (RTFS_IS_DIRECTORY(srcFsObjInfo.Attr.fMode))
1660 {
1661 if (itSrc->enmType != FsObjType_Directory)
1662 {
1663 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a file: %s"), strSrc.c_str());
1664 rc = VERR_NOT_A_FILE;
1665 break;
1666 }
1667 }
1668 else
1669 {
1670 if (itSrc->enmType == FsObjType_Directory)
1671 {
1672 strErrorInfo = Utf8StrFmt(GuestSession::tr("Source is not a directory: %s"), strSrc.c_str());
1673 rc = VERR_NOT_A_DIRECTORY;
1674 break;
1675 }
1676 }
1677
1678 FsList *pFsList = NULL;
1679 try
1680 {
1681 pFsList = new FsList(*this);
1682 rc = pFsList->Init(strSrc, strDst, *itSrc);
1683 if (RT_SUCCESS(rc))
1684 {
1685 if (itSrc->enmType == FsObjType_Directory)
1686 {
1687 rc = pFsList->AddDirFromHost(strSrc);
1688 }
1689 else
1690 rc = pFsList->AddEntryFromHost(RTPathFilename(strSrc.c_str()), &srcFsObjInfo);
1691 }
1692
1693 if (RT_FAILURE(rc))
1694 {
1695 delete pFsList;
1696 strErrorInfo = Utf8StrFmt(GuestSession::tr("Error adding source '%s' to list: %Rrc"), strSrc.c_str(), rc);
1697 break;
1698 }
1699
1700 mVecLists.push_back(pFsList);
1701 }
1702 catch (...)
1703 {
1704 rc = VERR_NO_MEMORY;
1705 break;
1706 }
1707
1708 AssertPtr(pFsList);
1709 cOperations += (ULONG)pFsList->mVecEntries.size();
1710
1711 itSrc++;
1712 }
1713
1714 if (cOperations) /* Use the first element as description (if available). */
1715 {
1716 Assert(mVecLists.size());
1717 Assert(mVecLists[0]->mVecEntries.size());
1718
1719 Utf8Str strFirstOp = mDest + mVecLists[0]->mVecEntries[0]->strPath;
1720
1721 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1722 TRUE /* aCancelable */, cOperations + 1 /* Number of operations */,
1723 Bstr(strFirstOp).raw());
1724 }
1725 else /* If no operations have been defined, go with an "empty" progress object when will be used for error handling. */
1726 hr = pProgress->init(static_cast<IGuestSession*>(mSession), Bstr(mDesc).raw(),
1727 TRUE /* aCancelable */, 1 /* cOperations */, Bstr(mDesc).raw());
1728
1729 if (RT_FAILURE(rc))
1730 {
1731 Assert(strErrorInfo.isNotEmpty());
1732 setProgressErrorMsg(VBOX_E_IPRT_ERROR, strErrorInfo);
1733 }
1734
1735 LogFlowFunc(("Returning %Rhrc (%Rrc)\n", hr, rc));
1736 return hr;
1737}
1738
1739int GuestSessionTaskCopyTo::Run(void)
1740{
1741 LogFlowThisFuncEnter();
1742
1743 AutoCaller autoCaller(mSession);
1744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1745
1746 int rc = VINF_SUCCESS;
1747
1748 FsLists::const_iterator itList = mVecLists.begin();
1749 while (itList != mVecLists.end())
1750 {
1751 FsList *pList = *itList;
1752 AssertPtr(pList);
1753
1754 const bool fFollowSymlinks = pList->mSourceSpec.fFollowSymlinks;
1755 const bool fCopyIntoExisting = pList->mSourceSpec.Type.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting;
1756 const uint32_t fDirMode = 0700; /** @todo Play safe by default; implement ACLs. */
1757
1758 LogFlowFunc(("List: srcRootAbs=%s, dstRootAbs=%s\n", pList->mSrcRootAbs.c_str(), pList->mDstRootAbs.c_str()));
1759
1760 /* Create the root directory. */
1761 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1762 {
1763 rc = directoryCreate(pList->mDstRootAbs.c_str(), DirectoryCreateFlag_None, fDirMode, fFollowSymlinks);
1764 if ( rc == VWRN_ALREADY_EXISTS
1765 && !fCopyIntoExisting)
1766 {
1767 break;
1768 }
1769 }
1770
1771 FsEntries::const_iterator itEntry = pList->mVecEntries.begin();
1772 while (itEntry != pList->mVecEntries.end())
1773 {
1774 FsEntry *pEntry = *itEntry;
1775 AssertPtr(pEntry);
1776
1777 Utf8Str strSrcAbs = pList->mSrcRootAbs;
1778 Utf8Str strDstAbs = pList->mDstRootAbs;
1779 if (pList->mSourceSpec.enmType == FsObjType_Directory)
1780 {
1781 strSrcAbs += pEntry->strPath;
1782 strDstAbs += pEntry->strPath;
1783 }
1784
1785 switch (pEntry->fMode & RTFS_TYPE_MASK)
1786 {
1787 case RTFS_TYPE_DIRECTORY:
1788 LogFlowFunc(("Directory '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
1789 if (!pList->mSourceSpec.fDryRun)
1790 {
1791 rc = directoryCreate(strDstAbs.c_str(), DirectoryCreateFlag_None, fDirMode, fFollowSymlinks);
1792 if ( rc == VERR_ALREADY_EXISTS
1793 && !fCopyIntoExisting)
1794 {
1795 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1796 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
1797 strDstAbs.c_str()));
1798 break;
1799 }
1800 }
1801 break;
1802
1803 case RTFS_TYPE_FILE:
1804 LogFlowFunc(("File '%s': %s -> %s\n", pEntry->strPath.c_str(), strSrcAbs.c_str(), strDstAbs.c_str()));
1805 if (!pList->mSourceSpec.fDryRun)
1806 rc = fileCopyToGuest(strSrcAbs, strDstAbs, FileCopyFlag_None);
1807 break;
1808
1809 default:
1810 LogFlowFunc(("Warning: Type %d for '%s' is not supported\n",
1811 pEntry->fMode & RTFS_TYPE_MASK, strSrcAbs.c_str()));
1812 break;
1813 }
1814
1815 if (RT_FAILURE(rc))
1816 break;
1817
1818 mProgress->SetNextOperation(Bstr(strSrcAbs).raw(), 1);
1819
1820 ++itEntry;
1821 }
1822
1823 if (RT_FAILURE(rc))
1824 break;
1825
1826 ++itList;
1827 }
1828
1829 if (RT_SUCCESS(rc))
1830 rc = setProgressSuccess();
1831
1832 LogFlowFuncLeaveRC(rc);
1833 return rc;
1834}
1835
1836GuestSessionTaskUpdateAdditions::GuestSessionTaskUpdateAdditions(GuestSession *pSession,
1837 const Utf8Str &strSource,
1838 const ProcessArguments &aArguments,
1839 uint32_t fFlags)
1840 : GuestSessionTask(pSession)
1841{
1842 m_strTaskName = "gctlUpGA";
1843
1844 mSource = strSource;
1845 mArguments = aArguments;
1846 mFlags = fFlags;
1847}
1848
1849GuestSessionTaskUpdateAdditions::~GuestSessionTaskUpdateAdditions(void)
1850{
1851
1852}
1853
1854int GuestSessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
1855{
1856 int rc = VINF_SUCCESS;
1857
1858 try
1859 {
1860 /* Filter out arguments which already are in the destination to
1861 * not end up having them specified twice. Not the fastest method on the
1862 * planet but does the job. */
1863 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
1864 while (itSource != aArgumentsSource.end())
1865 {
1866 bool fFound = false;
1867 ProcessArguments::iterator itDest = aArgumentsDest.begin();
1868 while (itDest != aArgumentsDest.end())
1869 {
1870 if ((*itDest).equalsIgnoreCase((*itSource)))
1871 {
1872 fFound = true;
1873 break;
1874 }
1875 ++itDest;
1876 }
1877
1878 if (!fFound)
1879 aArgumentsDest.push_back((*itSource));
1880
1881 ++itSource;
1882 }
1883 }
1884 catch(std::bad_alloc &)
1885 {
1886 return VERR_NO_MEMORY;
1887 }
1888
1889 return rc;
1890}
1891
1892int GuestSessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, RTVFS hVfsIso,
1893 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
1894 bool fOptional)
1895{
1896 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1897 AssertReturn(hVfsIso != NIL_RTVFS, VERR_INVALID_POINTER);
1898
1899 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1900 int rc = RTVfsFileOpen(hVfsIso, strFileSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ, &hVfsFile);
1901 if (RT_SUCCESS(rc))
1902 {
1903 size_t cbSrcSize = 0;
1904 rc = RTVfsFileGetSize(hVfsFile, &cbSrcSize);
1905 if (RT_SUCCESS(rc))
1906 {
1907 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
1908 strFileSource.c_str(), strFileDest.c_str()));
1909
1910 GuestFileOpenInfo dstOpenInfo;
1911 RT_ZERO(dstOpenInfo);
1912 dstOpenInfo.mFileName = strFileDest;
1913 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
1914 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
1915 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
1916
1917 ComObjPtr<GuestFile> dstFile; int rcGuest;
1918 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
1919 if (RT_FAILURE(rc))
1920 {
1921 switch (rc)
1922 {
1923 case VERR_GSTCTL_GUEST_ERROR:
1924 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
1925 break;
1926
1927 default:
1928 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1929 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
1930 strFileDest.c_str(), rc));
1931 break;
1932 }
1933 }
1934 else
1935 {
1936 rc = fileCopyToGuestInner(hVfsFile, dstFile, FileCopyFlag_None, 0 /*cbOffset*/, cbSrcSize);
1937
1938 int rc2 = dstFile->i_closeFile(&rcGuest);
1939 AssertRC(rc2);
1940 }
1941 }
1942
1943 RTVfsFileRelease(hVfsFile);
1944 if (RT_FAILURE(rc))
1945 return rc;
1946 }
1947 else
1948 {
1949 if (fOptional)
1950 return VINF_SUCCESS;
1951
1952 return rc;
1953 }
1954
1955 return rc;
1956}
1957
1958int GuestSessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
1959{
1960 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1961
1962 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
1963
1964 GuestProcessTool procTool;
1965 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1966 int vrc = procTool.init(pSession, procInfo, false /* Async */, &rcGuest);
1967 if (RT_SUCCESS(vrc))
1968 {
1969 if (RT_SUCCESS(rcGuest))
1970 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &rcGuest);
1971 if (RT_SUCCESS(vrc))
1972 vrc = procTool.terminatedOk();
1973 }
1974
1975 if (RT_FAILURE(vrc))
1976 {
1977 switch (vrc)
1978 {
1979 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
1980 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1981 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
1982 procInfo.mExecutable.c_str(), procTool.getRc()));
1983 break;
1984
1985 case VERR_GSTCTL_GUEST_ERROR:
1986 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1987 break;
1988
1989 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
1990 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1991 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
1992 procInfo.mExecutable.c_str()));
1993 break;
1994
1995 default:
1996 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1997 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
1998 procInfo.mExecutable.c_str(), vrc));
1999 break;
2000 }
2001 }
2002
2003 return vrc;
2004}
2005
2006int GuestSessionTaskUpdateAdditions::Run(void)
2007{
2008 LogFlowThisFuncEnter();
2009
2010 ComObjPtr<GuestSession> pSession = mSession;
2011 Assert(!pSession.isNull());
2012
2013 AutoCaller autoCaller(pSession);
2014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2015
2016 int rc = setProgress(10);
2017 if (RT_FAILURE(rc))
2018 return rc;
2019
2020 HRESULT hr = S_OK;
2021
2022 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
2023
2024 ComObjPtr<Guest> pGuest(mSession->i_getParent());
2025#if 0
2026 /*
2027 * Wait for the guest being ready within 30 seconds.
2028 */
2029 AdditionsRunLevelType_T addsRunLevel;
2030 uint64_t tsStart = RTTimeSystemMilliTS();
2031 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
2032 && ( addsRunLevel != AdditionsRunLevelType_Userland
2033 && addsRunLevel != AdditionsRunLevelType_Desktop))
2034 {
2035 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
2036 {
2037 rc = VERR_TIMEOUT;
2038 break;
2039 }
2040
2041 RTThreadSleep(100); /* Wait a bit. */
2042 }
2043
2044 if (FAILED(hr)) rc = VERR_TIMEOUT;
2045 if (rc == VERR_TIMEOUT)
2046 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2047 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
2048#else
2049 /*
2050 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
2051 * can continue.
2052 */
2053 AdditionsRunLevelType_T addsRunLevel;
2054 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
2055 || ( addsRunLevel != AdditionsRunLevelType_Userland
2056 && addsRunLevel != AdditionsRunLevelType_Desktop))
2057 {
2058 if (addsRunLevel == AdditionsRunLevelType_System)
2059 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2060 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
2061 else
2062 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2063 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
2064 rc = VERR_NOT_SUPPORTED;
2065 }
2066#endif
2067
2068 if (RT_SUCCESS(rc))
2069 {
2070 /*
2071 * Determine if we are able to update automatically. This only works
2072 * if there are recent Guest Additions installed already.
2073 */
2074 Utf8Str strAddsVer;
2075 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2076 if ( RT_SUCCESS(rc)
2077 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
2078 {
2079 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2080 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
2081 strAddsVer.c_str()));
2082 rc = VERR_NOT_SUPPORTED;
2083 }
2084 }
2085
2086 Utf8Str strOSVer;
2087 eOSType osType = eOSType_Unknown;
2088 if (RT_SUCCESS(rc))
2089 {
2090 /*
2091 * Determine guest OS type and the required installer image.
2092 */
2093 Utf8Str strOSType;
2094 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
2095 if (RT_SUCCESS(rc))
2096 {
2097 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
2098 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
2099 {
2100 osType = eOSType_Windows;
2101
2102 /*
2103 * Determine guest OS version.
2104 */
2105 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
2106 if (RT_FAILURE(rc))
2107 {
2108 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2109 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
2110 rc = VERR_NOT_SUPPORTED;
2111 }
2112
2113 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
2114 * can't do automated updates here. */
2115 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
2116 if ( RT_SUCCESS(rc)
2117 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
2118 {
2119 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
2120 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
2121 {
2122 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
2123 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
2124 * flag is set this update routine ends successfully as soon as the installer was started
2125 * (and the user has to deal with it in the guest). */
2126 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2127 {
2128 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2129 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
2130 rc = VERR_NOT_SUPPORTED;
2131 }
2132 }
2133 }
2134 else
2135 {
2136 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2137 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
2138 strOSType.c_str(), strOSVer.c_str()));
2139 rc = VERR_NOT_SUPPORTED;
2140 }
2141 }
2142 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
2143 {
2144 osType = eOSType_Solaris;
2145 }
2146 else /* Everything else hopefully means Linux :-). */
2147 osType = eOSType_Linux;
2148
2149#if 1 /* Only Windows is supported (and tested) at the moment. */
2150 if ( RT_SUCCESS(rc)
2151 && osType != eOSType_Windows)
2152 {
2153 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
2154 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
2155 strOSType.c_str()));
2156 rc = VERR_NOT_SUPPORTED;
2157 }
2158#endif
2159 }
2160 }
2161
2162 if (RT_SUCCESS(rc))
2163 {
2164 /*
2165 * Try to open the .ISO file to extract all needed files.
2166 */
2167 RTVFSFILE hVfsFileIso;
2168 rc = RTVfsFileOpenNormal(mSource.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsFileIso);
2169 if (RT_SUCCESS(rc))
2170 {
2171 RTVFS hVfsIso;
2172 rc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, NULL);
2173 if (RT_FAILURE(rc))
2174 {
2175 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2176 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
2177 mSource.c_str(), rc));
2178 }
2179 else
2180 {
2181 /* Set default installation directories. */
2182 Utf8Str strUpdateDir = "/tmp/";
2183 if (osType == eOSType_Windows)
2184 strUpdateDir = "C:\\Temp\\";
2185
2186 rc = setProgress(5);
2187
2188 /* Try looking up the Guest Additions installation directory. */
2189 if (RT_SUCCESS(rc))
2190 {
2191 /* Try getting the installed Guest Additions version to know whether we
2192 * can install our temporary Guest Addition data into the original installation
2193 * directory.
2194 *
2195 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
2196 * a different location then.
2197 */
2198 bool fUseInstallDir = false;
2199
2200 Utf8Str strAddsVer;
2201 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
2202 if ( RT_SUCCESS(rc)
2203 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
2204 {
2205 fUseInstallDir = true;
2206 }
2207
2208 if (fUseInstallDir)
2209 {
2210 if (RT_SUCCESS(rc))
2211 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
2212 if (RT_SUCCESS(rc))
2213 {
2214 if (osType == eOSType_Windows)
2215 {
2216 strUpdateDir.findReplace('/', '\\');
2217 strUpdateDir.append("\\Update\\");
2218 }
2219 else
2220 strUpdateDir.append("/update/");
2221 }
2222 }
2223 }
2224
2225 if (RT_SUCCESS(rc))
2226 LogRel(("Guest Additions update directory is: %s\n",
2227 strUpdateDir.c_str()));
2228
2229 /* Create the installation directory. */
2230 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2231 rc = pSession->i_directoryCreate(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &rcGuest);
2232 if (RT_FAILURE(rc))
2233 {
2234 switch (rc)
2235 {
2236 case VERR_GSTCTL_GUEST_ERROR:
2237 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
2238 break;
2239
2240 default:
2241 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2242 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
2243 strUpdateDir.c_str(), rc));
2244 break;
2245 }
2246 }
2247 if (RT_SUCCESS(rc))
2248 rc = setProgress(10);
2249
2250 if (RT_SUCCESS(rc))
2251 {
2252 /* Prepare the file(s) we want to copy over to the guest and
2253 * (maybe) want to run. */
2254 switch (osType)
2255 {
2256 case eOSType_Windows:
2257 {
2258 /* Do we need to install our certificates? We do this for W2K and up. */
2259 bool fInstallCert = false;
2260
2261 /* Only Windows 2000 and up need certificates to be installed. */
2262 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
2263 {
2264 fInstallCert = true;
2265 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
2266 }
2267 else
2268 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
2269
2270 if (fInstallCert)
2271 {
2272 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
2273 {
2274 { "vbox.cer", "CERT/VBOX.CER" },
2275 { "vbox-sha1.cer", "CERT/VBOX_SHA1.CER" },
2276 { "vbox-sha256.cer", "CERT/VBOX_SHA256.CER" },
2277 { "vbox-sha256-r3.cer", "CERT/VBOX_SHA256_R3.CER" },
2278 { "oracle-vbox.cer", "CERT/ORACLE_VBOX.CER" },
2279 };
2280 uint32_t fCopyCertUtil = ISOFILE_FLAG_COPY_FROM_ISO;
2281 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
2282 {
2283 /* Skip if not present on the ISO. */
2284 RTFSOBJINFO ObjInfo;
2285 rc = RTVfsQueryPathInfo(hVfsIso, s_aCertFiles[i].pszIso, &ObjInfo, RTFSOBJATTRADD_NOTHING,
2286 RTPATH_F_ON_LINK);
2287 if (RT_FAILURE(rc))
2288 continue;
2289
2290 /* Copy the certificate certificate. */
2291 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
2292 mFiles.push_back(ISOFile(s_aCertFiles[i].pszIso,
2293 strDstCert,
2294 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_OPTIONAL));
2295
2296 /* Out certificate installation utility. */
2297 /* First pass: Copy over the file (first time only) + execute it to remove any
2298 * existing VBox certificates. */
2299 GuestProcessStartupInfo siCertUtilRem;
2300 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
2301 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
2302 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2303 siCertUtilRem.mArguments.push_back(strDstCert);
2304 siCertUtilRem.mArguments.push_back(strDstCert);
2305 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2306 strUpdateDir + "VBoxCertUtil.exe",
2307 fCopyCertUtil | ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2308 siCertUtilRem));
2309 fCopyCertUtil = 0;
2310 /* Second pass: Only execute (but don't copy) again, this time installng the
2311 * recent certificates just copied over. */
2312 GuestProcessStartupInfo siCertUtilAdd;
2313 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
2314 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
2315 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
2316 siCertUtilAdd.mArguments.push_back(strDstCert);
2317 siCertUtilAdd.mArguments.push_back(strDstCert);
2318 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
2319 strUpdateDir + "VBoxCertUtil.exe",
2320 ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
2321 siCertUtilAdd));
2322 }
2323 }
2324 /* The installers in different flavors, as we don't know (and can't assume)
2325 * the guest's bitness. */
2326 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_X86.EXE",
2327 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
2328 ISOFILE_FLAG_COPY_FROM_ISO));
2329 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
2330 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
2331 ISOFILE_FLAG_COPY_FROM_ISO));
2332 /* The stub loader which decides which flavor to run. */
2333 GuestProcessStartupInfo siInstaller;
2334 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
2335 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
2336 * setup can take quite a while, so be on the safe side. */
2337 siInstaller.mTimeoutMS = 5 * 60 * 1000;
2338 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
2339 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
2340 /* Don't quit VBoxService during upgrade because it still is used for this
2341 * piece of code we're in right now (that is, here!) ... */
2342 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
2343 /* Tell the installer to report its current installation status
2344 * using a running VBoxTray instance via balloon messages in the
2345 * Windows taskbar. */
2346 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
2347 /* Add optional installer command line arguments from the API to the
2348 * installer's startup info. */
2349 rc = addProcessArguments(siInstaller.mArguments, mArguments);
2350 AssertRC(rc);
2351 /* If the caller does not want to wait for out guest update process to end,
2352 * complete the progress object now so that the caller can do other work. */
2353 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
2354 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
2355 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS.EXE",
2356 strUpdateDir + "VBoxWindowsAdditions.exe",
2357 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_EXECUTE, siInstaller));
2358 break;
2359 }
2360 case eOSType_Linux:
2361 /** @todo Add Linux support. */
2362 break;
2363 case eOSType_Solaris:
2364 /** @todo Add Solaris support. */
2365 break;
2366 default:
2367 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
2368 break;
2369 }
2370 }
2371
2372 if (RT_SUCCESS(rc))
2373 {
2374 /* We want to spend 40% total for all copying operations. So roughly
2375 * calculate the specific percentage step of each copied file. */
2376 uint8_t uOffset = 20; /* Start at 20%. */
2377 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2378
2379 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
2380
2381 std::vector<ISOFile>::const_iterator itFiles = mFiles.begin();
2382 while (itFiles != mFiles.end())
2383 {
2384 if (itFiles->fFlags & ISOFILE_FLAG_COPY_FROM_ISO)
2385 {
2386 bool fOptional = false;
2387 if (itFiles->fFlags & ISOFILE_FLAG_OPTIONAL)
2388 fOptional = true;
2389 rc = copyFileToGuest(pSession, hVfsIso, itFiles->strSource, itFiles->strDest, fOptional);
2390 if (RT_FAILURE(rc))
2391 {
2392 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2393 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
2394 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
2395 break;
2396 }
2397 }
2398
2399 rc = setProgress(uOffset);
2400 if (RT_FAILURE(rc))
2401 break;
2402 uOffset += uStep;
2403
2404 ++itFiles;
2405 }
2406 }
2407
2408 /* Done copying, close .ISO file. */
2409 RTVfsRelease(hVfsIso);
2410
2411 if (RT_SUCCESS(rc))
2412 {
2413 /* We want to spend 35% total for all copying operations. So roughly
2414 * calculate the specific percentage step of each copied file. */
2415 uint8_t uOffset = 60; /* Start at 60%. */
2416 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2417
2418 LogRel(("Executing Guest Additions update files ...\n"));
2419
2420 std::vector<ISOFile>::iterator itFiles = mFiles.begin();
2421 while (itFiles != mFiles.end())
2422 {
2423 if (itFiles->fFlags & ISOFILE_FLAG_EXECUTE)
2424 {
2425 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
2426 if (RT_FAILURE(rc))
2427 break;
2428 }
2429
2430 rc = setProgress(uOffset);
2431 if (RT_FAILURE(rc))
2432 break;
2433 uOffset += uStep;
2434
2435 ++itFiles;
2436 }
2437 }
2438
2439 if (RT_SUCCESS(rc))
2440 {
2441 LogRel(("Automatic update of Guest Additions succeeded\n"));
2442 rc = setProgressSuccess();
2443 }
2444 }
2445
2446 RTVfsFileRelease(hVfsFileIso);
2447 }
2448 }
2449
2450 if (RT_FAILURE(rc))
2451 {
2452 if (rc == VERR_CANCELLED)
2453 {
2454 LogRel(("Automatic update of Guest Additions was canceled\n"));
2455
2456 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2457 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
2458 }
2459 else
2460 {
2461 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
2462 if (!mProgress.isNull()) /* Progress object is optional. */
2463 {
2464 com::ProgressErrorInfo errorInfo(mProgress);
2465 if ( errorInfo.isFullAvailable()
2466 || errorInfo.isBasicAvailable())
2467 {
2468 strError = errorInfo.getText();
2469 }
2470 }
2471
2472 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
2473 strError.c_str(), hr));
2474 }
2475
2476 LogRel(("Please install Guest Additions manually\n"));
2477 }
2478
2479 /** @todo Clean up copied / left over installation files. */
2480
2481 LogFlowFuncLeaveRC(rc);
2482 return rc;
2483}
2484
Note: See TracBrowser for help on using the repository browser.

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