VirtualBox

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

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

Main/Guest Control: Renamed GuestSession::i_getPathStyle() -> i_getGuestPathStyle() + added GuestSession::i_getHostPathStyle(), to emphasized what is what.

Fixed source path style in GuestSession::fileCopyToGuest(), GuestSession::directoryCopyToGuest() and GuestSession::copyToGuest(). ​bugref:10286 [build fix, forgot a file]

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