Skip to content

Commit

Permalink
[ftp] Cope with RETR completion prior to all data received
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Michael Brown committed Jul 30, 2008
1 parent fe1f017 commit 8f4c2b4
Showing 1 changed file with 33 additions and 15 deletions.
48 changes: 33 additions & 15 deletions src/net/tcp/ftp.c
Expand Up @@ -35,6 +35,7 @@ enum ftp_state {
FTP_TYPE,
FTP_PASV,
FTP_RETR,
FTP_WAIT,
FTP_QUIT,
FTP_DONE,
};
Expand Down Expand Up @@ -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,
};

/**
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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 );

}

/**
Expand Down Expand Up @@ -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 );
}
}

/**
Expand Down

0 comments on commit 8f4c2b4

Please sign in to comment.