VirtualBox

Changeset 82727 in vbox


Ignore:
Timestamp:
Jan 13, 2020 2:55:50 PM (5 years ago)
Author:
vboxsync
Message:

IPRT/FTP: Implemented first support for data connection handling. Work in progress.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/ftp.h

    r82723 r82727  
    103103    /** Invalid reply type, do not use. */
    104104    RTFTPSERVER_REPLY_INVALID                        = 0,
     105    /** Command okay. */
     106    RTFTPSERVER_REPLY_OKAY                           = 200,
    105107    /** Command not implemented, superfluous at this site. */
    106108    RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL_SUPERFLUOUS = 202,
    107     /** Command okay. */
    108     RTFTPSERVER_REPLY_OKAY                           = 200,
    109109    /** Service ready for new user. */
    110110    RTFTPSERVER_REPLY_READY_FOR_NEW_USER             = 220,
     
    117117    /** User name okay, need password. */
    118118    RTFTPSERVER_REPLY_USERNAME_OKAY_NEED_PASSWORD    = 331,
     119    /** Can't open data connection. */
     120    RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN            = 425,
    119121    /** Connection closed; transfer aborted. */
    120122    RTFTPSERVER_REPLY_CONN_CLOSED_TRANSFER_ABORTED   = 426,
     
    143145{
    144146    /** User name. */
    145     char         *pszUser;
     147    char                       *pszUser;
    146148    /** Number of failed login attempts. */
    147     uint8_t       cFailedLoginAttempts;
     149    uint8_t                     cFailedLoginAttempts;
    148150    /** Timestamp (in ms) of last command issued by the client. */
    149     uint64_t      tsLastCmdMs;
     151    uint64_t                    tsLastCmdMs;
     152    /** Current set data type. */
     153    RTFTPSERVER_DATA_TYPE       enmDataType;
    150154} RTFTPSERVERCLIENTSTATE;
    151155/** Pointer to a FTP server client state. */
     
    202206     */
    203207    DECLCALLBACKMEMBER(int,  pfnOnUserDisconnect)(PRTFTPCALLBACKDATA pData);
     208    DECLCALLBACKMEMBER(int,  pfnOnFileOpen)(PRTFTPCALLBACKDATA pData, const char *pcszPath, uint32_t fMode, void **ppvHandle);
     209    DECLCALLBACKMEMBER(int,  pfnOnFileRead)(PRTFTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbToRead, size_t *pcbRead);
     210    DECLCALLBACKMEMBER(int,  pfnOnFileClose)(PRTFTPCALLBACKDATA pData, void *pvHandle);
    204211    /**
    205212     * Callback which gets invoked when the client wants to retrieve the size of a specific file.
     
    216223     * @param   pData           Pointer to generic callback data.
    217224     * @param   pcszPath        Path of file / directory to "stat". Optional. If NULL, the current directory will be used.
    218      * @param   pFsObjInfo      Where to return the RTFSOBJINFO data on success.
     225     * @param   pFsObjInfo      Where to return the RTFSOBJINFO data on success. Optional.
    219226     * @returns VBox status code.
    220227     */
  • trunk/src/VBox/Runtime/generic/ftp-server.cpp

    r82723 r82727  
    3232 * - No FTPS / SFTP support.
    3333 * - No passive mode ("PASV") support.
     34 * - No IPv6 support.
    3435 * - No proxy support.
    3536 * - No FXP support.
     
    4849#include <iprt/assert.h>
    4950#include <iprt/errcore.h>
     51#include <iprt/file.h> /* For file mode flags. */
    5052#include <iprt/getopt.h>
    5153#include <iprt/mem.h>
     
    128130    /** Retrieves a specific file. */
    129131    RTFTPSERVER_CMD_RETR,
    130     /** Recursively gets a directory (and its contents). */
    131     RTFTPSERVER_CMD_RGET,
    132132    /** Retrieves the size of a file. */
    133133    RTFTPSERVER_CMD_SIZE,
     
    147147
    148148/**
     149 * Structure for maintaining a single data connection.
     150 */
     151typedef struct RTFTPSERVERDATACONN
     152{
     153    /** Data connection IP. */
     154    RTNETADDRIPV4               Addr;
     155    /** Data connection port number. */
     156    uint16_t                    uPort;
     157    /** The current data socket to use.
     158     *  Can be NIL_RTSOCKET if no data port has been specified (yet) or has been closed. */
     159    RTSOCKET                    hSocket;
     160    /** Thread serving the data connection. */
     161    RTTHREAD                    hThread;
     162    /** Thread started indicator. */
     163    volatile bool               fStarted;
     164    /** Thread stop indicator. */
     165    volatile bool               fStop;
     166    char                        szFile[RTPATH_MAX];
     167} RTFTPSERVERDATACONN;
     168/** Pointer to a data connection struct. */
     169typedef RTFTPSERVERDATACONN *PRTFTPSERVERDATACONN;
     170
     171/**
    149172 * Structure for maintaining an internal FTP server client.
    150173 */
     
    157180    /** Actual client state. */
    158181    RTFTPSERVERCLIENTSTATE      State;
     182    /** Data connection information.
     183     *  At the moment we only allow one data connection per client at a time. */
     184    RTFTPSERVERDATACONN         DataConn;
    159185} RTFTPSERVERCLIENT;
    160186/** Pointer to an internal FTP server client state. */
     
    222248    } while (0)
    223249
     250
     251/*********************************************************************************************************************************
     252*   Defined Constants And Macros                                                                                                 *
     253*********************************************************************************************************************************/
     254
     255static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
     256
    224257/**
    225258 * Function prototypes for command handlers.
     
    236269static FNRTFTPSERVERCMD rtFtpServerHandleQUIT;
    237270static FNRTFTPSERVERCMD rtFtpServerHandleRETR;
    238 static FNRTFTPSERVERCMD rtFtpServerHandleRGET;
    239271static FNRTFTPSERVERCMD rtFtpServerHandleSIZE;
    240272static FNRTFTPSERVERCMD rtFtpServerHandleSTAT;
     
    242274static FNRTFTPSERVERCMD rtFtpServerHandleTYPE;
    243275static FNRTFTPSERVERCMD rtFtpServerHandleUSER;
     276
    244277
    245278/**
     
    272305    { RTFTPSERVER_CMD_QUIT,     "QUIT",         rtFtpServerHandleQUIT },
    273306    { RTFTPSERVER_CMD_RETR,     "RETR",         rtFtpServerHandleRETR },
    274     { RTFTPSERVER_CMD_RGET,     "RGET",         rtFtpServerHandleRGET },
    275307    { RTFTPSERVER_CMD_SIZE,     "SIZE",         rtFtpServerHandleSIZE },
    276308    { RTFTPSERVER_CMD_STAT,     "STAT",         rtFtpServerHandleSTAT },
     
    442474}
    443475
     476/**
     477 * Parses a string which consists of an IPv4 (ww,xx,yy,zz) and a port number (hi,lo), all separated by comma delimiters.
     478 * See RFC 959, 4.1.2.
     479 *
     480 * @returns VBox status code.
     481 * @param   pcszStr             String to parse.
     482 * @param   pAddr               Where to store the IPv4 address on success.
     483 * @param   puPort              Where to store the port number on success.
     484 */
     485static int rtFtpParseHostAndPort(const char *pcszStr, PRTNETADDRIPV4 pAddr, uint16_t *puPort)
     486{
     487    AssertPtrReturn(pcszStr, VERR_INVALID_POINTER);
     488    AssertPtrReturn(pAddr, VERR_INVALID_POINTER);
     489    AssertPtrReturn(puPort, VERR_INVALID_POINTER);
     490
     491    char *pszNext;
     492    int rc;
     493
     494    /* Parse IP (v4). */
     495    /** @todo I don't think IPv6 ever will be a thing here, or will it? */
     496    rc = RTStrToUInt8Ex(pcszStr, &pszNext, 10, &pAddr->au8[0]);
     497    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
     498        return VERR_INVALID_PARAMETER;
     499    if (*pszNext++ != ',')
     500        return VERR_INVALID_PARAMETER;
     501
     502    rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
     503    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
     504        return VERR_INVALID_PARAMETER;
     505    if (*pszNext++ != ',')
     506        return VERR_INVALID_PARAMETER;
     507
     508    rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
     509    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
     510        return VERR_INVALID_PARAMETER;
     511    if (*pszNext++ != ',')
     512        return VERR_INVALID_PARAMETER;
     513
     514    rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
     515    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
     516        return VERR_INVALID_PARAMETER;
     517    if (*pszNext++ != ',')
     518        return VERR_INVALID_PARAMETER;
     519
     520    /* Parse port. */
     521    uint8_t uPortHi;
     522    rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &uPortHi);
     523    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
     524        return VERR_INVALID_PARAMETER;
     525    if (*pszNext++ != ',')
     526        return VERR_INVALID_PARAMETER;
     527    uint8_t uPortLo;
     528    rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &uPortLo);
     529    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
     530        return VERR_INVALID_PARAMETER;
     531
     532    *puPort = RT_MAKE_U16(uPortLo, uPortHi);
     533
     534    return rc;
     535}
     536
     537/**
     538 * Opens a data connection to the client.
     539 *
     540 * @returns VBox status code.
     541 * @param   pDataConn           Data connection to open.
     542 * @param   pAddr               Address for the data connection.
     543 * @param   uPort               Port for the data connection.
     544 */
     545static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort)
     546{
     547    RT_NOREF(pAddr);
     548
     549    char szAddress[32];
     550    const ssize_t cchAdddress = RTStrPrintf2(szAddress, sizeof(szAddress), "%RU8.%RU8.%RU8.%RU8",
     551                                             pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3]);
     552    AssertReturn(cchAdddress > 0, VERR_NO_MEMORY);
     553
     554    return RTTcpClientConnect(szAddress, uPort, &pDataConn->hSocket);
     555}
     556
     557/**
     558 * Closes a data connection to the client.
     559 *
     560 * @returns VBox status code.
     561 * @param   pDataConn           Data connection to close.
     562 */
     563static int rtFtpServerDataPortClose(PRTFTPSERVERDATACONN pDataConn)
     564{
     565    int rc = VINF_SUCCESS;
     566
     567    if (pDataConn->hSocket != NIL_RTSOCKET)
     568    {
     569        rc = RTTcpClientClose(pDataConn->hSocket);
     570        pDataConn->hSocket = NIL_RTSOCKET;
     571    }
     572
     573    return rc;
     574}
     575
     576/**
     577 * Thread serving a data connection.
     578 *
     579 * @returns VBox status code.
     580 * @param   ThreadSelf          Thread handle. Unused at the moment.
     581 * @param   pvUser              Pointer to user-provided data. Of type PRTFTPSERVERCLIENT.
     582 */
     583static DECLCALLBACK(int) rtFtpServerDataConnThread(RTTHREAD ThreadSelf, void *pvUser)
     584{
     585    RT_NOREF(ThreadSelf);
     586
     587    PRTFTPSERVERCLIENT pClient = (PRTFTPSERVERCLIENT)pvUser;
     588    AssertPtr(pClient);
     589
     590    PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn;
     591
     592    int rc = rtFtpServerDataPortOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
     593    if (RT_FAILURE(rc))
     594        return rc;
     595
     596    uint32_t cbBuf = _64K; /** @todo Improve this. */
     597    void *pvBuf = RTMemAlloc(cbBuf);
     598    if (!pvBuf)
     599        return VERR_NO_MEMORY;
     600
     601    pDataConn->fStop    = false;
     602    pDataConn->fStarted = true;
     603
     604    RTThreadUserSignal(RTThreadSelf());
     605
     606    const char *pcszFile = pDataConn->szFile;
     607
     608    void *pvHandle = NULL; /* Opaque handle known to the actual implementation. */
     609
     610    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileOpen, pcszFile,
     611                                   RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &pvHandle);
     612    if (RT_SUCCESS(rc))
     613    {
     614        do
     615        {
     616            size_t cbRead;
     617            RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileRead, pvHandle, pvBuf, cbBuf, &cbRead);
     618            if (RT_SUCCESS(rc))
     619                rc = RTTcpWrite(pClient->DataConn.hSocket, pvBuf, cbRead);
     620
     621            if (   !cbRead
     622                || ASMAtomicReadBool(&pDataConn->fStop))
     623                break;
     624        }
     625        while (RT_SUCCESS(rc));
     626
     627        RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileClose, pvHandle);
     628    }
     629
     630    rtFtpServerDataPortClose(&pClient->DataConn);
     631
     632    RTMemFree(pvBuf);
     633    pvBuf = NULL;
     634
     635    return rc;
     636}
     637
     638/**
     639 * Opens a data connection to the client.
     640 *
     641 * @returns VBox status code.
     642 * @param   pClient             Client to open data connection for.
     643 * @param   pDataConn           Data connection to open.
     644 */
     645static int rtFtpServerDataConnCreate(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN pDataConn)
     646{
     647    int rc = RTThreadCreate(&pDataConn->hThread, rtFtpServerDataConnThread,
     648                            pClient, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
     649                            "ftpdata");
     650    if (RT_SUCCESS(rc))
     651    {
     652        int rc2 = RTThreadUserWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */);
     653        AssertRC(rc2);
     654
     655        if (!pDataConn->fStarted) /* Did the thread indicate that it started correctly? */
     656            rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
     657    }
     658
     659    return rc;
     660}
     661
     662/**
     663 * Closes a data connection to the client.
     664 *
     665 * @returns VBox status code.
     666 * @param   pClient             Client to close data connection for.
     667 * @param   pDataConn           Data connection to close.
     668 */
     669static int rtFtpServerDataConnDestroy(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN pDataConn)
     670{
     671    RT_NOREF(pClient);
     672
     673    if (pDataConn->hThread == NIL_RTTHREAD)
     674        return VINF_SUCCESS;
     675
     676    LogFlowFuncEnter();
     677
     678    /* Set stop indicator. */
     679    pDataConn->fStop = true;
     680
     681    int rcThread = VERR_WRONG_ORDER;
     682    int rc = RTThreadWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */, &rcThread);
     683    if (RT_SUCCESS(rc))
     684    {
     685        if (pDataConn->hSocket != NIL_RTSOCKET)
     686        {
     687            RTTcpClientClose(pDataConn->hSocket);
     688            pDataConn->hSocket = NIL_RTSOCKET;
     689        }
     690
     691        pDataConn->fStarted = false;
     692        pDataConn->hThread  = NIL_RTTHREAD;
     693
     694        rc = rcThread;
     695    }
     696
     697    return rc;
     698}
     699
    444700
    445701/*********************************************************************************************************************************
     
    449705static int rtFtpServerHandleABOR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    450706{
    451     RT_NOREF(pClient, cArgs, apcszArgs);
    452 
    453     /** @todo Anything to do here? */
    454     return VERR_NOT_IMPLEMENTED;
     707    RT_NOREF(cArgs, apcszArgs);
     708
     709    int rc = rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
     710    if (RT_SUCCESS(rc))
     711        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     712
     713    return rc;
    455714}
    456715
     
    551810static int rtFtpServerHandlePORT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    552811{
    553     RT_NOREF(pClient, cArgs, apcszArgs);
    554 
    555     /** @todo Anything to do here? */
    556     return VERR_NOT_IMPLEMENTED;
     812    if (cArgs != 1)
     813        return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
     814
     815    /* Only allow one data connection per client at a time. */
     816    rtFtpServerDataPortClose(&pClient->DataConn);
     817
     818    int rc = rtFtpParseHostAndPort(apcszArgs[0], &pClient->DataConn.Addr, &pClient->DataConn.uPort);
     819    if (RT_SUCCESS(rc))
     820    {
     821        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     822    }
     823    else
     824        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
     825
     826    return rc;
    557827}
    558828
     
    575845static int rtFtpServerHandleQUIT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    576846{
    577     RT_NOREF(pClient, cArgs, apcszArgs);
    578 
    579     /** @todo Anything to do here? */
    580     return VERR_NOT_IMPLEMENTED;
     847    RT_NOREF(cArgs, apcszArgs);
     848
     849    return rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
    581850}
    582851
    583852static int rtFtpServerHandleRETR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    584853{
    585     RT_NOREF(pClient, cArgs, apcszArgs);
    586 
    587     /** @todo Anything to do here? */
    588     return VERR_NOT_IMPLEMENTED;
    589 }
    590 
    591 static int rtFtpServerHandleRGET(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    592 {
    593     RT_NOREF(pClient, cArgs, apcszArgs);
    594 
    595     /** @todo Anything to do here? */
    596     return VERR_NOT_IMPLEMENTED;
     854    if (cArgs != 1)
     855        return VERR_INVALID_PARAMETER;
     856
     857    int rc;
     858
     859    const char *pcszPath = apcszArgs[0];
     860
     861    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pcszPath, NULL /* PRTFSOBJINFO */);
     862
     863    if (RT_SUCCESS(rc))
     864    {
     865        rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath);
     866        if (RT_SUCCESS(rc))
     867        {
     868            rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn);
     869            if (RT_SUCCESS(rc))
     870            {
     871                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     872            }
     873        }
     874    }
     875
     876    if (RT_FAILURE(rc))
     877    {
     878        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_REQ_ACTION_NOT_TAKEN);
     879        AssertRC(rc2);
     880    }
     881
     882    return rc;
    597883}
    598884
     
    678964static int rtFtpServerHandleTYPE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    679965{
    680     RT_NOREF(pClient, cArgs, apcszArgs);
    681 
    682     /** @todo Anything to do here? */
    683     return VERR_NOT_IMPLEMENTED;
     966    if (cArgs != 1)
     967        return VERR_INVALID_PARAMETER;
     968
     969    const char *pcszType = apcszArgs[0];
     970
     971    int rc = VINF_SUCCESS;
     972
     973    if (!RTStrICmp(pcszType, "A")) /* ASCII (can be 7 or 8 bits). */
     974    {
     975        pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_ASCII;
     976    }
     977    else if (!RTStrICmp(pcszType, "I")) /* Image (binary). */
     978    {
     979        pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_IMAGE;
     980    }
     981    else /** @todo Support "E" (EBCDIC) and/or "L <size>" (custom)? */
     982        rc = VERR_INVALID_PARAMETER;
     983
     984    return rc;
    684985}
    685986
     
    8711172    }
    8721173
     1174    /* Make sure to close any open data connections. */
     1175    int rc2 = rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
     1176    if (RT_SUCCESS(rc))
     1177        rc = rc2;
     1178
    8731179    return rc;
    8741180}
  • trunk/src/VBox/Runtime/tools/RTFTPServer.cpp

    r82723 r82727  
    7070    char szRootDir[RTPATH_MAX];
    7171    char szCWD[RTPATH_MAX];
     72    RTFILE hFile;
    7273} FTPSERVERDATA;
    7374typedef FTPSERVERDATA *PFTPSERVERDATA;
     
    194195}
    195196
     197static DECLCALLBACK(int) onFileOpen(PRTFTPCALLBACKDATA pData, const char *pcszPath, uint32_t fMode, void **ppvHandle)
     198{
     199    RT_NOREF(ppvHandle);
     200
     201    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
     202    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
     203
     204    return RTFileOpen(&pThis->hFile, pcszPath, fMode);
     205}
     206
     207static DECLCALLBACK(int) onFileRead(PRTFTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbToRead, size_t *pcbRead)
     208{
     209    RT_NOREF(pvHandle);
     210
     211    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
     212    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
     213
     214    return RTFileRead(pThis->hFile, pvBuf, cbToRead, pcbRead);
     215}
     216
     217static DECLCALLBACK(int) onFileClose(PRTFTPCALLBACKDATA pData, void *pvHandle)
     218{
     219    RT_NOREF(pvHandle);
     220
     221    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
     222    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
     223
     224    return RTFileClose(pThis->hFile);
     225}
     226
    196227static DECLCALLBACK(int) onFileGetSize(PRTFTPCALLBACKDATA pData, const char *pcszPath, uint64_t *puSize)
    197228{
     
    223254    if (RT_SUCCESS(rc))
    224255    {
    225         rc = RTFileQueryInfo(hFile, pFsObjInfo, RTFSOBJATTRADD_NOTHING);
     256        RTFSOBJINFO fsObjInfo;
     257        rc = RTFileQueryInfo(hFile, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
     258        if (RT_SUCCESS(rc))
     259        {
     260            if (pFsObjInfo)
     261                *pFsObjInfo = fsObjInfo;
     262        }
     263
    226264        RTFileClose(hFile);
    227265    }
     
    374412        Callbacks.pfnOnUserAuthenticate = onUserAuthenticate;
    375413        Callbacks.pfnOnUserDisconnect   = onUserDisonnect;
     414        Callbacks.pfnOnFileOpen         = onFileOpen;
     415        Callbacks.pfnOnFileRead         = onFileRead;
     416        Callbacks.pfnOnFileClose        = onFileClose;
    376417        Callbacks.pfnOnFileGetSize      = onFileGetSize;
    377418        Callbacks.pfnOnFileStat         = onFileStat;
Note: See TracChangeset for help on using the changeset viewer.

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