VirtualBox

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

Last change on this file since 79187 was 79187, checked in by vboxsync, 5 years ago

Main/GuestSessionImplTasks.cpp: Quick workaround for incorrect i_notifyComplete call. bugref:9320

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