Is this really just a move/rename? Does it preserve the git blame info? --joel
On Fri, Jun 3, 2022, 10:22 AM Frank Kuehndel < frank.kuehn...@embedded-brains.de> wrote: > From: Frank Kühndel <frank.kuehn...@embedded-brains.de> > > --- > cpukit/libfs/src/ftpfs/tftpDriver.c | 1088 +++++++++++++++++++++++++++ > 1 file changed, 1088 insertions(+) > create mode 100644 cpukit/libfs/src/ftpfs/tftpDriver.c > > diff --git a/cpukit/libfs/src/ftpfs/tftpDriver.c > b/cpukit/libfs/src/ftpfs/tftpDriver.c > new file mode 100644 > index 0000000000..d0eadcf99a > --- /dev/null > +++ b/cpukit/libfs/src/ftpfs/tftpDriver.c > @@ -0,0 +1,1088 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > + > +/** > + * @file > + * > + * Trivial File Transfer Protocol file system (TFTP client) for RFC 1350. > + * > + * Transfer file to/from remote host > + */ > + > +/* > + * Copyright (c) 1998 Eric Norum <e...@norum.ca> > + * > + * Modifications to support reference counting in the file system are > + * Copyright (c) 2012 embedded brains GmbH. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > "AS IS" > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, > THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR > PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS > BE > + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR > + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > BUSINESS > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN > + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF > THE > + * POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#ifdef HAVE_CONFIG_H > +#include "config.h" > +#endif > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <errno.h> > +#include <malloc.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <rtems.h> > +#include <rtems/libio_.h> > +#include <rtems/seterr.h> > +#include <rtems/tftp.h> > +#include <rtems/thread.h> > +#include <sys/types.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <arpa/inet.h> > +#include <netdb.h> > + > +#ifdef RTEMS_NETWORKING > +#include <rtems/rtems_bsdnet.h> > +#endif > + > +#ifdef RTEMS_TFTP_DRIVER_DEBUG > +int rtems_tftp_driver_debug = 1; > +#endif > + > +/* > + * Range of UDP ports to try > + */ > +#define UDP_PORT_BASE 3180 > + > +/* > + * Default limits > + */ > +#define PACKET_FIRST_TIMEOUT_MILLISECONDS 400L > +#define PACKET_TIMEOUT_MILLISECONDS 6000L > +#define OPEN_RETRY_LIMIT 10 > +#define IO_RETRY_LIMIT 10 > + > +/* > + * TFTP opcodes > + */ > +#define TFTP_OPCODE_RRQ 1 > +#define TFTP_OPCODE_WRQ 2 > +#define TFTP_OPCODE_DATA 3 > +#define TFTP_OPCODE_ACK 4 > +#define TFTP_OPCODE_ERROR 5 > + > +/* > + * Largest data transfer > + */ > +#define TFTP_BUFSIZE 512 > + > +/* > + * Packets transferred between machines > + */ > +union tftpPacket { > + /* > + * RRQ/WRQ packet > + */ > + struct tftpRWRQ { > + uint16_t opcode; > + char filename_mode[TFTP_BUFSIZE]; > + } tftpRWRQ; > + > + /* > + * DATA packet > + */ > + struct tftpDATA { > + uint16_t opcode; > + uint16_t blocknum; > + uint8_t data[TFTP_BUFSIZE]; > + } tftpDATA; > + > + /* > + * ACK packet > + */ > + struct tftpACK { > + uint16_t opcode; > + uint16_t blocknum; > + } tftpACK; > + > + /* > + * ERROR packet > + */ > + struct tftpERROR { > + uint16_t opcode; > + uint16_t errorCode; > + char errorMessage[TFTP_BUFSIZE]; > + } tftpERROR; > +}; > + > +/* > + * State of each TFTP stream > + */ > +struct tftpStream { > + /* > + * Buffer for storing most recently-received packet > + */ > + union tftpPacket pkbuf; > + > + /* > + * Last block number transferred > + */ > + uint16_t blocknum; > + > + /* > + * Data transfer socket > + */ > + int socket; > + struct sockaddr_in myAddress; > + struct sockaddr_in farAddress; > + > + /* > + * Indices into buffer > + */ > + int nleft; > + int nused; > + > + /* > + * Flags > + */ > + int firstReply; > + int eof; > + int writing; > +}; > + > +/* > + * Flags for filesystem info. > + */ > +#define TFTPFS_VERBOSE (1 << 0) > + > +/* > + * TFTP File system info. > + */ > +typedef struct tftpfs_info_s { > + uint32_t flags; > + rtems_mutex tftp_mutex; > + int nStreams; > + struct tftpStream ** volatile tftpStreams; > +} tftpfs_info_t; > + > +#define tftpfs_info_mount_table(_mt) ((tftpfs_info_t*) ((_mt)->fs_info)) > +#define tftpfs_info_pathloc(_pl) ((tftpfs_info_t*) > ((_pl)->mt_entry->fs_info)) > +#define tftpfs_info_iop(_iop) (tftpfs_info_pathloc > (&((_iop)->pathinfo))) > + > +/* > + * Number of streams open at the same time > + */ > + > +static const rtems_filesystem_operations_table rtems_tftp_ops; > +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers; > + > +static bool rtems_tftp_is_directory( > + const char *path, > + size_t pathlen > +) > +{ > + return path [pathlen - 1] == '/'; > +} > + > +int rtems_tftpfs_initialize( > + rtems_filesystem_mount_table_entry_t *mt_entry, > + const void *data > +) > +{ > + const char *device = mt_entry->dev; > + size_t devicelen = strlen (device); > + tftpfs_info_t *fs = NULL; > + char *root_path; > + > + if (devicelen == 0) { > + root_path = malloc (1); > + if (root_path == NULL) > + goto error; > + root_path [0] = '\0'; > + } > + else { > + root_path = malloc (devicelen + 2); > + if (root_path == NULL) > + goto error; > + > + root_path = memcpy (root_path, device, devicelen); > + root_path [devicelen] = '/'; > + root_path [devicelen + 1] = '\0'; > + } > + > + fs = malloc (sizeof (*fs)); > + if (fs == NULL) > + goto error; > + fs->flags = 0; > + fs->nStreams = 0; > + fs->tftpStreams = 0; > + > + mt_entry->fs_info = fs; > + mt_entry->mt_fs_root->location.node_access = root_path; > + mt_entry->mt_fs_root->location.handlers = &rtems_tftp_handlers; > + mt_entry->ops = &rtems_tftp_ops; > + > + /* > + * Now allocate a semaphore for mutual exclusion. > + * > + * NOTE: This could be in an fsinfo for this filesystem type. > + */ > + > + rtems_mutex_init (&fs->tftp_mutex, "TFTPFS"); > + > + if (data) { > + char* config = (char*) data; > + char* token; > + char* saveptr; > + token = strtok_r (config, " ", &saveptr); > + while (token) { > + if (strcmp (token, "verbose") == 0) > + fs->flags |= TFTPFS_VERBOSE; > + token = strtok_r (NULL, " ", &saveptr); > + } > + } > + > + return 0; > + > +error: > + > + free (fs); > + free (root_path); > + > + rtems_set_errno_and_return_minus_one (ENOMEM); > +} > + > +/* > + * Release a stream and clear the pointer to it > + */ > +static void > +releaseStream (tftpfs_info_t *fs, int s) > +{ > + if (fs->tftpStreams[s] && (fs->tftpStreams[s]->socket >= 0)) > + close (fs->tftpStreams[s]->socket); > + rtems_mutex_lock (&fs->tftp_mutex); > + free (fs->tftpStreams[s]); > + fs->tftpStreams[s] = NULL; > + rtems_mutex_unlock (&fs->tftp_mutex); > +} > + > +static void > +rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) > +{ > + tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry); > + int s; > + for (s = 0; s < fs->nStreams; s++) > + releaseStream (fs, s); > + rtems_mutex_destroy (&fs->tftp_mutex); > + free (fs); > + free (mt_entry->mt_fs_root->location.node_access); > +} > + > +/* > + * Map error message > + */ > +static int > +tftpErrno (struct tftpStream *tp) > +{ > + unsigned int tftpError; > + static const int errorMap[] = { > + EINVAL, > + ENOENT, > + EPERM, > + ENOSPC, > + EINVAL, > + ENXIO, > + EEXIST, > + ESRCH, > + }; > + > + tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); > + if (tftpError < (sizeof errorMap / sizeof errorMap[0])) > + return errorMap[tftpError]; > + else > + return 1000 + tftpError; > +} > + > +/* > + * Send a message to make the other end shut up > + */ > +static void > +sendStifle (struct tftpStream *tp, struct sockaddr_in *to) > +{ > + int len; > + struct { > + uint16_t opcode; > + uint16_t errorCode; > + char errorMessage[12]; > + } msg; > + > + /* > + * Create the error packet (Unknown transfer ID). > + */ > + msg.opcode = htons (TFTP_OPCODE_ERROR); > + msg.errorCode = htons (5); > + len = sizeof msg.opcode + sizeof msg.errorCode + 1; > + len += sprintf (msg.errorMessage, "GO AWAY"); > + > + /* > + * Send it > + */ > + sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, > sizeof *to); > +} > + > +/* > + * Wait for a data packet > + */ > +static int > +getPacket (struct tftpStream *tp, int retryCount) > +{ > + int len; > + struct timeval tv; > + > + if (retryCount == 0) { > + tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000L; > + tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000L) * 1000L; > + } > + else { > + tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000L; > + tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000L) * 1000L; > + } > + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); > + for (;;) { > + union { > + struct sockaddr s; > + struct sockaddr_in i; > + } from; > + socklen_t fromlen = sizeof from; > + len = recvfrom (tp->socket, &tp->pkbuf, > + sizeof tp->pkbuf, 0, > + &from.s, &fromlen); > + if (len < 0) > + break; > + if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { > + if (tp->firstReply) { > + tp->firstReply = 0; > + tp->farAddress.sin_port = from.i.sin_port; > + } > + if (tp->farAddress.sin_port == from.i.sin_port) > + break; > + } > + > + /* > + * Packet is from someone with whom we are > + * not interested. Tell them to go away. > + */ > + sendStifle (tp, &from.i); > + } > + tv.tv_sec = 0; > + tv.tv_usec = 0; > + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); > +#ifdef RTEMS_TFTP_DRIVER_DEBUG > + if (rtems_tftp_driver_debug) { > + if (len >= (int) sizeof tp->pkbuf.tftpACK) { > + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); > + switch (opcode) { > + default: > + printf ("TFTP: OPCODE %d\n", opcode); > + break; > + > + case TFTP_OPCODE_DATA: > + printf ("TFTP: RECV %d\n", ntohs > (tp->pkbuf.tftpDATA.blocknum)); > + break; > + > + case TFTP_OPCODE_ACK: > + printf ("TFTP: GOT ACK %d\n", ntohs > (tp->pkbuf.tftpACK.blocknum)); > + break; > + } > + } > + else { > + printf ("TFTP: %d-byte packet\n", len); > + } > + } > +#endif > + return len; > +} > + > +/* > + * Send an acknowledgement > + */ > +static int > +sendAck (struct tftpStream *tp) > +{ > +#ifdef RTEMS_TFTP_DRIVER_DEBUG > + if (rtems_tftp_driver_debug) > + printf ("TFTP: ACK %d\n", tp->blocknum); > +#endif > + > + /* > + * Create the acknowledgement > + */ > + tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); > + tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); > + > + /* > + * Send it > + */ > + if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, > 0, > + (struct sockaddr *)&tp->farAddress, > + sizeof tp->farAddress) < 0) > + return errno; > + return 0; > +} > + > +/* > + * Convert a path to canonical form > + */ > +static void > +fixPath (char *path) > +{ > + char *inp, *outp, *base; > + > + outp = inp = path; > + base = NULL; > + for (;;) { > + if (inp[0] == '.') { > + if (inp[1] == '\0') > + break; > + if (inp[1] == '/') { > + inp += 2; > + continue; > + } > + if (inp[1] == '.') { > + if (inp[2] == '\0') { > + if ((base != NULL) && (outp > base)) { > + outp--; > + while ((outp > base) && (outp[-1] != '/')) > + outp--; > + } > + break; > + } > + if (inp[2] == '/') { > + inp += 3; > + if (base == NULL) > + continue; > + if (outp > base) { > + outp--; > + while ((outp > base) && (outp[-1] != '/')) > + outp--; > + } > + continue; > + } > + } > + } > + if (base == NULL) > + base = inp; > + while (inp[0] != '/') { > + if ((*outp++ = *inp++) == '\0') > + return; > + } > + *outp++ = '/'; > + while (inp[0] == '/') > + inp++; > + } > + *outp = '\0'; > + return; > +} > + > +static void rtems_tftp_eval_path(rtems_filesystem_eval_path_context_t > *self) > +{ > + int eval_flags = rtems_filesystem_eval_path_get_flags (self); > + > + if ((eval_flags & RTEMS_FS_MAKE) == 0) { > + int rw = RTEMS_FS_PERMS_READ | RTEMS_FS_PERMS_WRITE; > + > + if ((eval_flags & rw) != rw) { > + rtems_filesystem_location_info_t *currentloc = > + rtems_filesystem_eval_path_get_currentloc (self); > + char *current = currentloc->node_access; > + size_t currentlen = strlen (current); > + const char *path = rtems_filesystem_eval_path_get_path (self); > + size_t pathlen = rtems_filesystem_eval_path_get_pathlen > (self); > + size_t len = currentlen + pathlen; > + > + rtems_filesystem_eval_path_clear_path (self); > + > + current = realloc (current, len + 1); > + if (current != NULL) { > + memcpy (current + currentlen, path, pathlen); > + current [len] = '\0'; > + if (!rtems_tftp_is_directory (current, len)) { > + fixPath (current); > + } > + currentloc->node_access = current; > + } else { > + rtems_filesystem_eval_path_error (self, ENOMEM); > + } > + } else { > + rtems_filesystem_eval_path_error (self, EINVAL); > + } > + } else { > + rtems_filesystem_eval_path_error (self, EIO); > + } > +} > + > +/* > + * The routine which does most of the work for the IMFS open handler > + */ > +static int rtems_tftp_open_worker( > + rtems_libio_t *iop, > + char *full_path_name, > + int oflag > +) > +{ > + tftpfs_info_t *fs; > + struct tftpStream *tp; > + int retryCount; > + struct in_addr farAddress; > + int s; > + int len; > + char *cp1; > + char *cp2; > + char *remoteFilename; > + rtems_interval now; > + char *hostname; > + > + /* > + * Get the file system info. > + */ > + fs = tftpfs_info_iop (iop); > + > + /* > + * Extract the host name component > + */ > + if (*full_path_name == '/') > + full_path_name++; > + > + hostname = full_path_name; > + cp1 = strchr (full_path_name, ':'); > + if (!cp1) { > +#ifdef RTEMS_NETWORKING > + hostname = "BOOTP_HOST"; > +#endif > + } else { > + *cp1 = '\0'; > + ++cp1; > + } > + > + /* > + * Convert hostname to Internet address > + */ > +#ifdef RTEMS_NETWORKING > + if (strcmp (hostname, "BOOTP_HOST") == 0) > + farAddress = rtems_bsdnet_bootp_server_address; > + else > +#endif > + if (inet_aton (hostname, &farAddress) == 0) { > + struct hostent *he = gethostbyname(hostname); > + if (he == NULL) > + return ENOENT; > + memcpy (&farAddress, he->h_addr, sizeof (farAddress)); > + } > + > + /* > + * Extract file pathname component > + */ > +#ifdef RTEMS_NETWORKING > + if (strcmp (cp1, "BOOTP_FILE") == 0) { > + cp1 = rtems_bsdnet_bootp_boot_file_name; > + } > +#endif > + if (*cp1 == '\0') > + return ENOENT; > + remoteFilename = cp1; > + if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) > + return ENOENT; > + > + /* > + * Find a free stream > + */ > + rtems_mutex_lock (&fs->tftp_mutex); > + for (s = 0 ; s < fs->nStreams ; s++) { > + if (fs->tftpStreams[s] == NULL) > + break; > + } > + if (s == fs->nStreams) { > + /* > + * Reallocate stream pointers > + * Guard against the case where realloc() returns NULL. > + */ > + struct tftpStream **np; > + > + np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof > *fs->tftpStreams); > + if (np == NULL) { > + rtems_mutex_unlock (&fs->tftp_mutex); > + return ENOMEM; > + } > + fs->tftpStreams = np; > + } > + tp = fs->tftpStreams[s] = malloc (sizeof (struct tftpStream)); > + rtems_mutex_unlock (&fs->tftp_mutex); > + if (tp == NULL) > + return ENOMEM; > + iop->data0 = s; > + iop->data1 = tp; > + > + /* > + * Create the socket > + */ > + if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { > + releaseStream (fs, s); > + return ENOMEM; > + } > + > + /* > + * Bind the socket to a local address > + */ > + retryCount = 0; > + now = rtems_clock_get_ticks_since_boot(); > + for (;;) { > + int try = (now + retryCount) % 10; > + > + tp->myAddress.sin_family = AF_INET; > + tp->myAddress.sin_port = htons (UDP_PORT_BASE + fs->nStreams * > try + s); > + tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); > + if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof > tp->myAddress) >= 0) > + break; > + if (++retryCount == 10) { > + releaseStream (fs, s); > + return EBUSY; > + } > + } > + > + /* > + * Set the UDP destination to the TFTP server > + * port on the remote machine. > + */ > + tp->farAddress.sin_family = AF_INET; > + tp->farAddress.sin_addr = farAddress; > + tp->farAddress.sin_port = htons (69); > + > + /* > + * Start the transfer > + */ > + tp->firstReply = 1; > + retryCount = 0; > + for (;;) { > + /* > + * Create the request > + */ > + if ((oflag & O_ACCMODE) == O_RDONLY) { > + tp->writing = 0; > + tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); > + } > + else { > + tp->writing = 1; > + tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ); > + } > + cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; > + cp2 = (char *) remoteFilename; > + while ((*cp1++ = *cp2++) != '\0') > + continue; > + cp2 = "octet"; > + while ((*cp1++ = *cp2++) != '\0') > + continue; > + len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; > + > + /* > + * Send the request > + */ > + if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, > + (struct sockaddr *)&tp->farAddress, > + sizeof tp->farAddress) < 0) { > + releaseStream (fs, s); > + return EIO; > + } > + > + /* > + * Get reply > + */ > + len = getPacket (tp, retryCount); > + if (len >= (int) sizeof tp->pkbuf.tftpACK) { > + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); > + if (!tp->writing > + && (opcode == TFTP_OPCODE_DATA) > + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { > + tp->nused = 0; > + tp->blocknum = 1; > + tp->nleft = len - 2 * sizeof (uint16_t ); > + tp->eof = (tp->nleft < TFTP_BUFSIZE); > + if (sendAck (tp) != 0) { > + releaseStream (fs, s); > + return EIO; > + } > + break; > + } > + if (tp->writing > + && (opcode == TFTP_OPCODE_ACK) > + && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) { > + tp->nused = 0; > + tp->blocknum = 1; > + break; > + } > + if (opcode == TFTP_OPCODE_ERROR) { > + int e = tftpErrno (tp); > + releaseStream (fs, s); > + return e; > + } > + } > + > + /* > + * Keep trying > + */ > + if (++retryCount >= OPEN_RETRY_LIMIT) { > + releaseStream (fs, s); > + return EIO; > + } > + } > + return 0; > +} > + > +static int rtems_tftp_open( > + rtems_libio_t *iop, > + const char *new_name, > + int oflag, > + mode_t mode > +) > +{ > + tftpfs_info_t *fs; > + char *full_path_name; > + int err; > + > + full_path_name = iop->pathinfo.node_access; > + > + if (rtems_tftp_is_directory (full_path_name, strlen > (full_path_name))) { > + rtems_set_errno_and_return_minus_one (ENOTSUP); > + } > + > + /* > + * Get the file system info. > + */ > + fs = tftpfs_info_iop (iop); > + > + if (fs->flags & TFTPFS_VERBOSE) > + printf ("TFTPFS: %s\n", full_path_name); > + > + err = rtems_tftp_open_worker (iop, full_path_name, oflag); > + if (err != 0) { > + rtems_set_errno_and_return_minus_one (err); > + } > + > + return 0; > +} > + > +/* > + * Read from a TFTP stream > + */ > +static ssize_t rtems_tftp_read( > + rtems_libio_t *iop, > + void *buffer, > + size_t count > +) > +{ > + char *bp; > + struct tftpStream *tp = iop->data1; > + int retryCount; > + int nwant; > + > + if (!tp) > + rtems_set_errno_and_return_minus_one( EIO ); > + > + /* > + * Read till user request is satisfied or EOF is reached > + */ > + bp = buffer; > + nwant = count; > + while (nwant) { > + if (tp->nleft) { > + int ncopy; > + if (nwant < tp->nleft) > + ncopy = nwant; > + else > + ncopy = tp->nleft; > + memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy); > + tp->nused += ncopy; > + tp->nleft -= ncopy; > + bp += ncopy; > + nwant -= ncopy; > + if (nwant == 0) > + break; > + } > + if (tp->eof) > + break; > + > + /* > + * Wait for the next packet > + */ > + retryCount = 0; > + for (;;) { > + int len = getPacket (tp, retryCount); > + if (len >= (int)sizeof tp->pkbuf.tftpACK) { > + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); > + uint16_t nextBlock = tp->blocknum + 1; > + if ((opcode == TFTP_OPCODE_DATA) > + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { > + tp->nused = 0; > + tp->nleft = len - 2 * sizeof (uint16_t); > + tp->eof = (tp->nleft < TFTP_BUFSIZE); > + tp->blocknum++; > + if (sendAck (tp) != 0) > + rtems_set_errno_and_return_minus_one (EIO); > + break; > + } > + if (opcode == TFTP_OPCODE_ERROR) > + rtems_set_errno_and_return_minus_one (tftpErrno (tp)); > + } > + > + /* > + * Keep trying? > + */ > + if (++retryCount == IO_RETRY_LIMIT) > + rtems_set_errno_and_return_minus_one (EIO); > + if (sendAck (tp) != 0) > + rtems_set_errno_and_return_minus_one (EIO); > + } > + } > + return count - nwant; > +} > + > +/* > + * Flush a write buffer and wait for acknowledgement > + */ > +static int rtems_tftp_flush (struct tftpStream *tp) > +{ > + int wlen, rlen; > + int retryCount = 0; > + > + wlen = tp->nused + 2 * sizeof (uint16_t ); > + for (;;) { > + tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA); > + tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum); > +#ifdef RTEMS_TFTP_DRIVER_DEBUG > + if (rtems_tftp_driver_debug) > + printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused); > +#endif > + if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0, > + (struct sockaddr > *)&tp->farAddress, > + sizeof tp->farAddress) < 0) > + return EIO; > + rlen = getPacket (tp, retryCount); > + /* > + * Our last packet won't necessarily be acknowledged! > + */ > + if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data)) > + return 0; > + if (rlen >= (int)sizeof tp->pkbuf.tftpACK) { > + int opcode = ntohs (tp->pkbuf.tftpACK.opcode); > + if ((opcode == TFTP_OPCODE_ACK) > + && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) { > + tp->nused = 0; > + tp->blocknum++; > + return 0; > + } > + if (opcode == TFTP_OPCODE_ERROR) > + return tftpErrno (tp); > + } > + > + /* > + * Keep trying? > + */ > + if (++retryCount == IO_RETRY_LIMIT) > + return EIO; > + } > +} > + > +/* > + * Close a TFTP stream > + */ > +static int rtems_tftp_close( > + rtems_libio_t *iop > +) > +{ > + tftpfs_info_t *fs; > + struct tftpStream *tp = iop->data1; > + int e = 0; > + > + /* > + * Get the file system info. > + */ > + fs = tftpfs_info_iop (iop); > + > + if (!tp) > + rtems_set_errno_and_return_minus_one (EIO); > + > + if (tp->writing) > + e = rtems_tftp_flush (tp); > + if (!tp->eof && !tp->firstReply) { > + /* > + * Tell the other end to stop > + */ > + rtems_interval ticksPerSecond; > + sendStifle (tp, &tp->farAddress); > + ticksPerSecond = rtems_clock_get_ticks_per_second(); > + rtems_task_wake_after (1 + ticksPerSecond / 10); > + } > + releaseStream (fs, iop->data0); > + if (e) > + rtems_set_errno_and_return_minus_one (e); > + return 0; > +} > + > +static ssize_t rtems_tftp_write( > + rtems_libio_t *iop, > + const void *buffer, > + size_t count > +) > +{ > + const char *bp; > + struct tftpStream *tp = iop->data1; > + int nleft, nfree, ncopy; > + > + /* > + * Bail out if an error has occurred > + */ > + if (!tp->writing) > + rtems_set_errno_and_return_minus_one (EIO); > + > + /* > + * Write till user request is satisfied > + * Notice that the buffer is flushed as soon as it is filled rather > + * than waiting for the next write or a close. This ensures that > + * the flush in close writes a less than full buffer so the far > + * end can detect the end-of-file condition. > + */ > + bp = buffer; > + nleft = count; > + while (nleft) { > + nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused; > + if (nleft < nfree) > + ncopy = nleft; > + else > + ncopy = nfree; > + memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy); > + tp->nused += ncopy; > + nleft -= ncopy; > + bp += ncopy; > + if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) { > + int e = rtems_tftp_flush (tp); > + if (e) { > + tp->writing = 0; > + rtems_set_errno_and_return_minus_one (e); > + } > + } > + } > + return count; > +} > + > +/* > + * Dummy version to let fopen(xxxx,"w") work properly. > + */ > +static int rtems_tftp_ftruncate( > + rtems_libio_t *iop RTEMS_UNUSED, > + off_t count RTEMS_UNUSED > +) > +{ > + return 0; > +} > + > +static int rtems_tftp_fstat( > + const rtems_filesystem_location_info_t *loc, > + struct stat *buf > +) > +{ > + const char *path = loc->node_access; > + size_t pathlen = strlen (path); > + > + buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO > + | (rtems_tftp_is_directory (path, pathlen) ? S_IFDIR : S_IFREG); > + > + return 0; > +} > + > +static int rtems_tftp_clone( > + rtems_filesystem_location_info_t *loc > +) > +{ > + int rv = 0; > + > + loc->node_access = strdup (loc->node_access); > + > + if (loc->node_access == NULL) { > + errno = ENOMEM; > + rv = -1; > + } > + > + return rv; > +} > + > +static void rtems_tftp_free_node_info( > + const rtems_filesystem_location_info_t *loc > +) > +{ > + free (loc->node_access); > +} > + > +static bool rtems_tftp_are_nodes_equal( > + const rtems_filesystem_location_info_t *a, > + const rtems_filesystem_location_info_t *b > +) > +{ > + return strcmp (a->node_access, b->node_access) == 0; > +} > + > +static const rtems_filesystem_operations_table rtems_tftp_ops = { > + .lock_h = rtems_filesystem_default_lock, > + .unlock_h = rtems_filesystem_default_unlock, > + .eval_path_h = rtems_tftp_eval_path, > + .link_h = rtems_filesystem_default_link, > + .are_nodes_equal_h = rtems_tftp_are_nodes_equal, > + .mknod_h = rtems_filesystem_default_mknod, > + .rmnod_h = rtems_filesystem_default_rmnod, > + .fchmod_h = rtems_filesystem_default_fchmod, > + .chown_h = rtems_filesystem_default_chown, > + .clonenod_h = rtems_tftp_clone, > + .freenod_h = rtems_tftp_free_node_info, > + .mount_h = rtems_filesystem_default_mount, > + .unmount_h = rtems_filesystem_default_unmount, > + .fsunmount_me_h = rtems_tftpfs_shutdown, > + .utimens_h = rtems_filesystem_default_utimens, > + .symlink_h = rtems_filesystem_default_symlink, > + .readlink_h = rtems_filesystem_default_readlink, > + .rename_h = rtems_filesystem_default_rename, > + .statvfs_h = rtems_filesystem_default_statvfs > +}; > + > +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = { > + .open_h = rtems_tftp_open, > + .close_h = rtems_tftp_close, > + .read_h = rtems_tftp_read, > + .write_h = rtems_tftp_write, > + .ioctl_h = rtems_filesystem_default_ioctl, > + .lseek_h = rtems_filesystem_default_lseek, > + .fstat_h = rtems_tftp_fstat, > + .ftruncate_h = rtems_tftp_ftruncate, > + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, > + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, > + .fcntl_h = rtems_filesystem_default_fcntl, > + .kqfilter_h = rtems_filesystem_default_kqfilter, > + .mmap_h = rtems_filesystem_default_mmap, > + .poll_h = rtems_filesystem_default_poll, > + .readv_h = rtems_filesystem_default_readv, > + .writev_h = rtems_filesystem_default_writev > +}; > -- > 2.35.3 > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel
_______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel