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

Joel Sherrill joel at rtems.org
Mon Jun 6 15:34:22 UTC 2022


On Mon, Jun 6, 2022 at 9:02 AM Frank Kühndel <
frank.kuehndel at embedded-brains.de> wrote:

> Hello Joel,
>
>
>
> ------------------------------
> *From: *"Joel Sherrill" <joel at rtems.org>
> *To: *"Frank Kühndel" <frank.kuehndel at embedded-brains.de>
> *Cc: *"rtems-devel at rtems.org" <devel at rtems.org>
> *Sent: *Saturday, June 4, 2022 1:05:25 AM
> *Subject: *Re: [PATCH 3/7] TFTPFS: Restore tftpDriver.c
>
> > Is this really just a move/rename? Does it preserve the git blame info?
>
> 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.
>
> 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.
>

OK. Work with Christian or Sebastian to make sure it gets committed
correctly.

--joel

>
> Greetings
> fk
>
>
> > --joel
>
> On Fri, Jun 3, 2022, 10:22 AM Frank Kuehndel <
> frank.kuehndel at embedded-brains.de> wrote:
>
>> 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
>>
>> _______________________________________________
>> devel mailing list
>> devel at rtems.org
>> http://lists.rtems.org/mailman/listinfo/devel
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20220606/e3604003/attachment-0001.htm>


More information about the devel mailing list