VirtualBox

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

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

Main/GuestSessionTask: Commented out RunAsync as it isn't used by anyone. bugref:9320

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

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