Changeset 82838 in vbox for trunk/src/VBox/Runtime
- Timestamp:
- Jan 23, 2020 9:15:08 AM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/generic/ftp-server.cpp
r82824 r82838 50 50 #include <iprt/asm.h> 51 51 #include <iprt/assert.h> 52 #include <iprt/circbuf.h> 52 53 #include <iprt/err.h> 53 54 #include <iprt/file.h> /* For file mode flags. */ … … 58 59 #include <iprt/poll.h> 59 60 #include <iprt/socket.h> 61 #include <iprt/sort.h> 60 62 #include <iprt/string.h> 61 63 #include <iprt/system.h> … … 236 238 * Will be free'd by the data connection thread. */ 237 239 char** papszArgs; 240 /** Circular buffer for caching data before writing. */ 241 PRTCIRCBUF pCircBuf; 238 242 } RTFTPSERVERDATACONN; 239 243 /** Pointer to a data connection struct. */ … … 334 338 static int rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn); 335 339 static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn); 340 static int rtFtpServerDataConnFlush(PRTFTPSERVERDATACONN pDataConn); 336 341 337 342 static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState); … … 404 409 }; 405 410 411 /** RFC-1123 month of the year names. */ 412 static const char * const g_apszMonths[1+12] = 413 { 414 "000", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 415 }; 416 406 417 /** Feature string which represents all commands we support in addition to RFC 959 (see RFC 2398). 407 418 * Must match the command table above. … … 850 861 LogFlowFuncEnter(); 851 862 863 rtFtpServerDataConnFlush(pDataConn); 864 852 865 rc = RTTcpClientClose(pDataConn->hSocket); 853 866 pDataConn->hSocket = NIL_RTSOCKET; … … 865 878 * @param pvData Data to write. 866 879 * @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. 868 881 */ 869 882 static int rtFtpServerDataConnWrite(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData, size_t *pcbWritten) 870 883 { 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 */ 900 static 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 */ 937 static 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 */ 958 static 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; 874 987 } 875 988 … … 896 1009 RTStrFree(pszFmt); 897 1010 898 rc = RTTcpWrite(pDataConn->hSocket, pszMsg, strlen(pszMsg));1011 rc = rtFtpServerDataConnAddData(pDataConn, pszMsg, strlen(pszMsg)); 899 1012 900 1013 RTStrFree(pszMsg); … … 1003 1116 pDataConn->uPort = pClient->uDataConnPort; 1004 1117 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 } 1006 1123 1007 1124 LogFlowFuncLeaveRC(VINF_SUCCESS); 1008 return VINF_SUCCESS;1125 return rc; 1009 1126 } 1010 1127 … … 1120 1237 rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs); 1121 1238 1239 RTCircBufDestroy(pDataConn->pCircBuf); 1240 1122 1241 RTMemFree(pDataConn); 1123 1242 pDataConn = NULL; … … 1296 1415 1297 1416 /** 1298 * Formats the given timestamp according to the desired --time-style.1417 * Formats the given timestamp according to (non-standardized) FTP LIST command. 1299 1418 * 1300 1419 * @returns pszDst 1301 * @param pTimestamp The timestamp .1420 * @param pTimestamp The timestamp to format. 1302 1421 * @param pszDst The output buffer. 1303 1422 * @param cbDst The output buffer size. … … 1305 1424 static const char *rtFtpServerFormatTimestamp(PCRTTIMESPEC pTimestamp, char *pszDst, size_t cbDst) 1306 1425 { 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; 1309 1462 } 1310 1463 … … 1393 1546 const char *pszOwner, const char *pszGroup, const char *pszTarget) 1394 1547 { 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 } 1395 1554 1396 1555 /* Make sure there is space in the collection for the new entry. */ … … 1449 1608 } 1450 1609 1610 /** @callback_method_impl{FNRTSORTCMP, Name} */ 1611 static 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} */ 1620 static 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 */ 1636 static void rtFtpServerCollSort(PRTFTPDIRCOLLECTION pCollection) 1637 { 1638 PFNRTSORTCMP pfnCmp = rtFtpServerCollEntryCmpDirFirstName; 1639 if (pfnCmp) 1640 RTSortApvShell((void **)pCollection->papEntries, pCollection->cEntries, pfnCmp, NULL); 1641 } 1642 1451 1643 /** 1452 1644 * Writes a directory collection to a specific data connection. … … 1461 1653 char *pszTmp, size_t cbTmp) 1462 1654 { 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; 1467 1656 size_t cchLinkCol = 1; 1468 1657 size_t cchUidCol = 1; … … 1516 1705 default: rtFtpServerDataConnPrintf(pDataConn, "?"); AssertFailed(); break; 1517 1706 } 1518 /** @todo sticy bits++ */ 1707 1519 1708 rtFtpServerDataConnPrintf(pDataConn, "%c%c%c", 1520 1709 fMode & RTFS_UNIX_IRUSR ? 'r' : '-', … … 1530 1719 fMode & RTFS_UNIX_IXOTH ? 'x' : '-'); 1531 1720 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 1548 1721 rtFtpServerDataConnPrintf(pDataConn, " %*u", 1549 1722 cchLinkCol, pEntry->Info.Attr.u.Unix.cHardlinks); 1723 1550 1724 if (cchUidCol) 1551 1725 rtFtpServerDataConnPrintf(pDataConn, " %*s", cchUidCol, … … 1555 1729 rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp)); 1556 1730 1557 rtFtpServerDataConnPrintf(pDataConn, "%*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp));1731 rtFtpServerDataConnPrintf(pDataConn, "%*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp)); 1558 1732 1559 1733 PCRTTIMESPEC pTime = (PCRTTIMESPEC)((uintptr_t)pEntry + offTime); … … 1657 1831 pvHandle = NULL; 1658 1832 1833 rtFtpServerCollSort(pColl); 1834 1659 1835 if (RT_SUCCESS(rc)) 1660 1836 { … … 1693 1869 if (RT_SUCCESS(rc)) 1694 1870 { 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) 1702 1872 { 1703 1873 rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn); … … 1705 1875 rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs); 1706 1876 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); 1712 1886 } 1713 1887 } … … 2234 2408 Assert(pClient->pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS); 2235 2409 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 2236 2415 rc = rtFtpServerDataConnStop(pClient->pDataConn); 2237 2416 if (RT_SUCCESS(rc))
Note:
See TracChangeset
for help on using the changeset viewer.