summaryrefslogtreecommitdiffstats
path: root/c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cpukit/include/rtems/score/gcov.h87
-rw-r--r--cpukit/include/rtems/score/priorityimpl.h33
-rw-r--r--cpukit/include/rtems/score/processormask.h106
-rw-r--r--cpukit/include/rtems/test-info.h6
-rw-r--r--cpukit/include/rtems/tftp.h407
-rw-r--r--cpukit/libcsupport/src/__gettod.c2
-rw-r--r--cpukit/libcsupport/src/__times.c4
-rw-r--r--cpukit/libcsupport/src/gcovfork.c94
-rw-r--r--cpukit/libfs/src/ftpfs/tftpDriver.c1757
-rw-r--r--cpukit/libfs/src/ftpfs/tftp_driver.h96
-rw-r--r--cpukit/libfs/src/ftpfs/tftpfs.c615
-rw-r--r--cpukit/libfs/src/jffs2/VERSION4
-rw-r--r--cpukit/libfs/src/jffs2/include/linux/jffs2.h5
-rw-r--r--cpukit/libfs/src/jffs2/src/acl.h1
-rw-r--r--cpukit/libfs/src/jffs2/src/erase.c2
-rw-r--r--cpukit/libfs/src/jffs2/src/fs-rtems.c24
-rw-r--r--cpukit/libfs/src/jffs2/src/gc.c35
-rw-r--r--cpukit/libfs/src/jffs2/src/nodelist.h2
-rw-r--r--cpukit/libfs/src/jffs2/src/os-rtems.h5
-rw-r--r--cpukit/libfs/src/jffs2/src/readinode.c5
-rw-r--r--cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h1
-rw-r--r--cpukit/libfs/src/jffs2/src/scan.c8
-rw-r--r--cpukit/libfs/src/jffs2/src/summary.h4
-rw-r--r--cpukit/libmd/md5.c2
-rw-r--r--cpukit/libmisc/shell/hexdump-conv.c4
-rw-r--r--cpukit/libmisc/shell/main_edit.c2
-rw-r--r--cpukit/libtest/testgcovbspreset.c54
-rw-r--r--cpukit/libtest/testgcovcpufatalhalt.c54
-rw-r--r--cpukit/libtest/testgcovdumpinfo.c66
-rw-r--r--cpukit/posix/src/_execve.c4
-rw-r--r--cpukit/score/cpu/aarch64/cpu.c3
-rw-r--r--cpukit/score/cpu/arm/aarch32-psma-init.c14
-rw-r--r--cpukit/score/cpu/arm/cpu.c5
-rw-r--r--cpukit/score/cpu/riscv/include/libcpu/byteorder.h2
-rw-r--r--cpukit/score/src/gcovdumpinfo.c96
-rw-r--r--cpukit/score/src/gcovdumpinfobase64.c104
-rw-r--r--cpukit/score/src/gcovinfoset.c43
-rw-r--r--cpukit/score/src/kern_ntptime.c20
-rw-r--r--cpukit/score/src/kern_tc.c25
-rw-r--r--cpukit/score/src/threadchangepriority.c10
-rw-r--r--cpukit/score/src/threadqops.c16
-rw-r--r--cpukit/score/src/threadqtimeout.c4
-rw-r--r--cpukit/score/src/watchdogtick.c4
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 );