VirtualBox

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

Last change on this file since 94935 was 94935, checked in by vboxsync, 2 years ago

Main/src-client/GuestSessionImplTasks.cpp: Adjust to the new rules wrt. to rc -> hrc,vrc usage, ​bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use