VirtualBox

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

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

Guest Control/Main: Don't use RT_ZERO on non-POD types. bugref:9320

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