VirtualBox

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

Last change on this file since 77097 was 77038, checked in by vboxsync, 6 years ago

Guest Control/Main: Fixed the number of operations when copying from guest to the host (better fix, with debug assertion).

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