Changeset 82822 in vbox for trunk/src/VBox
- Timestamp:
- Jan 22, 2020 11:48:43 AM (5 years ago)
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/generic/ftp-server.cpp
r82813 r82822 30 30 * - UTF-8 support only. 31 31 * - Only supports ASCII + binary (image type) file streams for now. 32 * - No directory / file caching yet. 32 33 * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++). 33 34 * - No FTPS / SFTP support. … … 85 86 /** Pointer to an internal FTP server instance. */ 86 87 typedef RTFTPSERVERINTERNAL *PRTFTPSERVERINTERNAL; 88 89 /** 90 * FTP directory entry. 91 */ 92 typedef 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. */ 108 typedef RTFTPDIRENTRY *PRTFTPDIRENTRY; 109 /** Pointer to a FTP directory entry pointer. */ 110 typedef PRTFTPDIRENTRY *PPRTFTPDIRENTRY; 111 112 /** 113 * Collection of directory entries. 114 * Used for also caching stuff. 115 */ 116 typedef 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. */ 132 typedef RTFTPDIRCOLLECTION *PRTFTPDIRCOLLECTION; 133 /** Pointer to a directory entry collection pointer. */ 134 typedef PRTFTPDIRCOLLECTION *PPRTFTPDIRCOLLECTION; 87 135 88 136 … … 468 516 469 517 /** 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 */ 524 static 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 /** 470 553 * Sets the current working directory for a client. 471 554 * 472 555 * @returns VBox status code. 473 * @param p Client Clientto set current working directory for.556 * @param pState Client state to set current working directory for. 474 557 * @param pcszPath Working directory to set. 475 558 */ 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; 559 static 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; 485 571 AssertRC(rc); 486 572 return rc; … … 789 875 790 876 /** 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 */ 884 static 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 /** 791 907 * Data connection thread for writing (sending) a file to the client. 792 908 * … … 1069 1185 1070 1186 if (RT_SUCCESS(rc)) 1071 r tFtpSetCWD(pClient, pszPath);1187 rc = rtFtpSetCWD(&pClient->State, pszPath); 1072 1188 1073 1189 RTStrFree(pszPath); 1190 1191 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY); 1074 1192 } 1075 1193 else 1076 1194 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); 1079 1201 } 1080 1202 … … 1091 1213 const char *pcszPath = apcszArgs[0]; 1092 1214 1215 if (!rtFtpServerPathIsValid(pcszPath, false /* fIsAbsolute */)) 1216 return VERR_INVALID_PARAMETER; 1217 1093 1218 RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathSetCurrent, pcszPath); 1094 1219 1095 1220 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); 1103 1226 } 1104 1227 … … 1119 1242 1120 1243 /** 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 */ 1252 static 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 */ 1275 static 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 */ 1292 static 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 */ 1306 static 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 */ 1315 static 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 */ 1327 DECLINLINE(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 */ 1355 static 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 */ 1365 static 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 */ 1393 static 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 1452 static 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 /** 1121 1561 * Thread for handling the LIST command's output in a separate data connection. 1122 1562 * … … 1137 1577 LogFlowFuncEnter(); 1138 1578 1139 uint32_t cbBuf = _64K; /** @todo Improve this. */1140 void *pvBuf = RTMemAlloc(cbBuf);1141 if (!pvBuf)1142 return VERR_NO_MEMORY;1143 1144 1579 int rc; 1580 1581 char szTmp[RTPATH_MAX * 2]; 1582 PRTFTPDIRCOLLECTION pColl = rtFtpServerDataConnDirCollAlloc(); 1583 AssertPtrReturn(pColl, VERR_NO_MEMORY); 1145 1584 1146 1585 /* Set start indicator. */ … … 1149 1588 RTThreadUserSignal(RTThreadSelf()); 1150 1589 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 1151 1600 for (;;) 1152 1601 { 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); 1159 1612 if (RT_SUCCESS(rc)) 1160 1613 { 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 1211 1629 if (RT_SUCCESS(rc)) 1212 1630 rc = rc2; 1213 1631 } 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 1668 static 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); 1214 1680 } 1215 1681 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 } 1217 1712 1218 1713 return rc; … … 1315 1810 RT_NOREF(cArgs, apcszArgs); 1316 1811 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 } 1324 1822 } 1325 1823 … … 1663 2161 RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnUserDisconnect, pClient->State.pszUser); 1664 2162 2163 rtFtpServerClientStateReset(&pClient->State); 2164 1665 2165 Assert(rcClient == RTFTPSERVER_REPLY_INVALID); 1666 2166 rcClient = RTFTPSERVER_REPLY_CLOSING_CTRL_CONN; … … 1756 2256 pState->pszUser = NULL; 1757 2257 1758 RTStrFree(pState->pszCWD);1759 pState->pszCWD = NULL;2258 int rc2 = rtFtpSetCWD(pState, "/"); 2259 AssertRC(rc2); 1760 2260 1761 2261 pState->cFailedLoginAttempts = 0; … … 1793 2293 */ 1794 2294 int rc = rtFtpServerSendReplyRcEx(&Client, RTFTPSERVER_REPLY_READY_FOR_NEW_USER, 1795 2295 "Welcome!"); 1796 2296 if (RT_SUCCESS(rc)) 1797 2297 { -
trunk/src/VBox/Runtime/tools/RTFTPServer.cpp
r82813 r82822 51 51 #include <iprt/getopt.h> 52 52 #include <iprt/initterm.h> 53 #include <iprt/mem.h> 53 54 #include <iprt/message.h> 54 55 #include <iprt/path.h> … … 73 74 } FTPSERVERDATA; 74 75 typedef FTPSERVERDATA *PFTPSERVERDATA; 76 77 typedef struct FTPDIRHANDLE 78 { 79 /** The VFS (chain) handle to use for this directory. */ 80 RTVFSDIR hVfsDir; 81 } FTPDIRHANDLE; 82 typedef FTPDIRHANDLE *PFTPDIRHANDLE; 75 83 76 84 … … 308 316 } 309 317 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 318 static DECLCALLBACK(int) onDirOpen(PRTFTPCALLBACKDATA pData, const char *pcszPath, void **ppvHandle) 319 { 315 320 PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser; 316 321 Assert(pData->cbUser == sizeof(FTPSERVERDATA)); 317 322 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 */); 321 337 if (RT_SUCCESS(rc)) 322 338 { 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 351 static 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 366 static 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 325 400 if (RT_SUCCESS(rc)) 326 401 { 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 } 328 430 } 329 431 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; 340 445 } 341 446 … … 451 556 Callbacks.pfnOnPathGetCurrent = onPathGetCurrent; 452 557 Callbacks.pfnOnPathUp = onPathUp; 453 Callbacks.pfnOnList = onList; 558 Callbacks.pfnOnDirOpen = onDirOpen; 559 Callbacks.pfnOnDirClose = onDirClose; 560 Callbacks.pfnOnDirRead = onDirRead; 454 561 455 562 RTFTPSERVER hFTPServer;
Note:
See TracChangeset
for help on using the changeset viewer.