VirtualBox

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

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

Guest Control/Main: Bail out early from the file copy from/to inner methods if the size to copy is 0 (i.e. empty files).

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

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