summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2008-07-30 21:22:49 +0200
committerMichael Brown2008-07-30 21:22:49 +0200
commit8f4c2b4a4c5c3a3d29a102a758e75b65cadf9946 (patch)
tree12303def603513d7ae85163a93c9988397845918
parent[romprefix] Update PCI ROM structure to PCI 3.0 (diff)
downloadipxe-8f4c2b4a4c5c3a3d29a102a758e75b65cadf9946.tar.gz
ipxe-8f4c2b4a4c5c3a3d29a102a758e75b65cadf9946.tar.xz
ipxe-8f4c2b4a4c5c3a3d29a102a758e75b65cadf9946.zip
[ftp] Cope with RETR completion prior to all data received
Based on a patch contributed by Sergey Vlasov <vsu@altlinux.ru> : In my testing with "qemu -net user" the 226 response to RETR was often received earlier than final packets of the data connection; this caused the received file to become truncated without any error indication. Fix this by adding an intermediate state FTP_TRANSFER between FTP_RETR and FTP_QUIT, so that the transfer is considered to be complete only when both the end of data connection is encountered and the final reply to the RETR command is received.
-rw-r--r--src/net/tcp/ftp.c48
1 files changed, 33 insertions, 15 deletions
diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c
index ffb2fbff..9a12064e 100644
--- a/src/net/tcp/ftp.c
+++ b/src/net/tcp/ftp.c
@@ -35,6 +35,7 @@ enum ftp_state {
FTP_TYPE,
FTP_PASV,
FTP_RETR,
+ FTP_WAIT,
FTP_QUIT,
FTP_DONE,
};
@@ -116,14 +117,15 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
* snprintf() call.
*/
static const char * ftp_strings[] = {
- [FTP_CONNECT] = "",
+ [FTP_CONNECT] = NULL,
[FTP_USER] = "USER anonymous\r\n",
[FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
[FTP_TYPE] = "TYPE I\r\n",
[FTP_PASV] = "PASV\r\n",
- [FTP_RETR] = "RETR %s\r\n",
+ [FTP_RETR] = "RETR %s\r\n",
+ [FTP_WAIT] = NULL,
[FTP_QUIT] = "QUIT\r\n",
- [FTP_DONE] = "",
+ [FTP_DONE] = NULL,
};
/**
@@ -170,6 +172,27 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
}
/**
+ * Move to next state and send the appropriate FTP control string
+ *
+ * @v ftp FTP request
+ *
+ */
+static void ftp_next_state ( struct ftp_request *ftp ) {
+
+ /* Move to next state */
+ if ( ftp->state < FTP_DONE )
+ ftp->state++;
+
+ /* Send control string if needed */
+ if ( ftp_strings[ftp->state] != NULL ) {
+ DBGC ( ftp, "FTP %p sending ", ftp );
+ DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
+ xfer_printf ( &ftp->control, ftp_strings[ftp->state],
+ ftp->uri->path );
+ }
+}
+
+/**
* Handle an FTP control channel response
*
* @v ftp FTP request
@@ -223,17 +246,9 @@ static void ftp_reply ( struct ftp_request *ftp ) {
}
}
- /* Move to next state */
- if ( ftp->state < FTP_DONE )
- ftp->state++;
-
- /* Send control string */
- if ( ftp->state < FTP_DONE ) {
- DBGC ( ftp, "FTP %p sending ", ftp );
- DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
- xfer_printf ( &ftp->control, ftp_strings[ftp->state],
- ftp->uri->path );
- }
+ /* Move to next state and send control string */
+ ftp_next_state ( ftp );
+
}
/**
@@ -331,8 +346,11 @@ static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
ftp, strerror ( rc ) );
/* If there was an error, close control channel and record status */
- if ( rc )
+ if ( rc ) {
ftp_done ( ftp, rc );
+ } else {
+ ftp_next_state ( ftp );
+ }
}
/**