forked from Imagelibrary/rtems
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:
committed by
Sebastian Huber
parent
c044f0502a
commit
679e7f109a
@@ -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
96
cpukit/libfs/src/ftpfs/tftp_driver.h
Normal file
96
cpukit/libfs/src/ftpfs/tftp_driver.h
Normal 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 */
|
||||
615
cpukit/libfs/src/ftpfs/tftpfs.c
Normal file
615
cpukit/libfs/src/ftpfs/tftpfs.c
Normal 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
|
||||
};
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user