VirtualBox

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

Last change on this file since 78425 was 77531, checked in by vboxsync, 6 years ago

Guest Control/Main: Fixed directory creation / mode flags for session tasks.

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