VirtualBox

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

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

Guest Control/Main: Also support copying files from the guest to symbolic links on the host. bugref:10286

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