VirtualBox

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

最後變更 在這個檔案從92822是 92822,由 vboxsync 提交於 3 年 前

Guest Control: Resolved a @todo: Implemented DirectoryCopyFlag_Recursive + DirectoryCopyFlag_FollowLinks and no longer do this implicitly internally.

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

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