[PATCH 5/8] tftp: support non rfc 2347 servers

Enrico Scholz enrico.scholz at sigma-chemnitz.de
Sun Aug 28 07:02:28 PDT 2022


State machine must be changed for servers without OACK support.  Here,
objects for data transfer must be allocated before the first datagram
is handled and it is possible that whole transfer finishes at this
point.

Fixes "tftp: allocate buffers and fifo dynamically"

Signed-off-by: Enrico Scholz <enrico.scholz at sigma-chemnitz.de>
---
 fs/tftp.c | 87 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 58 insertions(+), 29 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index fb6c368b3a64..7edea904aabe 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -64,13 +64,12 @@
 #define STATE_WRQ	2
 #define STATE_RDATA	3
 #define STATE_WDATA	4
-#define STATE_OACK	5
+/* OACK from server has been received and we can begin to sent either the ACK
+   (for RRQ) or data (for WRQ) */
+#define STATE_START	5
 #define STATE_WAITACK	6
 #define STATE_LAST	7
 #define STATE_DONE	8
-/* OACK from server has been received and we can begin to sent either the ACK
-   (for RRQ) or data (for WRQ) */
-#define STATE_START	9
 
 #define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
 #define TFTP_MTU_SIZE		1432	/* MTU based block size */
@@ -363,7 +362,6 @@ static char const * const tftp_states[] = {
 	[STATE_WRQ] = "WRQ",
 	[STATE_RDATA] = "RDATA",
 	[STATE_WDATA] = "WDATA",
-	[STATE_OACK] = "OACK",
 	[STATE_WAITACK] = "WAITACK",
 	[STATE_LAST] = "LAST",
 	[STATE_DONE] = "DONE",
@@ -431,7 +429,6 @@ static int tftp_send(struct file_priv *priv)
 		break;
 
 	case STATE_RDATA:
-	case STATE_OACK:
 		xp = pkt;
 		s = (uint16_t *)pkt;
 		*s++ = htons(TFTP_ACK);
@@ -649,6 +646,7 @@ static void tftp_recv(struct file_priv *priv,
 {
 	uint16_t opcode;
 	uint16_t block;
+	int rc;
 
 	/* according to RFC1350 minimal tftp packet length is 4 bytes */
 	if (len < 4)
@@ -711,12 +709,20 @@ static void tftp_recv(struct file_priv *priv,
 		len -= 2;
 		block = ntohs(*(uint16_t *)pkt);
 
-		if (priv->state == STATE_RRQ || priv->state == STATE_OACK) {
-			/* first block received */
-			priv->state = STATE_RDATA;
+		if (priv->state == STATE_RRQ) {
+			/* first block received; entered only with non rfc
+			   2347 (TFTP Option extension) compliant servers */
 			priv->tftp_con->udp->uh_dport = uh_sport;
+			priv->state = STATE_RDATA;
 			priv->last_block = 0;
-			tftp_window_cache_reset(&priv->cache);
+			priv->ack_block = priv->windowsize;
+
+			rc = tftp_allocate_transfer(priv);
+			if (rc < 0) {
+				priv->err = rc;
+				priv->state = STATE_DONE;
+				break;
+			}
 		}
 
 		if (priv->state != STATE_RDATA) {
@@ -726,7 +732,6 @@ static void tftp_recv(struct file_priv *priv,
 		}
 
 		tftp_handle_data(priv, block, pkt + 2, len);
-
 		break;
 
 	case TFTP_ERROR:
@@ -775,7 +780,7 @@ static int tftp_start_transfer(struct file_priv *priv)
 		priv->block = 1;
 	} else {
 		/* send ACK */
-		priv->state = STATE_OACK;
+		priv->state = STATE_RDATA;
 		priv->last_block = 0;
 		tftp_send(priv);
 	}
@@ -828,26 +833,49 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 		goto out1;
 
 	tftp_timer_reset(priv);
-	while (priv->state != STATE_DONE && priv->state != STATE_START) {
-		ret = tftp_poll(priv);
-		if (ret == TFTP_ERR_RESEND)
-			tftp_send(priv);
-		if (ret < 0)
-			goto out1;
-	}
 
-	if (priv->state == STATE_DONE) {
-		/* this should not happen; STATE_DONE without error happens
-		   after completing the transfer but this has not been started
-		   yet */
-		if (WARN_ON(priv->err == 0))
-			priv->err = -EIO;
+	/* - 'ret < 0'  ... error
+	   - 'ret == 0' ... further tftp_poll() required
+	   - 'ret == 1' ... startup finished */
+	do {
+		switch (priv->state) {
+		case STATE_DONE:
+			/* branch is entered in two situations:
+			   - non rfc 2347 compliant servers finished the
+			     transfer by sending a small file
+			   - some error occurred */
+			if (priv->err < 0)
+				ret = priv->err;
+			else
+				ret = 1;
+			break;
 
-		ret = priv->err;
-		goto out1;
-	}
+		case STATE_START:
+			ret = tftp_start_transfer(priv);
+			if (!ret)
+				ret = 1;
+			break;
+
+		case STATE_RDATA:
+			/* first data block of non rfc 2347 servers */
+			ret = 1;
+			break;
+
+		case STATE_RRQ:
+		case STATE_WRQ:
+			ret = tftp_poll(priv);
+			if (ret == TFTP_ERR_RESEND) {
+				tftp_send(priv);
+				ret = 0;
+			}
+			break;
+
+		default:
+			debug_assert(false);
+			break;
+		}
+	} while (ret == 0);
 
-	ret = tftp_start_transfer(priv);
 	if (ret < 0)
 		goto out1;
 
@@ -855,6 +883,7 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 out1:
 	net_unregister(priv->tftp_con);
 out:
+	debug_assert(!priv->fifo);
 	free(priv);
 
 	return ERR_PTR(ret);
-- 
2.37.2




More information about the barebox mailing list