diff options
Diffstat (limited to '')
43 files changed, 3024 insertions, 811 deletions
diff --git a/cpukit/include/rtems/score/gcov.h b/cpukit/include/rtems/score/gcov.h new file mode 100644 index 0000000000..b150c9f763 --- /dev/null +++ b/cpukit/include/rtems/score/gcov.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSScoreGcov + * + * @brief This header file provides the interfaces of the + * @ref RTEMSScoreGcov. + */ + +/* + * Copyright (c) 2013-2014 embedded brains GmbH. All rights reserved. + * + * 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 _RTEMS_SCORE_GCOV_H +#define _RTEMS_SCORE_GCOV_H + +#include <gcov.h> + +#include <rtems/linkersets.h> +#include <rtems/score/io.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup RTEMSScoreGcov Gcov Support + * + * @ingroup RTEMSScore + * + * @brief This group contains the gocv support. + * + * @{ + */ + +RTEMS_LINKER_ROSET_DECLARE( gcov_info, const struct gcov_info * ); + +/** + * @brief Dumps the gcov information as a binary gcfn and gcda data + * stream using the put character handler. + * + * @param put_char is the put character handler used to output the data stream. + * + * @param arg is the argument passed to the put character handler. + */ +void _Gcov_Dump_info( IO_Put_char put_char, void *arg ); + +/** + * @brief Dumps the gcov information as a base64 encoded gcfn and gcda data + * stream using the put character handler. + * + * @param put_char is the put character handler used to output the data stream. + * + * @param arg is the argument passed to the put character handler. + */ +void _Gcov_Dump_info_base64( IO_Put_char put_char, void *arg ); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_SCORE_GCOV_H */ diff --git a/cpukit/include/rtems/score/priorityimpl.h b/cpukit/include/rtems/score/priorityimpl.h index 1463bf6c2a..55cddf53be 100644 --- a/cpukit/include/rtems/score/priorityimpl.h +++ b/cpukit/include/rtems/score/priorityimpl.h @@ -125,26 +125,6 @@ RTEMS_INLINE_ROUTINE bool _Priority_Actions_is_empty( } /** - * @brief Checks if the priority actions is valid. - * - * @param aggregation The aggregation of the priority action. - * - * @retval true The @a aggregation is valid. - * @retval false The @a aggregation is not valid. - */ -RTEMS_INLINE_ROUTINE bool _Priority_Actions_is_valid( - const Priority_Aggregation *aggregation -) -{ -#if defined(RTEMS_SMP) - return aggregation != NULL; -#else - (void) aggregation; - return false; -#endif -} - -/** * @brief Moves the priority actions' actions. * * @param[in, out] actions The priority actions to move the actions away from. @@ -389,25 +369,22 @@ RTEMS_INLINE_ROUTINE void _Priority_Set_action( aggregation->Action.type = type; } +#if defined(RTEMS_SMP) /** * @brief Gets the next action of the priority aggregation. * - * @param aggregation The priority aggregation to get the next action of. + * @param aggregation is the priority aggregation to get the next action of. * - * @retval next_action The next action of @a aggregation if RTEMS_SMP is defined. - * @retval NULL RTEMS_SMP is not defined. + * @return Returns the next action of the priority aggregation or NULL if there + * is no next action. */ RTEMS_INLINE_ROUTINE Priority_Aggregation *_Priority_Get_next_action( const Priority_Aggregation *aggregation ) { -#if defined(RTEMS_SMP) return aggregation->Action.next; -#else - (void) aggregation; - return NULL; -#endif } +#endif /** * @brief Compares two priorities. diff --git a/cpukit/include/rtems/score/processormask.h b/cpukit/include/rtems/score/processormask.h index 000bb63c8b..40fb52a70f 100644 --- a/cpukit/include/rtems/score/processormask.h +++ b/cpukit/include/rtems/score/processormask.h @@ -47,6 +47,62 @@ extern "C" { #endif /* __cplusplus */ +/* + * Recent Newlib versions provide the bitset defines in the system reserved + * namespace. + */ +#ifndef __BIT_AND2 +#define __BIT_AND2 BIT_AND2 +#endif +#ifndef __BIT_CLR +#define __BIT_CLR BIT_CLR +#endif +#ifndef __BIT_CMP +#define __BIT_CMP BIT_CMP +#endif +#ifndef __BIT_COPY +#define __BIT_COPY BIT_COPY +#endif +#ifndef __BIT_COUNT +#define __BIT_COUNT BIT_COUNT +#endif +#ifndef __BITSET_DEFINE +#define __BITSET_DEFINE BITSET_DEFINE +#endif +#ifndef __BIT_EMPTY +#define __BIT_EMPTY BIT_EMPTY +#endif +#ifndef __BIT_FILL +#define __BIT_FILL BIT_FILL +#endif +#ifndef __BIT_FLS +#define __BIT_FLS BIT_FLS +#endif +#ifndef __BIT_ISSET +#define __BIT_ISSET BIT_ISSET +#endif +#ifndef __BIT_OR2 +#define __BIT_OR2 BIT_OR2 +#endif +#ifndef __BIT_OVERLAP +#define __BIT_OVERLAP BIT_OVERLAP +#endif +#ifndef __BIT_SET +#define __BIT_SET BIT_SET +#endif +#ifndef __BIT_SETOF +#define __BIT_SETOF BIT_SETOF +#endif +#ifndef __BIT_SUBSET +#define __BIT_SUBSET BIT_SUBSET +#endif +#ifndef __BIT_XOR2 +#define __BIT_XOR2 BIT_XOR2 +#endif +#ifndef __BIT_ZERO +#define __BIT_ZERO BIT_ZERO +#endif + /** * @defgroup RTEMSScoreProcessorMask Processor Mask * @@ -65,7 +121,7 @@ extern "C" { * @brief A bit map which is large enough to provide one bit for each processor * in the system. */ -typedef BITSET_DEFINE( Processor_mask, CPU_MAXIMUM_PROCESSORS ) Processor_mask; +typedef __BITSET_DEFINE( Processor_mask, CPU_MAXIMUM_PROCESSORS ) Processor_mask; /** * @brief Sets the bits of the mask to zero, also considers CPU_MAXIMUM_PROCESSORS. @@ -74,7 +130,7 @@ typedef BITSET_DEFINE( Processor_mask, CPU_MAXIMUM_PROCESSORS ) Processor_mask; */ RTEMS_INLINE_ROUTINE void _Processor_mask_Zero( Processor_mask *mask ) { - BIT_ZERO( CPU_MAXIMUM_PROCESSORS, mask ); + __BIT_ZERO( CPU_MAXIMUM_PROCESSORS, mask ); } /** @@ -87,7 +143,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Zero( Processor_mask *mask ) */ RTEMS_INLINE_ROUTINE bool _Processor_mask_Is_zero( const Processor_mask *mask ) { - return BIT_EMPTY( CPU_MAXIMUM_PROCESSORS, mask ); + return __BIT_EMPTY( CPU_MAXIMUM_PROCESSORS, mask ); } /** @@ -97,7 +153,7 @@ RTEMS_INLINE_ROUTINE bool _Processor_mask_Is_zero( const Processor_mask *mask ) */ RTEMS_INLINE_ROUTINE void _Processor_mask_Fill( Processor_mask *mask ) { - BIT_FILL( CPU_MAXIMUM_PROCESSORS, mask ); + __BIT_FILL( CPU_MAXIMUM_PROCESSORS, mask ); } /** @@ -110,7 +166,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Assign( Processor_mask *dst, const Processor_mask *src ) { - BIT_COPY( CPU_MAXIMUM_PROCESSORS, src, dst ); + __BIT_COPY( CPU_MAXIMUM_PROCESSORS, src, dst ); } /** @@ -124,7 +180,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Set( uint32_t index ) { - BIT_SET( CPU_MAXIMUM_PROCESSORS, index, mask ); + __BIT_SET( CPU_MAXIMUM_PROCESSORS, index, mask ); } /** @@ -138,7 +194,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Clear( uint32_t index ) { - BIT_CLR( CPU_MAXIMUM_PROCESSORS, index, mask ); + __BIT_CLR( CPU_MAXIMUM_PROCESSORS, index, mask ); } /** @@ -155,7 +211,7 @@ RTEMS_INLINE_ROUTINE bool _Processor_mask_Is_set( uint32_t index ) { - return BIT_ISSET( CPU_MAXIMUM_PROCESSORS, index, mask ); + return __BIT_ISSET( CPU_MAXIMUM_PROCESSORS, index, mask ); } /** @@ -172,7 +228,7 @@ RTEMS_INLINE_ROUTINE bool _Processor_mask_Is_equal( const Processor_mask *b ) { - return !BIT_CMP( CPU_MAXIMUM_PROCESSORS, a, b ); + return !__BIT_CMP( CPU_MAXIMUM_PROCESSORS, a, b ); } /** @@ -190,7 +246,7 @@ RTEMS_INLINE_ROUTINE bool _Processor_mask_Has_overlap( const Processor_mask *b ) { - return BIT_OVERLAP( CPU_MAXIMUM_PROCESSORS, a, b ); + return __BIT_OVERLAP( CPU_MAXIMUM_PROCESSORS, a, b ); } /** @@ -208,7 +264,7 @@ RTEMS_INLINE_ROUTINE bool _Processor_mask_Is_subset( const Processor_mask *small ) { - return BIT_SUBSET( CPU_MAXIMUM_PROCESSORS, big, small ); + return __BIT_SUBSET( CPU_MAXIMUM_PROCESSORS, big, small ); } /** @@ -224,23 +280,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_And( const Processor_mask *c ) { - BIT_AND2( CPU_MAXIMUM_PROCESSORS, a, b, c ); -} - -/** - * @brief Performs a bitwise a = b & ~c. - * - * @param[out] a The processor mask that is set by this operation. - * @param b The first parameter of the operation. - * @param c The second parameter of the operation. - */ -RTEMS_INLINE_ROUTINE void _Processor_mask_Nand( - Processor_mask *a, - const Processor_mask *b, - const Processor_mask *c -) -{ - BIT_NAND2( CPU_MAXIMUM_PROCESSORS, a, b, c ); + __BIT_AND2( CPU_MAXIMUM_PROCESSORS, a, b, c ); } /** @@ -256,7 +296,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Or( const Processor_mask *c ) { - BIT_OR2( CPU_MAXIMUM_PROCESSORS, a, b, c ); + __BIT_OR2( CPU_MAXIMUM_PROCESSORS, a, b, c ); } /** @@ -272,7 +312,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Xor( const Processor_mask *c ) { - BIT_XOR2( CPU_MAXIMUM_PROCESSORS, a, b, c ); + __BIT_XOR2( CPU_MAXIMUM_PROCESSORS, a, b, c ); } /** @@ -284,7 +324,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_Xor( */ RTEMS_INLINE_ROUTINE uint32_t _Processor_mask_Count( const Processor_mask *a ) { - return (uint32_t) BIT_COUNT( CPU_MAXIMUM_PROCESSORS, a ); + return (uint32_t) __BIT_COUNT( CPU_MAXIMUM_PROCESSORS, a ); } /** @@ -296,7 +336,7 @@ RTEMS_INLINE_ROUTINE uint32_t _Processor_mask_Count( const Processor_mask *a ) */ RTEMS_INLINE_ROUTINE uint32_t _Processor_mask_Find_last_set( const Processor_mask *a ) { - return (uint32_t) BIT_FLS( CPU_MAXIMUM_PROCESSORS, a ); + return (uint32_t) __BIT_FLS( CPU_MAXIMUM_PROCESSORS, a ); } /** @@ -347,7 +387,7 @@ RTEMS_INLINE_ROUTINE void _Processor_mask_From_index( uint32_t index ) { - BIT_SETOF( CPU_MAXIMUM_PROCESSORS, (int) index, mask ); + __BIT_SETOF( CPU_MAXIMUM_PROCESSORS, (int) index, mask ); } typedef enum { diff --git a/cpukit/include/rtems/test-info.h b/cpukit/include/rtems/test-info.h index 1bc963249c..c7c6c2438d 100644 --- a/cpukit/include/rtems/test-info.h +++ b/cpukit/include/rtems/test-info.h @@ -331,6 +331,12 @@ RTEMS_NO_RETURN void rtems_test_run( const RTEMS_TEST_STATE state ); +/** + * @brief Dumps the gcov information as a base64 encoded gcfn and gcda data + * stream using rtems_put_char(). + */ +void rtems_test_gcov_dump_info( void ); + /** @} */ #ifdef __cplusplus diff --git a/cpukit/include/rtems/tftp.h b/cpukit/include/rtems/tftp.h index 8fa516042b..d2328e3cdc 100644 --- a/cpukit/include/rtems/tftp.h +++ b/cpukit/include/rtems/tftp.h @@ -1,13 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * - * @brief * Trivial File Transfer Protocol (TFTP) + * @ingroup RTEMSImplTFTPFS + * + * @brief This header file provides interfaces and functions used to + * implement the TFTP file system. * - * Transfer file to/from remote host + * 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 diff --git a/cpukit/libcsupport/src/__gettod.c b/cpukit/libcsupport/src/__gettod.c index 610f2c4d4a..b43081c9eb 100644 --- a/cpukit/libcsupport/src/__gettod.c +++ b/cpukit/libcsupport/src/__gettod.c @@ -41,7 +41,7 @@ /* * Needed to get the prototype for the newlib helper method */ -#define _COMPILING_NEWLIB +#define _LIBC #include <sys/time.h> #include <reent.h> diff --git a/cpukit/libcsupport/src/__times.c b/cpukit/libcsupport/src/__times.c index 629a7bc633..14625b5aae 100644 --- a/cpukit/libcsupport/src/__times.c +++ b/cpukit/libcsupport/src/__times.c @@ -38,9 +38,9 @@ #endif /* - * Needed to get the prototype for this newlib helper method + * Needed to get the prototype for this libc helper method */ -#define _COMPILING_NEWLIB +#define _LIBC #include <rtems.h> diff --git a/cpukit/libcsupport/src/gcovfork.c b/cpukit/libcsupport/src/gcovfork.c new file mode 100644 index 0000000000..763412d735 --- /dev/null +++ b/cpukit/libcsupport/src/gcovfork.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup libcsupport + * + * @brief This source file contains functions required by GCC if code and + * branch coverage instrumentation (gcov) is enabled. + */ + +/* + * Copyright (C) 2021 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 <sys/types.h> + +#include <errno.h> + +#include <rtems/seterr.h> + +int __gcov_execl( const char *, char *, ... ); + +int __gcov_execl( const char *path, char *arg, ... ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +int __gcov_execlp( const char *, char *, ... ); + +int __gcov_execlp( const char *path, char *arg, ... ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +int __gcov_execle( const char *, char *, ... ); + +int __gcov_execle( const char *path, char *arg, ... ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +int __gcov_execv( const char *, char *const[] ); + +int __gcov_execv( const char *path, char *const argv[] ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +int __gcov_execvp( const char *, char *const[] ); + +int __gcov_execvp( const char *path, char *const argv[] ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +int __gcov_execve( const char *, char *const[], char *const[] ); + +int __gcov_execve( const char *path, char *const argv[], char *const envp[] ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} + +pid_t __gcov_fork( void ); + +pid_t __gcov_fork( void ) +{ + rtems_set_errno_and_return_minus_one( ENOSYS ); +} diff --git a/cpukit/libfs/src/ftpfs/tftpDriver.c b/cpukit/libfs/src/ftpfs/tftpDriver.c index bebe748ca5..59136ef59f 100644 --- a/cpukit/libfs/src/ftpfs/tftpDriver.c +++ b/cpukit/libfs/src/ftpfs/tftpDriver.c @@ -3,16 +3,26 @@ /** * @file * - * Trivial File Transfer Protocol file system (TFTP client) for RFC 1350. + * @ingroup RTEMSImplTFTPFS * - * Transfer file to/from remote host + * @brief This source file contains the implementation of + * a Trivial File Transfer Protocol (TFTP) client library. + * + * The code in this file provides the ability to read files from and + * to write files to remote servers using the Trivial File Transfer + * Protocol (TFTP). It is used by the @ref tftpfs.c "TFTP file system" and + * tested through its test suite. The + * following RFCs are implemented: + * + * + RFC 1350 "The TFTP Protocol (Revision 2)" + * + RFC 2347 "TFTP Option Extension" + * + RFC 2348 "TFTP Blocksize Option" + * + RFC 7440 "TFTP Windowsize Option" */ /* - * Copyright (c) 1998 Eric Norum <eric@norum.ca> - * - * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * 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 @@ -42,29 +52,21 @@ #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 <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 +#include "tftp_driver.h" /* * Range of UDP ports to try @@ -75,9 +77,21 @@ int rtems_tftp_driver_debug = 1; * Default limits */ #define PACKET_FIRST_TIMEOUT_MILLISECONDS 400L -#define PACKET_TIMEOUT_MILLISECONDS 6000L -#define OPEN_RETRY_LIMIT 10 -#define IO_RETRY_LIMIT 10 +#define TFTP_WINDOW_SIZE_MIN 1 +#define TFTP_BLOCK_SIZE_MIN 8 +#define TFTP_BLOCK_SIZE_MAX 65464 + +#define TFTP_BLOCK_SIZE_OPTION "blksize" +#define TFTP_WINDOW_SIZE_OPTION "windowsize" +#define TFTP_DECIMAL_BASE 10 + +#define TFTP_DEFAULT_SERVER_PORT 69 + +/* + * These values are suggested by RFC 7440. + */ +#define TFTP_RFC7440_DATA_RETRANSMISSIONS 6 +#define TFTP_RFC7440_TIMEOUT_MILLISECONDS 1000 /* * TFTP opcodes @@ -87,11 +101,46 @@ int rtems_tftp_driver_debug = 1; #define TFTP_OPCODE_DATA 3 #define TFTP_OPCODE_ACK 4 #define TFTP_OPCODE_ERROR 5 +#define TFTP_OPCODE_OACK 6 + +/* + * TFTP error codes + */ +#define TFTP_ERROR_CODE_NOT_DEFINED 0 +#define TFTP_ERROR_CODE_NOT_FOUND 1 +#define TFTP_ERROR_CODE_NO_ACCESS 2 +#define TFTP_ERROR_CODE_DISK_FULL 3 +#define TFTP_ERROR_CODE_ILLEGAL 4 +#define TFTP_ERROR_CODE_UNKNOWN_ID 5 +#define TFTP_ERROR_CODE_FILE_EXISTS 6 +#define TFTP_ERROR_CODE_NO_USER 7 +#define TFTP_ERROR_CODE_OPTION_NEGO 8 + +/* + * Special return values for process_*_packet() functions + * (other return values are POSIX errors) + */ +#define GOT_EXPECTED_PACKET 0 +#define GOT_DUPLICATE_OF_CURRENT_PACKET -1 +#define GOT_OLD_PACKET -2 +#define GOT_FIRST_OUT_OF_ORDER_PACKET -3 + +/* + * Special argument value for getPacket() + */ +#define GET_PACKET_DONT_WAIT -1 /* - * Largest data transfer + * Special return value for prepare_*_packet_for_sending() functions + * (other return values are the length of the packet to be send) */ -#define TFTP_BUFSIZE 512 +#define DO_NOT_SEND_PACKET 0 + +#define PKT_SIZE_FROM_BLK_SIZE(_blksize) ((_blksize) + 2 * sizeof (uint16_t)) +#define BLK_SIZE_FROM_PKT_SIZE(_pktsize) ((_pktsize) - 2 * sizeof (uint16_t)) +#define MUST_SEND_OPTIONS(_options) (\ + (_options).block_size != TFTP_RFC1350_BLOCK_SIZE || \ + (_options).window_size != TFTP_RFC1350_WINDOW_SIZE ) /* * Packets transferred between machines @@ -102,7 +151,7 @@ union tftpPacket { */ struct tftpRWRQ { uint16_t opcode; - char filename_mode[TFTP_BUFSIZE]; + char filename_mode[]; } tftpRWRQ; /* @@ -111,7 +160,7 @@ union tftpPacket { struct tftpDATA { uint16_t opcode; uint16_t blocknum; - uint8_t data[TFTP_BUFSIZE]; + uint8_t data[]; } tftpDATA; /* @@ -123,12 +172,20 @@ union tftpPacket { } tftpACK; /* + * OACK packet + */ + struct tftpOACK { + uint16_t opcode; + char options[]; + } tftpOACK; + + /* * ERROR packet */ struct tftpERROR { uint16_t opcode; uint16_t errorCode; - char errorMessage[TFTP_BUFSIZE]; + char errorMessage[]; } tftpERROR; }; @@ -137,16 +194,57 @@ union tftpPacket { */ struct tftpStream { /* - * Buffer for storing most recently-received packet + * Buffer for storing packets for sending and receiving. Can point + * to the same address when only one buffer is needed for reading. */ - union tftpPacket pkbuf; + union tftpPacket *receive_buf; + union tftpPacket *send_buf; /* - * Last block number transferred + * Current block number - i.e. the block currently send or received */ uint16_t blocknum; /* + * Size of the data area in a DATA single packet. + */ + size_t block_size; + + /* + * The maximum size of a packet. It depends linearly on the block_size. + * The receive_buf and (the packets in) the send_buf are of this size. + */ + size_t packet_size; + + /* + * The number of packets which can be stored in the send buffer. + * During option negotiation and for reading a file from the server + * only a buffer for a single packet is needed. In those cases, this + * value is always one. When a file is written to the server, + * the value equals the window size: + * send_buf_size_in_pkts == server_options.window_size + * + * Packet N is stored in + * send_buf + packet_size * (N % send_buf_size_in_pkts) + */ + uint16_t send_buf_size_in_pkts; + + /* + * When writing files with windowsize > 1, the number of the completely + * filled packet with the highest block number in the send buffer. + * When the user calls write(), the data will be written into + * the block after this one. + */ + uint16_t blocknum_last_filled; + + /* + * When writing files with windowsize > 1, the number of the packet + * which is the last one in the whole file (i.e. the user + * called close()). This block is never full (but maybe empty). + */ + uint16_t blocknum_eof_block; + + /* * Data transfer socket */ int socket; @@ -155,6 +253,9 @@ struct tftpStream { /* * Indices into buffer + * In case of sending a file with windowsize > 1, these values apply + * only to the packet with the highest number in the send buffer + * (blocknum_last_filled + 1). */ int nleft; int nused; @@ -163,143 +264,161 @@ struct tftpStream { * Flags */ int firstReply; - int eof; - int writing; + bool at_eof; + bool is_for_reading; + + /* + * Function pointers and members for use by communicate_with_server(). + */ + ssize_t (*prepare_packet_for_sending) ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *create_packet_data + ); + int (*process_data_packet) (struct tftpStream *tp, ssize_t len); + int (*process_ack_packet) (struct tftpStream *tp, ssize_t len); + int (*process_oack_packet) (struct tftpStream *tp, ssize_t len); + int (*process_error_packet) (struct tftpStream *tp, ssize_t len); + int retransmission_error_code; + bool ignore_out_of_order_packets; + int32_t blocknum_of_first_packet_of_window; + int error; + + /* + * Configuration and TFTP options + * + * * config.options are options desired by the user (i.e. the values + * send to the server). + * * server_options are the options agreed by the server + * (the option values actually used for the transfer of data). + */ + tftp_net_config config; + tftp_options server_options; }; /* - * Flags for filesystem info. + * Forward declaration cannot be avoided. */ -#define TFTPFS_VERBOSE (1 << 0) +static ssize_t prepare_data_packet_for_sending ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *path_name +); +static ssize_t prepare_ack_packet_for_sending ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *path_name +); /* - * TFTP File system info. + * Calculate the address of packet N in the send buffer */ -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))) +static union tftpPacket *get_send_buffer_packet ( + struct tftpStream *tp, + uint16_t packet_num +) +{ + return (union tftpPacket *) ( ( (char *) tp->send_buf) + tp->packet_size * + (packet_num % tp->send_buf_size_in_pkts) ); +} /* - * Number of streams open at the same time + * Create read or write request */ - -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( +static size_t create_request ( + union tftpPacket *send_buf, + size_t data_size, + bool is_for_reading, const char *path, - size_t pathlen + const tftp_options *options ) { - return path [pathlen - 1] == '/'; -} + size_t res_size; + char *cur = send_buf->tftpRWRQ.filename_mode; -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'; + send_buf->tftpRWRQ.opcode = htons ( + is_for_reading ? TFTP_OPCODE_RRQ : TFTP_OPCODE_WRQ + ); + + res_size = snprintf (cur, data_size, "%s%c%s", path, 0, "octet"); + if (res_size >= data_size) { + return -1; } - 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'; + res_size++; + data_size -= res_size; + cur += res_size; + + if (options->block_size != TFTP_RFC1350_BLOCK_SIZE) { + res_size = snprintf ( + cur, + data_size, + "%s%c%"PRIu16, + TFTP_BLOCK_SIZE_OPTION, + 0, + options->block_size + ); + if (res_size >= data_size) { + return -1; + } + res_size++; + data_size -= res_size; + cur += res_size; } - 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); + if (options->window_size != TFTP_RFC1350_WINDOW_SIZE) { + res_size = snprintf ( + cur, + data_size, + "%s%c%"PRIu16, + TFTP_WINDOW_SIZE_OPTION, + 0, + options->window_size + ); + if (res_size >= data_size) { + return -1; } + res_size++; + data_size -= res_size; + cur += res_size; } - - return 0; -error: - - free (fs); - free (root_path); - - rtems_set_errno_and_return_minus_one (ENOMEM); + return cur - (char *)send_buf; } -/* - * Release a stream and clear the pointer to it - */ -static void -releaseStream (tftpfs_info_t *fs, int s) +static bool parse_decimal_number ( + char **pos, + size_t *remain, + long min, + long max, + uint16_t *variable +) { - 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); -} + long value; + const char *start = *pos; + if (*remain < 2) { + return false; + } + value = strtoul(start, pos, TFTP_DECIMAL_BASE); + if (value < min || value > max || **pos != 0) { + return false; + } + *variable = (uint16_t) value; + (*pos)++; + *remain -= *pos - start; -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); + return true; } /* * Map error message */ -static int -tftpErrno (struct tftpStream *tp) +static int tftpErrno (uint16_t error_code) { unsigned int tftpError; static const int errorMap[] = { @@ -311,9 +430,10 @@ tftpErrno (struct tftpStream *tp) ENXIO, EEXIST, ESRCH, + ENOTSUP, /* Error: Option negotiation failed (RFC 2347) */ }; - tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); + tftpError = ntohs (error_code); if (tftpError < (sizeof errorMap / sizeof errorMap[0])) return errorMap[tftpError]; else @@ -321,25 +441,87 @@ tftpErrno (struct tftpStream *tp) } /* - * Send a message to make the other end shut up + * Parse options from an OACK packet + */ +static bool parse_options ( + union tftpPacket *receive_buf, + size_t packet_size, + tftp_options *options_in, + tftp_options *options_out +) +{ + char *pos = receive_buf->tftpOACK.options; + size_t remain = packet_size - sizeof (receive_buf->tftpOACK.opcode); + + /* + * Make sure there is a 0 byte in the end before comparing strings + */ + if (remain > 0 && pos[remain - 1] != 0) { + return false; + } + + while (remain > 0) { + if (strcasecmp(pos, TFTP_BLOCK_SIZE_OPTION) == 0 && + options_in->block_size != TFTP_RFC1350_BLOCK_SIZE) { + remain -= sizeof (TFTP_BLOCK_SIZE_OPTION); + pos += sizeof (TFTP_BLOCK_SIZE_OPTION); + if (!parse_decimal_number ( + &pos, + &remain, + TFTP_BLOCK_SIZE_MIN, + options_in->block_size, + &options_out->block_size)) { + return false; + }; + + } else if (strcasecmp(pos, TFTP_WINDOW_SIZE_OPTION) == 0 && + options_in->window_size != TFTP_RFC1350_WINDOW_SIZE) { + remain -= sizeof (TFTP_WINDOW_SIZE_OPTION); + pos += sizeof (TFTP_WINDOW_SIZE_OPTION); + if (!parse_decimal_number ( + &pos, + &remain, + TFTP_WINDOW_SIZE_MIN, + options_in->window_size, + &options_out->window_size)) { + return false; + }; + + } else { + return false; /* Unknown option */ + } + } + + return true; +} + +/* + * Send an error message */ -static void -sendStifle (struct tftpStream *tp, struct sockaddr_in *to) +static void send_error ( + struct tftpStream *tp, + struct sockaddr_in *to, + uint8_t error_code, + const char *error_message +) { int len; struct { uint16_t opcode; uint16_t errorCode; - char errorMessage[12]; + char errorMessage[80]; } 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"); + msg.errorCode = htons (error_code); + len = snprintf (msg.errorMessage, sizeof (msg.errorMessage), error_message); + if (len >= sizeof (msg.errorMessage)) { + len = sizeof (msg.errorMessage) - 1; + } + len += sizeof (msg.opcode) + sizeof (msg.errorCode) + 1; /* * Send it @@ -348,32 +530,47 @@ sendStifle (struct tftpStream *tp, struct sockaddr_in *to) } /* - * Wait for a data packet + * Send a message to make the other end shut up + */ +static void sendStifle (struct tftpStream *tp, struct sockaddr_in *to) +{ + send_error (tp, to, TFTP_ERROR_CODE_UNKNOWN_ID, "GO AWAY"); +} + +/* + * Wait for a packet */ -static int +static ssize_t getPacket (struct tftpStream *tp, int retryCount) { - int len; + ssize_t 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; + int flags = 0; + + if (retryCount == GET_PACKET_DONT_WAIT) { + flags = MSG_DONTWAIT; + } else if (retryCount == 0) { + tv.tv_sec = tp->config.first_timeout / 1000L; + tv.tv_usec = (tp->config.first_timeout % 1000L) * 1000L; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + } else { + tv.tv_sec = tp->config.timeout / 1000L; + tv.tv_usec = (tp->config.timeout % 1000L) * 1000L; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); } - 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); + len = recvfrom (tp->socket, + tp->receive_buf, + tp->packet_size, + flags, + &from.s, + &fromlen + ); if (len < 0) break; if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { @@ -391,397 +588,706 @@ getPacket (struct tftpStream *tp, int retryCount) */ 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); - } + if (retryCount != GET_PACKET_DONT_WAIT) { + tv.tv_sec = 0; + tv.tv_usec = 0; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); } -#endif return len; } +static int process_unexpected_packet (struct tftpStream *tp, ssize_t len) +{ + (void) len; + send_error ( + tp, + &tp->farAddress, + TFTP_ERROR_CODE_ILLEGAL, + "Got packet with unexpected opcode from server" + ); + return EPROTO; +} + +static int process_malformed_packet (struct tftpStream *tp, ssize_t len) +{ + (void) len; + send_error ( + tp, + &tp->farAddress, + TFTP_ERROR_CODE_ILLEGAL, + "Got malformed packet from server" + ); + return EPROTO; +} + +static int process_error_packet (struct tftpStream *tp, ssize_t len) +{ + (void) len; + return tftpErrno (tp->receive_buf->tftpERROR.errorCode); +} + /* - * Send an acknowledgement + * When an RRQ or a WRQ with options is sent and the server responds with + * an error, this function will trigger a re-sent of an RRQ or WRQ + * without options (falling back to old RFC1350). + * + * If someone wants to change the implementation to force using options + * (i.e. prevent fallback to RFC1350), at least these points must be + * considered: + * + * * Use `process_error_packet()` instead of + * `process_error_packet_option_negotiation()` + * * React to DATA and ACK packets, which are an immediate response to + * an RRQ or a WRQ with options, with an error packet. + * * Check the option values in the OACK whether they are in the + * desired range. */ -static int -sendAck (struct tftpStream *tp) +static int process_error_packet_option_negotiation ( + struct tftpStream *tp, ssize_t len +) { -#ifdef RTEMS_TFTP_DRIVER_DEBUG - if (rtems_tftp_driver_debug) - printf ("TFTP: ACK %d\n", tp->blocknum); -#endif - + (void) len; /* - * Create the acknowledgement + * Setting tp->config.options causes an RRQ or a WRQ to be created without + * options. + * Setting tp->server_option is defensive programming as these fields + * should already have these values. */ - tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); - tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); + tp->config.options.block_size = TFTP_RFC1350_BLOCK_SIZE; + tp->config.options.window_size = TFTP_RFC1350_WINDOW_SIZE; + tp->server_options.block_size = TFTP_RFC1350_BLOCK_SIZE; + tp->server_options.window_size = TFTP_RFC1350_WINDOW_SIZE; + tp->process_error_packet = process_error_packet; /* - * Send it + * GOT_FIRST_OUT_OF_ORDER_PACKET will trigger a re-send of the RRQ or WRQ. */ - if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, - (struct sockaddr *)&tp->farAddress, - sizeof tp->farAddress) < 0) - return errno; - return 0; + return GOT_FIRST_OUT_OF_ORDER_PACKET; } -/* - * Convert a path to canonical form - */ -static void -fixPath (char *path) +static int process_data_packet (struct tftpStream *tp, ssize_t len) { - char *inp, *outp, *base; + ssize_t plen; + int32_t pkt_blocknum; + union tftpPacket *send_buf; - 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++; + if (len < sizeof (tp->receive_buf->tftpACK)) { + return process_malformed_packet (tp, len); + } + pkt_blocknum = (int32_t) ntohs (tp->receive_buf->tftpACK.blocknum); + if (pkt_blocknum == 0) { + return process_malformed_packet (tp, len); + } + + /* + * In case of reading a file from the server: + * If the latest ACK packet(s) did not reach the server, the server + * starts the window from the last ACK it received. This if-clause + * ensures, the client sends an ACK after having seen `windowsize` + * packets. + */ + if (pkt_blocknum < tp->blocknum_of_first_packet_of_window && + pkt_blocknum >= (int32_t) tp->blocknum + 1 - + (int32_t) tp->server_options.window_size) { + tp->blocknum_of_first_packet_of_window = pkt_blocknum; + } + if (!tp->ignore_out_of_order_packets && + pkt_blocknum > (int32_t) tp->blocknum + 1) { + tp->ignore_out_of_order_packets = true; + return GOT_FIRST_OUT_OF_ORDER_PACKET; + } else if (pkt_blocknum == (int32_t) tp->blocknum) { + /* + * In case of reading a file from the server: + * If the last ACK packet send by the client did not reach the + * server, the server re-sends all packets of the window. In this + * case, the client must re-send the ACK packet after having + * received the last packet of the window (even through it has + * already received that packet before). + * GOT_OLD_PACKET would wrongly suppress this. + */ + return GOT_DUPLICATE_OF_CURRENT_PACKET; + } else if (pkt_blocknum != (int32_t) tp->blocknum + 1) { + return GOT_OLD_PACKET; } - *outp = '\0'; - return; + tp->ignore_out_of_order_packets = false; + + tp->blocknum++; + tp->nused = 0; /* Only for 2nd, 3rd, 4th DATA packet received */ + tp->nleft = BLK_SIZE_FROM_PKT_SIZE (len); + tp->at_eof = (tp->nleft < tp->server_options.block_size); + /* + * After the last DATA packet, the client must send a final ACK + */ + if (tp->at_eof) { + plen = prepare_ack_packet_for_sending (tp, true, &send_buf, NULL, NULL); + + /* + * Send it. Errors during send will not matter for this last ACK. + */ + sendto ( + tp->socket, + send_buf, + plen, + 0, + (struct sockaddr *) &tp->farAddress, + sizeof (tp->farAddress) + ); + } + tp->prepare_packet_for_sending = prepare_ack_packet_for_sending; + return GOT_EXPECTED_PACKET; } -static void rtems_tftp_eval_path(rtems_filesystem_eval_path_context_t *self) +static int process_ack_packet (struct tftpStream *tp, ssize_t len) { - 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); + uint16_t blocknum_ack; + if (len < sizeof (tp->receive_buf->tftpACK)) { + return process_malformed_packet (tp, len); + } + blocknum_ack = ntohs (tp->receive_buf->tftpACK.blocknum); + if ((int32_t) blocknum_ack == tp->blocknum_of_first_packet_of_window - 1 && + blocknum_ack != 0 + ) { + tp->blocknum = tp->blocknum_of_first_packet_of_window; + return GOT_DUPLICATE_OF_CURRENT_PACKET; } + if ((int32_t) blocknum_ack < tp->blocknum_of_first_packet_of_window || + blocknum_ack > tp->blocknum_last_filled) { + return GOT_OLD_PACKET; + } + tp->blocknum = blocknum_ack + 1; + tp->blocknum_of_first_packet_of_window = (int32_t) tp->blocknum; + tp->prepare_packet_for_sending = prepare_data_packet_for_sending; + return GOT_EXPECTED_PACKET; } -/* - * 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 +static ssize_t prepare_data_packet_for_sending ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *not_used ) { - 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; + (void) not_used; + ssize_t len; + *send_buf = get_send_buffer_packet (tp, tp->blocknum); + + len = PKT_SIZE_FROM_BLK_SIZE ( + (tp->blocknum == tp->blocknum_eof_block) ? tp->nused : tp->block_size + ); + (*send_buf)->tftpDATA.opcode = htons (TFTP_OPCODE_DATA); + (*send_buf)->tftpDATA.blocknum = htons (tp->blocknum); /* - * Get the file system info. - */ - fs = tftpfs_info_iop (iop); - - /* - * Extract the host name component + * If the client sends the last packet of a window, + * it must wait for an ACK and - in case no ACK is received - begin + * a retransmission with the first packet of the window. + * Note that the last DATA block for the whole transfer is also + * a "last packet of a window". */ - 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 + if ((int32_t) tp->blocknum + 1 >= + tp->blocknum_of_first_packet_of_window + tp->send_buf_size_in_pkts || + tp->blocknum == tp->blocknum_eof_block) { + tp->blocknum = (uint16_t) tp->blocknum_of_first_packet_of_window; } else { - *cp1 = '\0'; - ++cp1; + tp->blocknum++; + *wait_for_packet_reception = false; } + tp->process_data_packet = process_unexpected_packet; + tp->process_ack_packet = process_ack_packet; + tp->process_oack_packet = process_unexpected_packet; + tp->process_error_packet = process_error_packet; + /* - * Convert hostname to Internet address + * Our last packet won't necessarily be acknowledged! */ -#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)); + if (tp->blocknum == tp->blocknum_eof_block) { + tp->retransmission_error_code = 0; } - - /* - * Extract file pathname component - */ -#ifdef RTEMS_NETWORKING - if (strcmp (cp1, "BOOTP_FILE") == 0) { - cp1 = rtems_bsdnet_bootp_boot_file_name; + + return len; +} + +static ssize_t prepare_ack_packet_for_sending ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *path_name +) +{ + (void) wait_for_packet_reception; + if (!force_retransmission && + tp->blocknum_of_first_packet_of_window - 1 + + (int32_t) tp->server_options.window_size > (int32_t) tp->blocknum) { + return DO_NOT_SEND_PACKET; } -#endif - if (*cp1 == '\0') - return ENOENT; - remoteFilename = cp1; - if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) - return ENOENT; + tp->blocknum_of_first_packet_of_window = (int32_t) tp->blocknum + 1; /* - * Find a free stream + * Create the acknowledgement */ - rtems_mutex_lock (&fs->tftp_mutex); - for (s = 0 ; s < fs->nStreams ; s++) { - if (fs->tftpStreams[s] == NULL) - break; + *send_buf = tp->send_buf; + (*send_buf)->tftpACK.opcode = htons (TFTP_OPCODE_ACK); + (*send_buf)->tftpACK.blocknum = htons (tp->blocknum); + + tp->process_data_packet = process_data_packet; + tp->process_ack_packet = process_unexpected_packet; + tp->process_oack_packet = process_unexpected_packet; + tp->process_error_packet = process_error_packet; + + return sizeof (tp->send_buf->tftpACK); +} + +static int process_oack_packet (struct tftpStream *tp, ssize_t len) +{ + if (!parse_options(tp->receive_buf, + len, + &tp->config.options, + &tp->server_options)) { + send_error ( + tp, + &tp->farAddress, + TFTP_ERROR_CODE_OPTION_NEGO, + "Bad options, option values or malformed OACK packet" + ); + return EPROTO; } - if (s == fs->nStreams) { + if (tp->is_for_reading) { /* - * Reallocate stream pointers - * Guard against the case where realloc() returns NULL. + * Since no DATA packet has been received yet, tell + * tftp_read() there is no data left. */ - struct tftpStream **np; + tp->nleft = 0; + tp->prepare_packet_for_sending = prepare_ack_packet_for_sending; + } else { + tp->blocknum_of_first_packet_of_window = 1; + tp->blocknum = (uint16_t) tp->blocknum_of_first_packet_of_window; + tp->prepare_packet_for_sending = prepare_data_packet_for_sending; + } + return GOT_EXPECTED_PACKET; +} + +static ssize_t prepare_request_packet_for_sending ( + struct tftpStream *tp, + bool force_retransmission, + union tftpPacket **send_buf, + bool *wait_for_packet_reception, + const void *path_name +) +{ + (void) wait_for_packet_reception; + ssize_t len; + *send_buf = tp->send_buf; + len = create_request ( + *send_buf, + tp->block_size, + tp->is_for_reading, + path_name, + &tp->config.options + ); + + if (len < 0) { + tp->error = ENAMETOOLONG; + } else { + tp->process_data_packet = tp->is_for_reading ? + process_data_packet : process_unexpected_packet; + tp->process_ack_packet = tp->is_for_reading ? + process_unexpected_packet : process_ack_packet; + tp->process_oack_packet = MUST_SEND_OPTIONS(tp->config.options) ? + process_oack_packet : process_unexpected_packet; + tp->process_error_packet = MUST_SEND_OPTIONS(tp->config.options) ? + process_error_packet_option_negotiation : process_error_packet; + } - np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams); - if (np == NULL) { - rtems_mutex_unlock (&fs->tftp_mutex); - return ENOMEM; + /* + * getPacket() will change these values when the first packet is + * received. Yet, this first packet may be an unexpected one + * (e.g. an ERROR or having a wrong block number). + * If a second, third, forth, ... RRQ/WRQ is to be sent, it should + * be directed to the server port again and not to the port the + * first unexpected packet came from. + */ + tp->farAddress.sin_port = htons (tp->config.server_port); + tp->firstReply = 1; + + return len; +} + +/* + * Conduct one communication step with the server. For windowsize == 1, + * one step is: + * a) Send a packet to the server + * b) Receive a reply packet from the server + * c) Handle errors (if any) + * d) If no packet has been received from the server and the maximum + * retransmission count has not yet been reached, start over with a) + * The flow of packets (i.e. which packet to send and which packet(s) to + * expect from the server) is controlled by function pointers found in + * struct tftpStream. + * + * Besides of handling errors and retransmissions, the essential data exchange + * follows these patterns: + * + * Connection establishment and option negotiation: + * * Send RRQ/WRQ (read or write request packet) + * * Receive OACK (read and write) or ACK (write only) or DATA (read only) + * + * Read step with windowsize == 1: + * * Send ACK packet + * * Receive DATA packet + * Sending the very last ACK packet for a "read" session is treated as a + * special case. + * + * Write step with windowsize == 1: + * * Send DATA packet + * * Receive ACK packet + * + * A windowsize lager than one makes thinks more complicated. + * In this case, a step normally only receives (read) or only sends (write) + * a packet. The sending or receiving of the ACK packets is skipped normally + * and happens only at the last step of the window (in which case this last + * step is similar to the windowsize == 1 case): + * + * Normal read step with windowsize > 1: + * * Receive DATA packet + * Last read step of a window with windowsize > 1: + * * Send ACK packet + * * Receive DATA packet + * + * Normal write step with windowsize > 1: + * * Send DATA packet + * * Check for an ACK packet but do not wait for it + * Last write step of a window with windowsize > 1: + * * Send DATA packet + * * Receive ACK packet + * + * The "normal write step for windowsize > 1" checks whether an ACK packet + * has been received after each sending of a DATA packet. Package lost and + * exchanges in the network can give rise to situations in which the server + * sends more than a single ACK packet during a window. If these packets + * are not reacted on immediately, the network would be flooded with + * surplus packets. (An example where two ACK packets appear in a window + * appears in test case "read_file_windowsize_trouble" where the client/server + * roles are exchanged.) + */ +static int communicate_with_server ( + struct tftpStream *tp, + const void *create_packet_data +) +{ + ssize_t len; + uint16_t opcode; + union tftpPacket *send_buf; + bool received_duplicated_or_old_package = false; + bool force_retransmission = false; + bool wait_for_packet_reception; + int retryCount = 0; + while (tp->error == 0) { + + if (!received_duplicated_or_old_package) { + wait_for_packet_reception = true; + len = tp->prepare_packet_for_sending ( + tp, + force_retransmission, + &send_buf, + &wait_for_packet_reception, + create_packet_data + ); + if (len < 0) { + if (tp->error == 0) { + tp->error = EIO; + } + break; + } + + if (len != DO_NOT_SEND_PACKET) { + /* + * Send the packet + */ + if (sendto (tp->socket, send_buf, len, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) { + tp->error = EIO; + break; + } + } + } + received_duplicated_or_old_package = false; + force_retransmission = false; + + /* + * Get reply + */ + len = getPacket ( + tp, + wait_for_packet_reception ? retryCount : GET_PACKET_DONT_WAIT + ); + if (len >= (int) sizeof (tp->receive_buf->tftpDATA.opcode)) { + opcode = ntohs (tp->receive_buf->tftpDATA.opcode); + switch (opcode) { + case TFTP_OPCODE_DATA: + tp->error = tp->process_data_packet (tp, len); + break; + case TFTP_OPCODE_ACK: + tp->error = tp->process_ack_packet (tp, len); + break; + case TFTP_OPCODE_OACK: + tp->error = tp->process_oack_packet (tp, len); + break; + case TFTP_OPCODE_ERROR: + tp->error = tp->process_error_packet (tp, len); + break; + default: + tp->error = process_unexpected_packet (tp, len); + break; + } + if (tp->error == GOT_EXPECTED_PACKET) { + break; + } else if (tp->error == GOT_DUPLICATE_OF_CURRENT_PACKET) { + tp->error = 0; + } else if (tp->error == GOT_OLD_PACKET) { + received_duplicated_or_old_package = true; + tp->error = 0; + } else if (tp->error <= GOT_FIRST_OUT_OF_ORDER_PACKET) { + force_retransmission = true; + tp->error = 0; + } /* else ... tp->error > 0 means "exit this function with error" */ + } else if (len >= 0) { + tp->error = process_malformed_packet (tp, len); + } else if (len < 0 && !wait_for_packet_reception) { + tp->error = 0; + break; + } else { + /* + * Timeout or other problems to receive packets + * Attempt a retransmission + */ + if (++retryCount >= (int) tp->config.retransmissions) { + tp->error = tp->retransmission_error_code; + break; + } + force_retransmission = true; } - 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; + + return tp->error; +} + +/* + * Allocate and initialize an struct tftpStream object. + * + * This function does not check whether the config values are in valid ranges. + */ +static struct tftpStream *create_stream( + const tftp_net_config *config, + const struct in_addr *farAddress, + bool is_for_reading +) +{ + struct tftpStream *tp = NULL; + tp = malloc (sizeof (struct tftpStream)); + if (tp == NULL) { + return NULL; + } + + /* + * Initialize fields accessed by _Tftp_Destroy(). + */ + tp->receive_buf = NULL; + tp->send_buf = NULL; + tp->socket = 0; + + /* + * Allocate send and receive buffer for exchange of RRQ/WRQ and ACK/OACK. + */ + tp->block_size = TFTP_RFC1350_BLOCK_SIZE; + tp->packet_size = PKT_SIZE_FROM_BLK_SIZE (tp->block_size); + tp->receive_buf = malloc (tp->packet_size); + if (tp->receive_buf == NULL) { + _Tftp_Destroy (tp); + return NULL; + } + tp->send_buf = tp->receive_buf; + tp->send_buf_size_in_pkts = 1; /* * Create the socket */ if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { - releaseStream (fs, s); - return ENOMEM; + _Tftp_Destroy (tp); + return NULL; } /* - * Bind the socket to a local address + * Setup configuration and options */ - 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; - } + if ( config == NULL ) { + tftp_initialize_net_config (&tp->config); + } else { + tp->config = *config; } /* + * If the server does not confirm our option values later on, + * use numbers from the original RFC 1350 for the actual transfer. + */ + tp->server_options.block_size = TFTP_RFC1350_BLOCK_SIZE; + tp->server_options.window_size = TFTP_RFC1350_WINDOW_SIZE; + + /* * 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); + tp->farAddress.sin_family = AF_INET; + tp->farAddress.sin_addr = *farAddress; + tp->farAddress.sin_port = htons (tp->config.server_port); + + tp->nleft = 0; + tp->nused = 0; + tp->blocknum = 0; + tp->blocknum_last_filled = 0; + tp->blocknum_eof_block = UINT16_MAX; + tp->firstReply = 1; + tp->at_eof = false; + tp->is_for_reading = is_for_reading; + + tp->prepare_packet_for_sending = prepare_request_packet_for_sending; + tp->process_data_packet = process_unexpected_packet; + tp->process_ack_packet = process_unexpected_packet; + tp->process_oack_packet = process_unexpected_packet; + tp->process_error_packet = process_error_packet; + tp->retransmission_error_code = EIO; + tp->ignore_out_of_order_packets = false; + tp->blocknum_of_first_packet_of_window = INT32_MIN; + tp->error = 0; + + return tp; +} +/* + * Change the size of the receive and send buffer to match the options + * values acknowledged by the server. + */ +static struct tftpStream *reallocate_stream_buffer(struct tftpStream *tp) +{ + tp->block_size = tp->server_options.block_size; + tp->packet_size = PKT_SIZE_FROM_BLK_SIZE (tp->block_size); /* - * Start the transfer + * Defensive programming */ - 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; + if (tp->receive_buf == tp->send_buf) { + tp->send_buf = NULL; + } else { + free (tp->send_buf); + } - /* - * 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; - } + tp->receive_buf = realloc (tp->receive_buf, tp->packet_size); + if (tp->is_for_reading) { + tp->send_buf = tp->receive_buf; + } else { + tp->send_buf_size_in_pkts = tp->server_options.window_size; + tp->send_buf = malloc ( + tp->send_buf_size_in_pkts * tp->packet_size + ); + } - /* - * 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; - } - } + if (tp->receive_buf == NULL || tp->send_buf == NULL) { + sendStifle (tp, &tp->farAddress); + _Tftp_Destroy (tp); + return NULL; + } + return tp; +} - /* - * Keep trying - */ - if (++retryCount >= OPEN_RETRY_LIMIT) { - releaseStream (fs, s); - return EIO; +/* + * Convert hostname to an Internet address + */ +static struct in_addr *get_ip_address( + const char *hostname, + struct in_addr *farAddress +) +{ + struct hostent *he = gethostbyname(hostname); + if (he == NULL) { + return NULL; } + memcpy (farAddress, he->h_addr, sizeof (*farAddress)); + return farAddress; +} + +void tftp_initialize_net_config (tftp_net_config *config) +{ + static const tftp_net_config default_config = { + .retransmissions = TFTP_RFC7440_DATA_RETRANSMISSIONS, + .server_port = TFTP_DEFAULT_SERVER_PORT, + .timeout = TFTP_RFC7440_TIMEOUT_MILLISECONDS, + .first_timeout = PACKET_FIRST_TIMEOUT_MILLISECONDS, + .options = { + .block_size = TFTP_DEFAULT_BLOCK_SIZE, + .window_size = TFTP_DEFAULT_WINDOW_SIZE } - return 0; + }; + + if (config != NULL) { + memcpy (config, &default_config, sizeof (default_config)); + } } -static int rtems_tftp_open( - rtems_libio_t *iop, - const char *new_name, - int oflag, - mode_t mode +int tftp_open( + const char *hostname, + const char *path, + bool is_for_reading, + const tftp_net_config *config, + void **tftp_handle ) { - tftpfs_info_t *fs; - char *full_path_name; - int err; - - full_path_name = iop->pathinfo.node_access; + struct tftpStream *tp; + struct in_addr farAddress; + int err; - if (rtems_tftp_is_directory (full_path_name, strlen (full_path_name))) { - rtems_set_errno_and_return_minus_one (ENOTSUP); + /* + * Check parameters + */ + if (tftp_handle == NULL) { + return EINVAL; + } + *tftp_handle = NULL; + if (hostname == NULL || path == NULL) { + return EINVAL; + } + if (config != NULL && ( + config->options.window_size < TFTP_WINDOW_SIZE_MIN || + config->options.block_size < TFTP_BLOCK_SIZE_MIN || + config->options.block_size > TFTP_BLOCK_SIZE_MAX ) ) { + return EINVAL; } /* - * Get the file system info. + * Create tftpStream structure */ - fs = tftpfs_info_iop (iop); + if (get_ip_address( hostname, &farAddress ) == NULL) { + return ENOENT; + } + tp = create_stream( config, &farAddress, is_for_reading ); + if (tp == NULL) { + return ENOMEM; + } - if (fs->flags & TFTPFS_VERBOSE) - printf ("TFTPFS: %s\n", full_path_name); + /* + * Send RRQ or WRQ and wait for reply + */ + tp->prepare_packet_for_sending = prepare_request_packet_for_sending; + err = communicate_with_server (tp, path); + if ( err != 0 ) { + _Tftp_Destroy (tp); + return err; + } - err = rtems_tftp_open_worker (iop, full_path_name, oflag); - if (err != 0) { - rtems_set_errno_and_return_minus_one (err); + *tftp_handle = reallocate_stream_buffer ( tp ); + if( *tftp_handle == NULL ) { + return ENOMEM; } return 0; @@ -790,20 +1296,20 @@ static int rtems_tftp_open( /* * Read from a TFTP stream */ -static ssize_t rtems_tftp_read( - rtems_libio_t *iop, +ssize_t tftp_read( + void *tftp_handle, void *buffer, size_t count ) { char *bp; - struct tftpStream *tp = iop->data1; - int retryCount; + struct tftpStream *tp = tftp_handle; int nwant; + int err; + + if (tp == NULL || !tp->is_for_reading || buffer == NULL) + return -EIO; - if (!tp) - rtems_set_errno_and_return_minus_one( EIO ); - /* * Read till user request is satisfied or EOF is reached */ @@ -816,7 +1322,7 @@ static ssize_t rtems_tftp_read( ncopy = nwant; else ncopy = tp->nleft; - memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy); + memcpy (bp, &tp->receive_buf->tftpDATA.data[tp->nused], ncopy); tp->nused += ncopy; tp->nleft -= ncopy; bp += ncopy; @@ -824,39 +1330,29 @@ static ssize_t rtems_tftp_read( if (nwant == 0) break; } - if (tp->eof) + if (tp->at_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); + tp->retransmission_error_code = -EIO; + err = communicate_with_server(tp, NULL); + if (err == tp->retransmission_error_code) { + return -EIO; + } + /* + * If communicate_with_server() returns an error, either + * * an error message from the server was received or + * * an error message was already sent to the server + * Setting tp->at_eof true, prevents all further calls to + * communicate_with_server() and suppresses the sending of + * an error message to the server by tftp_close(). + */ + if (err != 0) { + tp->at_eof = true; + return -err; } } return count - nwant; @@ -864,101 +1360,110 @@ static ssize_t rtems_tftp_read( /* * Flush a write buffer and wait for acknowledgement + * + * This function returns only if there is at least one packet buffer free + * in the tp->send_buf. This ensures that tftp_write() can store + * further data for sending in this free packet buffer. + * + * When the end of file has been reached (i.e. tftp_close() called this + * function), this function returns only after all packets + * in the write buffer have been send and acknowledged (or if an error + * occurred). */ static int rtems_tftp_flush (struct tftpStream *tp) { - int wlen, rlen; - int retryCount = 0; + int err; - 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); + if (tp->at_eof) { + return 0; + } + + do { + err = communicate_with_server(tp, NULL); /* - * Our last packet won't necessarily be acknowledged! + * If communicate_with_server() returns an error, either + * * an error message from the server was received or + * * an error message was already sent to the server + * Setting tp->at_eof true, prevents all further calls to + * communicate_with_server() and suppresses the sending of + * an error message to the server by tftp_close(). */ - 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); + if (err != 0) { + tp->at_eof = true; + return err; } + } while( + (int32_t) tp->blocknum_last_filled + 1 >= + tp->blocknum_of_first_packet_of_window + tp->send_buf_size_in_pkts || + /* + * tp->blocknum_eof_block == tp->blocknum_last_filled + * holds only true when the user invoked tftp_close(). + */ + (tp->blocknum_eof_block == tp->blocknum_last_filled && + tp->blocknum_of_first_packet_of_window <= + (int32_t) tp->blocknum_eof_block) + ); - /* - * Keep trying? - */ - if (++retryCount == IO_RETRY_LIMIT) - return EIO; - } + return 0; } /* * Close a TFTP stream */ -static int rtems_tftp_close( - rtems_libio_t *iop +int tftp_close( + void *tftp_handle ) { - tftpfs_info_t *fs; - struct tftpStream *tp = iop->data1; + struct tftpStream *tp = tftp_handle; 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) + + if (tp == NULL) { + return 0; + } + + if (!tp->is_for_reading) { + tp->blocknum_last_filled++; + tp->blocknum_eof_block = tp->blocknum_last_filled; e = rtems_tftp_flush (tp); - if (!tp->eof && !tp->firstReply) { + tp->at_eof = true; + } + if (!tp->at_eof && !tp->firstReply) { /* * Tell the other end to stop */ rtems_interval ticksPerSecond; - sendStifle (tp, &tp->farAddress); + send_error ( + tp, + &tp->farAddress, + TFTP_ERROR_CODE_NO_USER, + "User (client) stopped reading or " + "server stopped sending packets (timeout)" + ); 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; + _Tftp_Destroy (tp); + return e; } -static ssize_t rtems_tftp_write( - rtems_libio_t *iop, +ssize_t tftp_write( + void *tftp_handle, const void *buffer, size_t count ) { const char *bp; - struct tftpStream *tp = iop->data1; + struct tftpStream *tp = tftp_handle; int nleft, nfree, ncopy; + int err; + union tftpPacket *send_buf; /* * Bail out if an error has occurred */ - if (!tp->writing) - rtems_set_errno_and_return_minus_one (EIO); + if (tp == NULL || tp->is_for_reading || tp->at_eof || buffer == NULL) { + return -EIO; + } /* * Write till user request is satisfied @@ -970,119 +1475,45 @@ static ssize_t rtems_tftp_write( bp = buffer; nleft = count; while (nleft) { - nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused; + nfree = tp->block_size - tp->nused; if (nleft < nfree) ncopy = nleft; else ncopy = nfree; - memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy); + send_buf = get_send_buffer_packet (tp, tp->blocknum_last_filled + 1); + memcpy (&send_buf->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); + if (tp->nused == tp->block_size) { + tp->blocknum_last_filled++; + err = rtems_tftp_flush (tp); + if (err) { + return -err; } + tp->nused = 0; } } 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 +void _Tftp_Destroy( + void *tftp_handle ) { - 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; + struct tftpStream *tp = tftp_handle; + if (tp == NULL) { + return; } - return rv; -} - -static void rtems_tftp_free_node_info( - const rtems_filesystem_location_info_t *loc -) -{ - free (loc->node_access); -} + if (tp->socket >= 0) { + close (tp->socket); + } -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; + if (tp->receive_buf == tp->send_buf) { + tp->send_buf = NULL; + } + free (tp->receive_buf); + free (tp->send_buf); + free (tp); } - -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 -}; diff --git a/cpukit/libfs/src/ftpfs/tftp_driver.h b/cpukit/libfs/src/ftpfs/tftp_driver.h new file mode 100644 index 0000000000..949bea1820 --- /dev/null +++ b/cpukit/libfs/src/ftpfs/tftp_driver.h @@ -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 */ diff --git a/cpukit/libfs/src/ftpfs/tftpfs.c b/cpukit/libfs/src/ftpfs/tftpfs.c new file mode 100644 index 0000000000..b699694117 --- /dev/null +++ b/cpukit/libfs/src/ftpfs/tftpfs.c @@ -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 +}; diff --git a/cpukit/libfs/src/jffs2/VERSION b/cpukit/libfs/src/jffs2/VERSION index 7d9c9f41b2..b13bed5750 100644 --- a/cpukit/libfs/src/jffs2/VERSION +++ b/cpukit/libfs/src/jffs2/VERSION @@ -1,9 +1,9 @@ -This directory contains a port of the JFFS2 file system from Linux v4.17. +This directory contains a port of the JFFS2 file system from Linux v5.9. To update to a newer Linux version use this command in a Git clone of Linux to generate the relevant patches: - git format-patch v4.17..v9.99 -- + git format-patch v5.9..v9.99 -- include/uapi/linux/jffs2.h \ fs/jffs2/LICENCE \ fs/jffs2/acl.h \ diff --git a/cpukit/libfs/src/jffs2/include/linux/jffs2.h b/cpukit/libfs/src/jffs2/include/linux/jffs2.h index a18b719f49..784ba0b969 100644 --- a/cpukit/libfs/src/jffs2/include/linux/jffs2.h +++ b/cpukit/libfs/src/jffs2/include/linux/jffs2.h @@ -77,11 +77,6 @@ #define JFFS2_ACL_VERSION 0x0001 -// Maybe later... -//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) -//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) - - #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at mount time, don't wait for it to happen later */ diff --git a/cpukit/libfs/src/jffs2/src/acl.h b/cpukit/libfs/src/jffs2/src/acl.h index 2e2b5745c3..12d0271bdd 100644 --- a/cpukit/libfs/src/jffs2/src/acl.h +++ b/cpukit/libfs/src/jffs2/src/acl.h @@ -22,6 +22,7 @@ struct jffs2_acl_entry_short { struct jffs2_acl_header { jint32_t a_version; + struct jffs2_acl_entry a_entries[]; }; #ifdef CONFIG_JFFS2_FS_POSIX_ACL diff --git a/cpukit/libfs/src/jffs2/src/erase.c b/cpukit/libfs/src/jffs2/src/erase.c index 9ffffafaea..e8ab569462 100644 --- a/cpukit/libfs/src/jffs2/src/erase.c +++ b/cpukit/libfs/src/jffs2/src/erase.c @@ -403,7 +403,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb { size_t retlen; int ret; - uint32_t uninitialized_var(bad_offset); + uint32_t bad_offset; switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { case -EAGAIN: goto refile; diff --git a/cpukit/libfs/src/jffs2/src/fs-rtems.c b/cpukit/libfs/src/jffs2/src/fs-rtems.c index 8bc3d85cc3..b863c74547 100644 --- a/cpukit/libfs/src/jffs2/src/fs-rtems.c +++ b/cpukit/libfs/src/jffs2/src/fs-rtems.c @@ -1316,30 +1316,6 @@ int rtems_jffs2_initialize( // //========================================================================== -unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, - struct jffs2_inode_info *f, - unsigned long offset, - unsigned long *priv) -{ - int ret; - struct super_block *sb = OFNI_BS_2SFFJ(c); - unsigned char *gc_buffer = &sb->s_gc_buffer[0]; - - ret = jffs2_read_inode_range(c, f, gc_buffer, - offset & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE); - if (ret) - return ERR_PTR(ret); - - return gc_buffer; -} - -void jffs2_gc_release_page(struct jffs2_sb_info *c, - unsigned char *ptr, - unsigned long *priv) -{ - /* Do nothing */ -} - static struct _inode *new_inode(struct super_block *sb) { diff --git a/cpukit/libfs/src/jffs2/src/gc.c b/cpukit/libfs/src/jffs2/src/gc.c index f557075ab8..04ec073d2b 100644 --- a/cpukit/libfs/src/jffs2/src/gc.c +++ b/cpukit/libfs/src/jffs2/src/gc.c @@ -1173,12 +1173,17 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { +#ifndef __rtems__ + struct inode *inode = OFNI_EDONI_2SFFJ(f); +#endif /* __rtems__ */ struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; uint32_t alloclen, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; - unsigned long pg; +#ifndef __rtems__ + struct page *page; +#endif /* __rtems__ */ unsigned char *pg_ptr; memset(&ri, 0, sizeof(ri)); @@ -1333,15 +1338,26 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era * end up here trying to GC the *same* page that jffs2_write_begin() is * trying to write out, read_cache_page() will not deadlock. */ mutex_unlock(&f->sem); - pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); - mutex_lock(&f->sem); - - if (IS_ERR(pg_ptr)) { +#ifndef __rtems__ + page = read_cache_page(inode->i_mapping, start >> PAGE_SHIFT, + jffs2_do_readpage_unlock, inode); + if (IS_ERR(page)) { pr_warn("read_cache_page() returned error: %ld\n", - PTR_ERR(pg_ptr)); - return PTR_ERR(pg_ptr); + PTR_ERR(page)); + mutex_lock(&f->sem); + return PTR_ERR(page); } + pg_ptr = kmap(page); +#else /* __rtems__ */ + pg_ptr = &OFNI_BS_2SFFJ(c)->s_gc_buffer[0]; + ret = jffs2_read_inode_range(c, f, pg_ptr, + start & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE); + if (ret) + return ret; +#endif /* __rtems__ */ + mutex_lock(&f->sem); + offset = start; while(offset < orig_end) { uint32_t datalen; @@ -1404,6 +1420,9 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era } } - jffs2_gc_release_page(c, pg_ptr, &pg); +#ifndef __rtems__ + kunmap(page); + put_page(page); +#endif /* __rtems__ */ return ret; } diff --git a/cpukit/libfs/src/jffs2/src/nodelist.h b/cpukit/libfs/src/jffs2/src/nodelist.h index 143f60dbcb..4eee0ac8ff 100644 --- a/cpukit/libfs/src/jffs2/src/nodelist.h +++ b/cpukit/libfs/src/jffs2/src/nodelist.h @@ -259,7 +259,7 @@ struct jffs2_full_dirent uint32_t ino; /* == zero for unlink */ unsigned int nhash; unsigned char type; - unsigned char name[0]; + unsigned char name[]; }; /* diff --git a/cpukit/libfs/src/jffs2/src/os-rtems.h b/cpukit/libfs/src/jffs2/src/os-rtems.h index db1be61e67..d9e4330371 100644 --- a/cpukit/libfs/src/jffs2/src/os-rtems.h +++ b/cpukit/libfs/src/jffs2/src/os-rtems.h @@ -56,7 +56,7 @@ static inline unsigned int full_name_hash(const void *salt, const unsigned char #define jffs2_can_mark_obsolete(c) (1) #define JFFS2_INODE_INFO(i) (&(i)->jffs2_i) -#define OFNI_EDONI_2SFFJ(f) ((struct _inode *) ( ((char *)f) - ((char *)(&((struct _inode *)NULL)->jffs2_i)) ) ) +#define OFNI_EDONI_2SFFJ(f) RTEMS_CONTAINER_OF(f, struct _inode, jffs2_i) #define ITIME(sec) (sec) #define I_SEC(tv) (tv) @@ -136,9 +136,6 @@ struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino); void jffs2_iput(struct _inode * i); void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); -unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - unsigned long offset, unsigned long *priv); -void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); /* Avoid polluting RTEMS namespace with names not starting in jffs2_ */ #define os_to_jffs2_mode(x) jffs2_from_os_mode(x) diff --git a/cpukit/libfs/src/jffs2/src/readinode.c b/cpukit/libfs/src/jffs2/src/readinode.c index e6c9452c03..831fb8f3f9 100644 --- a/cpukit/libfs/src/jffs2/src/readinode.c +++ b/cpukit/libfs/src/jffs2/src/readinode.c @@ -1294,7 +1294,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, dbg_readinode("symlink's target '%s' cached\n", f->target); } - /* fall through... */ + fallthrough; case S_IFBLK: case S_IFCHR: @@ -1434,11 +1434,12 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) } jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - +#ifdef __rtems__ if (f->target) { kfree(f->target); f->target = NULL; } +#endif /* __rtems__ */ fds = f->dents; while(fds) { diff --git a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h index 1017a71c1f..56395e1528 100644 --- a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h +++ b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h @@ -33,3 +33,4 @@ #define __ECOS 1 #define KBUILD_MODNAME "JFFS2" +#define fallthrough __attribute__((__fallthrough__)) diff --git a/cpukit/libfs/src/jffs2/src/scan.c b/cpukit/libfs/src/jffs2/src/scan.c index 177a0cdd3f..765bf55478 100644 --- a/cpukit/libfs/src/jffs2/src/scan.c +++ b/cpukit/libfs/src/jffs2/src/scan.c @@ -264,7 +264,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) } #endif if (c->nr_erasing_blocks) { - if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { + if (!c->used_size && !c->unchecked_size && + ((c->nr_free_blocks+empty_blocks+bad_blocks) != c->nr_blocks || bad_blocks == c->nr_blocks)) { pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n", empty_blocks, bad_blocks, c->nr_blocks); @@ -530,8 +531,11 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo err = jffs2_fill_scan_buf(c, sumptr, jeb->offset + c->sector_size - sumlen, sumlen - buf_len); - if (err) + if (err) { + if (sumlen > buf_size) + kfree(sumptr); return err; + } } } diff --git a/cpukit/libfs/src/jffs2/src/summary.h b/cpukit/libfs/src/jffs2/src/summary.h index 60207a2ae9..e4131cb1f1 100644 --- a/cpukit/libfs/src/jffs2/src/summary.h +++ b/cpukit/libfs/src/jffs2/src/summary.h @@ -61,7 +61,7 @@ struct jffs2_sum_dirent_flash jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ - uint8_t name[0]; /* dirent name */ + uint8_t name[]; /* dirent name */ } __attribute__((packed)); struct jffs2_sum_xattr_flash @@ -117,7 +117,7 @@ struct jffs2_sum_dirent_mem jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ - uint8_t name[0]; /* dirent name */ + uint8_t name[]; /* dirent name */ } __attribute__((packed)); struct jffs2_sum_xattr_mem diff --git a/cpukit/libmd/md5.c b/cpukit/libmd/md5.c index 4c909f37a0..5e3a100c7b 100644 --- a/cpukit/libmd/md5.c +++ b/cpukit/libmd/md5.c @@ -165,7 +165,7 @@ void MD5Update ( ends with the desired message digest in mdContext->digest[0...15]. */ void MD5Final ( - unsigned char hash[], + unsigned char hash[16], MD5_CTX *mdContext ) { UINT4 in[16]; diff --git a/cpukit/libmisc/shell/hexdump-conv.c b/cpukit/libmisc/shell/hexdump-conv.c index aa16f9b169..24b28353f3 100644 --- a/cpukit/libmisc/shell/hexdump-conv.c +++ b/cpukit/libmisc/shell/hexdump-conv.c @@ -117,7 +117,11 @@ retry: if (clen == 0) clen = 1; else if (clen == (size_t)-1 || (clen == (size_t)-2 && +#ifndef __rtems__ buf == peekbuf)) { +#else /* __rtems__ */ + &buf[0] == &peekbuf[0])) { +#endif /* __rtems__ */ memset(&pr->mbstate, 0, sizeof(pr->mbstate)); wc = *p; clen = 1; diff --git a/cpukit/libmisc/shell/main_edit.c b/cpukit/libmisc/shell/main_edit.c index ed1371f7fa..4cc742719a 100644 --- a/cpukit/libmisc/shell/main_edit.c +++ b/cpukit/libmisc/shell/main_edit.c @@ -55,7 +55,9 @@ #if defined(__linux__) || defined(__rtems__) #include <sys/ioctl.h> #include <termios.h> +#ifndef O_BINARY #define O_BINARY 0 +#endif static int linux_console; #endif diff --git a/cpukit/libtest/testgcovbspreset.c b/cpukit/libtest/testgcovbspreset.c new file mode 100644 index 0000000000..6e10615f8c --- /dev/null +++ b/cpukit/libtest/testgcovbspreset.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSTest + * + * @brief This source file contains the implementation of a wrapper for + * bsp_reset() which dumps the gcov information using + * rtems_test_gcov_dump_info() before the real bsp_reset() is called. + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/test-info.h> + +#include <rtems/score/cpu.h> + +void __real_bsp_reset( void ); + +void __wrap_bsp_reset( void ); + +void __wrap_bsp_reset( void ) +{ + rtems_test_gcov_dump_info(); + __real_bsp_reset(); +} diff --git a/cpukit/libtest/testgcovcpufatalhalt.c b/cpukit/libtest/testgcovcpufatalhalt.c new file mode 100644 index 0000000000..dd8f10149c --- /dev/null +++ b/cpukit/libtest/testgcovcpufatalhalt.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSTest + * + * @brief This source file contains the implementation of a wrapper for + * _CPU_Fatal_halt() which dumps the gcov information using + * rtems_test_gcov_dump_info() before the real _CPU_Fatal_halt() is called. + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/test-info.h> + +#include <rtems/score/cpu.h> + +void __real__CPU_Fatal_halt( uint32_t source, CPU_Uint32ptr error ); + +void __wrap__CPU_Fatal_halt( uint32_t source, CPU_Uint32ptr error ); + +void __wrap__CPU_Fatal_halt( uint32_t source, CPU_Uint32ptr error ) +{ + rtems_test_gcov_dump_info(); + __real__CPU_Fatal_halt( source, error ); +} diff --git a/cpukit/libtest/testgcovdumpinfo.c b/cpukit/libtest/testgcovdumpinfo.c new file mode 100644 index 0000000000..7139d7d623 --- /dev/null +++ b/cpukit/libtest/testgcovdumpinfo.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSTest + * + * @brief This source file contains the implementation of + * rtems_test_gcov_dump_info(). + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/test-info.h> + +#include <rtems/score/gcov.h> +#include <rtems/score/isrlock.h> +#include <rtems/bspIo.h> + +ISR_LOCK_DEFINE( static, gcov_dump_lock, "gcov dump" ); + +static bool gcov_dump_done; + +void rtems_test_gcov_dump_info( void ) +{ + ISR_lock_Context lock_context; + + _ISR_lock_ISR_disable_and_acquire( &gcov_dump_lock, &lock_context ); + + if ( !gcov_dump_done ) { + gcov_dump_done = true; + + _IO_Printf( rtems_put_char, NULL, "\n*** BEGIN OF GCOV INFO BASE64 ***\n" ); + _Gcov_Dump_info_base64( rtems_put_char, NULL ); + _IO_Printf( rtems_put_char, NULL, "\n*** END OF GCOV INFO BASE64 ***\n" ); + } + + _ISR_lock_Release_and_ISR_enable( &gcov_dump_lock, &lock_context ); +} diff --git a/cpukit/posix/src/_execve.c b/cpukit/posix/src/_execve.c index 2858d13082..63afadec43 100644 --- a/cpukit/posix/src/_execve.c +++ b/cpukit/posix/src/_execve.c @@ -43,9 +43,9 @@ #endif /* - * Needed to get the prototype for this newlib helper method + * Needed to get the prototype for this libc helper method */ -#define _COMPILING_NEWLIB +#define _LIBC #include <errno.h> #include <rtems/seterr.h> diff --git a/cpukit/score/cpu/aarch64/cpu.c b/cpukit/score/cpu/aarch64/cpu.c index 88e7ad8a8c..f0062adf30 100644 --- a/cpukit/score/cpu/aarch64/cpu.c +++ b/cpukit/score/cpu/aarch64/cpu.c @@ -180,6 +180,8 @@ void _CPU_ISR_install_vector( CPU_ISR_handler *old_handler ) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" /* Redirection table starts at the end of the vector table */ CPU_ISR_handler *table = (CPU_ISR_handler *) (MAX_EXCEPTIONS * 4); @@ -194,6 +196,7 @@ void _CPU_ISR_install_vector( if (current_handler != new_handler) { table [vector] = new_handler; } +#pragma GCC diagnostic pop } void _CPU_Initialize( void ) diff --git a/cpukit/score/cpu/arm/aarch32-psma-init.c b/cpukit/score/cpu/arm/aarch32-psma-init.c index ee9338f050..543e1b7d9b 100644 --- a/cpukit/score/cpu/arm/aarch32-psma-init.c +++ b/cpukit/score/cpu/arm/aarch32-psma-init.c @@ -45,7 +45,7 @@ #include <rtems/score/aarch32-system-registers.h> #include <rtems/score/cpu.h> -#define AARCH32_PSMA_REGION_MAX \ +#define AARCH32_PMSA_REGION_MAX \ ( ( AARCH32_MPUIR_REGION_MASK >> AARCH32_MPUIR_REGION_SHIFT ) + 1 ) static void _AArch32_PMSA_Configure( @@ -133,16 +133,16 @@ size_t _AArch32_PMSA_Map_sections_to_regions( if ( attr == region_attr ) { uint32_t region_end; - if ( end == region_base ) { - /* Extend the region region */ + if ( end - region_base <= AARCH32_PMSA_MIN_REGION_ALIGN ) { + /* Extend the region */ regions[ ri ].base = base; break; } region_end = region_limit + AARCH32_PMSA_MIN_REGION_ALIGN; - if ( base == region_end ) { - /* Extend the region region */ + if ( region_end - base <= AARCH32_PMSA_MIN_REGION_ALIGN ) { + /* Extend the region */ regions[ ri ].limit = limit; break; } @@ -153,7 +153,7 @@ size_t _AArch32_PMSA_Map_sections_to_regions( } } - if ( end <= region_base ) { + if ( base <= region_base ) { size_t i; if ( region_used >= region_max ) { @@ -196,7 +196,7 @@ void _AArch32_PMSA_Initialize( size_t section_count ) { - AArch32_PMSA_Region regions[ AARCH32_PSMA_REGION_MAX ]; + AArch32_PMSA_Region regions[ AARCH32_PMSA_REGION_MAX ]; size_t region_max; size_t region_used; diff --git a/cpukit/score/cpu/arm/cpu.c b/cpukit/score/cpu/arm/cpu.c index 5c5b253470..b2cc6039b0 100644 --- a/cpukit/score/cpu/arm/cpu.c +++ b/cpukit/score/cpu/arm/cpu.c @@ -167,8 +167,10 @@ void _CPU_ISR_install_vector( CPU_ISR_handler *old_handler ) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" /* Redirection table starts at the end of the vector table */ - CPU_ISR_handler *table = (CPU_ISR_handler *) (MAX_EXCEPTIONS * 4); + CPU_ISR_handler volatile *table = (CPU_ISR_handler *) (MAX_EXCEPTIONS * 4); CPU_ISR_handler current_handler = table [vector]; @@ -181,6 +183,7 @@ void _CPU_ISR_install_vector( if (current_handler != new_handler) { table [vector] = new_handler; } +#pragma GCC diagnostic pop } void _CPU_Initialize( void ) diff --git a/cpukit/score/cpu/riscv/include/libcpu/byteorder.h b/cpukit/score/cpu/riscv/include/libcpu/byteorder.h index 939e51fe84..1b4f6f3b1e 100644 --- a/cpukit/score/cpu/riscv/include/libcpu/byteorder.h +++ b/cpukit/score/cpu/riscv/include/libcpu/byteorder.h @@ -7,6 +7,8 @@ #ifndef _LIBCPU_BYTEORDER_H #define _LIBCPU_BYTEORDER_H +#include <stdint.h> + static inline void st_le32(volatile uint32_t *addr, uint32_t value) { *(addr)=value ; diff --git a/cpukit/score/src/gcovdumpinfo.c b/cpukit/score/src/gcovdumpinfo.c new file mode 100644 index 0000000000..8598fce578 --- /dev/null +++ b/cpukit/score/src/gcovdumpinfo.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSScoreIO + * + * @brief This source file contains the implementation of _Gcov_Ddump_info(). + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/score/gcov.h> + +typedef struct { + IO_Put_char put_char; + void *arg; +} Gcov_Context; + +static void _Gcov_Dump( const void *data, unsigned length, void *arg ) +{ + Gcov_Context *ctx; + IO_Put_char put_char; + void *ctx_arg; + const char *in; + const void *end; + + ctx = arg; + in = data; + end = in + length; + put_char = ctx->put_char; + ctx_arg = ctx->arg; + + while ( in != end ) { + ( *put_char )( *in, ctx_arg ); + ++in; + } +} + +static void _Gcov_Filename( const char *filename, void *arg ) +{ + __gcov_filename_to_gcfn( filename, _Gcov_Dump, arg ); +} + +static void *_Gcov_Allocate( unsigned length, void *arg ) +{ + (void) length; + (void) arg; + return NULL; +} + +void _Gcov_Dump_info( IO_Put_char put_char, void *arg ) +{ + Gcov_Context ctx; + const struct gcov_info * const *item; + + ctx.put_char = put_char; + ctx.arg = arg; + + RTEMS_LINKER_SET_FOREACH( gcov_info, item ) { + __gcov_info_to_gcda( + *item, + _Gcov_Filename, + _Gcov_Dump, + _Gcov_Allocate, + &ctx + ); + } +} diff --git a/cpukit/score/src/gcovdumpinfobase64.c b/cpukit/score/src/gcovdumpinfobase64.c new file mode 100644 index 0000000000..be07f03291 --- /dev/null +++ b/cpukit/score/src/gcovdumpinfobase64.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSScoreIO + * + * @brief This source file contains the implementation of + * _Gcov_Dump_info_base64(). + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/score/gcov.h> + +#include <limits.h> +#include <string.h> + +typedef struct { + IO_Put_char put_char; + void *arg; + int out; + size_t index; + char buf[ 57 ]; +} Gcov_Base64_context; + +static void _Gcov_Base64_put_char( int c, void *arg ) +{ + Gcov_Base64_context *ctx; + + ctx = arg; + + ( *ctx->put_char )( c, ctx->arg ); + ++ctx->out; + + if ( ctx->out >= 76 ) { + ctx->out = 0; + ( *ctx->put_char )( '\n', ctx->arg ); + } +} + +static void _Gcov_Base64_encode( int c, void *arg ) +{ + Gcov_Base64_context *ctx; + size_t index; + + ctx = arg; + index = ctx->index; + ctx->buf[ index ] = (char) c; + + if ( index == RTEMS_ARRAY_SIZE( ctx->buf ) - 1 ) { + index = 0; + _IO_Base64( + _Gcov_Base64_put_char, + ctx, + ctx->buf, + sizeof( ctx->buf ), + NULL, + INT_MAX + ); + } else { + ++index; + } + + ctx->index = index; +} + +void _Gcov_Dump_info_base64( IO_Put_char put_char, void *arg ) +{ + Gcov_Base64_context ctx; + + memset( &ctx, 0, sizeof( ctx ) ); + ctx.put_char = put_char; + ctx.arg = arg; + _Gcov_Dump_info( _Gcov_Base64_encode, &ctx ); + _IO_Base64( _Gcov_Base64_put_char, &ctx, ctx.buf, ctx.index, NULL, INT_MAX ); +} diff --git a/cpukit/score/src/gcovinfoset.c b/cpukit/score/src/gcovinfoset.c new file mode 100644 index 0000000000..6fd695043b --- /dev/null +++ b/cpukit/score/src/gcovinfoset.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSScoreIO + * + * @brief This source file contains the definition of the gcov information + * linker set. + */ + +/* + * Copyright (C) 2021, 2022 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 <rtems/score/gcov.h> + +RTEMS_LINKER_ROSET( gcov_info, const struct gcov_info * ); diff --git a/cpukit/score/src/kern_ntptime.c b/cpukit/score/src/kern_ntptime.c index 8a16702736..1233166a61 100644 --- a/cpukit/score/src/kern_ntptime.c +++ b/cpukit/score/src/kern_ntptime.c @@ -74,8 +74,24 @@ __FBSDID("$FreeBSD$"); #define ntp_update_second _Timecounter_NTP_update_second #define time_uptime _Timecounter_Time_uptime struct thread; -static __inline long lmax(long a, long b) { return (a > b ? a : b); } -static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); } + +static inline long +lmax(long a, long b) +{ + + if (a > b) + return (a); + return (b); +} + +static inline quad_t +qmin(quad_t a, quad_t b) +{ + + if (a < b) + return (a); + return (b); +} #endif /* __rtems__ */ #ifndef __rtems__ diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c index 89ece1fbde..643026a1c8 100644 --- a/cpukit/score/src/kern_tc.c +++ b/cpukit/score/src/kern_tc.c @@ -1980,7 +1980,8 @@ pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) #else /* __rtems__ */ _Assert(pps->wait != NULL); err = (*pps->wait)(pps, fapi->timeout); - return (err); + if (err != 0) + return (err); #endif /* __rtems__ */ } } @@ -2078,6 +2079,18 @@ pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) } #ifdef __rtems__ +/* + * The real implementation of hardpps() is defined in kern_ntptime.c. Use it + * only if the NTP support is needed by the application. + */ +RTEMS_WEAK void +hardpps(struct timespec *tsp, long nsec) +{ + + (void)tsp; + (void)nsec; +} + static int default_wait(struct pps_state *pps, struct timespec timeout) { @@ -2137,9 +2150,11 @@ pps_capture(struct pps_state *pps) pps->capffth = fftimehands; #endif pps->capcount = th->th_counter->tc_get_timecount(th->th_counter); +#if defined(RTEMS_SMP) atomic_thread_fence_acq(); if (pps->capgen != th->th_generation) pps->capgen = 0; +#endif } void @@ -2164,7 +2179,11 @@ pps_event(struct pps_state *pps, int event) if ((event & pps->ppsparam.mode) == 0) return; /* If the timecounter was wound up underneath us, bail out. */ +#if defined(RTEMS_SMP) if (pps->capgen == 0 || pps->capgen != +#else + if (pps->capgen != +#endif atomic_load_acq_int(&pps->capth->th_generation)) return; @@ -2310,9 +2329,13 @@ _Timecounter_Tick(void) { Per_CPU_Control *cpu_self = _Per_CPU_Get(); +#if defined(RTEMS_SMP) if (_Per_CPU_Is_boot_processor(cpu_self)) { +#endif tc_windup(NULL); +#if defined(RTEMS_SMP) } +#endif _Watchdog_Tick(cpu_self); } diff --git a/cpukit/score/src/threadchangepriority.c b/cpukit/score/src/threadchangepriority.c index 321bb15cab..80f030fdc6 100644 --- a/cpukit/score/src/threadchangepriority.c +++ b/cpukit/score/src/threadchangepriority.c @@ -135,11 +135,15 @@ static void _Thread_Priority_do_perform_actions( priority_aggregation = _Priority_Actions_move( &queue_context->Priority.Actions ); do { +#if defined(RTEMS_SMP) Priority_Aggregation *next_aggregation; +#endif Priority_Node *priority_action_node; Priority_Action_type priority_action_type; +#if defined(RTEMS_SMP) next_aggregation = _Priority_Get_next_action( priority_aggregation ); +#endif priority_action_node = priority_aggregation->Action.node; priority_action_type = priority_aggregation->Action.type; @@ -198,8 +202,12 @@ static void _Thread_Priority_do_perform_actions( break; } +#if defined(RTEMS_SMP) priority_aggregation = next_aggregation; - } while ( _Priority_Actions_is_valid( priority_aggregation ) ); + } while ( priority_aggregation != NULL ); +#else + } while ( false ); +#endif if ( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) { _Thread_queue_Context_add_priority_update( queue_context, the_thread ); diff --git a/cpukit/score/src/threadqops.c b/cpukit/score/src/threadqops.c index 33fc5a44cb..fbea9f6de6 100644 --- a/cpukit/score/src/threadqops.c +++ b/cpukit/score/src/threadqops.c @@ -404,8 +404,12 @@ static void _Thread_queue_Priority_priority_actions( break; } +#if defined(RTEMS_SMP) priority_aggregation = _Priority_Get_next_action( priority_aggregation ); - } while ( _Priority_Actions_is_valid( priority_aggregation ) ); + } while ( priority_aggregation != NULL ); +#else + } while ( false ); +#endif } static void _Thread_queue_Priority_do_initialize( @@ -734,14 +738,18 @@ static void _Thread_queue_Priority_inherit_priority_actions( priority_aggregation = _Priority_Actions_move( priority_actions ); do { +#if defined(RTEMS_SMP) Priority_Aggregation *next_aggregation; +#endif Scheduler_Node *scheduler_node; size_t scheduler_index; Thread_queue_Priority_queue *priority_queue; Scheduler_Node *scheduler_node_of_owner; Priority_Action_type priority_action_type; +#if defined(RTEMS_SMP) next_aggregation = _Priority_Get_next_action( priority_aggregation ); +#endif scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY( priority_aggregation ); scheduler_index = _Thread_queue_Scheduler_index( scheduler_node ); @@ -797,8 +805,12 @@ static void _Thread_queue_Priority_inherit_priority_actions( break; } +#if defined(RTEMS_SMP) priority_aggregation = next_aggregation; - } while ( _Priority_Actions_is_valid( priority_aggregation ) ); + } while ( priority_aggregation != NULL ); +#else + } while ( false ); +#endif } static void _Thread_queue_Priority_inherit_do_initialize( diff --git a/cpukit/score/src/threadqtimeout.c b/cpukit/score/src/threadqtimeout.c index 271ea27f27..10e194f6d8 100644 --- a/cpukit/score/src/threadqtimeout.c +++ b/cpukit/score/src/threadqtimeout.c @@ -132,7 +132,7 @@ void _Thread_queue_Add_timeout_monotonic_timespec( { struct timespec now; - _Timecounter_Getnanouptime( &now ); + _Timecounter_Nanouptime( &now ); _Thread_queue_Add_timeout_timespec( queue, the_thread, @@ -152,7 +152,7 @@ void _Thread_queue_Add_timeout_realtime_timespec( { struct timespec now; - _Timecounter_Getnanotime( &now ); + _Timecounter_Nanotime( &now ); _Thread_queue_Add_timeout_timespec( queue, the_thread, diff --git a/cpukit/score/src/watchdogtick.c b/cpukit/score/src/watchdogtick.c index 6edb3f071a..71311b598e 100644 --- a/cpukit/score/src/watchdogtick.c +++ b/cpukit/score/src/watchdogtick.c @@ -83,9 +83,13 @@ void _Watchdog_Tick( Per_CPU_Control *cpu ) Thread_Control *executing; const Thread_CPU_budget_operations *cpu_budget_operations; +#ifdef RTEMS_SMP if ( _Per_CPU_Is_boot_processor( cpu ) ) { +#endif ++_Watchdog_Ticks_since_boot; +#ifdef RTEMS_SMP } +#endif _ISR_lock_ISR_disable_and_acquire( &cpu->Watchdog.Lock, &lock_context ); |