VirtualBox

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

Last change on this file since 97344 was 97344, checked in by vboxsync, 3 years ago

Guest Control/Main: Fixes for copying to/from guests with mixed OSes. bugref:10286

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