Index: /trunk/include/iprt/ftp.h
===================================================================
--- /trunk/include/iprt/ftp.h	(revision 82771)
+++ /trunk/include/iprt/ftp.h	(revision 82772)
@@ -120,6 +120,8 @@
     /** Invalid reply type, do not use. */
     RTFTPSERVER_REPLY_INVALID                        = 0,
+    /** Data connection already open. */
+    RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN          = 125,
     /** Command okay. */
-    RTFTPSERVER_REPLY_FILE_STATUS_OKAY               = 150,
+    RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN  = 150,
     /** Command okay. */
     RTFTPSERVER_REPLY_OKAY                           = 200,
@@ -134,4 +136,6 @@
     /** Closing data connection. */
     RTFTPSERVER_REPLY_CLOSING_DATA_CONN              = 226,
+    /** Requested file action okay, completed. */
+    RTFTPSERVER_REPLY_FILE_ACTION_OKAY_COMPLETED     = 250,
     /** "PATHNAME" ok (created / exists). */
     RTFTPSERVER_REPLY_PATHNAME_OK                    = 257,
@@ -140,8 +144,14 @@
     /** User name okay, need password. */
     RTFTPSERVER_REPLY_USERNAME_OKAY_NEED_PASSWORD    = 331,
+    /** Service not available, closing control connection. */
+    RTFTPSERVER_REPLY_SVC_NOT_AVAIL_CLOSING_CTRL_CONN = 421,
     /** Can't open data connection. */
     RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN            = 425,
     /** Connection closed; transfer aborted. */
     RTFTPSERVER_REPLY_CONN_CLOSED_TRANSFER_ABORTED   = 426,
+    /** Requested file action not taken. */
+    RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN = 450,
+    /** Requested action aborted; local error in processing. */
+    RTFTPSERVER_REPLY_ACTION_ABORTED_LOCAL_ERROR     = 451,
     /** Syntax error, command unrecognized. */
     RTFTPSERVER_REPLY_ERROR_CMD_NOT_RECOGNIZED       = 500,
Index: /trunk/src/VBox/Runtime/generic/ftp-server.cpp
===================================================================
--- /trunk/src/VBox/Runtime/generic/ftp-server.cpp	(revision 82771)
+++ /trunk/src/VBox/Runtime/generic/ftp-server.cpp	(revision 82772)
@@ -201,4 +201,8 @@
     /** Actual client state. */
     RTFTPSERVERCLIENTSTATE      State;
+    /** The last set data connection IP. */
+    RTNETADDRIPV4               DataConnAddr;
+    /** The last set data connection port number. */
+    uint16_t                    uDataConnPort;
     /** Data connection information.
      *  At the moment we only allow one data connection per client at a time. */
@@ -274,9 +278,10 @@
 *********************************************************************************************************************************/
 
-static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
+static int  rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
+static int  rtFtpServerDataConnClose(PRTFTPSERVERDATACONN pDataConn);
 static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn);
-static int rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread,
-                                    uint8_t cArgs, const char * const *apcszArgs);
-static int rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn);
+static int  rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread, uint8_t cArgs, const char * const *apcszArgs);
+static int  rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn);
+static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn);
 
 static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState);
@@ -398,10 +403,12 @@
     AssertReturn(cch > 0, VERR_NO_MEMORY);
 
-    int rc = RTStrAPrintf(&pszMsg, "%RU32", enmReply);
+    int rc = RTStrAPrintf(&pszMsg, "%RU32 -", enmReply);
     AssertRCReturn(rc, rc);
 
+    /** @todo Support multi-line replies (see 4.2ff). */
+
     if (pszFmt)
     {
-        rc = RTStrAAppend(&pszMsg, " - ");
+        rc = RTStrAAppend(&pszMsg, " ");
         AssertRCReturn(rc, rc);
 
@@ -514,5 +521,5 @@
     } while (0);
 
-    char szTemp[32];
+    char szTemp[128];
 
     INFO_TO_STR("%c", chFileType);
@@ -698,5 +705,17 @@
     AssertReturn(cchAdddress > 0, VERR_NO_MEMORY);
 
-    return RTTcpClientConnect(szAddress, uPort, &pDataConn->hSocket);
+    int rc;
+
+    /* Try a bit harder if the data connection is not ready (yet). */
+    for (int i = 0; i < 10; i++)
+    {
+        rc = RTTcpClientConnect(szAddress, uPort, &pDataConn->hSocket);
+        if (RT_SUCCESS(rc))
+            break;
+        RTThreadSleep(100);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
 }
 
@@ -715,12 +734,9 @@
         LogFlowFuncEnter();
 
-        rc = RTTcpFlush(pDataConn->hSocket);
-        if (RT_SUCCESS(rc))
-        {
-            rc = RTTcpClientClose(pDataConn->hSocket);
-            pDataConn->hSocket = NIL_RTSOCKET;
-        }
-    }
-
+        rc = RTTcpClientClose(pDataConn->hSocket);
+        pDataConn->hSocket = NIL_RTSOCKET;
+    }
+
+    LogFlowFuncLeaveRC(rc);
     return rc;
 }
@@ -837,4 +853,8 @@
 
     pDataConn->pClient = pClient;
+
+    /* Use the last configured addr + port. */
+    pDataConn->Addr    = pClient->DataConnAddr;
+    pDataConn->uPort   = pClient->uDataConnPort;
 
     *ppDataConn = pDataConn;
@@ -909,10 +929,10 @@
 
 /**
- * Destroys a data connection.
- *
- * @returns VBox status code.
- * @param   pDataConn           Data connection to destroy. The pointer is not valid anymore after successful return.
- */
-static int rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn)
+ * Stops a data connection.
+ *
+ * @returns VBox status code.
+ * @param   pDataConn           Data connection to stop.
+ */
+static int rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn)
 {
     if (!pDataConn)
@@ -933,16 +953,31 @@
 
     if (RT_SUCCESS(rc))
-    {
         rtFtpServerDataConnClose(pDataConn);
-        rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
-
-        RTMemFree(pDataConn);
-        pDataConn = NULL;
-
-        /** @todo Also check / handle rcThread? */
-    }
 
     LogFlowFuncLeaveRC(rc);
     return rc;
+}
+
+/**
+ * Destroys a data connection.
+ *
+ * @returns VBox status code.
+ * @param   pDataConn           Data connection to destroy. The pointer is not valid anymore after successful return.
+ */
+static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn)
+{
+    if (!pDataConn)
+        return;
+
+    LogFlowFuncEnter();
+
+    rtFtpServerDataConnClose(pDataConn);
+    rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
+
+    RTMemFree(pDataConn);
+    pDataConn = NULL;
+
+    LogFlowFuncLeave();
+    return;
 }
 
@@ -975,7 +1010,8 @@
     RT_NOREF(cArgs, apcszArgs);
 
-    int rc = rtFtpServerDataConnDestroy(pClient->pDataConn);
-    if (RT_SUCCESS(rc))
-    {
+    int rc = rtFtpServerDataConnClose(pClient->pDataConn);
+    if (RT_SUCCESS(rc))
+    {
+        rtFtpServerDataConnDestroy(pClient->pDataConn);
         pClient->pDataConn = NULL;
 
@@ -1098,17 +1134,33 @@
     int rc;
 
-    /* Note: Data connection gets created when the PORT command was sent. */
-    if (pClient->pDataConn)
-    {
-        rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
+    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, cArgs ? apcszArgs[0] : NULL, NULL /* PRTFSOBJINFO */);
+
+    RTFTPSERVER_REPLY rcClient = RTFTPSERVER_REPLY_INVALID;
+
+    if (RT_SUCCESS(rc))
+    {
+        int rc2 = rtFtpServerSendReplyRc(pClient,   pClient->pDataConn
+                                                  ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
+                                                  : RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+
+        if (RT_SUCCESS(rc))
+        {
+            rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
+            if (RT_SUCCESS(rc))
+            {
+                rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
+            }
+
+            rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
+                                                  ? RTFTPSERVER_REPLY_FILE_ACTION_OKAY_COMPLETED
+                                                  : RTFTPSERVER_REPLY_CLOSING_DATA_CONN);
+            if (RT_SUCCESS(rc))
+                rc = rc2;
+        }
     }
     else
-        rc = VERR_FTP_DATA_CONN_NOT_FOUND;
-
-    int rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
-                                     ? RTFTPSERVER_REPLY_PATHNAME_OK
-                                     : RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
-    if (RT_SUCCESS(rc))
-        rc = rc2;
+        rcClient = RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN;
 
     return rc;
@@ -1163,22 +1215,15 @@
         return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
 
-    PRTFTPSERVERDATACONN pDataConn;
-    int rc = rtFtpServerDataConnCreate(pClient, &pDataConn);
-    if (RT_SUCCESS(rc))
-    {
-        pClient->pDataConn = pDataConn;
-
-        rc = rtFtpParseHostAndPort(apcszArgs[0], &pDataConn->Addr, &pDataConn->uPort);
-        if (RT_SUCCESS(rc))
-        {
-            rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
-        }
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
-        AssertRC(rc2);
-    }
+    RTFTPSERVER_REPLY rcClient;
+
+    int rc = rtFtpParseHostAndPort(apcszArgs[0], &pClient->DataConnAddr, &pClient->uDataConnPort);
+    if (RT_SUCCESS(rc))
+        rcClient = RTFTPSERVER_REPLY_OKAY;
+    else
+        rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
+
+    int rc2 = rtFtpServerSendReplyRc(pClient, rcClient);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
 
     return rc;
@@ -1205,7 +1250,16 @@
     RT_NOREF(cArgs, apcszArgs);
 
-    int rc = rtFtpServerDataConnDestroy(pClient->pDataConn);
-    if (RT_SUCCESS(rc))
+    rtFtpServerClientStateReset(&pClient->State);
+
+    int rc = rtFtpServerDataConnClose(pClient->pDataConn);
+    if (RT_SUCCESS(rc))
+    {
+        rtFtpServerDataConnDestroy(pClient->pDataConn);
         pClient->pDataConn = NULL;
+    }
+
+    int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
 
     return rc;
@@ -1230,5 +1284,5 @@
             rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnFileWriteThread, cArgs, apcszArgs);
             if (RT_SUCCESS(rc))
-                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STATUS_OKAY);
+                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STS_OK_OPENING_DATA_CONN);
         }
         else
@@ -1607,14 +1661,10 @@
                 Assert(pClient->pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
 
-                rc = rtFtpServerSendReplyRc(pClient, RT_SUCCESS(pClient->pDataConn->rc)
-                                                     ? RTFTPSERVER_REPLY_CLOSING_DATA_CONN
-                                                     : RTFTPSERVER_REPLY_CONN_CLOSED_TRANSFER_ABORTED);
-
-                int rc2 = rtFtpServerDataConnDestroy(pClient->pDataConn);
-                if (RT_SUCCESS(rc2))
+                rc = rtFtpServerDataConnStop(pClient->pDataConn);
+                if (RT_SUCCESS(rc))
+                {
+                    rtFtpServerDataConnDestroy(pClient->pDataConn);
                     pClient->pDataConn = NULL;
-
-                if (RT_SUCCESS(rc))
-                    rc = rc2;
+                }
             }
         }
@@ -1622,10 +1672,6 @@
 
     /* Make sure to destroy all data connections. */
-    int rc2 = rtFtpServerDataConnDestroy(pClient->pDataConn);
-    if (RT_SUCCESS(rc2))
-        pClient->pDataConn = NULL;
-
-    if (RT_SUCCESS(rc))
-        rc = rc2;
+    rtFtpServerDataConnDestroy(pClient->pDataConn);
+    pClient->pDataConn = NULL;
 
     LogFlowFuncLeaveRC(rc);
Index: /trunk/src/VBox/Runtime/tools/RTFTPServer.cpp
===================================================================
--- /trunk/src/VBox/Runtime/tools/RTFTPServer.cpp	(revision 82771)
+++ /trunk/src/VBox/Runtime/tools/RTFTPServer.cpp	(revision 82772)
@@ -253,8 +253,9 @@
 static DECLCALLBACK(int) onFileStat(PRTFTPCALLBACKDATA pData, const char *pcszPath, PRTFSOBJINFO pFsObjInfo)
 {
-    RT_NOREF(pData);
+    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
+    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
 
     RTFILE hFile;
-    int rc = RTFileOpen(&hFile, pcszPath,
+    int rc = RTFileOpen(&hFile, pcszPath ? pcszPath : pThis->szCWD,
                         RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
     if (RT_SUCCESS(rc))
@@ -309,4 +310,29 @@
     RT_NOREF(pData, pcszPath, pvData, cbData, pcbRead);
 
+#if 0
+    PFTPSERVERDATA pThis = (PFTPSERVERDATA)pData->pvUser;
+    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
+
+    RTFILE hFile;
+    int rc = RTFileOpen(&hFile, pcszPath ? pcszPath : pThis->szCWD,
+                        RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+    if (RT_SUCCESS(rc))
+    {
+        RTFSOBJINFO fsObjInfo;
+        rc = RTFileQueryInfo(hFile, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
+        if (RT_SUCCESS(rc))
+        {
+            rc = fsObjInfoToStr(&fsObjInfo, (char *)pvData, cbData);
+        }
+
+        RTFileClose(hFile);
+    }
+#endif
+
+    RTStrPrintf((char *)pvData, cbData, "-rwxr-xr-x 1 johndoe users    0 Apr  6  2017 foobar\r\n");
+
+    *pcbRead = strlen((char *)pvData);
+
+    /** @todo We ASSUME we're done here for now. */
     return VINF_EOF;
 }
