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