VirtualBox

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

Last change on this file since 107512 was 107512, checked in by vboxsync, 4 months ago

src/VBox/Main/src-client/GuestSessionImplTasks.cpp: Fixed warning found by Parfait (uninitialized attributes). jiraref:VBP-1424

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

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