VirtualBox

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

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

Guest Control/Main: Resolved another @todo in GuestSessionTask::setProgressErrorMsg(): Don't use format string.

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

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