VirtualBox

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

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

Guest Control/Main: Removed unused code.

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

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