VirtualBox

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

Last change on this file since 71895 was 71849, checked in by vboxsync, 7 years ago

Guest Control/Main: Added more cancellation code to copyTo/copyFrom tasks.

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