VirtualBox

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

Last change on this file since 107663 was 107663, checked in by vboxsync, 6 weeks ago

src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp: Fixed warnings found by Parfait (change of signedness on implicit conversion). jiraref:VBP-1424

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

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