VirtualBox

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

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

GuestSessionTask::fileCopyToGuest: More review comments. bugref:9320

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