[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.
pull/1/head
Michael Brown 2008-07-30 20:22:49 +01:00
parent fe1f017bde
commit 8f4c2b4a4c
1 changed files with 33 additions and 15 deletions

View File

@ -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,
};
/**
@ -169,6 +171,27 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
} while ( --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
*
@ -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 );
}
}
/**