VirtualBox

Changeset 82838 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Jan 23, 2020 9:15:08 AM (5 years ago)
Author:
vboxsync
Message:

IPRT/FTP: More code for listing directories, needed for some more FTP clients; added simple data connection data caching / flushing. bugref:9646

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/generic/ftp-server.cpp

    r82824 r82838  
    5050#include <iprt/asm.h>
    5151#include <iprt/assert.h>
     52#include <iprt/circbuf.h>
    5253#include <iprt/err.h>
    5354#include <iprt/file.h> /* For file mode flags. */
     
    5859#include <iprt/poll.h>
    5960#include <iprt/socket.h>
     61#include <iprt/sort.h>
    6062#include <iprt/string.h>
    6163#include <iprt/system.h>
     
    236238     *  Will be free'd by the data connection thread. */
    237239    char**                      papszArgs;
     240    /** Circular buffer for caching data before writing. */
     241    PRTCIRCBUF                  pCircBuf;
    238242} RTFTPSERVERDATACONN;
    239243/** Pointer to a data connection struct. */
     
    334338static int  rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn);
    335339static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn);
     340static int  rtFtpServerDataConnFlush(PRTFTPSERVERDATACONN pDataConn);
    336341
    337342static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState);
     
    404409};
    405410
     411/** RFC-1123 month of the year names. */
     412static const char * const g_apszMonths[1+12] =
     413{
     414    "000", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     415};
     416
    406417/** Feature string which represents all commands we support in addition to RFC 959 (see RFC 2398).
    407418 *  Must match the command table above.
     
    850861        LogFlowFuncEnter();
    851862
     863        rtFtpServerDataConnFlush(pDataConn);
     864
    852865        rc = RTTcpClientClose(pDataConn->hSocket);
    853866        pDataConn->hSocket = NIL_RTSOCKET;
     
    865878 * @param   pvData              Data to write.
    866879 * @param   cbData              Size (in bytes) of data to write.
    867  * @param   pcbWritten          How many bytes were written. Optional and unused atm.
     880 * @param   pcbWritten          How many bytes were written. Optional.
    868881 */
    869882static int rtFtpServerDataConnWrite(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData, size_t *pcbWritten)
    870883{
    871     RT_NOREF(pcbWritten);
    872 
    873     return RTTcpWrite(pDataConn->hSocket, pvData, cbData);
     884    int rc = RTTcpWrite(pDataConn->hSocket, pvData, cbData);
     885    if (RT_SUCCESS(rc))
     886    {
     887        if (pcbWritten)
     888            *pcbWritten = cbData;
     889    }
     890
     891    return rc;
     892}
     893
     894/**
     895 * Flushes a data connection.
     896 *
     897 * @returns VBox status code.
     898 * @param   pDataConn           Data connection to flush.
     899 */
     900static int rtFtpServerDataConnFlush(PRTFTPSERVERDATACONN pDataConn)
     901{
     902    int rc = VINF_SUCCESS;
     903
     904    size_t cbUsed = RTCircBufUsed(pDataConn->pCircBuf);
     905    while (cbUsed)
     906    {
     907        void   *pvBlock;
     908        size_t  cbBlock;
     909        RTCircBufAcquireReadBlock(pDataConn->pCircBuf, cbUsed, &pvBlock, &cbBlock);
     910        if (cbBlock)
     911        {
     912            size_t cbWritten;
     913            rc = rtFtpServerDataConnWrite(pDataConn, pvBlock, cbBlock, &cbWritten);
     914            if (RT_SUCCESS(rc))
     915            {
     916
     917                AssertBreak(cbUsed >= cbWritten);
     918                cbUsed -= cbWritten;
     919            }
     920
     921            RTCircBufReleaseReadBlock(pDataConn->pCircBuf, cbWritten);
     922
     923            if (RT_FAILURE(rc))
     924                break;
     925        }
     926    }
     927
     928    return rc;
     929}
     930
     931/**
     932 * Checks if flushing a data connection is necessary, and if so, flush it.
     933 *
     934 * @returns VBox status code.
     935 * @param   pDataConn           Data connection to check / do flushing for.
     936 */
     937static int rtFtpServerDataCheckFlush(PRTFTPSERVERDATACONN pDataConn)
     938{
     939    int rc = VINF_SUCCESS;
     940
     941    size_t cbUsed = RTCircBufUsed(pDataConn->pCircBuf);
     942    if (cbUsed >= _4K) /** @todo Make this more dynamic. */
     943    {
     944        rc = rtFtpServerDataConnFlush(pDataConn);
     945    }
     946
     947    return rc;
     948}
     949
     950/**
     951 * Adds new data for a data connection to be sent.
     952 *
     953 * @returns VBox status code.
     954 * @param   pDataConn           Data connection to add new data to.
     955 * @param   pvData              Pointer to data to add.
     956 * @param   cbData              Size (in bytes) of data to add.
     957 */
     958static int rtFtpServerDataConnAddData(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData)
     959{
     960    AssertReturn(cbData <= RTCircBufFree(pDataConn->pCircBuf), VERR_BUFFER_OVERFLOW);
     961
     962    int rc = VINF_SUCCESS;
     963
     964    size_t cbToWrite = cbData;
     965    do
     966    {
     967        void   *pvBlock;
     968        size_t  cbBlock;
     969        RTCircBufAcquireWriteBlock(pDataConn->pCircBuf, cbToWrite, &pvBlock, &cbBlock);
     970        if (cbBlock)
     971        {
     972            AssertBreak(cbData >= cbBlock);
     973            memcpy(pvBlock, pvData, cbBlock);
     974
     975            AssertBreak(cbToWrite >= cbBlock);
     976            cbToWrite -= cbBlock;
     977
     978            RTCircBufReleaseWriteBlock(pDataConn->pCircBuf, cbBlock);
     979        }
     980
     981    } while (cbToWrite);
     982
     983    if (RT_SUCCESS(rc))
     984        rc = rtFtpServerDataCheckFlush(pDataConn);
     985
     986    return rc;
    874987}
    875988
     
    8961009    RTStrFree(pszFmt);
    8971010
    898     rc = RTTcpWrite(pDataConn->hSocket, pszMsg, strlen(pszMsg));
     1011    rc = rtFtpServerDataConnAddData(pDataConn, pszMsg, strlen(pszMsg));
    8991012
    9001013    RTStrFree(pszMsg);
     
    10031116    pDataConn->uPort   = pClient->uDataConnPort;
    10041117
    1005     *ppDataConn = pDataConn;
     1118    int rc = RTCircBufCreate(&pDataConn->pCircBuf, _16K); /** @todo Some random value; improve. */
     1119    if (RT_SUCCESS(rc))
     1120    {
     1121        *ppDataConn = pDataConn;
     1122    }
    10061123
    10071124    LogFlowFuncLeaveRC(VINF_SUCCESS);
    1008     return VINF_SUCCESS;
     1125    return rc;
    10091126}
    10101127
     
    11201237    rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
    11211238
     1239    RTCircBufDestroy(pDataConn->pCircBuf);
     1240
    11221241    RTMemFree(pDataConn);
    11231242    pDataConn = NULL;
     
    12961415
    12971416/**
    1298  * Formats the given timestamp according to the desired --time-style.
     1417 * Formats the given timestamp according to (non-standardized) FTP LIST command.
    12991418 *
    13001419 * @returns pszDst
    1301  * @param   pTimestamp      The timestamp.
     1420 * @param   pTimestamp      The timestamp to format.
    13021421 * @param   pszDst          The output buffer.
    13031422 * @param   cbDst           The output buffer size.
     
    13051424static const char *rtFtpServerFormatTimestamp(PCRTTIMESPEC pTimestamp, char *pszDst, size_t cbDst)
    13061425{
    1307     /** @todo timestamp formatting according to the given style.   */
    1308     return RTTimeSpecToString(pTimestamp, pszDst, cbDst);
     1426    RTTIME Time;
     1427    RTTimeExplode(&Time, pTimestamp);
     1428
     1429    /* Calc the UTC offset part. */
     1430    int32_t offUtc = Time.offUTC;
     1431    Assert(offUtc <= 840 && offUtc >= -840);
     1432    char     chSign;
     1433    if (offUtc >= 0)
     1434        chSign = '+';
     1435    else
     1436    {
     1437        chSign = '-';
     1438        offUtc = -offUtc;
     1439    }
     1440    uint32_t offUtcHour   = (uint32_t)offUtc / 60;
     1441    uint32_t offUtcMinute = (uint32_t)offUtc % 60;
     1442
     1443    /** @todo Cache this. */
     1444    RTTIMESPEC TimeSpecNow;
     1445    RTTimeNow(&TimeSpecNow);
     1446    RTTIME TimeNow;
     1447    RTTimeExplode(&TimeNow, &TimeSpecNow);
     1448
     1449    /* Only include the year if it's not the same year as today. */
     1450    if (TimeNow.i32Year != Time.i32Year)
     1451    {
     1452        RTStrPrintf(pszDst, cbDst, "%s  %02RU8  %5RU32",
     1453                    g_apszMonths[Time.u8Month], Time.u8MonthDay, Time.i32Year);
     1454    }
     1455    else /* ... otherwise include the (rough) time (as GMT). */
     1456    {
     1457        RTStrPrintf(pszDst, cbDst, "%s  %02RU8  %02RU32:%02RU32",
     1458                    g_apszMonths[Time.u8Month], Time.u8MonthDay, offUtcHour, offUtcMinute);
     1459    }
     1460
     1461    return pszDst;
    13091462}
    13101463
     
    13931546                                              const char *pszOwner, const char *pszGroup, const char *pszTarget)
    13941547{
     1548    /* Filter out entries we don't want to report to the client, even if they were reported by the actual implementation. */
     1549    if (   !RTStrCmp(pszEntry, ".")
     1550        || !RTStrCmp(pszEntry, ".."))
     1551    {
     1552        return VINF_SUCCESS;
     1553    }
    13951554
    13961555    /* Make sure there is space in the collection for the new entry. */
     
    14491608}
    14501609
     1610/** @callback_method_impl{FNRTSORTCMP, Name} */
     1611static DECLCALLBACK(int) rtFtpServerCollEntryCmpName(void const *pvElement1, void const *pvElement2, void *pvUser)
     1612{
     1613    RT_NOREF(pvUser);
     1614    PRTFTPDIRENTRY pEntry1 = (PRTFTPDIRENTRY)pvElement1;
     1615    PRTFTPDIRENTRY pEntry2 = (PRTFTPDIRENTRY)pvElement2;
     1616    return RTStrCmp(pEntry1->szName, pEntry2->szName);
     1617}
     1618
     1619/** @callback_method_impl{FNRTSORTCMP, Dirs first + Name} */
     1620static DECLCALLBACK(int) rtFtpServerCollEntryCmpDirFirstName(void const *pvElement1, void const *pvElement2, void *pvUser)
     1621{
     1622    RT_NOREF(pvUser);
     1623    PRTFTPDIRENTRY pEntry1 = (PRTFTPDIRENTRY)pvElement1;
     1624    PRTFTPDIRENTRY pEntry2 = (PRTFTPDIRENTRY)pvElement2;
     1625    int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
     1626    if (!iDiff)
     1627        iDiff = rtFtpServerCollEntryCmpName(pEntry1, pEntry2, pvUser);
     1628    return iDiff;
     1629}
     1630
     1631/**
     1632 * Sorts a given directory collection according to the FTP server's LIST style.
     1633 *
     1634 * @param   pCollection         Collection to sort.
     1635 */
     1636static void rtFtpServerCollSort(PRTFTPDIRCOLLECTION pCollection)
     1637{
     1638    PFNRTSORTCMP pfnCmp = rtFtpServerCollEntryCmpDirFirstName;
     1639    if (pfnCmp)
     1640        RTSortApvShell((void **)pCollection->papEntries, pCollection->cEntries, pfnCmp, NULL);
     1641}
     1642
    14511643/**
    14521644 * Writes a directory collection to a specific data connection.
     
    14611653                                           char *pszTmp, size_t cbTmp)
    14621654{
    1463     /*
    1464      * Figure the width of the size, the link count, the uid, the gid, and the inode columns.
    1465      */
    1466     size_t cchSizeCol  = 1;
     1655    size_t cchSizeCol  = 4;
    14671656    size_t cchLinkCol  = 1;
    14681657    size_t cchUidCol   = 1;
     
    15161705            default:                    rtFtpServerDataConnPrintf(pDataConn, "?"); AssertFailed(); break;
    15171706        }
    1518         /** @todo sticy bits++ */
     1707
    15191708        rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
    15201709                                  fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
     
    15301719                                  fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
    15311720
    1532         rtFtpServerDataConnPrintf(pDataConn, " %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
    1533                                   fMode & RTFS_DOS_READONLY          ? 'R' : '-',
    1534                                   fMode & RTFS_DOS_HIDDEN            ? 'H' : '-',
    1535                                   fMode & RTFS_DOS_SYSTEM            ? 'S' : '-',
    1536                                   fMode & RTFS_DOS_DIRECTORY         ? 'D' : '-',
    1537                                   fMode & RTFS_DOS_ARCHIVED          ? 'A' : '-',
    1538                                   fMode & RTFS_DOS_NT_DEVICE         ? 'd' : '-',
    1539                                   fMode & RTFS_DOS_NT_NORMAL         ? 'N' : '-',
    1540                                   fMode & RTFS_DOS_NT_TEMPORARY      ? 'T' : '-',
    1541                                   fMode & RTFS_DOS_NT_SPARSE_FILE    ? 'P' : '-',
    1542                                   fMode & RTFS_DOS_NT_REPARSE_POINT  ? 'J' : '-',
    1543                                   fMode & RTFS_DOS_NT_COMPRESSED     ? 'C' : '-',
    1544                                   fMode & RTFS_DOS_NT_OFFLINE        ? 'O' : '-',
    1545                                   fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
    1546                                   fMode & RTFS_DOS_NT_ENCRYPTED      ? 'E' : '-');
    1547 
    15481721        rtFtpServerDataConnPrintf(pDataConn, " %*u",
    15491722                                  cchLinkCol, pEntry->Info.Attr.u.Unix.cHardlinks);
     1723
    15501724        if (cchUidCol)
    15511725            rtFtpServerDataConnPrintf(pDataConn, " %*s", cchUidCol,
     
    15551729                                      rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp));
    15561730
    1557         rtFtpServerDataConnPrintf(pDataConn," %*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp));
     1731        rtFtpServerDataConnPrintf(pDataConn, "%*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp));
    15581732
    15591733        PCRTTIMESPEC pTime = (PCRTTIMESPEC)((uintptr_t)pEntry + offTime);
     
    16571831    pvHandle = NULL;
    16581832
     1833    rtFtpServerCollSort(pColl);
     1834
    16591835    if (RT_SUCCESS(rc))
    16601836    {
     
    16931869        if (RT_SUCCESS(rc))
    16941870        {
    1695             int rc2 = rtFtpServerSendReplyRc(pClient,   pClient->pDataConn
    1696                                                       ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
    1697                                                       : RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN);
    1698             if (RT_SUCCESS(rc))
    1699                 rc = rc2;
    1700 
    1701             if (RT_SUCCESS(rc))
     1871            if (pClient->pDataConn == NULL)
    17021872            {
    17031873                rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
     
    17051875                    rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
    17061876
    1707                 rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
    1708                                                       ? RTFTPSERVER_REPLY_FILE_ACTION_OKAY_COMPLETED
    1709                                                       : RTFTPSERVER_REPLY_CLOSING_DATA_CONN);
    1710                 if (RT_SUCCESS(rc))
    1711                     rc = rc2;
     1877                int rc2 = rtFtpServerSendReplyRc(  pClient, RT_SUCCESS(rc)
     1878                                                 ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
     1879                                                 : RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
     1880                AssertRC(rc2);
     1881            }
     1882            else
     1883            {
     1884                 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN);
     1885                 AssertRC(rc2);
    17121886            }
    17131887        }
     
    22342408                Assert(pClient->pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
    22352409
     2410                int rc2 = rtFtpServerSendReplyRc(pClient,
     2411                                                   RT_SUCCESS(pClient->pDataConn->rc)
     2412                                                 ? RTFTPSERVER_REPLY_CLOSING_DATA_CONN : RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
     2413                AssertRC(rc2);
     2414
    22362415                rc = rtFtpServerDataConnStop(pClient->pDataConn);
    22372416                if (RT_SUCCESS(rc))
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