[PATCH 3/7] TFTPFS: Restore tftpDriver.c

Frank Kuehndel frank.kuehndel at embedded-brains.de
Fri Jun 3 15:22:42 UTC 2022


From: Frank Kühndel <frank.kuehndel at 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 <eric at 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



More information about the devel mailing list