VirtualBox

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

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

Guest Control/Main: Made (release) logging of paths a bit more uniform + some logging fixes.

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