VirtualBox

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

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

Guest Control/Main: Fixed running out of context IDs when copying huge directory listings. Internally IGuestFile::i_closeFile() wasn't unregistering the file object from the guest session, only the public interface IGuestFile::close() did so. bugref:10286

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

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