VirtualBox

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

Last change on this file since 71825 was 71819, checked in by vboxsync, 7 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.9 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 71819 2018-04-11 11:00:35Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session tasks.
4 */
5
6/*
7 * Copyright (C) 2012-2018 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
45
46/*********************************************************************************************************************************
47* Defines *
48*********************************************************************************************************************************/
49
50/**
51 * (Guest Additions) ISO file flags.
52 * Needed for handling Guest Additions updates.
53 */
54#define ISOFILE_FLAG_NONE 0
55/** Copy over the file from host to the
56 * guest. */
57#define ISOFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
58/** Execute file on the guest after it has
59 * been successfully transfered. */
60#define ISOFILE_FLAG_EXECUTE RT_BIT(7)
61/** File is optional, does not have to be
62 * existent on the .ISO. */
63#define ISOFILE_FLAG_OPTIONAL RT_BIT(8)
64
65
66// session task classes
67/////////////////////////////////////////////////////////////////////////////
68
69GuestSessionTask::GuestSessionTask(GuestSession *pSession)
70 : ThreadTask("GenericGuestSessionTask")
71{
72 mSession = pSession;
73}
74
75GuestSessionTask::~GuestSessionTask(void)
76{
77}
78
79HRESULT GuestSessionTask::createAndSetProgressObject()
80{
81 LogFlowThisFunc(("Task Description = %s, pTask=%p\n", mDesc.c_str(), this));
82
83 ComObjPtr<Progress> pProgress;
84 HRESULT hr = S_OK;
85 /* Create the progress object. */
86 hr = pProgress.createObject();
87 if (FAILED(hr))
88 return VERR_COM_UNEXPECTED;
89
90 hr = pProgress->init(static_cast<IGuestSession*>(mSession),
91 Bstr(mDesc).raw(),
92 TRUE /* aCancelable */);
93 if (FAILED(hr))
94 return VERR_COM_UNEXPECTED;
95
96 mProgress = pProgress;
97
98 LogFlowFuncLeave();
99
100 return hr;
101}
102
103int GuestSessionTask::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
104{
105 LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
106
107 mDesc = strDesc;
108 mProgress = pProgress;
109 HRESULT hrc = createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
110
111 LogFlowThisFunc(("Returning hrc=%Rhrc\n", hrc));
112 return Global::vboxStatusCodeToCOM(hrc);
113}
114
115
116int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
117 const Utf8Str &strPath, Utf8Str &strValue)
118{
119 ComObjPtr<Console> pConsole = pGuest->i_getConsole();
120 const ComPtr<IMachine> pMachine = pConsole->i_machine();
121
122 Assert(!pMachine.isNull());
123 Bstr strTemp, strFlags;
124 LONG64 i64Timestamp;
125 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
126 strTemp.asOutParam(),
127 &i64Timestamp, strFlags.asOutParam());
128 if (SUCCEEDED(hr))
129 {
130 strValue = strTemp;
131 return VINF_SUCCESS;
132 }
133 return VERR_NOT_FOUND;
134}
135
136int GuestSessionTask::setProgress(ULONG uPercent)
137{
138 if (mProgress.isNull()) /* Progress is optional. */
139 return VINF_SUCCESS;
140
141 BOOL fCanceled;
142 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
143 && fCanceled)
144 return VERR_CANCELLED;
145 BOOL fCompleted;
146 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
147 && fCompleted)
148 {
149 AssertMsgFailed(("Setting value of an already completed progress\n"));
150 return VINF_SUCCESS;
151 }
152 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
153 if (FAILED(hr))
154 return VERR_COM_UNEXPECTED;
155
156 return VINF_SUCCESS;
157}
158
159int GuestSessionTask::setProgressSuccess(void)
160{
161 if (mProgress.isNull()) /* Progress is optional. */
162 return VINF_SUCCESS;
163
164 BOOL fCanceled;
165 BOOL fCompleted;
166 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
167 && !fCanceled
168 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
169 && !fCompleted)
170 {
171 HRESULT hr = mProgress->i_notifyComplete(S_OK);
172 if (FAILED(hr))
173 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
174 }
175
176 return VINF_SUCCESS;
177}
178
179HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
180{
181 LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
182 hr, strMsg.c_str()));
183
184 if (mProgress.isNull()) /* Progress is optional. */
185 return hr; /* Return original rc. */
186
187 BOOL fCanceled;
188 BOOL fCompleted;
189 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
190 && !fCanceled
191 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
192 && !fCompleted)
193 {
194 HRESULT hr2 = mProgress->i_notifyComplete(hr,
195 COM_IIDOF(IGuestSession),
196 GuestSession::getStaticComponentName(),
197 strMsg.c_str());
198 if (FAILED(hr2))
199 return hr2;
200 }
201 return hr; /* Return original rc. */
202}
203
204/**
205 * Creates a directory on the guest.
206 *
207 * @return VBox status code. VWRN_ALREADY_EXISTS if directory on the guest already exists.
208 * @param strPath Absolute path to directory on the guest (guest style path) to create.
209 * @param enmDirecotryCreateFlags Directory creation flags.
210 * @param uMode Directory mode to use for creation.
211 * @param fFollowSymlinks Whether to follow symlinks on the guest or not.
212 */
213int GuestSessionTask::directoryCreate(const com::Utf8Str &strPath,
214 DirectoryCreateFlag_T enmDirecotryCreateFlags, uint32_t uMode, bool fFollowSymlinks)
215{
216 LogFlowFunc(("strPath=%s, fFlags=0x%x, uMode=%RU32, fFollowSymlinks=%RTbool\n",
217 strPath.c_str(), enmDirecotryCreateFlags, uMode, fFollowSymlinks));
218
219 GuestFsObjData objData; int rcGuest;
220
221 int rc = mSession->i_directoryQueryInfo(strPath, fFollowSymlinks, objData, &rcGuest);
222 if (RT_SUCCESS(rc))
223 {
224 return VWRN_ALREADY_EXISTS;
225 }
226 else
227 {
228 switch (rc)
229 {
230 case VERR_GSTCTL_GUEST_ERROR:
231 {
232 switch (rcGuest)
233 {
234 case VERR_FILE_NOT_FOUND:
235 case VERR_PATH_NOT_FOUND:
236 rc = mSession->i_directoryCreate(strPath.c_str(), uMode, enmDirecotryCreateFlags, &rcGuest);
237 break;
238 default:
239 break;
240 }
241 break;
242 }
243
244 default:
245 break;
246 }
247 }
248
249 if (RT_FAILURE(rc))
250 {
251 if (rc == VERR_GSTCTL_GUEST_ERROR)
252 {
253 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
254 }
255 else
256 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
257 Utf8StrFmt(GuestSession::tr("Error creating directory on the guest: %Rrc"), strPath.c_str(), rc));
258 }
259
260 LogFlowFuncLeaveRC(rc);
261 return rc;
262}
263
264/**
265 * Main function for copying a file from guest to the host.
266 *
267 * @return VBox status code.
268 * @param srcFile Guest file (source) to copy to the host. Must be in opened and ready state already.
269 * @param phDstFile Pointer to host file handle (destination) to copy to. Must be in opened and ready state already.
270 * @param fFileCopyFlags File copy flags.
271 * @param offCopy Offset (in bytes) where to start copying the source file.
272 * @param cbSize Size (in bytes) to copy from the source file.
273 */
274int GuestSessionTask::fileCopyFromGuestInner(ComObjPtr<GuestFile> &srcFile, PRTFILE phDstFile, FileCopyFlag_T fFileCopyFlags,
275 uint64_t offCopy, uint64_t cbSize)
276{
277 RT_NOREF(fFileCopyFlags);
278
279 BOOL fCanceled = FALSE;
280 uint64_t cbWrittenTotal = 0;
281 uint64_t cbToRead = cbSize;
282
283 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
284
285 int rc = VINF_SUCCESS;
286
287 if (offCopy)
288 {
289 uint64_t offActual;
290 rc = srcFile->i_seekAt(offCopy, GUEST_FILE_SEEKTYPE_BEGIN, uTimeoutMs, &offActual);
291 if (RT_FAILURE(rc))
292 {
293 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
294 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
295 return rc;
296 }
297 }
298
299 BYTE byBuf[_64K];
300 while (cbToRead)
301 {
302 uint32_t cbRead;
303 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
304 rc = srcFile->i_readData(cbChunk, uTimeoutMs, byBuf, sizeof(byBuf), &cbRead);
305 if (RT_FAILURE(rc))
306 {
307 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
308 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64, failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
309 break;
310 }
311
312 rc = RTFileWrite(*phDstFile, byBuf, cbRead, NULL /* No partial writes */);
313 if (RT_FAILURE(rc))
314 {
315 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
316 Utf8StrFmt(GuestSession::tr("Writing to %RU32 bytes to file failed: %Rrc"), cbRead, rc));
317 break;
318 }
319
320 Assert(cbToRead >= cbRead);
321 cbToRead -= cbRead;
322
323 /* Update total bytes written to the guest. */
324 cbWrittenTotal += cbRead;
325 Assert(cbWrittenTotal <= cbSize);
326
327 /* Did the user cancel the operation above? */
328 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
329 && fCanceled)
330 break;
331
332 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
333 if (RT_FAILURE(rc))
334 break;
335 }
336
337 if (RT_FAILURE(rc))
338 return rc;
339
340 /*
341 * Even if we succeeded until here make sure to check whether we really transfered
342 * everything.
343 */
344 if ( cbSize > 0
345 && cbWrittenTotal == 0)
346 {
347 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
348 * to the destination -> access denied. */
349 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
350 Utf8StrFmt(GuestSession::tr("Writing guest file to host failed: Access denied")));
351 rc = VERR_ACCESS_DENIED;
352 }
353 else if (cbWrittenTotal < cbSize)
354 {
355 /* If we did not copy all let the user know. */
356 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
357 Utf8StrFmt(GuestSession::tr("Copying guest file to host to failed (%RU64/%RU64 bytes transfered)"),
358 cbWrittenTotal, cbSize));
359 rc = VERR_INTERRUPTED;
360 }
361
362 LogFlowFuncLeaveRC(rc);
363 return rc;
364}
365
366/**
367 * Copies a file from the guest to the host.
368 *
369 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
370 * @param strSource Full path of source file on the guest to copy.
371 * @param strDest Full destination path and file name (host style) to copy file to.
372 * @param fFileCopyFlags File copy flags.
373 */
374int GuestSessionTask::fileCopyFromGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
375{
376 LogFlowThisFunc(("strSource=%s, strDest=%s, enmFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
377
378 GuestFileOpenInfo srcOpenInfo;
379 RT_ZERO(srcOpenInfo);
380 srcOpenInfo.mFileName = strSource;
381 srcOpenInfo.mOpenAction = FileOpenAction_OpenExisting;
382 srcOpenInfo.mAccessMode = FileAccessMode_ReadOnly;
383 srcOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
384
385 ComObjPtr<GuestFile> srcFile;
386
387 GuestFsObjData srcObjData;
388 RT_ZERO(srcObjData);
389
390 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
391 int rc = mSession->i_fsQueryInfo(strSource, TRUE /* fFollowSymlinks */, srcObjData, &rcGuest);
392 if (RT_FAILURE(rc))
393 {
394 switch (rc)
395 {
396 case VERR_GSTCTL_GUEST_ERROR:
397 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
398 break;
399
400 default:
401 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
402 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
403 strSource.c_str(), rc));
404 break;
405 }
406 }
407 else
408 {
409 switch (srcObjData.mType)
410 {
411 case FsObjType_File:
412 break;
413
414 case FsObjType_Symlink:
415 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
416 {
417 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
418 Utf8StrFmt(GuestSession::tr("Source file \"%s\" is a symbolic link"),
419 strSource.c_str(), rc));
420 rc = VERR_IS_A_SYMLINK;
421 }
422 break;
423
424 default:
425 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
426 Utf8StrFmt(GuestSession::tr("Source element \"%s\" is not a file"), strSource.c_str()));
427 rc = VERR_NOT_A_FILE;
428 break;
429 }
430 }
431
432 if (RT_FAILURE(rc))
433 return rc;
434
435 rc = mSession->i_fileOpen(srcOpenInfo, srcFile, &rcGuest);
436 if (RT_FAILURE(rc))
437 {
438 switch (rc)
439 {
440 case VERR_GSTCTL_GUEST_ERROR:
441 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
442 break;
443
444 default:
445 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
446 Utf8StrFmt(GuestSession::tr("Source file \"%s\" could not be opened: %Rrc"),
447 strSource.c_str(), rc));
448 break;
449 }
450 }
451
452 if (RT_FAILURE(rc))
453 return rc;
454
455 char *pszDstFile = NULL;
456 RTFSOBJINFO dstObjInfo;
457 RT_ZERO(dstObjInfo);
458
459 bool fSkip = false; /* Whether to skip handling the file. */
460
461 if (RT_SUCCESS(rc))
462 {
463 rc = RTPathQueryInfo(strDest.c_str(), &dstObjInfo, RTFSOBJATTRADD_NOTHING);
464 if (RT_SUCCESS(rc))
465 {
466 if (fFileCopyFlags & FileCopyFlag_NoReplace)
467 {
468 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
469 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"), strDest.c_str()));
470 rc = VERR_ALREADY_EXISTS;
471 }
472
473 if (fFileCopyFlags & FileCopyFlag_Update)
474 {
475 RTTIMESPEC srcModificationTimeTS;
476 RTTimeSpecSetSeconds(&srcModificationTimeTS, srcObjData.mModificationTime);
477 if (RTTimeSpecCompare(&srcModificationTimeTS, &dstObjInfo.ModificationTime) <= 0)
478 {
479 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
480 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
481 strDest.c_str()));
482 fSkip = true;
483 }
484 }
485 }
486 else
487 {
488 if (rc != VERR_FILE_NOT_FOUND) /* Destination file does not exist (yet)? */
489 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
490 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
491 strDest.c_str(), rc));
492 }
493 }
494
495 if (fSkip)
496 {
497 int rc2 = srcFile->i_closeFile(&rcGuest);
498 AssertRC(rc2);
499 return VINF_SUCCESS;
500 }
501
502 if (RT_SUCCESS(rc))
503 {
504 if (RTFS_IS_FILE(dstObjInfo.Attr.fMode))
505 {
506 if (fFileCopyFlags & FileCopyFlag_NoReplace)
507 {
508 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
509 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
510 strDest.c_str(), rc));
511 rc = VERR_ALREADY_EXISTS;
512 }
513 else
514 pszDstFile = RTStrDup(strDest.c_str());
515 }
516 else if (RTFS_IS_DIRECTORY(dstObjInfo.Attr.fMode))
517 {
518 if ( strDest.endsWith("\\")
519 || strDest.endsWith("/"))
520 {
521 /* Build the final file name with destination path (on the host). */
522 char szDstPath[RTPATH_MAX];
523 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
524
525 RTPathAppend(szDstPath, sizeof(szDstPath), RTPathFilename(strSource.c_str()));
526
527 pszDstFile = RTStrDup(szDstPath);
528 }
529 else
530 {
531 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
532 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
533 strDest.c_str(), rc));
534 rc = VERR_ALREADY_EXISTS;
535 }
536 }
537 else if (RTFS_IS_SYMLINK(dstObjInfo.Attr.fMode))
538 {
539 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
540 {
541 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
542 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
543 strDest.c_str(), rc));
544 rc = VERR_IS_A_SYMLINK;
545 }
546 else
547 pszDstFile = RTStrDup(strDest.c_str());
548 }
549 else
550 {
551 LogFlowThisFunc(("Object type %RU32 not implemented yet\n", dstObjInfo.Attr.fMode));
552 rc = VERR_NOT_IMPLEMENTED;
553 }
554 }
555 else if (rc == VERR_FILE_NOT_FOUND)
556 pszDstFile = RTStrDup(strDest.c_str());
557
558 if ( RT_SUCCESS(rc)
559 || rc == VERR_FILE_NOT_FOUND)
560 {
561 if (!pszDstFile)
562 {
563 setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No memory to allocate destination file path")));
564 rc = VERR_NO_MEMORY;
565 }
566 else
567 {
568 RTFILE hDstFile;
569 rc = RTFileOpen(&hDstFile, pszDstFile,
570 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
571 if (RT_SUCCESS(rc))
572 {
573 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n", strSource.c_str(), pszDstFile, srcObjData.mObjectSize));
574
575 rc = fileCopyFromGuestInner(srcFile, &hDstFile, fFileCopyFlags, 0 /* Offset, unused */, (uint64_t)srcObjData.mObjectSize);
576
577 int rc2 = RTFileClose(hDstFile);
578 AssertRC(rc2);
579 }
580 else
581 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
582 Utf8StrFmt(GuestSession::tr("Opening/creating destination file \"%s\" failed: %Rrc"),
583 pszDstFile, rc));
584 }
585 }
586
587 RTStrFree(pszDstFile);
588
589 int rc2 = srcFile->i_closeFile(&rcGuest);
590 AssertRC(rc2);
591
592 LogFlowFuncLeaveRC(rc);
593 return rc;
594}
595
596/**
597 * Main function for copying a file from host to the guest.
598 *
599 * @return VBox status code.
600 * @param phSrcFile Pointer to host file handle (source) to copy from. Must be in opened and ready state already.
601 * @param dstFile Guest file (destination) to copy to the guest. Must be in opened and ready state already.
602 * @param fFileCopyFlags File copy flags.
603 * @param offCopy Offset (in bytes) where to start copying the source file.
604 * @param cbSize Size (in bytes) to copy from the source file.
605 */
606int GuestSessionTask::fileCopyToGuestInner(PRTFILE phSrcFile, ComObjPtr<GuestFile> &dstFile, FileCopyFlag_T fFileCopyFlags,
607 uint64_t offCopy, uint64_t cbSize)
608{
609 RT_NOREF(fFileCopyFlags);
610
611 BOOL fCanceled = FALSE;
612 uint64_t cbWrittenTotal = 0;
613 uint64_t cbToRead = cbSize;
614
615 uint32_t uTimeoutMs = 30 * 1000; /* 30s timeout. */
616
617 int rc = VINF_SUCCESS;
618
619 if (offCopy)
620 {
621 uint64_t offActual;
622 rc = RTFileSeek(*phSrcFile, offCopy, RTFILE_SEEK_END, &offActual);
623 if (RT_FAILURE(rc))
624 {
625 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
626 Utf8StrFmt(GuestSession::tr("Seeking to offset %RU64 failed: %Rrc"), offCopy, rc));
627 return rc;
628 }
629 }
630
631 BYTE byBuf[_64K];
632 while (cbToRead)
633 {
634 size_t cbRead;
635 const uint32_t cbChunk = RT_MIN(cbToRead, sizeof(byBuf));
636 rc = RTFileRead(*phSrcFile, byBuf, cbChunk, &cbRead);
637 if (RT_FAILURE(rc))
638 {
639 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
640 Utf8StrFmt(GuestSession::tr("Reading %RU32 bytes @ %RU64, failed: %Rrc"), cbChunk, cbWrittenTotal, rc));
641 break;
642 }
643
644 rc = dstFile->i_writeData(uTimeoutMs, byBuf, (uint32_t)cbRead, NULL /* No partial writes */);
645 if (RT_FAILURE(rc))
646 {
647 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
648 Utf8StrFmt(GuestSession::tr("Writing to %zu bytes to file failed: %Rrc"), cbRead, rc));
649 break;
650 }
651
652 Assert(cbToRead >= cbRead);
653 cbToRead -= cbRead;
654
655 /* Update total bytes written to the guest. */
656 cbWrittenTotal += cbRead;
657 Assert(cbWrittenTotal <= cbSize);
658
659 /* Did the user cancel the operation above? */
660 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
661 && fCanceled)
662 break;
663
664 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)cbSize / 100.0)));
665 if (RT_FAILURE(rc))
666 break;
667 }
668
669 if (RT_FAILURE(rc))
670 return rc;
671
672 /*
673 * Even if we succeeded until here make sure to check whether we really transfered
674 * everything.
675 */
676 if ( cbSize > 0
677 && cbWrittenTotal == 0)
678 {
679 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
680 * to the destination -> access denied. */
681 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
682 Utf8StrFmt(GuestSession::tr("Writing to destination file failed: Access denied")));
683 rc = VERR_ACCESS_DENIED;
684 }
685 else if (cbWrittenTotal < cbSize)
686 {
687 /* If we did not copy all let the user know. */
688 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
689 Utf8StrFmt(GuestSession::tr("Copying to destination failed (%RU64/%RU64 bytes transfered)"),
690 cbWrittenTotal, cbSize));
691 rc = VERR_INTERRUPTED;
692 }
693
694 LogFlowFuncLeaveRC(rc);
695 return rc;
696}
697
698/**
699 * Copies a file from the guest to the host.
700 *
701 * @return VBox status code. VINF_NO_CHANGE if file was skipped.
702 * @param strSource Full path of source file on the host to copy.
703 * @param strDest Full destination path and file name (guest style) to copy file to.
704 * @param fFileCopyFlags File copy flags.
705 */
706int GuestSessionTask::fileCopyToGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
707{
708 LogFlowThisFunc(("strSource=%s, strDest=%s, fFileCopyFlags=0x%x\n", strSource.c_str(), strDest.c_str(), fFileCopyFlags));
709
710 Utf8Str strDestFinal = strDest;
711
712 GuestFsObjData dstObjData;
713 RT_ZERO(dstObjData);
714
715 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
716 int rc = mSession->i_fsQueryInfo(strDest, TRUE /* fFollowSymlinks */, dstObjData, &rcGuest);
717 if (RT_FAILURE(rc))
718 {
719 switch (rc)
720 {
721 case VERR_GSTCTL_GUEST_ERROR:
722 if (rcGuest == VERR_FILE_NOT_FOUND) /* File might not exist on the guest yet. */
723 {
724 rc = VINF_SUCCESS;
725 }
726 else
727 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
728 break;
729
730 default:
731 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
732 Utf8StrFmt(GuestSession::tr("Destination file lookup for \"%s\" failed: %Rrc"),
733 strDest.c_str(), rc));
734 break;
735 }
736 }
737 else
738 {
739 switch (dstObjData.mType)
740 {
741 case FsObjType_Directory:
742 {
743 if ( strDest.endsWith("\\")
744 || strDest.endsWith("/"))
745 {
746 /* Build the final file name with destination path (on the host). */
747 char szDstPath[RTPATH_MAX];
748 RTStrPrintf2(szDstPath, sizeof(szDstPath), "%s", strDest.c_str());
749
750 RTPathAppend(szDstPath, sizeof(szDstPath), RTPathFilename(strSource.c_str()));
751
752 strDestFinal = szDstPath;
753 }
754 else
755 {
756 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
757 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" already exists"),
758 strDest.c_str(), rc));
759 rc = VERR_ALREADY_EXISTS;
760 }
761 break;
762 }
763
764 case FsObjType_File:
765 if (fFileCopyFlags & FileCopyFlag_NoReplace)
766 {
767 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
768 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" already exists"),
769 strDest.c_str(), rc));
770 rc = VERR_ALREADY_EXISTS;
771 }
772 break;
773
774 case FsObjType_Symlink:
775 if (!(fFileCopyFlags & FileCopyFlag_FollowLinks))
776 {
777 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
778 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" is a symbolic link"),
779 strDest.c_str(), rc));
780 rc = VERR_IS_A_SYMLINK;
781 }
782 break;
783
784 default:
785 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
786 Utf8StrFmt(GuestSession::tr("Destination element \"%s\" not supported"), strDest.c_str()));
787 rc = VERR_NOT_SUPPORTED;
788 break;
789 }
790 }
791
792 if (RT_FAILURE(rc))
793 return rc;
794
795 GuestFileOpenInfo dstOpenInfo;
796 RT_ZERO(dstOpenInfo);
797 dstOpenInfo.mFileName = strDestFinal;
798 if (fFileCopyFlags & FileCopyFlag_NoReplace)
799 dstOpenInfo.mOpenAction = FileOpenAction_CreateNew;
800 else
801 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
802 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
803 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
804
805 ComObjPtr<GuestFile> dstFile;
806 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
807 if (RT_FAILURE(rc))
808 {
809 switch (rc)
810 {
811 case VERR_GSTCTL_GUEST_ERROR:
812 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
813 break;
814
815 default:
816 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
817 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
818 strDestFinal.c_str(), rc));
819 break;
820 }
821 }
822
823 if (RT_FAILURE(rc))
824 return rc;
825
826 RTFSOBJINFO srcObjInfo;
827 RT_ZERO(srcObjInfo);
828
829 bool fSkip = false; /* Whether to skip handling the file. */
830
831 if (RT_SUCCESS(rc))
832 {
833 rc = RTPathQueryInfo(strSource.c_str(), &srcObjInfo, RTFSOBJATTRADD_NOTHING);
834 if (RT_SUCCESS(rc))
835 {
836 if (fFileCopyFlags & FileCopyFlag_Update)
837 {
838 RTTIMESPEC dstModificationTimeTS;
839 RTTimeSpecSetSeconds(&dstModificationTimeTS, dstObjData.mModificationTime);
840 if (RTTimeSpecCompare(&dstModificationTimeTS, &srcObjInfo.ModificationTime) <= 0)
841 {
842 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
843 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" has same or newer modification date"),
844 strDestFinal.c_str()));
845 fSkip = true;
846 }
847 }
848 }
849 else
850 {
851 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
852 Utf8StrFmt(GuestSession::tr("Source file lookup for \"%s\" failed: %Rrc"),
853 strSource.c_str(), rc));
854 }
855 }
856
857 if (fSkip)
858 {
859 int rc2 = dstFile->i_closeFile(&rcGuest);
860 AssertRC(rc2);
861 return VINF_SUCCESS;
862 }
863
864 if (RT_SUCCESS(rc))
865 {
866 RTFILE hSrcFile;
867 rc = RTFileOpen(&hSrcFile, strSource.c_str(),
868 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
869 if (RT_SUCCESS(rc))
870 {
871 LogFlowThisFunc(("Copying '%s' to '%s' (%RI64 bytes) ...\n",
872 strSource.c_str(), strDestFinal.c_str(), srcObjInfo.cbObject));
873
874 rc = fileCopyToGuestInner(&hSrcFile, dstFile, fFileCopyFlags, 0 /* Offset, unused */, srcObjInfo.cbObject);
875
876 int rc2 = RTFileClose(hSrcFile);
877 AssertRC(rc2);
878 }
879 else
880 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
881 Utf8StrFmt(GuestSession::tr("Opening source file \"%s\" failed: %Rrc"),
882 strSource.c_str(), rc));
883 }
884
885 int rc2 = dstFile->i_closeFile(&rcGuest);
886 AssertRC(rc2);
887
888 LogFlowFuncLeaveRC(rc);
889 return rc;
890}
891
892/**
893 * Translates a source path to a destination path (can be both sides,
894 * either host or guest). The source root is needed to determine the start
895 * of the relative source path which also needs to present in the destination
896 * path.
897 *
898 * @return IPRT status code.
899 * @param strSourceRoot Source root path. No trailing directory slash!
900 * @param strSource Actual source to transform. Must begin with
901 * the source root path!
902 * @param strDest Destination path.
903 * @param strOut where to store the output path.
904 */
905int GuestSessionTask::pathConstructOnGuest(const Utf8Str &strSourceRoot, const Utf8Str &strSource,
906 const Utf8Str &strDest, Utf8Str &strOut)
907{
908 RT_NOREF(strSourceRoot, strSource, strDest, strOut);
909 return VINF_SUCCESS;
910}
911
912SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
913 uint32_t uFlags,
914 uint32_t uTimeoutMS)
915 : GuestSessionTask(pSession),
916 mFlags(uFlags),
917 mTimeoutMS(uTimeoutMS)
918{
919 m_strTaskName = "gctlSesOpen";
920}
921
922SessionTaskOpen::~SessionTaskOpen(void)
923{
924
925}
926
927int SessionTaskOpen::Run(void)
928{
929 LogFlowThisFuncEnter();
930
931 AutoCaller autoCaller(mSession);
932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
933
934 int vrc = mSession->i_startSession(NULL /*pvrcGuest*/);
935 /* Nothing to do here anymore. */
936
937 LogFlowFuncLeaveRC(vrc);
938 return vrc;
939}
940
941SessionTaskCopyDirFrom::SessionTaskCopyDirFrom(GuestSession *pSession, const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter,
942 DirectoryCopyFlag_T fDirCopyFlags)
943 : GuestSessionCopyTask(pSession)
944{
945 m_strTaskName = "gctlCpyDirFrm";
946
947 mSource = strSource;
948 mDest = strDest;
949 mFilter = strFilter;
950
951 u.Dir.fCopyFlags = fDirCopyFlags;
952}
953
954SessionTaskCopyDirFrom::~SessionTaskCopyDirFrom(void)
955{
956}
957
958/**
959 * Copys a directory (tree) from guest to the host.
960 *
961 * @return IPRT status code.
962 * @param strSource Source directory on the guest to copy to the host.
963 * @param strFilter DOS-style wildcard filter (?, *). Optional.
964 * @param strDest Destination directory on the host.
965 * @param fRecursive Whether to recursively copy the directory contents or not.
966 * @param fFollowSymlinks Whether to follow symlinks or not.
967 * @param strSubDir Current sub directory to handle. Needs to NULL and only
968 * is needed for recursion.
969 */
970int SessionTaskCopyDirFrom::directoryCopyToHost(const Utf8Str &strSource, const Utf8Str &strFilter,
971 const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
972 const Utf8Str &strSubDir /* For recursion. */)
973{
974 Utf8Str strSrcDir = strSource;
975 Utf8Str strDstDir = strDest;
976 Utf8Str strSrcSubDir = strSubDir;
977
978 /* Validation and sanity. */
979 if ( !strSrcDir.endsWith("/")
980 && !strSrcDir.endsWith("\\"))
981 strSrcDir += "/";
982
983 if ( !strDstDir.endsWith("/")
984 && !strDstDir.endsWith("\\"))
985 strDstDir+= "/";
986
987 if ( strSrcSubDir.isNotEmpty() /* Optional, for recursion. */
988 && !strSrcSubDir.endsWith("/")
989 && !strSrcSubDir.endsWith("\\"))
990 strSrcSubDir += "/";
991
992 Utf8Str strSrcCur = strSrcDir + strSrcSubDir;
993
994 LogFlowFunc(("Entering '%s'\n", strSrcCur.c_str()));
995
996 int rc;
997
998 if (strSrcSubDir.isNotEmpty())
999 {
1000 const uint32_t fMode = 0700; /** @todo */
1001
1002 rc = RTDirCreate(Utf8Str(strDstDir + strSrcSubDir).c_str(), fMode, 0);
1003 if (RT_FAILURE(rc))
1004 return rc;
1005 }
1006
1007 GuestDirectoryOpenInfo dirOpenInfo;
1008 dirOpenInfo.mFilter = strFilter;
1009 dirOpenInfo.mPath = strSrcCur;
1010 dirOpenInfo.mFlags = 0; /** @todo Handle flags? */
1011
1012 ComObjPtr <GuestDirectory> pDir; int rcGuest;
1013 rc = mSession->i_directoryOpen(dirOpenInfo, pDir, &rcGuest);
1014
1015 if (RT_FAILURE(rc))
1016 {
1017 switch (rc)
1018 {
1019 case VERR_INVALID_PARAMETER:
1020 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1021 Utf8StrFmt(GuestSession::tr("Opening directory \"%s\" failed: Invalid parameter"), dirOpenInfo.mPath.c_str()));
1022 break;
1023
1024 case VERR_GSTCTL_GUEST_ERROR:
1025 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1026 break;
1027
1028 default:
1029 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1030 Utf8StrFmt(GuestSession::tr("Opening directory \"%s\" failed: %Rrc"), dirOpenInfo.mPath.c_str(), rc));
1031 break;
1032 }
1033
1034 return rc;
1035 }
1036
1037 ComObjPtr<GuestFsObjInfo> fsObjInfo;
1038 while (RT_SUCCESS(rc = pDir->i_readInternal(fsObjInfo, &rcGuest)))
1039 {
1040 FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC. */
1041 HRESULT hr2 = fsObjInfo->COMGETTER(Type)(&enmObjType);
1042 AssertComRC(hr2);
1043
1044 com::Bstr bstrName;
1045 hr2 = fsObjInfo->COMGETTER(Name)(bstrName.asOutParam());
1046 AssertComRC(hr2);
1047
1048 Utf8Str strName(bstrName);
1049
1050 switch (enmObjType)
1051 {
1052 case FsObjType_Directory:
1053 {
1054 bool fSkip = false;
1055
1056 if ( strName.equals(".")
1057 || strName.equals(".."))
1058 {
1059 fSkip = true;
1060 }
1061
1062 if ( strFilter.isNotEmpty()
1063 && !RTStrSimplePatternMatch(strFilter.c_str(), strName.c_str()))
1064 fSkip = true;
1065
1066 if ( fRecursive
1067 && !fSkip)
1068 rc = directoryCopyToHost(strSrcDir, strFilter, strDstDir, fRecursive, fFollowSymlinks,
1069 strSrcSubDir + strName);
1070 break;
1071 }
1072
1073 case FsObjType_Symlink:
1074 {
1075 if (fFollowSymlinks)
1076 { /* Fall through to next case is intentional. */ }
1077 else
1078 break;
1079 RT_FALL_THRU();
1080 }
1081
1082 case FsObjType_File:
1083 {
1084 if ( strFilter.isNotEmpty()
1085 && !RTStrSimplePatternMatch(strFilter.c_str(), strName.c_str()))
1086 {
1087 break; /* Filter does not match. */
1088 }
1089
1090 if (RT_SUCCESS(rc))
1091 {
1092 Utf8Str strSrcFile = strSrcDir + strSrcSubDir + strName;
1093 Utf8Str strDstFile = strDstDir + strSrcSubDir + strName;
1094 rc = fileCopyFromGuest(strSrcFile, strDstFile, FileCopyFlag_None);
1095 }
1096 break;
1097 }
1098
1099 default:
1100 break;
1101 }
1102 }
1103
1104 if (rc == VERR_NO_MORE_FILES) /* Reading done? */
1105 rc = VINF_SUCCESS;
1106
1107 pDir->i_closeInternal(&rcGuest);
1108
1109 LogFlowFunc(("Leaving '%s', rc=%Rrc\n", strSrcCur.c_str(), rc));
1110 return rc;
1111}
1112
1113int SessionTaskCopyDirFrom::Run(void)
1114{
1115 LogFlowThisFuncEnter();
1116
1117 AutoCaller autoCaller(mSession);
1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1119
1120 const bool fRecursive = true; /** @todo Make this configurable. */
1121 const bool fFollowSymlinks = true; /** @todo Make this configurable. */
1122 const uint32_t fDirMode = 0700; /* Play safe by default. */
1123
1124 int rc = VINF_SUCCESS;
1125
1126 /* Figure out if we need to copy the entire source directory or just its contents. */
1127 Utf8Str strSrcDir = mSource;
1128 Utf8Str strDstDir = mDest;
1129 if ( !strSrcDir.endsWith("/")
1130 && !strSrcDir.endsWith("\\"))
1131 {
1132 if ( !strDstDir.endsWith("/")
1133 && !strDstDir.endsWith("\\"))
1134 strDstDir += "/";
1135
1136 strDstDir += Utf8StrFmt("%s", RTPathFilename(strSrcDir.c_str()));
1137 }
1138
1139 /* Create the root target directory on the host.
1140 * The target directory might already exist on the host (based on u.Dir.fCopyFlags). */
1141 const bool fExists = RTDirExists(strDstDir.c_str());
1142 if ( fExists
1143 && !(u.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting))
1144 {
1145 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1146 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" exists when it must not"), strDstDir.c_str()));
1147 rc = VERR_ALREADY_EXISTS;
1148 }
1149 else if (!fExists)
1150 {
1151 rc = RTDirCreate(strDstDir.c_str(), fDirMode, 0);
1152 if (RT_FAILURE(rc))
1153 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1154 Utf8StrFmt(GuestSession::tr("Error creating destination directory \"%s\", rc=%Rrc"),
1155 strDstDir.c_str(), rc));
1156 }
1157
1158 if (RT_FAILURE(rc))
1159 return rc;
1160
1161 RTDIR hDir;
1162 rc = RTDirOpen(&hDir, strDstDir.c_str());
1163 if (RT_FAILURE(rc))
1164 {
1165 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1166 Utf8StrFmt(GuestSession::tr("Error opening destination directory \"%s\", rc=%Rrc"),
1167 strDstDir.c_str(), rc));
1168 return rc;
1169 }
1170
1171 /* At this point the directory on the host was created and (hopefully) is ready
1172 * to receive further content. */
1173 rc = directoryCopyToHost(strSrcDir, mFilter, strDstDir, fRecursive, fFollowSymlinks,
1174 "" /* strSubDir; for recursion */);
1175 if (RT_SUCCESS(rc))
1176 rc = setProgressSuccess();
1177
1178 RTDirClose(hDir);
1179
1180 LogFlowFuncLeaveRC(rc);
1181 return rc;
1182}
1183
1184SessionTaskCopyDirTo::SessionTaskCopyDirTo(GuestSession *pSession,
1185 const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter,
1186 DirectoryCopyFlag_T fDirCopyFlags)
1187 : GuestSessionCopyTask(pSession)
1188{
1189 m_strTaskName = "gctlCpyDirTo";
1190
1191 mSource = strSource;
1192 mDest = strDest;
1193 mFilter = strFilter;
1194
1195 u.Dir.fCopyFlags = fDirCopyFlags;
1196}
1197
1198SessionTaskCopyDirTo::~SessionTaskCopyDirTo(void)
1199{
1200
1201}
1202
1203/**
1204 * Copys a directory (tree) from host to the guest.
1205 *
1206 * @return IPRT status code.
1207 * @param strSource Source directory on the host to copy to the guest.
1208 * @param strFilter DOS-style wildcard filter (?, *). Optional.
1209 * @param strDest Destination directory on the guest.
1210 * @param fRecursive Whether to recursively copy the directory contents or not.
1211 * @param fFollowSymlinks Whether to follow symlinks or not.
1212 * @param strSubDir Current sub directory to handle. Needs to NULL and only
1213 * is needed for recursion.
1214 */
1215int SessionTaskCopyDirTo::directoryCopyToGuest(const Utf8Str &strSource, const Utf8Str &strFilter,
1216 const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
1217 const Utf8Str &strSubDir /* For recursion. */)
1218{
1219 Utf8Str strSrcDir = strSource;
1220 Utf8Str strDstDir = strDest;
1221 Utf8Str strSrcSubDir = strSubDir;
1222
1223 /* Validation and sanity. */
1224 if ( !strSrcDir.endsWith("/")
1225 && !strSrcDir.endsWith("\\"))
1226 strSrcDir += "/";
1227
1228 if ( !strDstDir.endsWith("/")
1229 && !strDstDir.endsWith("\\"))
1230 strDstDir+= "/";
1231
1232 if ( strSrcSubDir.isNotEmpty() /* Optional, for recursion. */
1233 && !strSrcSubDir.endsWith("/")
1234 && !strSrcSubDir.endsWith("\\"))
1235 strSrcSubDir += "/";
1236
1237 Utf8Str strSrcCur = strSrcDir + strSrcSubDir;
1238
1239 LogFlowFunc(("Entering '%s'\n", strSrcCur.c_str()));
1240
1241 int rc;
1242
1243 if (strSrcSubDir.isNotEmpty())
1244 {
1245 const uint32_t uMode = 0700; /** @todo */
1246 rc = directoryCreate(strDstDir + strSrcSubDir, DirectoryCreateFlag_Parents, uMode, fFollowSymlinks);
1247 if (RT_FAILURE(rc))
1248 return rc;
1249 }
1250
1251 /*
1252 * Open directory without a filter - RTDirOpenFiltered unfortunately
1253 * cannot handle sub directories so we have to do the filtering ourselves.
1254 */
1255 RTDIR hDir;
1256 rc = RTDirOpen(&hDir, strSrcCur.c_str());
1257 if (RT_SUCCESS(rc))
1258 {
1259 /*
1260 * Enumerate the directory tree.
1261 */
1262 size_t cbDirEntry = 0;
1263 PRTDIRENTRYEX pDirEntry = NULL;
1264 while (RT_SUCCESS(rc))
1265 {
1266 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1267 if (RT_FAILURE(rc))
1268 {
1269 if (rc == VERR_NO_MORE_FILES)
1270 rc = VINF_SUCCESS;
1271 break;
1272 }
1273
1274#ifdef LOG_ENABLED
1275 Utf8Str strDbgCurEntry = strSrcCur + Utf8Str(pDirEntry->szName);
1276 LogFlowFunc(("Handling '%s' (fMode=0x%x)\n", strDbgCurEntry.c_str(), pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK));
1277#endif
1278 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1279 {
1280 case RTFS_TYPE_DIRECTORY:
1281 {
1282 /* Skip "." and ".." entries. */
1283 if (RTDirEntryExIsStdDotLink(pDirEntry))
1284 break;
1285
1286 bool fSkip = false;
1287
1288 if ( strFilter.isNotEmpty()
1289 && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
1290 fSkip = true;
1291
1292 if ( fRecursive
1293 && !fSkip)
1294 rc = directoryCopyToGuest(strSrcDir, strFilter, strDstDir, fRecursive, fFollowSymlinks,
1295 strSrcSubDir + Utf8Str(pDirEntry->szName));
1296 break;
1297 }
1298
1299 case RTFS_TYPE_SYMLINK:
1300 if (fFollowSymlinks)
1301 { /* Fall through to next case is intentional. */ }
1302 else
1303 break;
1304 RT_FALL_THRU();
1305
1306 case RTFS_TYPE_FILE:
1307 {
1308 if ( strFilter.isNotEmpty()
1309 && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
1310 {
1311 break; /* Filter does not match. */
1312 }
1313
1314 if (RT_SUCCESS(rc))
1315 {
1316 Utf8Str strSrcFile = strSrcDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
1317 Utf8Str strDstFile = strDstDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
1318 rc = fileCopyToGuest(strSrcFile, strDstFile, FileCopyFlag_None);
1319 }
1320 break;
1321 }
1322
1323 default:
1324 break;
1325 }
1326 if (RT_FAILURE(rc))
1327 break;
1328 }
1329
1330 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1331 RTDirClose(hDir);
1332 }
1333
1334 LogFlowFunc(("Leaving '%s', rc=%Rrc\n", strSrcCur.c_str(), rc));
1335 return rc;
1336}
1337
1338int SessionTaskCopyDirTo::Run(void)
1339{
1340 LogFlowThisFuncEnter();
1341
1342 AutoCaller autoCaller(mSession);
1343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1344
1345 const bool fRecursive = true; /** @todo Make this configurable. */
1346 const bool fFollowSymlinks = true; /** @todo Make this configurable. */
1347 const uint32_t uDirMode = 0700; /* Play safe by default. */
1348
1349 /* Figure out if we need to copy the entire source directory or just its contents. */
1350 Utf8Str strSrcDir = mSource;
1351 Utf8Str strDstDir = mDest;
1352 if ( !strSrcDir.endsWith("/")
1353 && !strSrcDir.endsWith("\\"))
1354 {
1355 if ( !strDstDir.endsWith("/")
1356 && !strDstDir.endsWith("\\"))
1357 strDstDir += "/";
1358
1359 strDstDir += Utf8StrFmt("%s", RTPathFilename(strSrcDir.c_str()));
1360 }
1361
1362 /* Create the root target directory on the guest.
1363 * The target directory might already exist on the guest (based on u.Dir.fCopyFlags). */
1364 int rc = directoryCreate(strDstDir, DirectoryCreateFlag_None, uDirMode, fFollowSymlinks);
1365 if ( rc == VWRN_ALREADY_EXISTS
1366 && !(u.Dir.fCopyFlags & DirectoryCopyFlag_CopyIntoExisting))
1367 {
1368 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1369 Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" exists when it must not"), strDstDir.c_str()));
1370 }
1371
1372 if (RT_FAILURE(rc))
1373 return rc;
1374
1375 /* At this point the directory on the guest was created and (hopefully) is ready
1376 * to receive further content. */
1377 rc = directoryCopyToGuest(strSrcDir, mFilter, strDstDir, fRecursive, fFollowSymlinks,
1378 "" /* strSubDir; for recursion */);
1379 if (RT_SUCCESS(rc))
1380 rc = setProgressSuccess();
1381
1382 LogFlowFuncLeaveRC(rc);
1383 return rc;
1384}
1385
1386SessionTaskCopyFileTo::SessionTaskCopyFileTo(GuestSession *pSession,
1387 const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
1388 : GuestSessionCopyTask(pSession)
1389{
1390 m_strTaskName = "gctlCpyFileTo";
1391
1392 mSource = strSource;
1393 mDest = strDest;
1394
1395 u.File.fCopyFlags = fFileCopyFlags;
1396}
1397
1398SessionTaskCopyFileTo::~SessionTaskCopyFileTo(void)
1399{
1400
1401}
1402
1403int SessionTaskCopyFileTo::Run(void)
1404{
1405 LogFlowThisFuncEnter();
1406
1407 AutoCaller autoCaller(mSession);
1408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1409
1410 int rc = fileCopyToGuest(mSource, mDest, u.File.fCopyFlags);
1411 if (RT_SUCCESS(rc))
1412 rc = setProgressSuccess();
1413
1414 LogFlowFuncLeaveRC(rc);
1415 return rc;
1416}
1417
1418SessionTaskCopyFileFrom::SessionTaskCopyFileFrom(GuestSession *pSession,
1419 const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T fFileCopyFlags)
1420 : GuestSessionCopyTask(pSession)
1421{
1422 m_strTaskName = "gctlCpyFileFrm";
1423
1424 mSource = strSource;
1425 mDest = strDest;
1426
1427 u.File.fCopyFlags = fFileCopyFlags;
1428}
1429
1430SessionTaskCopyFileFrom::~SessionTaskCopyFileFrom(void)
1431{
1432
1433}
1434
1435int SessionTaskCopyFileFrom::Run(void)
1436{
1437 LogFlowThisFuncEnter();
1438
1439 AutoCaller autoCaller(mSession);
1440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1441
1442 int rc = fileCopyFromGuest(mSource, mDest, u.File.fCopyFlags);
1443 if (RT_SUCCESS(rc))
1444 rc = setProgressSuccess();
1445
1446 LogFlowFuncLeaveRC(rc);
1447 return rc;
1448}
1449
1450SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
1451 const Utf8Str &strSource,
1452 const ProcessArguments &aArguments,
1453 uint32_t fFlags)
1454 : GuestSessionTask(pSession)
1455{
1456 m_strTaskName = "gctlUpGA";
1457
1458 mSource = strSource;
1459 mArguments = aArguments;
1460 mFlags = fFlags;
1461}
1462
1463SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
1464{
1465
1466}
1467
1468int SessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
1469{
1470 int rc = VINF_SUCCESS;
1471
1472 try
1473 {
1474 /* Filter out arguments which already are in the destination to
1475 * not end up having them specified twice. Not the fastest method on the
1476 * planet but does the job. */
1477 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
1478 while (itSource != aArgumentsSource.end())
1479 {
1480 bool fFound = false;
1481 ProcessArguments::iterator itDest = aArgumentsDest.begin();
1482 while (itDest != aArgumentsDest.end())
1483 {
1484 if ((*itDest).equalsIgnoreCase((*itSource)))
1485 {
1486 fFound = true;
1487 break;
1488 }
1489 ++itDest;
1490 }
1491
1492 if (!fFound)
1493 aArgumentsDest.push_back((*itSource));
1494
1495 ++itSource;
1496 }
1497 }
1498 catch(std::bad_alloc &)
1499 {
1500 return VERR_NO_MEMORY;
1501 }
1502
1503 return rc;
1504}
1505
1506int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
1507 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
1508 bool fOptional)
1509{
1510 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1511 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
1512
1513 uint32_t cbSrcOffset;
1514 size_t cbSrcSize;
1515
1516 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbSrcOffset, &cbSrcSize);
1517 if (RT_FAILURE(rc))
1518 {
1519 if (fOptional)
1520 return VINF_SUCCESS;
1521
1522 return rc;
1523 }
1524
1525 Assert(cbSrcOffset);
1526 Assert(cbSrcSize);
1527 rc = RTFileSeek(pISO->file, cbSrcOffset, RTFILE_SEEK_BEGIN, NULL);
1528 if (RT_SUCCESS(rc))
1529 {
1530 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
1531 strFileSource.c_str(), strFileDest.c_str()));
1532
1533 GuestFileOpenInfo dstOpenInfo;
1534 RT_ZERO(dstOpenInfo);
1535 dstOpenInfo.mFileName = strFileDest;
1536 dstOpenInfo.mOpenAction = FileOpenAction_CreateOrReplace;
1537 dstOpenInfo.mAccessMode = FileAccessMode_WriteOnly;
1538 dstOpenInfo.mSharingMode = FileSharingMode_All; /** @todo Use _Read when implemented. */
1539
1540 ComObjPtr<GuestFile> dstFile; int rcGuest;
1541 rc = mSession->i_fileOpen(dstOpenInfo, dstFile, &rcGuest);
1542 if (RT_FAILURE(rc))
1543 {
1544 switch (rc)
1545 {
1546 case VERR_GSTCTL_GUEST_ERROR:
1547 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest));
1548 break;
1549
1550 default:
1551 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1552 Utf8StrFmt(GuestSession::tr("Destination file \"%s\" could not be opened: %Rrc"),
1553 strFileDest.c_str(), rc));
1554 break;
1555 }
1556 }
1557 else
1558 {
1559 rc = fileCopyToGuestInner(&pISO->file, dstFile, FileCopyFlag_None, cbSrcOffset, cbSrcSize);
1560
1561 int rc2 = dstFile->i_closeFile(&rcGuest);
1562 AssertRC(rc2);
1563 }
1564
1565 if (RT_FAILURE(rc))
1566 return rc;
1567 }
1568
1569 return rc;
1570}
1571
1572int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
1573{
1574 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1575
1576 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
1577
1578 GuestProcessTool procTool;
1579 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1580 int vrc = procTool.init(pSession, procInfo, false /* Async */, &rcGuest);
1581 if (RT_SUCCESS(vrc))
1582 {
1583 if (RT_SUCCESS(rcGuest))
1584 vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &rcGuest);
1585 if (RT_SUCCESS(vrc))
1586 vrc = procTool.terminatedOk();
1587 }
1588
1589 if (RT_FAILURE(vrc))
1590 {
1591 switch (vrc)
1592 {
1593 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
1594 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1595 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
1596 procInfo.mExecutable.c_str(), procTool.getRc()));
1597 break;
1598
1599 case VERR_GSTCTL_GUEST_ERROR:
1600 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1601 break;
1602
1603 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
1604 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1605 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
1606 procInfo.mExecutable.c_str()));
1607 break;
1608
1609 default:
1610 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1611 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
1612 procInfo.mExecutable.c_str(), vrc));
1613 break;
1614 }
1615 }
1616
1617 return vrc;
1618}
1619
1620int SessionTaskUpdateAdditions::Run(void)
1621{
1622 LogFlowThisFuncEnter();
1623
1624 ComObjPtr<GuestSession> pSession = mSession;
1625 Assert(!pSession.isNull());
1626
1627 AutoCaller autoCaller(pSession);
1628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1629
1630 int rc = setProgress(10);
1631 if (RT_FAILURE(rc))
1632 return rc;
1633
1634 HRESULT hr = S_OK;
1635
1636 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
1637
1638 ComObjPtr<Guest> pGuest(mSession->i_getParent());
1639#if 0
1640 /*
1641 * Wait for the guest being ready within 30 seconds.
1642 */
1643 AdditionsRunLevelType_T addsRunLevel;
1644 uint64_t tsStart = RTTimeSystemMilliTS();
1645 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1646 && ( addsRunLevel != AdditionsRunLevelType_Userland
1647 && addsRunLevel != AdditionsRunLevelType_Desktop))
1648 {
1649 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
1650 {
1651 rc = VERR_TIMEOUT;
1652 break;
1653 }
1654
1655 RTThreadSleep(100); /* Wait a bit. */
1656 }
1657
1658 if (FAILED(hr)) rc = VERR_TIMEOUT;
1659 if (rc == VERR_TIMEOUT)
1660 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1661 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
1662#else
1663 /*
1664 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
1665 * can continue.
1666 */
1667 AdditionsRunLevelType_T addsRunLevel;
1668 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1669 || ( addsRunLevel != AdditionsRunLevelType_Userland
1670 && addsRunLevel != AdditionsRunLevelType_Desktop))
1671 {
1672 if (addsRunLevel == AdditionsRunLevelType_System)
1673 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1674 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1675 else
1676 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1677 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1678 rc = VERR_NOT_SUPPORTED;
1679 }
1680#endif
1681
1682 if (RT_SUCCESS(rc))
1683 {
1684 /*
1685 * Determine if we are able to update automatically. This only works
1686 * if there are recent Guest Additions installed already.
1687 */
1688 Utf8Str strAddsVer;
1689 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1690 if ( RT_SUCCESS(rc)
1691 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1692 {
1693 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1694 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1695 strAddsVer.c_str()));
1696 rc = VERR_NOT_SUPPORTED;
1697 }
1698 }
1699
1700 Utf8Str strOSVer;
1701 eOSType osType = eOSType_Unknown;
1702 if (RT_SUCCESS(rc))
1703 {
1704 /*
1705 * Determine guest OS type and the required installer image.
1706 */
1707 Utf8Str strOSType;
1708 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1709 if (RT_SUCCESS(rc))
1710 {
1711 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1712 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1713 {
1714 osType = eOSType_Windows;
1715
1716 /*
1717 * Determine guest OS version.
1718 */
1719 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1720 if (RT_FAILURE(rc))
1721 {
1722 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1723 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1724 rc = VERR_NOT_SUPPORTED;
1725 }
1726
1727 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1728 * can't do automated updates here. */
1729 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1730 if ( RT_SUCCESS(rc)
1731 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1732 {
1733 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1734 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
1735 {
1736 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1737 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1738 * flag is set this update routine ends successfully as soon as the installer was started
1739 * (and the user has to deal with it in the guest). */
1740 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1741 {
1742 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1743 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1744 rc = VERR_NOT_SUPPORTED;
1745 }
1746 }
1747 }
1748 else
1749 {
1750 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1751 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
1752 strOSType.c_str(), strOSVer.c_str()));
1753 rc = VERR_NOT_SUPPORTED;
1754 }
1755 }
1756 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1757 {
1758 osType = eOSType_Solaris;
1759 }
1760 else /* Everything else hopefully means Linux :-). */
1761 osType = eOSType_Linux;
1762
1763#if 1 /* Only Windows is supported (and tested) at the moment. */
1764 if ( RT_SUCCESS(rc)
1765 && osType != eOSType_Windows)
1766 {
1767 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1768 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1769 strOSType.c_str()));
1770 rc = VERR_NOT_SUPPORTED;
1771 }
1772#endif
1773 }
1774 }
1775
1776 RTISOFSFILE iso;
1777 if (RT_SUCCESS(rc))
1778 {
1779 /*
1780 * Try to open the .ISO file to extract all needed files.
1781 */
1782 rc = RTIsoFsOpen(&iso, mSource.c_str());
1783 if (RT_FAILURE(rc))
1784 {
1785 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1786 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1787 mSource.c_str(), rc));
1788 }
1789 else
1790 {
1791 /* Set default installation directories. */
1792 Utf8Str strUpdateDir = "/tmp/";
1793 if (osType == eOSType_Windows)
1794 strUpdateDir = "C:\\Temp\\";
1795
1796 rc = setProgress(5);
1797
1798 /* Try looking up the Guest Additions installation directory. */
1799 if (RT_SUCCESS(rc))
1800 {
1801 /* Try getting the installed Guest Additions version to know whether we
1802 * can install our temporary Guest Addition data into the original installation
1803 * directory.
1804 *
1805 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1806 * a different location then.
1807 */
1808 bool fUseInstallDir = false;
1809
1810 Utf8Str strAddsVer;
1811 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1812 if ( RT_SUCCESS(rc)
1813 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1814 {
1815 fUseInstallDir = true;
1816 }
1817
1818 if (fUseInstallDir)
1819 {
1820 if (RT_SUCCESS(rc))
1821 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1822 if (RT_SUCCESS(rc))
1823 {
1824 if (osType == eOSType_Windows)
1825 {
1826 strUpdateDir.findReplace('/', '\\');
1827 strUpdateDir.append("\\Update\\");
1828 }
1829 else
1830 strUpdateDir.append("/update/");
1831 }
1832 }
1833 }
1834
1835 if (RT_SUCCESS(rc))
1836 LogRel(("Guest Additions update directory is: %s\n",
1837 strUpdateDir.c_str()));
1838
1839 /* Create the installation directory. */
1840 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1841 rc = pSession->i_directoryCreate(strUpdateDir, 755 /* Mode */, DirectoryCreateFlag_Parents, &rcGuest);
1842 if (RT_FAILURE(rc))
1843 {
1844 switch (rc)
1845 {
1846 case VERR_GSTCTL_GUEST_ERROR:
1847 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(rcGuest));
1848 break;
1849
1850 default:
1851 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1852 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1853 strUpdateDir.c_str(), rc));
1854 break;
1855 }
1856 }
1857 if (RT_SUCCESS(rc))
1858 rc = setProgress(10);
1859
1860 if (RT_SUCCESS(rc))
1861 {
1862 /* Prepare the file(s) we want to copy over to the guest and
1863 * (maybe) want to run. */
1864 switch (osType)
1865 {
1866 case eOSType_Windows:
1867 {
1868 /* Do we need to install our certificates? We do this for W2K and up. */
1869 bool fInstallCert = false;
1870
1871 /* Only Windows 2000 and up need certificates to be installed. */
1872 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1873 {
1874 fInstallCert = true;
1875 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1876 }
1877 else
1878 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1879
1880 if (fInstallCert)
1881 {
1882 static struct { const char *pszDst, *pszIso; } const s_aCertFiles[] =
1883 {
1884 { "vbox.cer", "CERT/VBOX.CER" },
1885 { "vbox-sha1.cer", "CERT/VBOX_SHA1.CER" },
1886 { "vbox-sha256.cer", "CERT/VBOX_SHA256.CER" },
1887 { "vbox-sha256-r3.cer", "CERT/VBOX_SHA256_R3.CER" },
1888 { "oracle-vbox.cer", "CERT/ORACLE_VBOX.CER" },
1889 };
1890 uint32_t fCopyCertUtil = ISOFILE_FLAG_COPY_FROM_ISO;
1891 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCertFiles); i++)
1892 {
1893 /* Skip if not present on the ISO. */
1894 uint32_t offIgn;
1895 size_t cbIgn;
1896 rc = RTIsoFsGetFileInfo(&iso, s_aCertFiles[i].pszIso, &offIgn, &cbIgn);
1897 if (RT_FAILURE(rc))
1898 continue;
1899
1900 /* Copy the certificate certificate. */
1901 Utf8Str const strDstCert(strUpdateDir + s_aCertFiles[i].pszDst);
1902 mFiles.push_back(ISOFile(s_aCertFiles[i].pszIso,
1903 strDstCert,
1904 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_OPTIONAL));
1905
1906 /* Out certificate installation utility. */
1907 /* First pass: Copy over the file (first time only) + execute it to remove any
1908 * existing VBox certificates. */
1909 GuestProcessStartupInfo siCertUtilRem;
1910 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
1911 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1912 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1913 siCertUtilRem.mArguments.push_back(strDstCert);
1914 siCertUtilRem.mArguments.push_back(strDstCert);
1915 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
1916 strUpdateDir + "VBoxCertUtil.exe",
1917 fCopyCertUtil | ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
1918 siCertUtilRem));
1919 fCopyCertUtil = 0;
1920 /* Second pass: Only execute (but don't copy) again, this time installng the
1921 * recent certificates just copied over. */
1922 GuestProcessStartupInfo siCertUtilAdd;
1923 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
1924 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1925 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1926 siCertUtilAdd.mArguments.push_back(strDstCert);
1927 siCertUtilAdd.mArguments.push_back(strDstCert);
1928 mFiles.push_back(ISOFile("CERT/VBOXCERTUTIL.EXE",
1929 strUpdateDir + "VBoxCertUtil.exe",
1930 ISOFILE_FLAG_EXECUTE | ISOFILE_FLAG_OPTIONAL,
1931 siCertUtilAdd));
1932 }
1933 }
1934 /* The installers in different flavors, as we don't know (and can't assume)
1935 * the guest's bitness. */
1936 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_X86.EXE",
1937 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1938 ISOFILE_FLAG_COPY_FROM_ISO));
1939 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1940 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1941 ISOFILE_FLAG_COPY_FROM_ISO));
1942 /* The stub loader which decides which flavor to run. */
1943 GuestProcessStartupInfo siInstaller;
1944 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1945 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
1946 * setup can take quite a while, so be on the safe side. */
1947 siInstaller.mTimeoutMS = 5 * 60 * 1000;
1948 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1949 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1950 /* Don't quit VBoxService during upgrade because it still is used for this
1951 * piece of code we're in right now (that is, here!) ... */
1952 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1953 /* Tell the installer to report its current installation status
1954 * using a running VBoxTray instance via balloon messages in the
1955 * Windows taskbar. */
1956 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1957 /* Add optional installer command line arguments from the API to the
1958 * installer's startup info. */
1959 rc = addProcessArguments(siInstaller.mArguments, mArguments);
1960 AssertRC(rc);
1961 /* If the caller does not want to wait for out guest update process to end,
1962 * complete the progress object now so that the caller can do other work. */
1963 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1964 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1965 mFiles.push_back(ISOFile("VBOXWINDOWSADDITIONS.EXE",
1966 strUpdateDir + "VBoxWindowsAdditions.exe",
1967 ISOFILE_FLAG_COPY_FROM_ISO | ISOFILE_FLAG_EXECUTE, siInstaller));
1968 break;
1969 }
1970 case eOSType_Linux:
1971 /** @todo Add Linux support. */
1972 break;
1973 case eOSType_Solaris:
1974 /** @todo Add Solaris support. */
1975 break;
1976 default:
1977 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
1978 break;
1979 }
1980 }
1981
1982 if (RT_SUCCESS(rc))
1983 {
1984 /* We want to spend 40% total for all copying operations. So roughly
1985 * calculate the specific percentage step of each copied file. */
1986 uint8_t uOffset = 20; /* Start at 20%. */
1987 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
1988
1989 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1990
1991 std::vector<ISOFile>::const_iterator itFiles = mFiles.begin();
1992 while (itFiles != mFiles.end())
1993 {
1994 if (itFiles->fFlags & ISOFILE_FLAG_COPY_FROM_ISO)
1995 {
1996 bool fOptional = false;
1997 if (itFiles->fFlags & ISOFILE_FLAG_OPTIONAL)
1998 fOptional = true;
1999 rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest, fOptional);
2000 if (RT_FAILURE(rc))
2001 {
2002 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2003 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
2004 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
2005 break;
2006 }
2007 }
2008
2009 rc = setProgress(uOffset);
2010 if (RT_FAILURE(rc))
2011 break;
2012 uOffset += uStep;
2013
2014 ++itFiles;
2015 }
2016 }
2017
2018 /* Done copying, close .ISO file. */
2019 RTIsoFsClose(&iso);
2020
2021 if (RT_SUCCESS(rc))
2022 {
2023 /* We want to spend 35% total for all copying operations. So roughly
2024 * calculate the specific percentage step of each copied file. */
2025 uint8_t uOffset = 60; /* Start at 60%. */
2026 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
2027
2028 LogRel(("Executing Guest Additions update files ...\n"));
2029
2030 std::vector<ISOFile>::iterator itFiles = mFiles.begin();
2031 while (itFiles != mFiles.end())
2032 {
2033 if (itFiles->fFlags & ISOFILE_FLAG_EXECUTE)
2034 {
2035 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
2036 if (RT_FAILURE(rc))
2037 break;
2038 }
2039
2040 rc = setProgress(uOffset);
2041 if (RT_FAILURE(rc))
2042 break;
2043 uOffset += uStep;
2044
2045 ++itFiles;
2046 }
2047 }
2048
2049 if (RT_SUCCESS(rc))
2050 {
2051 LogRel(("Automatic update of Guest Additions succeeded\n"));
2052 rc = setProgressSuccess();
2053 }
2054 }
2055 }
2056
2057 if (RT_FAILURE(rc))
2058 {
2059 if (rc == VERR_CANCELLED)
2060 {
2061 LogRel(("Automatic update of Guest Additions was canceled\n"));
2062
2063 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
2064 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
2065 }
2066 else
2067 {
2068 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
2069 if (!mProgress.isNull()) /* Progress object is optional. */
2070 {
2071 com::ProgressErrorInfo errorInfo(mProgress);
2072 if ( errorInfo.isFullAvailable()
2073 || errorInfo.isBasicAvailable())
2074 {
2075 strError = errorInfo.getText();
2076 }
2077 }
2078
2079 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
2080 strError.c_str(), hr));
2081 }
2082
2083 LogRel(("Please install Guest Additions manually\n"));
2084 }
2085
2086 /** @todo Clean up copied / left over installation files. */
2087
2088 LogFlowFuncLeaveRC(rc);
2089 return rc;
2090}
2091
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