VirtualBox

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

Last change on this file since 98273 was 98272, checked in by vboxsync, 23 months ago

Main/Guest*: rc -> hrc/vrc. bugref:10223

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