VirtualBox

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

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

Guest Control/Main: Check if destination/source entries in Copy[To|From] tasks are not empty. bugref:9320

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