VirtualBox

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

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

Guest Control/Main: Resolved a @todo: Removed the escaping stuff from GuestPath::Translate() and only remove (clean up) double slashes according to the destination's path style. Also, don't force any path conversions when source path style == dest path style, as this would lose (i.e. forcefully get converted) stuff which actually would be valid ('\' in UNIX paths, for instance). ​bugref:10286

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