TFTPFS: Implement block and window size options

The original file cpukit/libfs/src/ftpfs/tftpDriver.c
is split into two:

tftpfs.c     - This file contains the code from tftpDriver.c
               related to file system operations such as mount(),
               open(), read(), and so on.

tftpDriver.c - In the original file remains only the code related
               to networking.  This code implements the Trivial
               File Transfer Protocol (TFTP).

Moreover, the code is extended to support

  * RFC 2347 TFTP Option Extension
  * RFC 2348 TFTP Blocksize Option
  * RFC 7440 TFTP Windowsize Option

Update #4666.
This commit is contained in:
Frank Kühndel
2022-06-01 16:31:06 +02:00
committed by Sebastian Huber
parent c044f0502a
commit 679e7f109a
5 changed files with 2216 additions and 694 deletions

View File

@@ -1,13 +1,20 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @brief * Trivial File Transfer Protocol (TFTP)
* @ingroup RTEMSImplTFTPFS
*
* Transfer file to/from remote host
* @brief This header file provides interfaces and functions used to
* implement the TFTP file system.
*
* This file declares the public functions of the Trivial File
* Transfer Protocol (TFTP) file system.
*/
/*
* Copyright (c) 1998 Eric Norum <eric@norum.ca>
* Copyright (C) 1998 W. Eric Norum <eric@norum.ca>
* Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,16 +38,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Usage:
*
* To open `/bootfiles/image' on `hostname' for reading:
* fd = open ("/TFTP/hostname/bootfiles/image", O_RDONLY);
*
* The 'TFTP' is the mount path and the `hostname' must be four dot-separated
* decimal values.
*/
#ifndef _RTEMS_TFTP_H
#define _RTEMS_TFTP_H
@@ -48,9 +45,14 @@
extern "C" {
#endif
#include <stdint.h>
#include <rtems/fs.h>
/*
/**
* @brief Do not call directly, use mount().
*
* @ingroup RTEMSImplTFTPFS
*
* Filesystem Mount table entry.
*/
int rtems_tftpfs_initialize(
@@ -58,6 +60,383 @@ int rtems_tftpfs_initialize(
const void *data
);
/**
* @defgroup RTEMSAPITFTPFS Trivial File Transfer Protocol (TFTP) API
*
* @ingroup RTEMSAPIIO
*
* @brief The TFTP client library provides an API to read files from and
* to write files to remote servers using the Trivial File Transfer
* Protocol (TFTP).
*
* See the _RTEMS Filesystem Design Guide_ Chapter _Trivial FTP Client
* Filesystem_.
*
* Usage as TFTP File System
* =========================
*
* To open `/bootfiles/image` on `hostname` for reading:
*
* fd = open ("/TFTP/hostname:bootfiles/image", O_RDONLY);
*
* The `TFTP` is the mount path and the `hostname` must be
*
* + an IPv4 address (like `127.0.0.1`) or
* + the (full-qualified) name of an IPv4 host (acceptable to
* `gethostbyname()`)
*
* IPv6 is currently not supported. `bootfiles/image` is a path on the
* TFTP server `hostname` where the file (here `image`) can be found.
*
* Usage of TFTP Client
* ====================
*
* The pseudo-code below shows the principal usage for reading a file.
*
* @code
* int res;
* ssize_t bytes;
* void *tftp_handle;
* tftp_net_config config;
* const size_t buffer_size = 4000;
* char data_buffer[buffer_size];
*
* tftp_initialize_net_config( &config );
* config.options.window_size = 1; // Set desired config values
*
* res = tftp_open(
* "127.0.0.1",
* "filename.txt",
* true, // is_for_reading
* &config,
* &tftp_handle
* );
*
* if ( res != 0 || tftp_handle == NULL ) {
* // Error
* }
*
* // Use tftp_read() (probably in a loop) ...
* bytes = tftp_read(
* tftp_handle,
* data_buffer,
* buffer_size
* );
* // ... or use tftp_write() instead when the file is open for writing.
*
* res = tftp_close( tftp_handle );
*
* if ( res != 0 ) {
* // Error ... check! Especially when writing!
* }
* @endcode
*
* @{
*/
/*
* The functions below use of the TFTP client library standalone
* - i.e. without going through the file system.
*/
/**
* @brief This block size meets RFC 1350 and avoids the sending of
* the `blksize` option to the TFTP server.
*/
#define TFTP_RFC1350_BLOCK_SIZE 512
/**
* @brief This window size avoids the sending of the `windowsize`
* option to the TFTP server.
*
* This effectively mimics the operation defined in RFC 1350 which
* does not know any window size.
*/
#define TFTP_RFC1350_WINDOW_SIZE 1
/**
* @brief This block size is suggested in RFC 2348 and is used as
* default if no different block size is provided.
*/
#define TFTP_DEFAULT_BLOCK_SIZE 1456
/**
* @brief This window size is suggested in RFC 2348 and is used as
* default if no different window size is provided.
*/
#define TFTP_DEFAULT_WINDOW_SIZE 8
/**
* @brief This structure represents TFTP options negotiated between
* client and server.
*
* RFC 2347 is the basis for the TFTP option.
*/
typedef struct tftp_options {
/**
* @brief This member represents the desired size of a data block.
*
* The TFTP blocksize option is introduced in RFC 2348. It defines the
* number of octets in the data packets transferred. Valid values
* range between 8 and 65464 octets, inclusive. Values larger
* than 1468 may cause packet fragmentation over standard Ethernet.
* A value of 512 will prevent this option from being sent to
* the server.
*
* The default value is 1456.
*/
uint16_t block_size;
/**
* @brief This member represents the desired size of a window.
*
* The TFTP windowsize option is introduced in RFC 7440. It defines the
* number of data packets send before the receiver must send an
* acknowledgment packet. Valid values range between 1 and 65535
* packets, inclusive. Simple TFTP servers usually do not support this
* option. This option may negatively contribute to network
* congestion. This can be avoided by using a window size of 1.
* A value of 1 will prevent this option from being sent to
* the server.
*
* The default value is 8.
*/
uint16_t window_size;
} tftp_options;
/**
* @brief This structure represents configuration value used by the TFTP
* client.
*
* As defaults the values suggested in RFC 7440 are used.
*/
typedef struct tftp_net_config {
/**
* @brief This member defines how many attempts are made to send a
* network packet to the server.
*
* Repetitions occur when the server does not response to a packet
* send by the client within a timeout period. When the here defined
* number of repetitions is reached and the server does still not
* respond, the connection is considered broken and the file transfer
* is ended with an error.
*
* The default value is 6.
*/
uint16_t retransmissions;
/**
* @brief This member defines the port on which the server is listening
* for incoming connections.
*
* The default port number is 69.
*/
uint16_t server_port;
/**
* @brief This member defines the maximum time in milliseconds the
* client waits for an answer packet from the server.
*
* If the time out is exceeded, the client will re-transmit the last
* packet it send to the server. In case @c window_size is larger one,
* several packets may subject to re-transmission.
*
* Note that this timeout applies only after the first re-transmission
* of a packet. The timeout till the first re-transmission is
* @c first_timeout.
*
* The default value is 1000ms.
*/
uint32_t timeout;
/**
* @brief This member defines the maximum time in milliseconds the
* client waits for the first answer packet from the server.
*
* The @c first_timeout is used instead of the regular @c timeout
* for the first wait-period directly after the client sends a packet
* for the first time to the server. That is, this is the timeout
* of the first re-transmission. For any following re-transmissions
* of the current packet the regular @c timeout is used.
*
* The default value is 400ms.
*/
uint32_t first_timeout;
/**
* @brief This member represents the options to be sent to the server.
*
* These option values are sent to the server. Yet, the server may
*
* + ignore one or all options
* + ask the client to use a different value for an option
* + reject the whole request with an error
*
* If the server rejects a request with options, the current client
* implementation will automatically send a second request without
* options. Hence, the user should be aware that the actual file
* transfer may not use the option values specified here.
*/
tftp_options options;
} tftp_net_config;
/**
* @brief Set all members of a @c tftp_net_config structure to their
* default values.
*
* @param config references a @c tftp_net_config structure.
* The values are set to the defaults defined in
* @ref tftp_net_config "`type tftp_net_config`".
*/
void tftp_initialize_net_config(
tftp_net_config *config
);
/**
* @brief Opens and starts a TFTP client session to read or write a
* single file.
*
* This directive resolves the hostname or IP address, establishes a connection
* to the TFTP server and initiates the data transfer. It will not return
* before an error is encountered or the TFTP server has responded to the
* read or write request with a network packet.
*
* TFTP uses timeouts (of unspecified length). It does not know keep-alive
* messages. If the client does not respond to the server in due time,
* the server sets the connection faulty and drops it. To avoid this
* the user of this code must read or write enough data fast enough.
*
* "Enough data" means at least so much data which fills a single data
* packet or all packets of a window if windows are used. The data
* can be read or written in anything from one single large chunk to
* byte-by-byte pieces. The point is, one cannot pause the reading
* or writing for longer periods of time.
*
* @param hostname is the IPv4 address as string or the name of the TFTP
* server to connect to.
* @param path is the pathname at the TFTP server side of the file to
* read or write. According to RFC 1350 the path must be in
* NETASCII. This is ASCII as defined in "USA Standard Code for
* Information Interchange" with the modifications specified in "Telnet
* Protocol Specification".
* @param is_for_reading indicated whether the file is to be read or written.
* A value of @c true indicates that the file is intended to be read from
* the server. A value of @c false indicates that the file is to be
* written to the server.
* @param config either references a structure defining the configuration
* values for this file transfer or is @c NULL. If it is @c NULL, default
* configuration values are used. See @ref tftp_net_config
* "type tftp_net_config" for a description and the defaults values.
* This function copies the data so that the memory pointed to by
* @c config can be used for other purposes after the call returns.
* @param[out] tftp_handle references a place where a handle of the connection
* can be stored. On success a pointer to a handle is stored. On failure
* (return value other than 0) a @c NULL pointer is stored. This handle
* must be provided to all further calls to @c tftp_read(),
* @c tftp_write(), and @c tftp_close().
*
* When this directive stores a non-NULL pointer in this place, a call
* to @c tftp_close() is mandatory to release allocated resources.
* This parameter cannot be @c NULL.
*
* @retval 0 When the client session was opened successfully.
* @return Returns a POSIX @c errno value in case an error occurred.
*/
int tftp_open(
const char *hostname,
const char *path,
bool is_for_reading,
const tftp_net_config *config,
void **tftp_handle
);
/**
* @brief Read data from a TFTP server.
*
* This directive attempts to read data from a TFTP connection open for
* reading.
*
* Upon success, the buffer is always filled with @c count bytes of received
* data with the exception when the end of the file has been reached.
*
* TFTP cannot recover from errors. Once an error is reported, the
* connection must be and can only be closed.
*
* @param tftp_handle is the reference returned by a call to tftp_open().
* The file must be opened for reading.
* @param[out] buffer references a memory area into which the received
* data is written.
* @param count defines the size of the @c buffer in bytes.
*
* @retval 0 The end of the file has been reached. There is no more data.
* @return If greater or equal to 0, returns the number of bytes written
* into the buffer. If the return value is negative, an error occurred.
* In this case the negated value is a POSIX @c errno value.
*/
ssize_t tftp_read(
void *tftp_handle,
void *buffer,
size_t count
);
/**
* @brief Write data to a TFTP server.
*
* This directive attempts to write data to a TFTP connection open for
* writing.
*
* On a successful call, all data in the @c buffer will be used. Yet, this
* does not imply that all data is actually sent. This depends on
* whether a whole data packet or window can be filled.
*
* TFTP cannot recover from errors. Once an error is reported, the connection
* must be and can only be closed.
*
* @param tftp_handle is the reference returned by a call to tftp_open().
* The file must be opened for writing.
* @param buffer references a memory area which contains the data to be
* sent.
* @param count defines the size of the data in @c buffer in bytes.
*
* @return If greater or equal to 0, returns the number of bytes used
* from the buffer. The value is always @c count on a successful call.
* If the return value is negative, an error occurred. In this case
* the negated value is a POSIX @c errno value.
*/
ssize_t tftp_write(
void *tftp_handle,
const void *buffer,
size_t count
);
/**
* @brief Close a TFTP client connection.
*
* This directive sends all data which are still stored in a write buffer
* to the server (if any), tells the server that the connection ends (if
* required by RFC 1350) and releases any resources allocated at the
* client side.
*
* @note Especially, when writing a file to the server, the return
* code of `tftp_close()` should be checked. Invoking
* `tftp_close()` triggers the sending of the last -- not
* completely filled -- data block. This may fail the same way as any
* `tftp_write()` may fail. Therefore, an error returned by
* `tftp_close()` likely indicates that the file was not
* completely transferred.
*
* @param tftp_handle is the reference returned by a call to tftp_open().
* If this parameter is @c NULL, the directive call is a no-op.
*
* @retval 0 When the client session was closed successfully.
* @return Returns a POSIX @c errno value in case an error occurred.
*/
int tftp_close(
void *tftp_handle
);
/** @} */
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSImplTFTPFS
*
* @brief This header file provides private interfaces of the
* TFTP client library.
*
* This file declares the private functions of the Trivial File
* Transfer Protocol (TFTP) client library.
*/
/*
* Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifndef _TFTP_DRIVER_H
#define _TFTP_DRIVER_H
/* Remove for C++ code */
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup RTEMSImplTFTPFS Trivial File Transfer Protocol (TFTP) file system
*
* @ingroup FileSystemTypesAndMount
*
* @brief The TFTP file system provides the ability to read files from and
* to write files to remote servers using the Trivial File Transfer
* Protocol (TFTP).
*
* The file `spec/build/cpukit/libtftpfs.yml` specifies how the RTEMS
* WAF build system has to compile, link and install `libtftpfs`.
*
* There also exists a @ref RTEMSTestSuiteTestsTFTPFS
* "TFTP file system test suite".
*
* @{
*/
/**
* @brief Free the resources associated with a TFTP client connection.
*
* This directive releases any resources allocated at the client side.
* The connection is not closed which implies that the server will not
* be informed and data is likely lost. According to RFC 1350 the
* server will recognize the defect connection by timeouts.
* This directive is internally used when the TFTP file system is unmounted.
*
* @param tftp_handle is the reference returned by a call to tftp_open().
* If this parameter is @c NULL, the directive call is a no-op.
*/
void _Tftp_Destroy(
void *tftp_handle
);
/* Only non-private to ease unit testing */
ssize_t _Tftpfs_Parse_options(
const char *option_str,
tftp_net_config *tftp_config,
uint32_t *flags
);
/** @} */
/* Remove for C++ code */
#ifdef __cplusplus
}
#endif
#endif /* _TFTP_DRIVER_H */

View File

@@ -0,0 +1,615 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSImplTFTPFS
*
* @brief This source file contains the implementation of
* the Trivial File Transfer Protocol (TFTP) file system.
*
* The code in this file handles the file system operations (such as
* `mount()`, `open()`, `read()`, `write()`, `close()` etc.).
* The networking part, i.e. the actual Trivial File Transfer Protocol
* implementation, is realized in another file - the
* @ref tftpDriver.c "TFTP client library".
*/
/*
* Copyright (C) 1998 W. Eric Norum <eric@norum.ca>
* Copyright (C) 2012, 2022 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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 <inttypes.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 "tftp_driver.h"
/*
* 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;
size_t nStreams;
void ** volatile tftpStreams;
tftp_net_config tftp_config;
} 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)))
/* Forward declarations */
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] == '/';
}
/*
* Return value:
* 0 if options have been pracessed without error
* N+1 if parsing failed at position N
*/
ssize_t _Tftpfs_Parse_options(
const char *option_str,
tftp_net_config *tftp_config,
uint32_t *flags
)
{
const char *cur_pos = option_str;
size_t verbose_len = strlen ("verbose");
size_t rfc1350_len = strlen ("rfc1350");
int len;
while(cur_pos != NULL && *cur_pos != '\0') {
if (strncmp (cur_pos, "verbose", verbose_len) == 0) {
*flags |= TFTPFS_VERBOSE;
len = (int) verbose_len;
} else if (strncmp (cur_pos, "rfc1350", rfc1350_len) == 0) {
tftp_config->options.block_size = TFTP_RFC1350_BLOCK_SIZE;
tftp_config->options.window_size = TFTP_RFC1350_WINDOW_SIZE;
len = (int) rfc1350_len;
} else if (sscanf(
cur_pos,
"blocksize=%"SCNu16"%n",
&tftp_config->options.block_size,
&len
) == 1) {
} else if (sscanf(
cur_pos,
"windowsize=%"SCNu16"%n",
&tftp_config->options.window_size,
&len
) == 1) {
} else if (*cur_pos == ',') { /* skip surplus "," */
len = 0;
} else {
return cur_pos - option_str + 1;
}
cur_pos += len;
if (*cur_pos != ',' && *cur_pos != '\0') {
return cur_pos - option_str + 1;
}
if (*cur_pos == ',') {
cur_pos++;
}
}
return 0;
}
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;
size_t err_pos;
int errno_store = ENOMEM;
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;
tftp_initialize_net_config (&fs->tftp_config);
err_pos = _Tftpfs_Parse_options (data, &fs->tftp_config, &fs->flags);
if (err_pos != 0) {
printf(
"TFTP FS: ERROR in mount options '%s'.\n"
"TFTP FS: Cannot parse from this point: '%s'\n",
((char *) data),
((char *) data) + (err_pos - 1)
);
errno_store = EINVAL;
goto error;
}
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");
return 0;
error:
free (fs);
free (root_path);
rtems_set_errno_and_return_minus_one (errno_store);
}
/*
* Clear the pointer to a stream
*/
static void
releaseStream (tftpfs_info_t *fs, size_t s)
{
rtems_mutex_lock (&fs->tftp_mutex);
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);
size_t s;
void *tp;
for (s = 0; s < fs->nStreams; s++) {
tp = fs->tftpStreams[s];
releaseStream (fs, s);
_Tftp_Destroy(tp);
}
rtems_mutex_destroy (&fs->tftp_mutex);
free (fs);
free (mt_entry->mt_fs_root->location.node_access);
}
/*
* 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;
void *tp;
size_t s;
char *cp1;
char *remoteFilename;
char *hostname;
int err;
/*
* 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) {
return EINVAL; /* No ':' in path: no hostname or no filename */
} else {
*cp1 = '\0';
++cp1;
}
/*
* Extract file pathname component
*/
if (*cp1 == '\0')
return ENOENT;
remoteFilename = cp1;
/*
* Establish the connection
*/
err = tftp_open (
hostname,
remoteFilename,
(oflag & O_ACCMODE) == O_RDONLY,
&fs->tftp_config,
&tp
);
if (err != 0) {
return err;
}
/*
* 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.
*/
void **np;
np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams);
if (np == NULL) {
rtems_mutex_unlock (&fs->tftp_mutex);
tftp_close( tp );
return ENOMEM;
}
fs->tftpStreams = np;
}
fs->tftpStreams[s] = tp;
rtems_mutex_unlock (&fs->tftp_mutex);
iop->data0 = s;
iop->data1 = tp;
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
)
{
void *tp = iop->data1;
ssize_t result = tftp_read (tp, buffer, count);
if (result < 0) {
rtems_set_errno_and_return_minus_one (-result);
}
return result;
}
/*
* Close a TFTP stream
*/
static int rtems_tftp_close(
rtems_libio_t *iop
)
{
tftpfs_info_t *fs;
void *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);
releaseStream (fs, iop->data0);
e = tftp_close (tp);
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
)
{
void *tp = iop->data1;
ssize_t result = tftp_write (tp, buffer, count);
if (result < 0) {
rtems_set_errno_and_return_minus_one (-result);
}
return result;
}
/*
* 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
};

View File

@@ -2,7 +2,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: library
cflags: []
copyrights:
- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
- Copyright (C) 2020, 2022 embedded brains GmbH (http://www.embedded-brains.de)
cppflags: []
cxxflags: []
enabled-by: true
@@ -16,5 +16,6 @@ install-path: ${BSP_LIBDIR}
links: []
source:
- cpukit/libfs/src/ftpfs/tftpDriver.c
- cpukit/libfs/src/ftpfs/tftpfs.c
target: tftpfs
type: build