VirtualBox

Changeset 82822 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jan 22, 2020 11:48:43 AM (5 years ago)
Author:
vboxsync
Message:

IPRT/FTP: More work on directory listing.

Location:
trunk/src/VBox/Runtime
Files:
2 edited

Legend:

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

    r82813 r82822  
    3030 * - UTF-8 support only.
    3131 * - Only supports ASCII + binary (image type) file streams for now.
     32 * - No directory / file caching yet.
    3233 * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++).
    3334 * - No FTPS / SFTP support.
     
    8586/** Pointer to an internal FTP server instance. */
    8687typedef RTFTPSERVERINTERNAL *PRTFTPSERVERINTERNAL;
     88
     89/**
     90 * FTP directory entry.
     91 */
     92typedef struct RTFTPDIRENTRY
     93{
     94    /** The information about the entry. */
     95    RTFSOBJINFO Info;
     96    /** Symbolic link target (allocated after the name). */
     97    const char *pszTarget;
     98    /** Owner if applicable (allocated after the name). */
     99    const char *pszOwner;
     100    /** Group if applicable (allocated after the name). */
     101    const char *pszGroup;
     102    /** The length of szName. */
     103    size_t      cchName;
     104    /** The entry name. */
     105    char        szName[RT_FLEXIBLE_ARRAY];
     106} RTFTPDIRENTRY;
     107/** Pointer to a FTP directory entry. */
     108typedef RTFTPDIRENTRY *PRTFTPDIRENTRY;
     109/** Pointer to a FTP directory entry pointer. */
     110typedef PRTFTPDIRENTRY *PPRTFTPDIRENTRY;
     111
     112/**
     113 * Collection of directory entries.
     114 * Used for also caching stuff.
     115 */
     116typedef struct RTFTPDIRCOLLECTION
     117{
     118    /** Current size of papEntries. */
     119    size_t                cEntries;
     120    /** Memory allocated for papEntries. */
     121    size_t                cEntriesAllocated;
     122    /** Current entries pending sorting and display. */
     123    PPRTFTPDIRENTRY       papEntries;
     124
     125    /** Total number of bytes allocated for the above entries. */
     126    uint64_t              cbTotalAllocated;
     127    /** Total number of file content bytes.    */
     128    uint64_t              cbTotalFiles;
     129
     130} RTFTPDIRCOLLECTION;
     131/** Pointer to a directory collection. */
     132typedef RTFTPDIRCOLLECTION *PRTFTPDIRCOLLECTION;
     133/** Pointer to a directory entry collection pointer. */
     134typedef PRTFTPDIRCOLLECTION *PPRTFTPDIRCOLLECTION;
    87135
    88136
     
    468516
    469517/**
     518 * Validates if a given absolute path is valid or not.
     519 *
     520 * @returns \c true if path is valid, or \c false if not.
     521 * @param   pcszPath            Path to check.
     522 * @param   fIsAbsolute         Whether the path to check is an absolute path or not.
     523 */
     524static bool rtFtpServerPathIsValid(const char *pcszPath, bool fIsAbsolute)
     525{
     526    if (!pcszPath)
     527        return false;
     528
     529    bool fIsValid =    strlen(pcszPath)
     530                    && RTStrIsValidEncoding(pcszPath)
     531                    && RTStrStr(pcszPath, "..") == NULL;     /** @todo Very crude for now -- improve this. */
     532    if (   fIsValid
     533        && fIsAbsolute)
     534    {
     535        RTFSOBJINFO objInfo;
     536        int rc2 = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
     537        if (RT_SUCCESS(rc2))
     538        {
     539            fIsValid =    RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
     540                       || RTFS_IS_FILE(objInfo.Attr.fMode);
     541
     542            /* No symlinks and other stuff not allowed. */
     543        }
     544        else
     545            fIsValid = false;
     546    }
     547
     548    LogFlowFunc(("pcszPath=%s -> %RTbool\n", pcszPath, fIsValid));
     549    return fIsValid;
     550}
     551
     552/**
    470553 * Sets the current working directory for a client.
    471554 *
    472555 * @returns VBox status code.
    473  * @param   pClient             Client to set current working directory for.
     556 * @param   pState              Client state to set current working directory for.
    474557 * @param   pcszPath            Working directory to set.
    475558 */
    476 static int rtFtpSetCWD(PRTFTPSERVERCLIENT pClient, const char *pcszPath)
    477 {
    478     RTStrFree(pClient->State.pszCWD);
    479 
    480     pClient->State.pszCWD = RTStrDup(pcszPath);
    481 
    482     LogFlowFunc(("Current CWD is now '%s'\n", pClient->State.pszCWD));
    483 
    484     int rc = pClient->State.pszCWD ? VINF_SUCCESS : VERR_NO_MEMORY;
     559static int rtFtpSetCWD(PRTFTPSERVERCLIENTSTATE pState, const char *pcszPath)
     560{
     561    RTStrFree(pState->pszCWD);
     562
     563    if (!rtFtpServerPathIsValid(pcszPath, false /* fIsAbsolute */))
     564        return VERR_INVALID_PARAMETER;
     565
     566    pState->pszCWD = RTStrDup(pcszPath);
     567
     568    LogFlowFunc(("Current CWD is now '%s'\n", pState->pszCWD));
     569
     570    int rc = pState->pszCWD ? VINF_SUCCESS : VERR_NO_MEMORY;
    485571    AssertRC(rc);
    486572    return rc;
     
    789875
    790876/**
     877 * Does a printf-style write on a data connection.
     878 *
     879 * @returns VBox status code.
     880 * @param   pDataConn           Data connection to write to.
     881 * @param   enmReply            Reply code to send.
     882 * @param   pcszFormat          Format string of message to send with the reply code.
     883 */
     884static int rtFtpServerDataConnPrintf(PRTFTPSERVERDATACONN pDataConn, const char *pcszFormat, ...)
     885{
     886    va_list args;
     887    va_start(args, pcszFormat);
     888    char *pszFmt = NULL;
     889    const int cch = RTStrAPrintfV(&pszFmt, pcszFormat, args);
     890    va_end(args);
     891    AssertReturn(cch > 0, VERR_NO_MEMORY);
     892
     893    char *pszMsg = NULL;
     894    int rc = RTStrAAppend(&pszMsg, pszFmt);
     895    AssertRCReturn(rc, rc);
     896
     897    RTStrFree(pszFmt);
     898
     899    rc = RTTcpWrite(pDataConn->hSocket, pszMsg, strlen(pszMsg));
     900
     901    RTStrFree(pszMsg);
     902
     903    return rc;
     904}
     905
     906/**
    791907 * Data connection thread for writing (sending) a file to the client.
    792908 *
     
    10691185
    10701186            if (RT_SUCCESS(rc))
    1071                 rtFtpSetCWD(pClient, pszPath);
     1187                rc = rtFtpSetCWD(&pClient->State, pszPath);
    10721188
    10731189            RTStrFree(pszPath);
     1190
     1191            rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
    10741192        }
    10751193        else
    10761194            rc = VERR_NO_MEMORY;
    1077 
    1078         return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     1195    }
     1196
     1197    if (RT_FAILURE(rc))
     1198    {
     1199        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
     1200        AssertRC(rc2);
    10791201    }
    10801202
     
    10911213    const char *pcszPath = apcszArgs[0];
    10921214
     1215    if (!rtFtpServerPathIsValid(pcszPath, false /* fIsAbsolute */))
     1216        return VERR_INVALID_PARAMETER;
     1217
    10931218    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathSetCurrent, pcszPath);
    10941219
    10951220    if (RT_SUCCESS(rc))
    1096     {
    1097         rtFtpSetCWD(pClient, pcszPath);
    1098 
    1099         return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
    1100     }
    1101 
    1102     return rc;
     1221        rc = rtFtpSetCWD(&pClient->State, pcszPath);
     1222
     1223    return rtFtpServerSendReplyRc(pClient,
     1224                                    RT_SUCCESS(rc)
     1225                                  ? RTFTPSERVER_REPLY_OKAY : RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
    11031226}
    11041227
     
    11191242
    11201243/**
     1244 * Formats the given user ID according to the specified options.
     1245 *
     1246 * @returns pszDst
     1247 * @param   uid             The UID to format.
     1248 * @param   pszOwner        The owner returned by the FS.
     1249 * @param   pszDst          The output buffer.
     1250 * @param   cbDst           The output buffer size.
     1251 */
     1252static const char *rtFtpServerDecimalFormatOwner(RTUID uid, const char *pszOwner, char *pszDst, size_t cbDst)
     1253{
     1254    if (pszOwner)
     1255    {
     1256        RTStrCopy(pszDst, cbDst, pszOwner);
     1257        return pszDst;
     1258    }
     1259    if (uid == NIL_RTUID)
     1260        return "<Nil>";
     1261
     1262    RTStrFormatU64(pszDst, cbDst, uid, 10, 0, 0, 0);
     1263    return pszDst;
     1264}
     1265
     1266/**
     1267 * Formats the given group ID according to the specified options.
     1268 *
     1269 * @returns pszDst
     1270 * @param   gid             The GID to format.
     1271 * @param   pszOwner        The owner returned by the FS.
     1272 * @param   pszDst          The output buffer.
     1273 * @param   cbDst           The output buffer size.
     1274 */
     1275static const char *rtFtpServerDecimalFormatGroup(RTGID gid, const char *pszGroup, char *pszDst, size_t cbDst)
     1276{
     1277    if (pszGroup)
     1278    {
     1279        RTStrCopy(pszDst, cbDst, pszGroup);
     1280        return pszDst;
     1281    }
     1282    if (gid == NIL_RTGID)
     1283        return "<Nil>";
     1284
     1285    RTStrFormatU64(pszDst, cbDst, gid, 10, 0, 0, 0);
     1286    return pszDst;
     1287}
     1288
     1289/**
     1290 * Format file size.
     1291 */
     1292static const char *rtFtpServerFormatSize(uint64_t cb, char *pszDst, size_t cbDst)
     1293{
     1294    RTStrFormatU64(pszDst, cbDst, cb, 10, 0, 0, 0);
     1295    return pszDst;
     1296}
     1297
     1298/**
     1299 * Formats the given timestamp according to the desired --time-style.
     1300 *
     1301 * @returns pszDst
     1302 * @param   pTimestamp      The timestamp.
     1303 * @param   pszDst          The output buffer.
     1304 * @param   cbDst           The output buffer size.
     1305 */
     1306static const char *rtFtpServerFormatTimestamp(PCRTTIMESPEC pTimestamp, char *pszDst, size_t cbDst)
     1307{
     1308    /** @todo timestamp formatting according to the given style.   */
     1309    return RTTimeSpecToString(pTimestamp, pszDst, cbDst);
     1310}
     1311
     1312/**
     1313 * Format name, i.e. escape, hide, quote stuff.
     1314 */
     1315static const char *rtFtpServerFormatName(const char *pszName, char *pszDst, size_t cbDst)
     1316{
     1317    /** @todo implement name formatting.   */
     1318    RT_NOREF(pszDst, cbDst);
     1319    return pszName;
     1320}
     1321
     1322/**
     1323 * Figures out the length for a 32-bit number when formatted as decimal.
     1324 * @returns Number of digits.
     1325 * @param   uValue              The number.
     1326 */
     1327DECLINLINE(size_t) rtFtpServerDecimalFormatLengthU32(uint32_t uValue)
     1328{
     1329    if (uValue < 10)
     1330        return 1;
     1331    if (uValue < 100)
     1332        return 2;
     1333    if (uValue < 1000)
     1334        return 3;
     1335    if (uValue < 10000)
     1336        return 4;
     1337    if (uValue < 100000)
     1338        return 5;
     1339    if (uValue < 1000000)
     1340        return 6;
     1341    if (uValue < 10000000)
     1342        return 7;
     1343    if (uValue < 100000000)
     1344        return 8;
     1345    if (uValue < 1000000000)
     1346        return 9;
     1347    return 10;
     1348}
     1349
     1350/**
     1351 * Allocates a new directory collection.
     1352 *
     1353 * @returns The collection allocated.
     1354 */
     1355static PRTFTPDIRCOLLECTION rtFtpServerDataConnDirCollAlloc(void)
     1356{
     1357    return (PRTFTPDIRCOLLECTION)RTMemAllocZ(sizeof(RTFTPDIRCOLLECTION));
     1358}
     1359
     1360/**
     1361 * Frees a directory collection and its entries.
     1362 *
     1363 * @param   pCollection         The collection to free.
     1364 */
     1365static void rtFtpServerDataConnDirCollFree(PRTFTPDIRCOLLECTION pCollection)
     1366{
     1367    PPRTFTPDIRENTRY    papEntries  = pCollection->papEntries;
     1368    size_t             j           = pCollection->cEntries;
     1369    while (j-- > 0)
     1370    {
     1371        RTMemFree(papEntries[j]);
     1372        papEntries[j] = NULL;
     1373    }
     1374    RTMemFree(papEntries);
     1375    pCollection->papEntries        = NULL;
     1376    pCollection->cEntries          = 0;
     1377    pCollection->cEntriesAllocated = 0;
     1378    RTMemFree(pCollection);
     1379}
     1380
     1381/**
     1382 * Adds one entry to a collection.
     1383 *
     1384 * @returns VBox status code.
     1385 * @param   pCollection         The collection to add entry to.
     1386 * @param   pszEntry            The entry name.
     1387 * @param   pInfo               The entry info.
     1388 * @param   pszOwner            The owner name if available, otherwise NULL.
     1389 * @param   pszGroup            The group anme if available, otherwise NULL.
     1390 * @param   pszTarget           The symbolic link target if applicable and
     1391 *                              available, otherwise NULL.
     1392 */
     1393static int rtFtpServerDataConnDirCollAddEntry(PRTFTPDIRCOLLECTION pCollection, const char *pszEntry, PRTFSOBJINFO pInfo,
     1394                                              const char *pszOwner, const char *pszGroup, const char *pszTarget)
     1395{
     1396
     1397    /* Make sure there is space in the collection for the new entry. */
     1398    if (pCollection->cEntries >= pCollection->cEntriesAllocated)
     1399    {
     1400        size_t cNew = pCollection->cEntriesAllocated ? pCollection->cEntriesAllocated * 2 : 16;
     1401        void *pvNew = RTMemRealloc(pCollection->papEntries, cNew * sizeof(pCollection->papEntries[0]));
     1402        if (!pvNew)
     1403            return VERR_NO_MEMORY;
     1404        pCollection->papEntries        = (PPRTFTPDIRENTRY)pvNew;
     1405        pCollection->cEntriesAllocated = cNew;
     1406    }
     1407
     1408    /* Create and insert a new entry. */
     1409    size_t const cchEntry = strlen(pszEntry);
     1410    size_t const cbOwner  = pszOwner  ? strlen(pszOwner)  + 1 : 0;
     1411    size_t const cbGroup  = pszGroup  ? strlen(pszGroup)  + 1 : 0;
     1412    size_t const cbTarget = pszTarget ? strlen(pszTarget) + 1 : 0;
     1413    size_t const cbEntry  = RT_UOFFSETOF_DYN(RTFTPDIRENTRY, szName[cchEntry + 1 + cbOwner + cbGroup + cbTarget]);
     1414    PRTFTPDIRENTRY pEntry = (PRTFTPDIRENTRY)RTMemAlloc(cbEntry);
     1415    if (pEntry)
     1416    {
     1417        pEntry->Info      = *pInfo;
     1418        pEntry->pszTarget = NULL; /** @todo symbolic links. */
     1419        pEntry->pszOwner  = NULL;
     1420        pEntry->pszGroup  = NULL;
     1421        pEntry->cchName   = cchEntry;
     1422        memcpy(pEntry->szName, pszEntry, cchEntry);
     1423        pEntry->szName[cchEntry] = '\0';
     1424
     1425        char *psz = &pEntry->szName[cchEntry + 1];
     1426        if (pszTarget)
     1427        {
     1428            pEntry->pszTarget = psz;
     1429            memcpy(psz, pszTarget, cbTarget);
     1430            psz += cbTarget;
     1431        }
     1432        if (pszOwner)
     1433        {
     1434            pEntry->pszOwner = psz;
     1435            memcpy(psz, pszOwner, cbOwner);
     1436            psz += cbOwner;
     1437        }
     1438        if (pszGroup)
     1439        {
     1440            pEntry->pszGroup = psz;
     1441            memcpy(psz, pszGroup, cbGroup);
     1442        }
     1443
     1444        pCollection->papEntries[pCollection->cEntries++] = pEntry;
     1445        pCollection->cbTotalAllocated += pEntry->Info.cbAllocated;
     1446        pCollection->cbTotalFiles     += pEntry->Info.cbObject;
     1447        return VINF_SUCCESS;
     1448    }
     1449    return VERR_NO_MEMORY;
     1450}
     1451
     1452static int rtFtpServerDataConnDirCollWrite(PRTFTPSERVERDATACONN pDataConn, PRTFTPDIRCOLLECTION pCollection,
     1453                                           char *pszTmp, size_t cbTmp)
     1454{
     1455    /*
     1456     * Figure the width of the size, the link count, the uid, the gid, and the inode columns.
     1457     */
     1458    size_t cchSizeCol  = 1;
     1459    size_t cchLinkCol  = 1;
     1460    size_t cchUidCol   = 1;
     1461    size_t cchGidCol   = 1;
     1462
     1463    size_t i = pCollection->cEntries;
     1464    while (i-- > 0)
     1465    {
     1466        PRTFTPDIRENTRY pEntry = pCollection->papEntries[i];
     1467
     1468        rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp);
     1469        size_t cchTmp = strlen(pszTmp);
     1470        if (cchTmp > cchSizeCol)
     1471            cchSizeCol = cchTmp;
     1472
     1473        cchTmp = rtFtpServerDecimalFormatLengthU32(pEntry->Info.Attr.u.Unix.cHardlinks) + 1;
     1474        if (cchTmp > cchLinkCol)
     1475            cchLinkCol = cchTmp;
     1476
     1477        rtFtpServerDecimalFormatOwner(pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp);
     1478        cchTmp = strlen(pszTmp);
     1479        if (cchTmp > cchUidCol)
     1480            cchUidCol = cchTmp;
     1481
     1482        rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp);
     1483        cchTmp = strlen(pszTmp);
     1484        if (cchTmp > cchGidCol)
     1485            cchGidCol = cchTmp;
     1486    }
     1487
     1488    size_t offTime = RT_UOFFSETOF(RTFTPDIRENTRY, Info.ModificationTime);
     1489
     1490    /*
     1491     * Display the entries.
     1492     */
     1493    for (i = 0; i < pCollection->cEntries; i++)
     1494    {
     1495        PRTFTPDIRENTRY pEntry = pCollection->papEntries[i];
     1496
     1497        RTFMODE fMode = pEntry->Info.Attr.fMode;
     1498        switch (fMode & RTFS_TYPE_MASK)
     1499        {
     1500            case RTFS_TYPE_FIFO:        rtFtpServerDataConnPrintf(pDataConn, "f"); break;
     1501            case RTFS_TYPE_DEV_CHAR:    rtFtpServerDataConnPrintf(pDataConn, "c"); break;
     1502            case RTFS_TYPE_DIRECTORY:   rtFtpServerDataConnPrintf(pDataConn, "d"); break;
     1503            case RTFS_TYPE_DEV_BLOCK:   rtFtpServerDataConnPrintf(pDataConn, "b"); break;
     1504            case RTFS_TYPE_FILE:        rtFtpServerDataConnPrintf(pDataConn, "-"); break;
     1505            case RTFS_TYPE_SYMLINK:     rtFtpServerDataConnPrintf(pDataConn, "l"); break;
     1506            case RTFS_TYPE_SOCKET:      rtFtpServerDataConnPrintf(pDataConn, "s"); break;
     1507            case RTFS_TYPE_WHITEOUT:    rtFtpServerDataConnPrintf(pDataConn, "w"); break;
     1508            default:                    rtFtpServerDataConnPrintf(pDataConn, "?"); AssertFailed(); break;
     1509        }
     1510        /** @todo sticy bits++ */
     1511        rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
     1512                                  fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
     1513                                  fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
     1514                                  fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
     1515        rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
     1516                                  fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
     1517                                  fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
     1518                                  fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
     1519        rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
     1520                                  fMode & RTFS_UNIX_IROTH ? 'r' : '-',
     1521                                  fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
     1522                                  fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
     1523
     1524        rtFtpServerDataConnPrintf(pDataConn, " %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
     1525                                  fMode & RTFS_DOS_READONLY          ? 'R' : '-',
     1526                                  fMode & RTFS_DOS_HIDDEN            ? 'H' : '-',
     1527                                  fMode & RTFS_DOS_SYSTEM            ? 'S' : '-',
     1528                                  fMode & RTFS_DOS_DIRECTORY         ? 'D' : '-',
     1529                                  fMode & RTFS_DOS_ARCHIVED          ? 'A' : '-',
     1530                                  fMode & RTFS_DOS_NT_DEVICE         ? 'd' : '-',
     1531                                  fMode & RTFS_DOS_NT_NORMAL         ? 'N' : '-',
     1532                                  fMode & RTFS_DOS_NT_TEMPORARY      ? 'T' : '-',
     1533                                  fMode & RTFS_DOS_NT_SPARSE_FILE    ? 'P' : '-',
     1534                                  fMode & RTFS_DOS_NT_REPARSE_POINT  ? 'J' : '-',
     1535                                  fMode & RTFS_DOS_NT_COMPRESSED     ? 'C' : '-',
     1536                                  fMode & RTFS_DOS_NT_OFFLINE        ? 'O' : '-',
     1537                                  fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
     1538                                  fMode & RTFS_DOS_NT_ENCRYPTED      ? 'E' : '-');
     1539
     1540        rtFtpServerDataConnPrintf(pDataConn, " %*u",
     1541                                  cchLinkCol, pEntry->Info.Attr.u.Unix.cHardlinks);
     1542        if (cchUidCol)
     1543            rtFtpServerDataConnPrintf(pDataConn, " %*s", cchUidCol,
     1544                                      rtFtpServerDecimalFormatOwner(pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp));
     1545        if (cchGidCol)
     1546            rtFtpServerDataConnPrintf(pDataConn, " %*s", cchGidCol,
     1547                                      rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp));
     1548
     1549        rtFtpServerDataConnPrintf(pDataConn," %*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp));
     1550
     1551        PCRTTIMESPEC pTime = (PCRTTIMESPEC)((uintptr_t)pEntry + offTime);
     1552        rtFtpServerDataConnPrintf(pDataConn," %s", rtFtpServerFormatTimestamp(pTime, pszTmp, cbTmp));
     1553
     1554        rtFtpServerDataConnPrintf(pDataConn," %s\r\n", rtFtpServerFormatName(pEntry->szName, pszTmp, cbTmp));
     1555    }
     1556
     1557    return VINF_SUCCESS;
     1558}
     1559
     1560/**
    11211561 * Thread for handling the LIST command's output in a separate data connection.
    11221562 *
     
    11371577    LogFlowFuncEnter();
    11381578
    1139     uint32_t cbBuf = _64K; /** @todo Improve this. */
    1140     void *pvBuf = RTMemAlloc(cbBuf);
    1141     if (!pvBuf)
    1142         return VERR_NO_MEMORY;
    1143 
    11441579    int rc;
     1580
     1581    char szTmp[RTPATH_MAX * 2];
     1582    PRTFTPDIRCOLLECTION pColl = rtFtpServerDataConnDirCollAlloc();
     1583    AssertPtrReturn(pColl, VERR_NO_MEMORY);
    11451584
    11461585    /* Set start indicator. */
     
    11491588    RTThreadUserSignal(RTThreadSelf());
    11501589
     1590    /* The first argument might indicate a directory to list.
     1591     * If no argument is given, the implementation must use the last directory set. */
     1592    char *pszPath = RTStrDup(  pDataConn->cArgs == 1
     1593                             ? pDataConn->papszArgs[0] : pDataConn->pClient->State.pszCWD); /** @todo Needs locking. */
     1594    AssertPtrReturn(pszPath, VERR_NO_MEMORY);
     1595    /* The paths already have been validated in the actual command handlers. */
     1596
     1597    void *pvHandle;
     1598    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirOpen, pszPath, &pvHandle);
     1599
    11511600    for (;;)
    11521601    {
    1153         /* The first argument might indicate a directory to list.
    1154          * If no argument is given, the implementation must use the last directory set. */
    1155         size_t cbRead = 0;
    1156         RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnList,
    1157                                          pDataConn->cArgs == 1
    1158                                        ? pDataConn->papszArgs[0] : NULL, pvBuf, cbBuf, &cbRead);
     1602        RTFSOBJINFO objInfo;
     1603        RT_ZERO(objInfo);
     1604
     1605        char *pszEntry  = NULL;
     1606        char *pszOwner  = NULL;
     1607        char *pszGroup  = NULL;
     1608        char *pszTarget = NULL;
     1609
     1610        RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirRead, pvHandle, &pszEntry,
     1611                                       &objInfo, &pszOwner, &pszGroup, &pszTarget);
    11591612        if (RT_SUCCESS(rc))
    11601613        {
    1161             int rc2 = rtFtpServerDataConnWrite(pDataConn, pvBuf, cbRead, NULL /* pcbWritten */);
    1162             AssertRC(rc2);
    1163 
    1164             if (rc == VINF_EOF)
    1165                 break;
    1166         }
    1167         else
    1168             break;
    1169 
    1170         if (ASMAtomicReadBool(&pDataConn->fStop))
    1171             break;
    1172     }
    1173 
    1174     RTMemFree(pvBuf);
    1175     pvBuf = NULL;
    1176 
    1177     pDataConn->fStopped = true;
    1178     pDataConn->rc       = rc;
    1179 
    1180     LogFlowFuncLeaveRC(rc);
    1181     return rc;
    1182 }
    1183 
    1184 static int rtFtpServerHandleLIST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    1185 {
    1186     int rc;
    1187 
    1188     RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, cArgs ? apcszArgs[0] : NULL, NULL /* PRTFSOBJINFO */);
    1189 
    1190     RTFTPSERVER_REPLY rcClient = RTFTPSERVER_REPLY_INVALID;
    1191 
    1192     if (RT_SUCCESS(rc))
    1193     {
    1194         int rc2 = rtFtpServerSendReplyRc(pClient,   pClient->pDataConn
    1195                                                   ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
    1196                                                   : RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN);
    1197         if (RT_SUCCESS(rc))
    1198             rc = rc2;
    1199 
    1200         if (RT_SUCCESS(rc))
    1201         {
    1202             rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
    1203             if (RT_SUCCESS(rc))
    1204             {
    1205                 rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
    1206             }
    1207 
    1208             rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
    1209                                                   ? RTFTPSERVER_REPLY_FILE_ACTION_OKAY_COMPLETED
    1210                                                   : RTFTPSERVER_REPLY_CLOSING_DATA_CONN);
     1614            int rc2 = rtFtpServerDataConnDirCollAddEntry(pColl, pszEntry,
     1615                                                         &objInfo, pszOwner, pszGroup, pszTarget);
     1616
     1617            RTStrFree(pszEntry);
     1618            pszEntry = NULL;
     1619
     1620            RTStrFree(pszOwner);
     1621            pszOwner = NULL;
     1622
     1623            RTStrFree(pszGroup);
     1624            pszGroup = NULL;
     1625
     1626            RTStrFree(pszTarget);
     1627            pszTarget = NULL;
     1628
    12111629            if (RT_SUCCESS(rc))
    12121630                rc = rc2;
    12131631        }
     1632        else
     1633        {
     1634            if (rc == VERR_NO_MORE_FILES)
     1635            {
     1636                rc = VINF_SUCCESS;
     1637                break;
     1638            }
     1639        }
     1640
     1641        if (RT_FAILURE(rc))
     1642            break;
     1643
     1644        if (ASMAtomicReadBool(&pDataConn->fStop))
     1645            break;
     1646    }
     1647
     1648    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirClose, pvHandle);
     1649    pvHandle = NULL;
     1650
     1651    if (RT_SUCCESS(rc))
     1652    {
     1653        int rc2 = rtFtpServerDataConnDirCollWrite(pDataConn, pColl, szTmp, sizeof(szTmp));
     1654        AssertRC(rc2);
     1655    }
     1656
     1657    rtFtpServerDataConnDirCollFree(pColl);
     1658
     1659    RTStrFree(pszPath);
     1660
     1661    pDataConn->fStopped = true;
     1662    pDataConn->rc       = rc;
     1663
     1664    LogFlowFuncLeaveRC(rc);
     1665    return rc;
     1666}
     1667
     1668static int rtFtpServerHandleLIST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
     1669{
     1670    /* If no argument is given, use the server's CWD as the path. */
     1671    const char *pcszPath = cArgs ? apcszArgs[0] : pClient->State.pszCWD;
     1672    AssertPtr(pcszPath);
     1673
     1674    int rc = VINF_SUCCESS;
     1675
     1676    if (!rtFtpServerPathIsValid(pcszPath, false /* fIsAbsolute */))
     1677    {
     1678        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
     1679        AssertRC(rc2);
    12141680    }
    12151681    else
    1216         rcClient = RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN;
     1682    {
     1683        RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pcszPath, NULL /* PRTFSOBJINFO */);
     1684
     1685        if (RT_SUCCESS(rc))
     1686        {
     1687            int rc2 = rtFtpServerSendReplyRc(pClient,   pClient->pDataConn
     1688                                                      ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
     1689                                                      : RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN);
     1690            if (RT_SUCCESS(rc))
     1691                rc = rc2;
     1692
     1693            if (RT_SUCCESS(rc))
     1694            {
     1695                rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
     1696                if (RT_SUCCESS(rc))
     1697                    rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
     1698
     1699                rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
     1700                                                      ? RTFTPSERVER_REPLY_FILE_ACTION_OKAY_COMPLETED
     1701                                                      : RTFTPSERVER_REPLY_CLOSING_DATA_CONN);
     1702                if (RT_SUCCESS(rc))
     1703                    rc = rc2;
     1704            }
     1705        }
     1706        else
     1707        {
     1708            int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
     1709            AssertRC(rc2);
     1710        }
     1711    }
    12171712
    12181713    return rc;
     
    13151810    RT_NOREF(cArgs, apcszArgs);
    13161811
    1317     rtFtpServerClientStateReset(&pClient->State);
    1318 
    1319     int rc = rtFtpServerDataConnClose(pClient->pDataConn);
    1320     if (RT_SUCCESS(rc))
    1321     {
    1322         rtFtpServerDataConnDestroy(pClient->pDataConn);
    1323         pClient->pDataConn = NULL;
     1812    int rc = VINF_SUCCESS;
     1813
     1814    if (pClient->pDataConn)
     1815    {
     1816        rc = rtFtpServerDataConnClose(pClient->pDataConn);
     1817        if (RT_SUCCESS(rc))
     1818        {
     1819            rtFtpServerDataConnDestroy(pClient->pDataConn);
     1820            pClient->pDataConn = NULL;
     1821        }
    13241822    }
    13251823
     
    16632161            RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnUserDisconnect, pClient->State.pszUser);
    16642162
     2163            rtFtpServerClientStateReset(&pClient->State);
     2164
    16652165            Assert(rcClient == RTFTPSERVER_REPLY_INVALID);
    16662166            rcClient = RTFTPSERVER_REPLY_CLOSING_CTRL_CONN;
     
    17562256    pState->pszUser = NULL;
    17572257
    1758     RTStrFree(pState->pszCWD);
    1759     pState->pszCWD = NULL;
     2258    int rc2 = rtFtpSetCWD(pState, "/");
     2259    AssertRC(rc2);
    17602260
    17612261    pState->cFailedLoginAttempts = 0;
     
    17932293     */
    17942294    int rc = rtFtpServerSendReplyRcEx(&Client, RTFTPSERVER_REPLY_READY_FOR_NEW_USER,
    1795                                       "Welcome!");
     2295                                     "Welcome!");
    17962296    if (RT_SUCCESS(rc))
    17972297    {
  • trunk/src/VBox/Runtime/tools/RTFTPServer.cpp

    r82813 r82822  
    5151#include <iprt/getopt.h>
    5252#include <iprt/initterm.h>
     53#include <iprt/mem.h>
    5354#include <iprt/message.h>
    5455#include <iprt/path.h>
     
    7374} FTPSERVERDATA;
    7475typedef FTPSERVERDATA *PFTPSERVERDATA;
     76
     77typedef struct FTPDIRHANDLE
     78{
     79    /** The VFS (chain) handle to use for this directory. */
     80    RTVFSDIR hVfsDir;
     81} FTPDIRHANDLE;
     82typedef FTPDIRHANDLE *PFTPDIRHANDLE;
    7583
    7684
     
    308316}
    309317
    310 static DECLCALLBACK(int) onList(PRTFTPCALLBACKDATA pData, const char *pcszPath, void *pvData, size_t cbData, size_t *pcbRead)
    311 {
    312     RT_NOREF(pData, pcszPath, pvData, cbData, pcbRead);
    313 
    314 #if 0
     318static DECLCALLBACK(int) onDirOpen(PRTFTPCALLBACKDATA pData, const char *pcszPath, void **ppvHandle)
     319{
    315320    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
    316321    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
    317322
    318     RTFILE hFile;
    319     int rc = RTFileOpen(&hFile, pcszPath ? pcszPath : pThis->szCWD,
    320                         RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
     323    PFTPDIRHANDLE pHandle = (PFTPDIRHANDLE)RTMemAllocZ(sizeof(FTPDIRHANDLE));
     324    if (!pHandle)
     325        return VERR_NO_MEMORY;
     326
     327    /* Construct absolute path. */
     328    char *pszPathAbs = NULL;
     329    int rc = RTStrAAppend(&pszPathAbs, pThis->szRootDir);
     330    AssertRCReturn(rc, rc);
     331    rc = RTStrAAppend(&pszPathAbs, pcszPath);
     332    AssertRCReturn(rc, rc);
     333
     334    RTPrintf("Opening directory '%s'\n", pszPathAbs);
     335
     336    rc = RTVfsChainOpenDir(pszPathAbs, 0 /*fFlags*/, &pHandle->hVfsDir, NULL /* poffError */, NULL /* pErrInfo */);
    321337    if (RT_SUCCESS(rc))
    322338    {
    323         RTFSOBJINFO fsObjInfo;
    324         rc = RTFileQueryInfo(hFile, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
     339        *ppvHandle = pHandle;
     340    }
     341    else
     342    {
     343        RTMemFree(pHandle);
     344    }
     345
     346    RTStrFree(pszPathAbs);
     347
     348    return rc;
     349}
     350
     351static DECLCALLBACK(int) onDirClose(PRTFTPCALLBACKDATA pData, void *pvHandle)
     352{
     353    RT_NOREF(pData);
     354
     355    PFTPDIRHANDLE pHandle = (PFTPDIRHANDLE)pvHandle;
     356    AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
     357
     358    RTVfsDirRelease(pHandle->hVfsDir);
     359
     360    RTMemFree(pHandle);
     361    pHandle = NULL;
     362
     363    return VINF_SUCCESS;
     364}
     365
     366static DECLCALLBACK(int) onDirRead(PRTFTPCALLBACKDATA pData, void *pvHandle, char **ppszEntry,
     367                                   PRTFSOBJINFO pInfo, char **ppszOwner, char **ppszGroup, char **ppszTarget)
     368{
     369    RT_NOREF(pData);
     370    RT_NOREF(ppszTarget); /* No symlinks yet */
     371
     372    PFTPDIRHANDLE pHandle = (PFTPDIRHANDLE)pvHandle;
     373    AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
     374
     375    size_t          cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
     376    PRTDIRENTRYEX   pDirEntry         = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
     377    if (!pDirEntry)
     378        return VERR_NO_MEMORY;
     379
     380    int rc;
     381
     382    for (;;)
     383    {
     384        size_t cbDirEntry = cbDirEntryAlloced;
     385        rc = RTVfsDirReadEx(pHandle->hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
     386        if (RT_FAILURE(rc))
     387        {
     388            if (rc == VERR_BUFFER_OVERFLOW)
     389            {
     390                RTMemTmpFree(pDirEntry);
     391                cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
     392                pDirEntry  = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
     393                if (pDirEntry)
     394                    continue;
     395            }
     396            else if (rc != VERR_NO_MORE_FILES)
     397                break;
     398        }
     399
    325400        if (RT_SUCCESS(rc))
    326401        {
    327             rc = fsObjInfoToStr(&fsObjInfo, (char *)pvData, cbData);
     402            if (pDirEntry->Info.Attr.u.Unix.uid != NIL_RTUID)
     403            {
     404                RTFSOBJINFO OwnerInfo;
     405                rc = RTVfsDirQueryPathInfo(pHandle->hVfsDir,
     406                                           pDirEntry->szName, &OwnerInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
     407                if (   RT_SUCCESS(rc)
     408                    && OwnerInfo.Attr.u.UnixOwner.szName[0])
     409                {
     410                    *ppszOwner = RTStrDup(&OwnerInfo.Attr.u.UnixOwner.szName[0]);
     411                    if (!*ppszOwner)
     412                        rc = VERR_NO_MEMORY;
     413                }
     414            }
     415
     416            if (   RT_SUCCESS(rc)
     417                && pDirEntry->Info.Attr.u.Unix.gid != NIL_RTGID)
     418            {
     419                RTFSOBJINFO GroupInfo;
     420                rc = RTVfsDirQueryPathInfo(pHandle->hVfsDir,
     421                                           pDirEntry->szName, &GroupInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
     422                if (   RT_SUCCESS(rc)
     423                    && GroupInfo.Attr.u.UnixGroup.szName[0])
     424                {
     425                    *ppszGroup = RTStrDup(&GroupInfo.Attr.u.UnixGroup.szName[0]);
     426                    if (!*ppszGroup)
     427                        rc = VERR_NO_MEMORY;
     428                }
     429            }
    328430        }
    329431
    330         RTFileClose(hFile);
    331     }
    332 #endif
    333 
    334     RTStrPrintf((char *)pvData, cbData, "-rwxr-xr-x 1 johndoe users    0 Apr  6  2017 foobar\r\n");
    335 
    336     *pcbRead = strlen((char *)pvData);
    337 
    338     /** @todo We ASSUME we're done here for now. */
    339     return VINF_EOF;
     432        *ppszEntry = RTStrDup(pDirEntry->szName);
     433        AssertPtrReturn(*ppszEntry, VERR_NO_MEMORY);
     434
     435        *pInfo = pDirEntry->Info;
     436
     437        break;
     438
     439    } /* for */
     440
     441    RTMemTmpFree(pDirEntry);
     442    pDirEntry = NULL;
     443
     444    return rc;
    340445}
    341446
     
    451556        Callbacks.pfnOnPathGetCurrent   = onPathGetCurrent;
    452557        Callbacks.pfnOnPathUp           = onPathUp;
    453         Callbacks.pfnOnList             = onList;
     558        Callbacks.pfnOnDirOpen          = onDirOpen;
     559        Callbacks.pfnOnDirClose         = onDirClose;
     560        Callbacks.pfnOnDirRead          = onDirRead;
    454561
    455562        RTFTPSERVER hFTPServer;
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