VirtualBox

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

Last change on this file since 107968 was 107968, checked in by vboxsync, 4 weeks ago

Main: Marked the ARM64-based Linux Guest Additions installer optional in the automatic Guest Additions Updater code. This makes it possible to test (failing) older Guest Additions which don't support ARM64 on ARM64 guests. bugref:10734

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

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