From 3211ab90697057536f9146b5162dba3791235b0b Mon Sep 17 00:00:00 2001 From: Sebastien Lorquet Date: Fri, 9 Mar 2018 07:08:06 -0600 Subject: [PATCH] apps/netutils/tftpc: This commit modifies the TFTP client functions to use a data read/write callback instead of a file.This allows TFTP to write to arbitrary destination (in my case, a MTD device - for firmware update). Two new functions are introduced for this, named tftpget_cb and tftpput_cb. They are just made of most of the existing code. The previously existing tftpget/tftpput functions are now wrappers on the new ones, with callbacks that read/write from files, so my modifications are backwards compatible with existing applications, eg the associated nsh commands dont need to be changed. --- include/netutils/tftp.h | 43 +++- netutils/tftpc/tftpc_get.c | 444 ++++++++++++++++++++----------------- netutils/tftpc/tftpc_put.c | 230 +++++++++++-------- 3 files changed, 414 insertions(+), 303 deletions(-) diff --git a/include/netutils/tftp.h b/include/netutils/tftp.h index 2969fb700..640bb583d 100644 --- a/include/netutils/tftp.h +++ b/include/netutils/tftp.h @@ -1,8 +1,10 @@ /**************************************************************************** * apps/include/netutils/tftp.h * - * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2008-2009, 2011, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Copyright (C) 2018 Sebastien Lorquet. All rights reserved. + * Author: Sebastien Lorquet * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,17 +42,38 @@ * Included Files ****************************************************************************/ +#include + #include #include /**************************************************************************** - * Pre-processor Definitions + * Public Type Definitions ****************************************************************************/ /**************************************************************************** - * Public Type Definitions + * Name: tftp_callback_t + * + * Description: This callback type is used for data exchange with the tftp + * protocol handler. + * + * Input Parameters: + * ctx - pointer passed to the get or put TFTP function + * offset - GET: Always zero + * PUT: Data offset within the transmitted file + * buf - GET: Pointer to the received data + * PUT: Location of data buffer that will be transferred + * len - GET: Size of the received data (usually 512) + * PUT: Size of the provided buffer + * Return value: + * GET: Number of bytes that were written to the destination by the user + * PUT: Number of bytes that were retrieved from the user data source + * ****************************************************************************/ +typedef ssize_t (*tftp_callback_t)(FAR void *ctx, uint32_t offset, + FAR uint8_t *buf, size_t len); + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -63,8 +86,18 @@ extern "C" #define EXTERN extern #endif -int tftpget(const char *remote, const char *local, in_addr_t addr, bool binary); -int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary); +int tftpget_cb(FAR const char *remote, in_addr_t addr, bool binary, + tftp_callback_t cb, FAR void *ctx); + +int tftpput_cb(FAR const char *remote, in_addr_t addr, bool binary, + tftp_callback_t cb, FAR void *ctx); + +#if CONFIG_NFILE_DESCRIPTORS > 0 +int tftpget(FAR const char *remote, FAR const char *local, in_addr_t addr, + bool binary); +int tftpput(FAR const char *local, FAR const char *remote, in_addr_t addr, + bool binary); +#endif #undef EXTERN #ifdef __cplusplus diff --git a/netutils/tftpc/tftpc_get.c b/netutils/tftpc/tftpc_get.c index 35dc3dadf..1747e5da0 100644 --- a/netutils/tftpc/tftpc_get.c +++ b/netutils/tftpc/tftpc_get.c @@ -1,8 +1,10 @@ /**************************************************************************** - * netuils/tftp/tftpc_get.c + * netutils/tftp/tftpc_get.c * - * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2008-2009, 2011, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Copyright (C) 2018 Sebastien Lorquet. All rights reserved. + * Author: Sebastien Lorquet * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -56,7 +58,7 @@ #include "tftpc_internal.h" -#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) /**************************************************************************** * Pre-processor Definitions @@ -68,12 +70,242 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: tftp_parsedatapacket + ****************************************************************************/ + +static inline int tftp_parsedatapacket(FAR const uint8_t *packet, + FAR uint16_t *opcode, + FAR uint16_t *blockno) +{ + *opcode = (uint16_t)packet[0] << 8 | (uint16_t)packet[1]; + if (*opcode == TFTP_DATA) + { + *blockno = (uint16_t)packet[2] << 8 | (uint16_t)packet[3]; + return OK; + } +#ifdef CONFIG_DEBUG_NET_WARN + else if (*opcode == TFTP_ERR) + { + (void)tftp_parseerrpacket(packet); + } +#endif + + return ERROR; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tftpget_cb + * + * Input Parameters: + * remote - The name of the file on the TFTP server. + * addr - The IP address of the server in network order + * binary - TRUE: Perform binary ('octect') transfer + * FALSE: Perform text ('netascii') transfer + * cb - callback that will be called with data packets + * ctx - pointer passed to the previous callback + * + ****************************************************************************/ + +int tftpget_cb(FAR const char *remote, in_addr_t addr, bool binary, + tftp_callback_t tftp_cb, FAR void *ctx) +{ + struct sockaddr_in server; /* The address of the TFTP server */ + struct sockaddr_in from; /* The address the last UDP message recv'd from */ + FAR uint8_t *packet; /* Allocated memory to hold one packet */ + uint16_t blockno = 0; /* The current transfer block number */ + uint16_t opcode; /* Received opcode */ + uint16_t rblockno; /* Received block number */ + int len; /* Generic length */ + int sd; /* Socket descriptor for socket I/O */ + int retry; /* Retry counter */ + int nbytesrecvd = 0; /* The number of bytes received in the packet */ + int ndatabytes; /* The number of data bytes received */ + int result = ERROR; /* Assume failure */ + int ret; /* Generic return status */ + + /* Allocate the buffer to used for socket/disk I/O */ + + packet = (FAR uint8_t*)zalloc(TFTP_IOBUFSIZE); + if (!packet) + { + nerr("ERROR: packet memory allocation failure\n"); + set_errno(ENOMEM); + return result; + } + + /* Initialize a UDP socket and setup the server addresss */ + + sd = tftp_sockinit(&server, addr); + if (sd < 0) + { + goto errout; + } + + /* Then enter the transfer loop. Loop until the entire file has + * been received or until an error occurs. + */ + + do + { + /* Increment the TFTP block number for the next transfer */ + + blockno++; + + /* Send the next block if the file within a loop. We will + * retry up to TFTP_RETRIES times before giving up on the + * transfer. + */ + + for (retry = 0; retry < TFTP_RETRIES; retry++) + { + /* Send the read request using the well-known port number before + * receiving the first block. Each retry of the first block will + * re-send the request. + */ + + if (blockno == 1) + { + len = tftp_mkreqpacket(packet, TFTP_RRQ, remote, + binary); + server.sin_port = HTONS(CONFIG_NETUTILS_TFTP_PORT); + ret = tftp_sendto(sd, packet, len, &server); + if (ret != len) + { + goto errout_with_sd; + } + + /* Subsequent sendto will use the port number selected by the TFTP + * server in the DATA packet. Setting the server port to zero + * here indicates that we have not yet received the server port + * number. + */ + + server.sin_port = 0; + } + + /* Get the next packet from the server */ + + nbytesrecvd = tftp_recvfrom(sd, packet, TFTP_IOBUFSIZE, &from); + + /* Check if anything valid was received */ + + if (nbytesrecvd > 0) + { + /* Verify the sender address and port number */ + + if (server.sin_addr.s_addr != from.sin_addr.s_addr) + { + ninfo("Invalid address in DATA\n"); + retry--; + continue; + } + + if (server.sin_port && server.sin_port != from.sin_port) + { + ninfo("Invalid port in DATA\n"); + len = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, + TFTP_ERRST_UNKID); + ret = tftp_sendto(sd, packet, len, &from); + retry--; + continue; + } + + /* Parse the incoming DATA packet */ + + if (nbytesrecvd < TFTP_DATAHEADERSIZE) + { + /* Packet is not big enough to be parsed */ + + ninfo("Tiny data packet ignored\n"); + continue; + } + + if (tftp_parsedatapacket(packet, &opcode, &rblockno) != OK || + blockno != rblockno) + { + /* Opcode is not TFTP_DATA or the block number is + * unexpected. + */ + + ninfo("Parse failure\n"); + if (opcode > TFTP_MAXRFC1350) + { + len = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, + TFTP_ERRST_ILLEGALOP); + ret = tftp_sendto(sd, packet, len, &from); + } + continue; + } + + /* Replace the server port to the one in the good data response */ + + if (!server.sin_port) + { + server.sin_port = from.sin_port; + } + + /* Then break out of the loop */ + + break; + } + } + + /* Did we exhaust all of the retries? */ + + if (retry == TFTP_RETRIES) + { + ninfo("Retry limit exceeded\n"); + goto errout_with_sd; + } + + /* Write the received data chunk to the file */ + + ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE; + tftp_dumpbuffer("Recvd DATA", packet + TFTP_DATAHEADERSIZE, ndatabytes); + if (tftp_cb(ctx, 0, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0) + { + goto errout_with_sd; + } + + /* Send the acknowledgment */ + + len = tftp_mkackpacket(packet, blockno); + ret = tftp_sendto(sd, packet, len, &server); + if (ret != len) + { + goto errout_with_sd; + } + ninfo("ACK blockno %d\n", blockno); + } + while (ndatabytes >= TFTP_DATASIZE); + + /* Return success */ + + result = OK; + +errout_with_sd: + close(sd); + +errout: + free(packet); + + return result; +} + +#if CONFIG_NFILE_DESCRIPTORS > 0 /**************************************************************************** * Name: tftp_write ****************************************************************************/ -static inline ssize_t tftp_write(int fd, const uint8_t *buf, size_t len) +static ssize_t tftp_write(FAR void *ctx, uint32_t offset, FAR uint8_t *buf, + size_t len) { + int fd = (int)ctx; size_t left = len; ssize_t nbyteswritten; @@ -106,33 +338,6 @@ static inline ssize_t tftp_write(int fd, const uint8_t *buf, size_t len) return len; } -/**************************************************************************** - * Name: tftp_parsedatapacket - ****************************************************************************/ - -static inline int tftp_parsedatapacket(const uint8_t *packet, - uint16_t *opcode, uint16_t *blockno) -{ - *opcode = (uint16_t)packet[0] << 8 | (uint16_t)packet[1]; - if (*opcode == TFTP_DATA) - { - *blockno = (uint16_t)packet[2] << 8 | (uint16_t)packet[3]; - return OK; - } -#ifdef CONFIG_DEBUG_NET_WARN - else if (*opcode == TFTP_ERR) - { - (void)tftp_parseerrpacket(packet); - } -#endif - - return ERROR; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - /**************************************************************************** * Name: tftpget * @@ -149,30 +354,8 @@ static inline int tftp_parsedatapacket(const uint8_t *packet, int tftpget(FAR const char *remote, FAR const char *local, in_addr_t addr, bool binary) { - struct sockaddr_in server; /* The address of the TFTP server */ - struct sockaddr_in from; /* The address the last UDP message recv'd from */ - FAR uint8_t *packet; /* Allocated memory to hold one packet */ - uint16_t blockno = 0; /* The current transfer block number */ - uint16_t opcode; /* Received opcode */ - uint16_t rblockno; /* Received block number */ - int len; /* Generic length */ - int sd; /* Socket descriptor for socket I/O */ int fd; /* File descriptor for file I/O */ - int retry; /* Retry counter */ - int nbytesrecvd = 0; /* The number of bytes received in the packet */ - int ndatabytes; /* The number of data bytes received */ - int result = ERROR; /* Assume failure */ - int ret; /* Generic return status */ - - /* Allocate the buffer to used for socket/disk I/O */ - - packet = (FAR uint8_t*)zalloc(TFTP_IOBUFSIZE); - if (!packet) - { - nerr("ERROR: packet memory allocation failure\n"); - set_errno(ENOMEM); - goto errout; - } + int result = ERROR; /* Generic return status */ /* Open the file for writing */ @@ -180,161 +363,16 @@ int tftpget(FAR const char *remote, FAR const char *local, in_addr_t addr, if (fd < 0) { nerr("ERROR: open failed: %d\n", errno); - goto errout_with_packet; - } - - /* Initialize a UDP socket and setup the server addresss */ - - sd = tftp_sockinit(&server, addr); - if (sd < 0) - { - goto errout_with_fd; + goto errout; } - /* Then enter the transfer loop. Loop until the entire file has - * been received or until an error occurs. - */ + result = tftpget_cb(remote, addr, binary, tftp_write, (void*)fd); - do - { - /* Increment the TFTP block number for the next transfer */ - - blockno++; - - /* Send the next block if the file within a loop. We will - * retry up to TFTP_RETRIES times before giving up on the - * transfer. - */ - - for (retry = 0; retry < TFTP_RETRIES; retry++) - { - /* Send the read request using the well-known port number before - * receiving the first block. Each retry of the first block will - * re-send the request. - */ - - if (blockno == 1) - { - len = tftp_mkreqpacket(packet, TFTP_RRQ, remote, binary); - server.sin_port = HTONS(CONFIG_NETUTILS_TFTP_PORT); - ret = tftp_sendto(sd, packet, len, &server); - if (ret != len) - { - goto errout_with_sd; - } - - /* Subsequent sendto will use the port number selected by the TFTP - * server in the DATA packet. Setting the server port to zero - * here indicates that we have not yet received the server port number. - */ - - server.sin_port = 0; - } - - /* Get the next packet from the server */ - - nbytesrecvd = tftp_recvfrom(sd, packet, TFTP_IOBUFSIZE, &from); - - /* Check if anything valid was received */ - - if (nbytesrecvd > 0) - { - /* Verify the sender address and port number */ - - if (server.sin_addr.s_addr != from.sin_addr.s_addr) - { - ninfo("Invalid address in DATA\n"); - retry--; - continue; - } - - if (server.sin_port && server.sin_port != from.sin_port) - { - ninfo("Invalid port in DATA\n"); - len = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, TFTP_ERRST_UNKID); - ret = tftp_sendto(sd, packet, len, &from); - retry--; - continue; - } - - /* Parse the incoming DATA packet */ - - if (nbytesrecvd < TFTP_DATAHEADERSIZE) - { - /* Packet is not big enough to be parsed */ - - ninfo("Tiny data packet ignored\n"); - continue; - } - - if (tftp_parsedatapacket(packet, &opcode, &rblockno) != OK || - blockno != rblockno) - { - /* Opcode is not TFTP_DATA or the block number is unexpected */ - - ninfo("Parse failure\n"); - if (opcode > TFTP_MAXRFC1350) - { - len = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, TFTP_ERRST_ILLEGALOP); - ret = tftp_sendto(sd, packet, len, &from); - } - continue; - } - - /* Replace the server port to the one in the good data response */ - - if (!server.sin_port) - { - server.sin_port = from.sin_port; - } - - /* Then break out of the loop */ - - break; - } - } - - /* Did we exhaust all of the retries? */ - - if (retry == TFTP_RETRIES) - { - ninfo("Retry limit exceeded\n"); - goto errout_with_sd; - } - - /* Write the received data chunk to the file */ - - ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE; - tftp_dumpbuffer("Recvd DATA", packet + TFTP_DATAHEADERSIZE, ndatabytes); - if (tftp_write(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0) - { - goto errout_with_sd; - } - - /* Send the acknowledgment */ - - len = tftp_mkackpacket(packet, blockno); - ret = tftp_sendto(sd, packet, len, &server); - if (ret != len) - { - goto errout_with_sd; - } - ninfo("ACK blockno %d\n", blockno); - } - while (ndatabytes >= TFTP_DATASIZE); - - /* Return success */ - - result = OK; - -errout_with_sd: - close(sd); -errout_with_fd: close(fd); -errout_with_packet: - free(packet); + errout: return result; } +#endif #endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NFILE_DESCRIPTORS > 0 */ diff --git a/netutils/tftpc/tftpc_put.c b/netutils/tftpc/tftpc_put.c index 944f9da47..73eb2f098 100644 --- a/netutils/tftpc/tftpc_put.c +++ b/netutils/tftpc/tftpc_put.c @@ -1,8 +1,10 @@ /**************************************************************************** * netuils/tftp/tftpc_put.c * - * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2008-2009, 2011, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Copyright (C) 2018 Sebastien Lorquet. All rights reserved. + * Author: Sebastien Lorquet * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -54,7 +56,7 @@ #include "tftpc_internal.h" -#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) && CONFIG_NFILE_DESCRIPTORS > 0 +#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) /**************************************************************************** * Pre-processor Definitions @@ -70,53 +72,6 @@ * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: tftp_read - ****************************************************************************/ - -static inline ssize_t tftp_read(int fd, uint8_t *buf, size_t buflen) -{ - ssize_t nbytesread; - ssize_t totalread = 0; - - while (totalread < buflen) - { - /* Read the data... repeating the read in the event that it was - * interrupted by a signal. - */ - - do - { - nbytesread = read(fd, buf, buflen - totalread); - } - while (nbytesread < 0 && errno == EINTR); - - /* Check for non-EINTR errors */ - - if (nbytesread < 0) - { - nerr("ERROR: read failed: %d\n", errno); - return ERROR; - } - - /* Check for end of file */ - - else if (nbytesread == 0) - { - break; - } - - /* Handle partial reads. Partial reads can happen normally - * when the source is some device driver that returns data - * in bits and pieces as received (such as a pipe) - */ - - totalread += nbytesread; - buf += nbytesread; - } - return totalread; -} - /**************************************************************************** * Name: tftp_mkdatapacket * @@ -139,9 +94,9 @@ static inline ssize_t tftp_read(int fd, uint8_t *buf, size_t buflen) * ****************************************************************************/ -int tftp_mkdatapacket(int fd, off_t offset, uint8_t *packet, uint16_t blockno) +int tftp_mkdatapacket(off_t offset, FAR uint8_t *packet, uint16_t blockno, + tftp_callback_t tftp_cb, FAR void *ctx) { - off_t tmp; int nbytesread; /* Format the DATA message header */ @@ -151,18 +106,8 @@ int tftp_mkdatapacket(int fd, off_t offset, uint8_t *packet, uint16_t blockno) packet[2] = blockno >> 8; packet[3] = blockno & 0xff; - /* Seek to the correct offset in the file */ - - tmp = lseek(fd, offset, SEEK_SET); - if (tmp == (off_t)-1) - { - nerr("ERROR: lseek failed: %d\n", errno); - return ERROR; - } - - /* Read the file data into the packet buffer */ - - nbytesread = tftp_read(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE); + nbytesread = tftp_cb(ctx, offset, &packet[TFTP_DATAHEADERSIZE], + TFTP_DATASIZE); if (nbytesread < 0) { return ERROR; @@ -191,10 +136,11 @@ int tftp_mkdatapacket(int fd, off_t offset, uint8_t *packet, uint16_t blockno) * ****************************************************************************/ -static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, - uint16_t *port, uint16_t *blockno) +static int tftp_rcvack(int sd, FAR uint8_t *packet, + FAR struct sockaddr_in *server, FAR uint16_t *port, + FAR uint16_t *blockno) { - struct sockaddr_in from; /* The address the last UDP message recv'd from */ + struct sockaddr_in from; /* The address the last UDP msg recv'd from */ ssize_t nbytes; /* The number of bytes received. */ uint16_t opcode; /* The received opcode */ uint16_t rblockno; /* The received block number */ @@ -235,7 +181,9 @@ static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, } else { - /* Get the port being used by the server if that has not yet been established */ + /* Get the port being used by the server if that has not yet + * been established. + */ if (!*port) { @@ -243,7 +191,9 @@ static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, server->sin_port = from.sin_port; } - /* Verify that the packet was received from the correct host and port */ + /* Verify that the packet was received from the correct host and + * port. + */ if (server->sin_addr.s_addr != from.sin_addr.s_addr) { @@ -254,7 +204,8 @@ static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, if (*port != server->sin_port) { ninfo("Invalid port in DATA\n"); - packetlen = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, TFTP_ERRST_UNKID); + packetlen = tftp_mkerrpacket(packet, TFTP_ERR_UNKID, + TFTP_ERRST_UNKID); (void)tftp_sendto(sd, packet, packetlen, server); continue; } @@ -281,7 +232,8 @@ static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, #endif if (opcode > TFTP_MAXRFC1350) { - packetlen = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, TFTP_ERRST_ILLEGALOP); + packetlen = tftp_mkerrpacket(packet, TFTP_ERR_ILLEGALOP, + TFTP_ERRST_ILLEGALOP); (void)tftp_sendto(sd, packet, packetlen, server); } @@ -310,35 +262,36 @@ static int tftp_rcvack(int sd, uint8_t *packet, struct sockaddr_in *server, ****************************************************************************/ /**************************************************************************** - * Name: tftpput + * Name: tftpput_cb * * Input Parameters: - * local - Path to the file system object to be sent. * remote - The name of the file on the TFTP server. * addr - The IP address of the server in network order * binary - TRUE: Perform binary ('octect') transfer * FALSE: Perform text ('netascii') transfer + * cb - callback that will be called with data packets + * ctx - pointer passed to the previous callback * ****************************************************************************/ -int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) +int tftpput_cb(FAR const char *remote, in_addr_t addr, bool binary, + tftp_callback_t cb, FAR void *ctx) { struct sockaddr_in server; /* The address of the TFTP server */ - uint8_t *packet; /* Allocated memory to hold one packet */ + FAR uint8_t *packet; /* Allocated memory to hold one packet */ off_t offset; /* Offset into source file */ uint16_t blockno; /* The current transfer block number */ uint16_t rblockno; /* The ACK'ed block number */ - uint16_t port = 0; /* This is the port number for the transfer */ + uint16_t port = 0; /* This is the port nbr for the transfer */ int packetlen; /* The length of the data packet */ int sd; /* Socket descriptor for socket I/O */ - int fd; /* File descriptor for file I/O */ int retry; /* Retry counter */ int result = ERROR; /* Assume failure */ int ret; /* Generic return status */ /* Allocate the buffer to used for socket/disk I/O */ - packet = (uint8_t*)zalloc(TFTP_IOBUFSIZE); + packet = (FAR uint8_t*)zalloc(TFTP_IOBUFSIZE); if (!packet) { nerr("ERROR: packet memory allocation failure\n"); @@ -346,21 +299,12 @@ int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) goto errout; } - /* Open the file for reading */ - - fd = open(local, O_RDONLY); - if (fd < 0) - { - nerr("ERROR: open failed: %d\n", errno); - goto errout_with_packet; - } - /* Initialize a UDP socket and setup the server addresss */ sd = tftp_sockinit(&server, addr); if (sd < 0) { - goto errout_with_fd; + goto errout_with_packet; } /* Send the write request using the well known port. This may need @@ -410,7 +354,7 @@ int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) { /* Construct the next data packet */ - packetlen = tftp_mkdatapacket(fd, offset, packet, blockno); + packetlen = tftp_mkdatapacket(offset, packet, blockno, cb, ctx); if (packetlen < 0) { goto errout_with_sd; @@ -435,8 +379,8 @@ int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) if (rblockno == blockno) { - /* Yes.. If we are at the end of the file and if all of the packets - * have been ACKed, then we are done. + /* Yes.. If we are at the end of the file and if all of the + * packets have been ACKed, then we are done. */ if (packetlen < TFTP_PACKETSIZE) @@ -446,9 +390,9 @@ int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) /* Not the last block.. set up for the next block */ - blockno++; - offset += TFTP_DATASIZE; - retry = 0; + blockno += 1; + offset += TFTP_DATASIZE; + retry = 0; /* Skip the retry test */ @@ -474,12 +418,108 @@ int tftpput(const char *local, const char *remote, in_addr_t addr, bool binary) errout_with_sd: close(sd); -errout_with_fd: - close(fd); errout_with_packet: free(packet); errout: return result; } +#if CONFIG_NFILE_DESCRIPTORS > 0 +/**************************************************************************** + * Name: tftp_read + ****************************************************************************/ + +static ssize_t tftp_read(FAR void *ctx, uint32_t offset, FAR uint8_t *buf, + size_t buflen) +{ + int fd = (int)ctx; + off_t tmp; + ssize_t nbytesread; + ssize_t totalread = 0; + + /* Seek to the correct offset in the file */ + + tmp = lseek(fd, offset, SEEK_SET); + if (tmp == (off_t)-1) + { + nerr("ERROR: lseek failed: %d\n", errno); + return ERROR; + } + + /* Read the file data into the packet buffer */ + + while (totalread < buflen) + { + /* Read the data... repeating the read in the event that it was + * interrupted by a signal. + */ + + do + { + nbytesread = read(fd, buf, buflen - totalread); + } + while (nbytesread < 0 && errno == EINTR); + + /* Check for non-EINTR errors */ + + if (nbytesread < 0) + { + nerr("ERROR: read failed: %d\n", errno); + return ERROR; + } + + /* Check for end of file */ + + else if (nbytesread == 0) + { + break; + } + + /* Handle partial reads. Partial reads can happen normally + * when the source is some device driver that returns data + * in bits and pieces as received (such as a pipe) + */ + + totalread += nbytesread; + buf += nbytesread; + } + return totalread; +} + +/**************************************************************************** + * Name: tftpput + * + * Input Parameters: + * local - Path to the file system object to be sent. + * remote - The name of the file on the TFTP server. + * addr - The IP address of the server in network order + * binary - TRUE: Perform binary ('octect') transfer + * FALSE: Perform text ('netascii') transfer + * + ****************************************************************************/ + +int tftpput(FAR const char *local, FAR const char *remote, in_addr_t addr, + bool binary) +{ + int fd; /* File descriptor for file I/O */ + int result = ERROR; /* Assume failure */ + + /* Open the file for reading */ + + fd = open(local, O_RDONLY); + if (fd < 0) + { + nerr("ERROR: open failed: %d\n", errno); + goto errout; + } + + result = tftpput_cb(remote, addr, binary, tftp_read, (void *)fd); + + close(fd); + +errout: + return result; +} +#endif + #endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NFILE_DESCRIPTORS > 0 */