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

Frank Kühndel frank.kuehndel at embedded-brains.de
Mon Jun 6 14:02:12 UTC 2022


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. 

Greetings 
fk 


> --joel 

On Fri, Jun 3, 2022, 10:22 AM Frank Kuehndel < [ mailto:frank.kuehndel at embedded-brains.de | frank.kuehndel at embedded-brains.de ] > wrote: 


From: Frank Kühndel < [ mailto:frank.kuehndel at embedded-brains.de | 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 < [ mailto:eric at norum.ca | 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 
[ mailto:devel at rtems.org | devel at rtems.org ] 
[ http://lists.rtems.org/mailman/listinfo/devel | http://lists.rtems.org/mailman/listinfo/devel ] 




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20220606/510e4d8d/attachment-0001.htm>


More information about the devel mailing list