<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div>Hello Joel,<br></div><div><br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><b>From: </b>"Joel Sherrill" <joel@rtems.org><br><b>To: </b>"Frank Kühndel" <frank.kuehndel@embedded-brains.de><br><b>Cc: </b>"rtems-devel@rtems.org" <devel@rtems.org><br><b>Sent: </b>Saturday, June 4, 2022 1:05:25 AM<br><b>Subject: </b>Re: [PATCH 3/7] TFTPFS: Restore tftpDriver.c<br></div><div><br></div><div data-marker="__QUOTED_TEXT__"><div dir="auto">> Is this really just a move/rename? Does it preserve the git blame info?</div><div dir="auto"><div dir="auto"><br></div><div dir="auto">This (PATCH 3/7) is really a file "copy/recreate" (git blame info will *NOT* be preserved). The move/rename is (at the very beginning of) PATCH 2/7. My understanding is, that the git blame info will be "moved" by PATCH 2/7 from original file tftpDriver.c to the "copied/moved" tftpfs.c file provided the person commiting the patches does not apply the trick described in the link I send in the cover letter of this patch set. If that trick is applied both files tftpDriver.c and tftpfs.c will have an intact git blame info - at least as long as nothing "surprising" like a git rebase happens.<br data-mce-bogus="1"></div><div dir="auto"><br data-mce-bogus="1"></div><div dir="auto">My feeling is that more original code survived in tftpfs.c than in tftpDriver.c. Therefore, in case the trick is not applied (i.e. only one file ends up with an intact git blame info) it is preferable that this be tftpfs.c.<br data-mce-bogus="1"></div><div dir="auto"><br data-mce-bogus="1"></div><div dir="auto">Greetings<br data-mce-bogus="1"></div><div dir="auto">fk<br data-mce-bogus="1"></div><div dir="auto"><br data-mce-bogus="1"></div><div dir="auto"><br data-mce-bogus="1"></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" target="_blank" rel="nofollow noopener noreferrer">frank.kuehndel@embedded-brains.de</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 0.8ex;border-left:1px #ccc solid;padding-left:1ex">From: Frank Kühndel <<a href="mailto:frank.kuehndel@embedded-brains.de" rel="noreferrer nofollow noopener noreferrer" target="_blank">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" rel="noreferrer nofollow noopener noreferrer" target="_blank">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" rel="noreferrer nofollow noopener noreferrer" target="_blank">devel@rtems.org</a><br>
<a href="http://lists.rtems.org/mailman/listinfo/devel" rel="noreferrer noreferrer nofollow noopener noreferrer" target="_blank">http://lists.rtems.org/mailman/listinfo/devel</a><br data-mce-bogus="1"></blockquote></div><br></div></div></body></html>