VirtualBox

Changeset 45109 in vbox for trunk/src/VBox/Main/src-client


Ignore:
Timestamp:
Mar 20, 2013 4:41:00 PM (12 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
84401
Message:

GuestCtrl: More stuff for IGuestFile and IGuestSession handling (work in progress).

Location:
trunk/src/VBox/Main/src-client
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp

    r45076 r45109  
    189189        }
    190190
     191        case CALLBACKTYPE_FILE_NOTIFY:
     192        {
     193            pvData = (PCALLBACKDATA_FILE_NOTIFY)RTMemAllocZ(sizeof(CALLBACKDATA_FILE_NOTIFY));
     194            AssertPtrReturn(pvData, VERR_NO_MEMORY);
     195            cbData = sizeof(CALLBACKDATA_FILE_NOTIFY);
     196            break;
     197        }
     198
    191199        default:
    192200            AssertMsgFailed(("Unknown callback type specified (%ld)\n", enmType));
     
    220228        }
    221229
    222         case CALLBACKTYPE_FILE_READ:
    223         {
    224             PCALLBACKPAYLOAD_FILE_NOTIFY_READ pThis = (PCALLBACKPAYLOAD_FILE_NOTIFY_READ)pvData;
     230        case CALLBACKTYPE_FILE_NOTIFY:
     231        {
     232            PCALLBACKDATA_FILE_NOTIFY pThis = (PCALLBACKDATA_FILE_NOTIFY)pvData;
    225233            AssertPtr(pThis);
    226             if (pThis->pvData)
    227                 RTMemFree(pThis->pvData);
    228             break;
     234            switch (pThis->uType)
     235            {
     236                case GUEST_FILE_NOTIFYTYPE_READ:
     237                {
     238                    if (pThis->u.read.pvData)
     239                    {
     240                        RTMemFree(pThis->u.read.pvData);
     241                        pThis->u.read.pvData = NULL;
     242                    }
     243
     244                    break;
     245                }
     246
     247                default:
     248                    break;
     249            }
    229250        }
    230251
     
    264285            Assert(cbCallback == sizeof(CALLBACKDATA_SESSION_NOTIFY));
    265286
    266             pThis->uType   = pCB->uType;
    267             pThis->uResult = pCB->uResult;
     287            memcpy(pThis, pCB, sizeof(CALLBACKDATA_SESSION_NOTIFY));
    268288            break;
    269289        }
     
    275295            Assert(cbCallback == sizeof(CALLBACKDATA_PROC_STATUS));
    276296
    277             pThis->uFlags  = pCB->uFlags;
    278             pThis->uPID    = pCB->uPID;
    279             pThis->uStatus = pCB->uStatus;
     297            memcpy(pThis, pCB, sizeof(CALLBACKDATA_PROC_STATUS));
    280298            break;
    281299        }
     
    305323            Assert(cbCallback == sizeof(CALLBACKDATA_PROC_INPUT));
    306324
    307             pThis->uProcessed = pCB->uProcessed;
    308             pThis->uFlags     = pCB->uFlags;
    309             pThis->uPID       = pCB->uPID;
    310             pThis->uStatus    = pCB->uStatus;
    311             break;
    312         }
    313 
     325            memcpy(pThis, pCB, sizeof(CALLBACKDATA_PROC_INPUT));
     326            break;
     327        }
     328
     329        case CALLBACKTYPE_FILE_NOTIFY:
     330        {
     331            PCALLBACKDATA_FILE_NOTIFY pThis = (PCALLBACKDATA_FILE_NOTIFY)pvData;
     332            PCALLBACKDATA_FILE_NOTIFY pCB = (PCALLBACKDATA_FILE_NOTIFY)pvCallback;
     333            Assert(cbCallback == sizeof(CALLBACKDATA_FILE_NOTIFY));
     334
     335            memcpy(pThis, pCB, sizeof(CALLBACKDATA_FILE_NOTIFY));
     336
     337            switch (pThis->uType)
     338            {
     339                case GUEST_FILE_NOTIFYTYPE_READ:
     340                {
     341                    pThis->u.read.pvData   = RTMemAlloc(pCB->u.read.cbData);
     342                    AssertPtrReturn(pThis->u.read.pvData, VERR_NO_MEMORY);
     343                    memcpy(pThis->u.read.pvData, pCB->u.read.pvData, pCB->u.read.cbData);
     344                    pThis->u.read.cbData   = pCB->u.read.cbData;
     345                    break;
     346                }
     347
     348                default:
     349                    break;
     350            }
     351            break;
     352        }
     353
     354#if 0
    314355        case CALLBACKTYPE_FILE_OPEN:
    315356        {
     
    382423            break;
    383424        }
    384 
     425#endif
    385426        default:
    386427            AssertMsgFailed(("Callback type not supported (%ld)\n", mType));
     
    12871328}
    12881329
     1330void GuestObject::callbackDelete(GuestCtrlCallback *pCallback)
     1331{
     1332    if (pCallback)
     1333    {
     1334        delete pCallback;
     1335        pCallback = NULL;
     1336    }
     1337}
     1338
    12891339bool GuestObject::callbackExists(uint32_t uContextID)
    12901340{
     
    13061356    if (it != mObject.mCallbacks.end())
    13071357    {
    1308         delete it->second;
    13091358        mObject.mCallbacks.erase(it);
    13101359
  • trunk/src/VBox/Main/src-client/GuestFileImpl.cpp

    r44863 r45109  
    6161/////////////////////////////////////////////////////////////////////////////
    6262
     63/**
     64 * Initializes a file object but does *not* open the file on the guest
     65 * yet. This is done in the dedidcated openFile call.
     66 *
     67 * @return  IPRT status code.
     68 * @param   pConsole                Pointer to console object.
     69 * @param   pSession                Pointer to session object.
     70 * @param   uFileID                 Host-based file ID (part of the context ID).
     71 * @param   openInfo                File opening information.
     72 */
    6373int GuestFile::init(Console *pConsole, GuestSession *pSession, ULONG uFileID, const GuestFileOpenInfo &openInfo)
    6474{
     
    7686    if (RT_SUCCESS(vrc))
    7787    {
     88        mData.mID = 0;
    7889        mData.mInitialSize = 0;
    7990
     
    257268    {
    258269        case GUEST_DISCONNECTED:
    259             vrc = onGuestDisconnected(pCbCtx, pCallback, pSvcCb); /* Affects all callbacks. */
     270            vrc = onGuestDisconnected(pCbCtx, pSvcCb, pCallback); /* Affects all callbacks. */
    260271            break;
    261272
    262273        case GUEST_FILE_NOTIFY:
    263             vrc = onFileNotify(pCbCtx, pCallback, pSvcCb);
     274            vrc = onFileNotify(pCbCtx, pSvcCb, pCallback);
    264275            break;
    265276
     
    273284    LogFlowFuncLeaveRC(vrc);
    274285#endif
     286    return vrc;
     287}
     288
     289int GuestFile::closeFile(int *pGuestRc)
     290{
     291    LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFileName.c_str()));
     292
     293    /* Prepare HGCM call. */
     294    VBOXHGCMSVCPARM paParms[4];
     295    int i = 1; /* Context ID will be set in sendFileComannd(). */
     296    paParms[i++].setUInt32(mData.mID /* Guest file ID */);
     297
     298    int guestRc;
     299    int vrc = sendFileCommand(HOST_FILE_CLOSE, i, paParms, 30 * 1000 /* 30s timeout */,
     300                              &guestRc, NULL /* ppCallback */);
     301    if (pGuestRc)
     302        *pGuestRc = guestRc;
     303
     304    LogFlowFuncLeaveRC(vrc);
    275305    return vrc;
    276306}
     
    309339}
    310340
    311 int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
    312 {
    313     AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
     341/* static */
     342Utf8Str GuestFile::guestErrorToString(int guestRc)
     343{
     344    Utf8Str strError;
     345
     346    /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
     347    switch (guestRc)
     348    {
     349        case VERR_INVALID_VM_HANDLE:
     350            strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
     351            break;
     352
     353        case VERR_HGCM_SERVICE_NOT_FOUND:
     354            strError += Utf8StrFmt(tr("The guest execution service is not available"));
     355            break;
     356
     357        case VERR_TIMEOUT:
     358            strError += Utf8StrFmt(tr("The guest did not respond within time"));
     359            break;
     360
     361        case VERR_CANCELLED:
     362            strError += Utf8StrFmt(tr("The session operation was canceled"));
     363            break;
     364
     365        case VERR_MAX_PROCS_REACHED:
     366            strError += Utf8StrFmt(tr("Maximum number of concurrent guest files has been reached"));
     367            break;
     368
     369        case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
     370            strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
     371            break;
     372
     373        case VERR_NOT_FOUND:
     374            strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
     375            break;
     376
     377        default:
     378            strError += Utf8StrFmt("%Rrc", guestRc);
     379            break;
     380    }
     381
     382    return strError;
     383}
     384
     385int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData,
     386                            GuestCtrlCallback *pCallback)
     387{
     388    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
    314389    AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
     390    /* pCallback is optional. */
    315391
    316392    if (pSvcCbData->mParms < 3)
    317393        return VERR_INVALID_PARAMETER;
    318394
    319     uint32_t uType;
    320     void *pvData; uint32_t cbData;
     395    int vrc = VINF_SUCCESS;
     396
     397    int idx = 0; /* Current parameter index. */
     398    CALLBACKDATA_FILE_NOTIFY dataCb;
    321399    /* pSvcCb->mpaParms[0] always contains the context ID. */
    322     pSvcCbData->mpaParms[1].getUInt32(&uType);
    323     pSvcCbData->mpaParms[2].getPointer(&pvData, &cbData);
    324 
    325     LogFlowThisFunc(("strName=%s, uType=%RU32, pvData=%p, cbData=%RU32, pCallback=%p\n",
    326                      mData.mOpenInfo.mFileName.c_str(), uType, pvData, cbData, pCallback));
     400    pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.uType);
     401    pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.rc);
     402
     403    switch (dataCb.uType)
     404    {
     405        case GUEST_FILE_NOTIFYTYPE_ERROR:
     406            /* No extra data. */
     407            break;
     408
     409        case GUEST_FILE_NOTIFYTYPE_OPEN:
     410            if (pSvcCbData->mParms == 4)
     411            {
     412                pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.open.uHandle);
     413
     414                AssertMsg(mData.mID == 0, ("File ID already set to %RU32\n", mData.mID));
     415                mData.mID = dataCb.u.open.uHandle;
     416                AssertMsg(mData.mID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID),
     417                          ("File ID %RU32 does not match context ID %RU32\n", mData.mID,
     418                           VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
     419            }
     420            else
     421                vrc = VERR_NOT_SUPPORTED;
     422            break;
     423
     424        case GUEST_FILE_NOTIFYTYPE_CLOSE:
     425            /* No extra data. */
     426            break;
     427
     428        case GUEST_FILE_NOTIFYTYPE_READ:
     429            if (pSvcCbData->mParms == 4)
     430            {
     431                pSvcCbData->mpaParms[idx++].getPointer(&dataCb.u.read.pvData,
     432                                                       &dataCb.u.read.cbData);
     433
     434                mData.mOffCurrent += dataCb.u.read.cbData;
     435            }
     436            else
     437                vrc = VERR_NOT_SUPPORTED;
     438            break;
     439
     440        case GUEST_FILE_NOTIFYTYPE_WRITE:
     441            if (pSvcCbData->mParms == 4)
     442            {
     443                pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.write.cbWritten);
     444
     445                mData.mOffCurrent += dataCb.u.write.cbWritten;
     446            }
     447            else
     448                vrc = VERR_NOT_SUPPORTED;
     449            break;
     450
     451        case GUEST_FILE_NOTIFYTYPE_SEEK:
     452            if (pSvcCbData->mParms == 4)
     453            {
     454                pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.seek.uOffActual);
     455
     456                mData.mOffCurrent = dataCb.u.seek.uOffActual;
     457            }
     458            else
     459                vrc = VERR_NOT_SUPPORTED;
     460            break;
     461
     462        case GUEST_FILE_NOTIFYTYPE_TELL:
     463            if (pSvcCbData->mParms == 4)
     464            {
     465                pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.tell.uOffActual);
     466
     467                mData.mOffCurrent = dataCb.u.tell.uOffActual;
     468            }
     469            else
     470                vrc = VERR_NOT_SUPPORTED;
     471            break;
     472
     473        default:
     474            vrc = VERR_NOT_SUPPORTED;
     475            break;
     476    }
     477
     478    LogFlowThisFunc(("strName=%s, uType=%RU32, rc=%Rrc, pCallback=%p\n",
     479                     mData.mOpenInfo.mFileName.c_str(), dataCb.uType, dataCb.rc, pCallback));
     480
     481    int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
     482    if (RT_SUCCESS(vrc))
     483    {
     484        /* Nothing to do here yet. */
     485    }
     486    else if (vrc == VERR_NOT_SUPPORTED)
     487    {
     488        /* Also let the callback know. */
     489        guestRc = VERR_NOT_SUPPORTED;
     490    }
    327491
    328492    /* Signal callback in every case (if available). */
    329     int vrc = VINF_SUCCESS;
    330493    if (pCallback)
    331494    {
    332         vrc = pCallback->SetData(pvData, cbData);
    333 
    334         int rc2 = pCallback->Signal();
     495        int rc2 = pCallback->SetData(&dataCb, sizeof(dataCb));
    335496        if (RT_SUCCESS(vrc))
    336497            vrc = rc2;
     498        rc2 = pCallback->Signal(guestRc);
     499        if (RT_SUCCESS(vrc))
     500            vrc = rc2;
    337501    }
    338502
     
    341505}
    342506
    343 int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
    344 {
    345     AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
    346 
    347     LogFlowThisFunc(("strFile=%s, pCallback=%p\n", mData.mOpenInfo.mFileName.c_str(), pCallback));
     507int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData,
     508                                   GuestCtrlCallback *pCallback)
     509{
     510    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
     511    AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
     512    /* pCallback is optional. */
     513
     514    LogFlowThisFunc(("strFile=%s, pCallback=%p\n",
     515                     mData.mOpenInfo.mFileName.c_str(), pCallback));
    348516
    349517    /* First, signal callback in every case. */
     
    364532                     mData.mOpenInfo.mDisposition.c_str(), mData.mOpenInfo.mCreationMode));
    365533
    366     /* Wait until the caller function (if kicked off by a thread)
    367      * has returned and continue operation. */
     534    /* Prepare HGCM call. */
     535    VBOXHGCMSVCPARM paParms[8];
     536    int i = 1; /* Context ID will be set in sendFileComannd(). */
     537    paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(),
     538                            (ULONG)mData.mOpenInfo.mFileName.length() + 1);
     539    paParms[i++].setPointer((void*)mData.mOpenInfo.mOpenMode.c_str(),
     540                            (ULONG)mData.mOpenInfo.mOpenMode.length() + 1);
     541    paParms[i++].setPointer((void*)mData.mOpenInfo.mDisposition.c_str(),
     542                            (ULONG)mData.mOpenInfo.mDisposition.length() + 1);
     543    paParms[i++].setUInt32(mData.mOpenInfo.mCreationMode);
     544    paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset);
     545
     546    int vrc = sendFileCommand(HOST_FILE_OPEN, i, paParms, 30 * 1000 /* 30s timeout */,
     547                              pGuestRc, NULL /* ppCallback */);
     548
     549    LogFlowFuncLeaveRC(vrc);
     550    return vrc;
     551}
     552
     553int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS, void *pvData, size_t cbData,
     554                        size_t *pcbRead, int *pGuestRc)
     555{
     556    LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
     557                     uSize, uTimeoutMS, pvData, cbData));
     558
     559    /* Prepare HGCM call. */
     560    VBOXHGCMSVCPARM paParms[4];
     561    int i = 1; /* Context ID will be set in sendFileComannd(). */
     562    paParms[i++].setUInt32(mData.mID /* File handle */);
     563    paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
     564
     565    GuestCtrlCallback *pCallback = NULL; int guestRc;
     566    int vrc = sendFileCommand(HOST_FILE_READ, i, paParms, uTimeoutMS,
     567                              &guestRc, &pCallback);
     568
     569    if (RT_SUCCESS(vrc))
     570    {
     571        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     572
     573        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
     574        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
     575        AssertPtr(pData);
     576        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_READ);
     577
     578        size_t cbRead = pData->u.read.cbData;
     579        if (cbRead)
     580        {
     581            Assert(cbData >= cbRead);
     582            memcpy(pvData, pData->u.read.pvData, cbRead);
     583        }
     584
     585        LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
     586
     587        if (pcbRead)
     588            *pcbRead = cbRead;
     589    }
     590
     591    callbackDelete(pCallback);
     592
     593    if (pGuestRc)
     594        *pGuestRc = guestRc;
     595
     596    LogFlowFuncLeaveRC(vrc);
     597    return vrc;
     598}
     599
     600int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
     601                          void *pvData, size_t cbData,
     602                          size_t *pcbRead, int *pGuestRc)
     603{
     604    LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
     605                     uOffset, uSize, uTimeoutMS, pvData, cbData));
     606
     607    /* Prepare HGCM call. */
     608    VBOXHGCMSVCPARM paParms[4];
     609    int i = 1; /* Context ID will be set in sendFileComannd(). */
     610    paParms[i++].setUInt32(mData.mID /* File handle */);
     611    paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
     612    paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
     613
     614    GuestCtrlCallback *pCallback = NULL; int guestRc;
     615    int vrc = sendFileCommand(HOST_FILE_READ_AT, i, paParms, uTimeoutMS,
     616                              &guestRc, &pCallback);
     617
     618    if (RT_SUCCESS(vrc))
     619    {
     620        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     621
     622        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
     623        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
     624        AssertPtr(pData);
     625        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_READ);
     626
     627        size_t cbRead = pData->u.read.cbData;
     628        if (cbRead)
     629        {
     630            Assert(cbData >= cbRead);
     631            memcpy(pvData, pData->u.read.pvData, cbRead);
     632        }
     633
     634        LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
     635
     636        if (pcbRead)
     637            *pcbRead = cbRead;
     638    }
     639
     640    callbackDelete(pCallback);
     641
     642    if (pGuestRc)
     643        *pGuestRc = guestRc;
     644
     645    LogFlowFuncLeaveRC(vrc);
     646    return vrc;
     647}
     648
     649int GuestFile::seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType,
     650                      uint32_t uTimeoutMS, int *pGuestRc)
     651{
     652    LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32\n",
     653                     uOffset, uTimeoutMS));
     654
     655    /* Prepare HGCM call. */
     656    VBOXHGCMSVCPARM paParms[4];
     657    int i = 1; /* Context ID will be set in sendFileComannd(). */
     658    paParms[i++].setUInt32(mData.mID /* File handle */);
     659    paParms[i++].setUInt32(eSeekType /* Seek method */);
     660    paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
     661
     662    int guestRc;
     663    int vrc = sendFileCommand(HOST_FILE_SEEK, i, paParms, uTimeoutMS,
     664                              &guestRc, NULL /* ppCallback */);
     665    if (pGuestRc)
     666        *pGuestRc = guestRc;
     667
     668    LogFlowFuncLeaveRC(vrc);
     669    return vrc;
     670}
     671
     672/**
     673 * Handles the common parts of sending a file command to the guest.
     674 * If ppCallback is returned it must be removed via callbackRemove()
     675 * by the caller in any case.
     676 *
     677 * @return  IPRT status code.
     678 * @param   uFunction               HGCM function of command to send.
     679 * @param   uParms                  Number of HGCM parameters to send.
     680 *                                  At least one parameter must be present.
     681 * @param   paParms                 Array of HGCM parameters to send.
     682 *                                  Index [0] must not be used and will be
     683 *                                  filled out by the function.
     684 * @param   uTimeoutMS              Timeout (in ms).
     685 * @param   pGuestRc                Guest result. Optional.
     686 * @param   ppCallback              Pointer which will receive the callback for
     687 *                                  further processing by the caller. Must
     688 *                                  be deleted with callbackDelete() when done. Optional.
     689 */
     690int GuestFile::sendFileCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
     691                               uint32_t uTimeoutMS, int *pGuestRc, GuestCtrlCallback **ppCallback)
     692{
     693    AssertReturn(uParms, VERR_INVALID_PARAMETER);
     694    AssertPtrReturn(paParms, VERR_INVALID_POINTER);
     695    /** pGuestRc is optional. */
     696    /** ppCallback is optional. */
     697
     698    LogFlowThisFunc(("strFile=%s, uFunction=%RU32, uParms=%RU32\n",
     699                     mData.mOpenInfo.mFileName.c_str(), uFunction, uParms));
     700
    368701    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    369702
     
    376709    uint32_t uContextID = 0;
    377710
    378     GuestCtrlCallback *pCallbackOpen;
     711    GuestCtrlCallback *pCallback;
    379712    try
    380713    {
    381         pCallbackOpen = new GuestCtrlCallback();
     714        pCallback = new GuestCtrlCallback();
    382715    }
    383716    catch(std::bad_alloc &)
     
    389722    {
    390723        /* Create callback and add it to the map. */
    391         vrc = pCallbackOpen->Init(CALLBACKTYPE_FILE_OPEN);
     724        vrc = pCallback->Init(CALLBACKTYPE_FILE_NOTIFY);
    392725        if (RT_SUCCESS(vrc))
    393             vrc = callbackAdd(pCallbackOpen, &uContextID);
     726            vrc = callbackAdd(pCallback, &uContextID);
    394727    }
    395728
    396729    if (RT_SUCCESS(vrc))
    397730    {
     731        /* Assign context ID. */
     732        paParms[0].setUInt32(uContextID);
     733
    398734        GuestSession *pSession = mData.mSession;
    399735        AssertPtr(pSession);
    400736
    401         const GuestCredentials &sessionCreds = pSession->getCredentials();
    402 
    403         if (RT_SUCCESS(vrc))
    404         {
    405             /* Prepare HGCM call. */
    406             VBOXHGCMSVCPARM paParms[8];
    407             int i = 0;
    408             paParms[i++].setUInt32(uContextID);
    409             paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(),
    410                                     (ULONG)mData.mOpenInfo.mFileName.length() + 1);
    411             paParms[i++].setPointer((void*)mData.mOpenInfo.mOpenMode.c_str(),
    412                                     (ULONG)mData.mOpenInfo.mOpenMode.length() + 1);
    413             paParms[i++].setPointer((void*)mData.mOpenInfo.mDisposition.c_str(),
    414                                     (ULONG)mData.mOpenInfo.mDisposition.length() + 1);
    415             paParms[i++].setUInt32(mData.mOpenInfo.mCreationMode);
    416             paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset);
    417 
    418             /* Note: Don't hold the write lock in here. */
    419             vrc = sendCommand(HOST_FILE_OPEN, i, paParms);
    420         }
    421 
    422         /* Drop the write lock again before waiting. */
    423         alock.release();
     737        alock.release(); /* Drop the write lock again. */
     738
     739        /* Note: Don't hold the write lock in here. */
     740        vrc = sendCommand(uFunction, uParms, paParms);
    424741
    425742        if (RT_SUCCESS(vrc))
     
    429746             * Note: Be sure not keeping a AutoRead/WriteLock here.
    430747             */
    431             LogFlowThisFunc(("Waiting for callback (30s) ...\n"));
    432             vrc = pCallbackOpen->Wait(30 *  1000 /* Wait 30s max. */);
     748            LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n",
     749                             uTimeoutMS));
     750            vrc = pCallback->Wait(uTimeoutMS);
    433751            if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
    434752            {
    435                 int guestRc = pCallbackOpen->GetResultCode();
     753                int guestRc = pCallback->GetResultCode();
    436754                if (RT_SUCCESS(guestRc))
    437755                {
    438 
     756                    /* Nothing to do here yet. */
    439757                }
     758                else
     759                    vrc = VERR_GSTCTL_GUEST_ERROR;
    440760
    441761                if (pGuestRc)
     
    443763                LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
    444764            }
    445             else
    446                 vrc = VERR_TIMEOUT;
    447765        }
    448766
    449         AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
    450 
    451         AssertPtr(pCallbackOpen);
     767        alock.acquire(); /* Get write lock again. */
     768
     769        AssertPtr(pCallback);
    452770        int rc2 = callbackRemove(uContextID);
    453771        if (RT_SUCCESS(vrc))
    454772            vrc = rc2;
    455     }
     773
     774        if (ppCallback)
     775        {
     776            /* Return callback to the caller which then will be
     777             * responsible for removing it. Don't forget to lock write
     778             * access before using this callback then! */
     779            *ppCallback = pCallback;
     780        }
     781        else
     782        {
     783            delete pCallback;
     784        }
     785    }
     786
     787    LogFlowFuncLeaveRC(vrc);
     788    return vrc;
     789}
     790
     791/* static */
     792HRESULT GuestFile::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
     793{
     794    AssertPtr(pInterface);
     795    AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
     796
     797    return pInterface->setError(VBOX_E_IPRT_ERROR, GuestFile::guestErrorToString(guestRc).c_str());
     798}
     799
     800int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, size_t cbData,
     801                         uint32_t *pcbWritten, int *pGuestRc)
     802{
     803    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
     804    AssertReturn(cbData, VERR_INVALID_PARAMETER);
     805
     806    LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
     807                     uTimeoutMS, pvData, cbData));
     808
     809    /* Prepare HGCM call. */
     810    VBOXHGCMSVCPARM paParms[4];
     811    int i = 1; /* Context ID will be set in sendFileComannd(). */
     812    paParms[i++].setUInt32(mData.mID /* File handle */);
     813    paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
     814    paParms[i++].setPointer(pvData, cbData);
     815
     816    GuestCtrlCallback *pCallback = NULL; int guestRc;
     817    int vrc = sendFileCommand(HOST_FILE_WRITE, i, paParms, uTimeoutMS,
     818                              &guestRc, &pCallback);
     819
     820    if (RT_SUCCESS(vrc))
     821    {
     822        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     823
     824        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
     825        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
     826        AssertPtr(pData);
     827        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_WRITE);
     828
     829        size_t cbWritten = pData->u.write.cbWritten;
     830        LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
     831
     832        if (pcbWritten)
     833            *pcbWritten = cbWritten;
     834    }
     835
     836    callbackDelete(pCallback);
     837
     838    if (pGuestRc)
     839        *pGuestRc = guestRc;
     840
     841    LogFlowFuncLeaveRC(vrc);
     842    return vrc;
     843}
     844
     845int GuestFile::writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
     846                           void *pvData, size_t cbData,
     847                           uint32_t *pcbWritten, int *pGuestRc)
     848{
     849    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
     850    AssertReturn(cbData, VERR_INVALID_PARAMETER);
     851
     852    LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
     853                     uOffset, uTimeoutMS, pvData, cbData));
     854
     855    /* Prepare HGCM call. */
     856    VBOXHGCMSVCPARM paParms[4];
     857    int i = 1; /* Context ID will be set in sendFileComannd(). */
     858    paParms[i++].setUInt32(mData.mID /* File handle */);
     859    paParms[i++].setUInt64(uOffset /* Offset where to starting writing */);
     860    paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
     861    paParms[i++].setPointer(pvData, cbData);
     862
     863    GuestCtrlCallback *pCallback = NULL; int guestRc;
     864    int vrc = sendFileCommand(HOST_FILE_WRITE_AT, i, paParms, uTimeoutMS,
     865                              &guestRc, &pCallback);
     866
     867    if (RT_SUCCESS(vrc))
     868    {
     869        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     870
     871        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
     872        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
     873        AssertPtr(pData);
     874        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_WRITE);
     875
     876        size_t cbWritten = pData->u.write.cbWritten;
     877        LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
     878
     879        if (pcbWritten)
     880            *pcbWritten = cbWritten;
     881    }
     882
     883    callbackDelete(pCallback);
     884
     885    if (pGuestRc)
     886        *pGuestRc = guestRc;
    456887
    457888    LogFlowFuncLeaveRC(vrc);
     
    472903    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    473904
     905    /* Close file on guest. */
     906    int guestRc;
     907    int rc = closeFile(&guestRc);
     908    /* On failure don't return here, instead do all the cleanup
     909     * work first and then return an error. */
     910
    474911    AssertPtr(mData.mSession);
    475     int rc = mData.mSession->fileRemoveFromList(this);
     912    int rc2 = mData.mSession->fileRemoveFromList(this);
     913    if (RT_SUCCESS(rc))
     914        rc = rc2;
    476915
    477916    /*
     
    483922
    484923    LogFlowFuncLeaveRC(rc);
     924    if (RT_FAILURE(rc))
     925    {
     926        if (rc == VERR_GSTCTL_GUEST_ERROR)
     927            return GuestFile::setErrorExternal(this, guestRc);
     928
     929        return setError(VBOX_E_IPRT_ERROR,
     930                        tr("Closing guest file failed with %Rrc\n"), rc);
     931    }
     932
    485933    return S_OK;
    486934#endif /* VBOX_WITH_GUEST_CONTROL */
     
    504952    ReturnComNotImplemented();
    505953#else
    506     AutoCaller autoCaller(this);
    507     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    508 
    509     ReturnComNotImplemented();
     954    if (aToRead == 0)
     955        return setError(E_INVALIDARG, tr("The size to read is zero"));
     956    CheckComArgOutSafeArrayPointerValid(aData);
     957
     958    AutoCaller autoCaller(this);
     959    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     960
     961    com::SafeArray<BYTE> data((size_t)aToRead);
     962    Assert(data.size() >= aToRead);
     963
     964    HRESULT hr = S_OK;
     965
     966    size_t cbRead; int guestRc;
     967    int vrc = readData(aToRead, aTimeoutMS,
     968                       data.raw(), aToRead, &cbRead, &guestRc);
     969    if (RT_SUCCESS(vrc))
     970    {
     971        if (data.size() != cbRead)
     972            data.resize(cbRead);
     973        data.detachTo(ComSafeArrayOutArg(aData));
     974    }
     975    else
     976    {
     977        switch (vrc)
     978        {
     979            case VERR_GSTCTL_GUEST_ERROR:
     980                hr = GuestFile::setErrorExternal(this, guestRc);
     981                break;
     982
     983            default:
     984                hr = setError(VBOX_E_IPRT_ERROR,
     985                              tr("Reading from file \"%s\" failed: %Rrc"),
     986                              mData.mOpenInfo.mFileName.c_str(), vrc);
     987                break;
     988        }
     989    }
     990
     991    LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64\n", vrc, cbRead));
     992
     993    LogFlowFuncLeaveRC(vrc);
     994    return hr;
    510995#endif /* VBOX_WITH_GUEST_CONTROL */
    511996}
     
    5161001    ReturnComNotImplemented();
    5171002#else
    518     AutoCaller autoCaller(this);
    519     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    520 
    521     ReturnComNotImplemented();
     1003    if (aToRead == 0)
     1004        return setError(E_INVALIDARG, tr("The size to read is zero"));
     1005    CheckComArgOutSafeArrayPointerValid(aData);
     1006
     1007    AutoCaller autoCaller(this);
     1008    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     1009
     1010    com::SafeArray<BYTE> data((size_t)aToRead);
     1011    Assert(data.size() >= aToRead);
     1012
     1013    HRESULT hr = S_OK;
     1014
     1015    size_t cbRead; int guestRc;
     1016    int vrc = readDataAt(aOffset, aToRead, aTimeoutMS,
     1017                         data.raw(), aToRead, &cbRead, &guestRc);
     1018    if (RT_SUCCESS(vrc))
     1019    {
     1020        if (data.size() != cbRead)
     1021            data.resize(cbRead);
     1022        data.detachTo(ComSafeArrayOutArg(aData));
     1023    }
     1024    else
     1025    {
     1026        switch (vrc)
     1027        {
     1028            case VERR_GSTCTL_GUEST_ERROR:
     1029                hr = GuestFile::setErrorExternal(this, guestRc);
     1030                break;
     1031
     1032            default:
     1033                hr = setError(VBOX_E_IPRT_ERROR,
     1034                              tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"),
     1035                              mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
     1036                break;
     1037        }
     1038    }
     1039
     1040    LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64\n", vrc, cbRead));
     1041
     1042    LogFlowFuncLeaveRC(vrc);
     1043    return hr;
    5221044#endif /* VBOX_WITH_GUEST_CONTROL */
    5231045}
     
    5281050    ReturnComNotImplemented();
    5291051#else
    530     AutoCaller autoCaller(this);
    531     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    532 
    533     ReturnComNotImplemented();
     1052    LogFlowThisFuncEnter();
     1053
     1054    AutoCaller autoCaller(this);
     1055    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     1056
     1057    HRESULT hr = S_OK;
     1058
     1059    GUEST_FILE_SEEKTYPE eSeekType;
     1060    switch (aType)
     1061    {
     1062        case FileSeekType_Set:
     1063            eSeekType = GUEST_FILE_SEEKTYPE_BEGIN;
     1064            break;
     1065
     1066        case FileSeekType_Current:
     1067            eSeekType = GUEST_FILE_SEEKTYPE_CURRENT;
     1068            break;
     1069
     1070        default:
     1071            return setError(E_INVALIDARG, tr("Invalid seek type specified"));
     1072            break;
     1073    }
     1074
     1075    int guestRc;
     1076    int vrc = seekAt(aOffset, eSeekType,
     1077                     30 * 1000 /* 30s timeout */, &guestRc);
     1078    if (RT_FAILURE(vrc))
     1079    {
     1080        switch (vrc)
     1081        {
     1082            case VERR_GSTCTL_GUEST_ERROR:
     1083                hr = GuestFile::setErrorExternal(this, guestRc);
     1084                break;
     1085
     1086            default:
     1087                hr = setError(VBOX_E_IPRT_ERROR,
     1088                              tr("Seeking file \"%s\" (to offset %RU64) failed: %Rrc"),
     1089                              mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
     1090                break;
     1091        }
     1092    }
     1093
     1094    LogFlowFuncLeaveRC(vrc);
     1095    return hr;
    5341096#endif /* VBOX_WITH_GUEST_CONTROL */
    5351097}
     
    5521114    ReturnComNotImplemented();
    5531115#else
    554     AutoCaller autoCaller(this);
    555     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    556 
    557     ReturnComNotImplemented();
     1116    LogFlowThisFuncEnter();
     1117
     1118    CheckComArgOutPointerValid(aWritten);
     1119
     1120    AutoCaller autoCaller(this);
     1121    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     1122
     1123    HRESULT hr = S_OK;
     1124
     1125    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); int guestRc;
     1126    int vrc = writeData(aTimeoutMS, data.raw(), data.size(), (uint32_t*)aWritten, &guestRc);
     1127    if (RT_FAILURE(vrc))
     1128    {
     1129        switch (vrc)
     1130        {
     1131            case VERR_GSTCTL_GUEST_ERROR:
     1132                hr = GuestFile::setErrorExternal(this, guestRc);
     1133                break;
     1134
     1135            default:
     1136                hr = setError(VBOX_E_IPRT_ERROR,
     1137                              tr("Writing to file \"%s\" failed: %Rrc"),
     1138                              mData.mOpenInfo.mFileName.c_str(), vrc);
     1139                break;
     1140        }
     1141    }
     1142
     1143    LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, aWritten));
     1144
     1145    LogFlowFuncLeaveRC(vrc);
     1146    return hr;
    5581147#endif /* VBOX_WITH_GUEST_CONTROL */
    5591148}
     
    5641153    ReturnComNotImplemented();
    5651154#else
    566     AutoCaller autoCaller(this);
    567     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    568 
    569     ReturnComNotImplemented();
    570 #endif /* VBOX_WITH_GUEST_CONTROL */
    571 }
    572 
     1155    LogFlowThisFuncEnter();
     1156
     1157    CheckComArgOutPointerValid(aWritten);
     1158
     1159    AutoCaller autoCaller(this);
     1160    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     1161
     1162    HRESULT hr = S_OK;
     1163
     1164    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); int guestRc;
     1165    int vrc = writeData(aTimeoutMS, data.raw(), data.size(), (uint32_t*)aWritten, &guestRc);
     1166    if (RT_FAILURE(vrc))
     1167    {
     1168        switch (vrc)
     1169        {
     1170            case VERR_GSTCTL_GUEST_ERROR:
     1171                hr = GuestFile::setErrorExternal(this, guestRc);
     1172                break;
     1173
     1174            default:
     1175                hr = setError(VBOX_E_IPRT_ERROR,
     1176                              tr("Writing to file \"%s\" (at offset %RU64) failed: %Rrc"),
     1177                              mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
     1178                break;
     1179        }
     1180    }
     1181
     1182    LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, aWritten));
     1183
     1184    LogFlowFuncLeaveRC(vrc);
     1185    return hr;
     1186#endif /* VBOX_WITH_GUEST_CONTROL */
     1187}
     1188
  • trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp

    r45078 r45109  
    949949    alock.acquire();
    950950
    951     AssertPtr(pCallbackRead);
    952951    int rc2 = callbackRemove(uContextID);
    953952    if (RT_SUCCESS(vrc))
    954953        vrc = rc2;
     954
     955    callbackDelete(pCallbackRead);
    955956
    956957    LogFlowFuncLeaveRC(vrc);
     
    10151016    uint32_t uContextID = 0;
    10161017
    1017     GuestCtrlCallback *pCallbackStart;
     1018    GuestCtrlCallback *pCallbackStart = NULL;
    10181019    try
    10191020    {
     
    11741175        AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
    11751176
    1176         AssertPtr(pCallbackStart);
    11771177        int rc2 = callbackRemove(uContextID);
    11781178        if (RT_SUCCESS(vrc))
    11791179            vrc = rc2;
    11801180    }
     1181
     1182    callbackDelete(pCallbackStart);
    11811183
    11821184    LogFlowFuncLeaveRC(vrc);
     
    13051307    alock.acquire();
    13061308
    1307     AssertPtr(pCallbackTerminate);
    13081309    int rc2 = callbackRemove(uContextID);
    13091310    if (RT_SUCCESS(vrc))
    13101311        vrc = rc2;
     1312
     1313    callbackDelete(pCallbackTerminate);
    13111314
    13121315    LogFlowFuncLeaveRC(vrc);
     
    16171620        vrc = rc2;
    16181621
     1622    callbackDelete(pCallbackWrite);
     1623
    16191624    LogFlowFuncLeaveRC(vrc);
    16201625    return vrc;
     
    18071812    AutoCaller autoCaller(this);
    18081813    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    1809 
    1810     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    18111814
    18121815    HRESULT hr = S_OK;
  • trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp

    r45078 r45109  
    968968        return rc;
    969969
    970     /* Add the created file to our vector. */
    971     mData.mFiles[uNewFileID] = pFile;
    972     mData.mNumObjects++;
    973     Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
    974 
    975     LogFlowFunc(("Added new file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
    976                  openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
     970    int guestRc;
     971    rc = pFile->openFile(&guestRc);
     972    if (RT_SUCCESS(rc))
     973    {
     974        /* Add the created file to our vector. */
     975        mData.mFiles[uNewFileID] = pFile;
     976        mData.mNumObjects++;
     977        Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
     978
     979        LogFlowFunc(("Added new file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
     980                     openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
     981    }
     982
     983    if (pGuestRc)
     984        *pGuestRc = guestRc;
    977985
    978986    LogFlowFuncLeaveRC(rc);
     
    17941802    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    17951803
    1796     /* Close session on guest session. */
     1804    /* Close session on guest. */
    17971805    int guestRc;
    17981806    int rc = closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
     
    18161824    if (RT_FAILURE(rc))
    18171825    {
    1818         /** @todo Handle guestRc! */
     1826        if (rc == VERR_GSTCTL_GUEST_ERROR)
     1827            return GuestSession::setErrorExternal(this, guestRc);
     1828
    18191829        return setError(VBOX_E_IPRT_ERROR,
    18201830                        tr("Closing guest session failed with %Rrc\n"), rc);
     
    24832493        {
    24842494            case VERR_GSTCTL_GUEST_ERROR:
    2485                 hr = GuestProcess::setErrorExternal(this, guestRc);
     2495                hr = GuestFile::setErrorExternal(this, guestRc);
    24862496                break;
    24872497
    24882498            default:
    2489                 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening file \"%s\" failed: %Rrc"),
     2499                hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
    24902500                              Utf8Str(aPath).c_str(), vrc);
    24912501                break;
Note: See TracChangeset for help on using the changeset viewer.

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