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