diff options
Diffstat (limited to '')
85 files changed, 3860 insertions, 856 deletions
diff --git a/cpukit/libfs/src/defaults/default_are_nodes_equal.c b/cpukit/libfs/src/defaults/default_are_nodes_equal.c index bdcac110d5..2b9ddee13b 100644 --- a/cpukit/libfs/src/defaults/default_are_nodes_equal.c +++ b/cpukit/libfs/src/defaults/default_are_nodes_equal.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_clone.c b/cpukit/libfs/src/defaults/default_clone.c index 7d42355fa0..b173a24d1b 100644 --- a/cpukit/libfs/src/defaults/default_clone.c +++ b/cpukit/libfs/src/defaults/default_clone.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_eval_path.c b/cpukit/libfs/src/defaults/default_eval_path.c index c2514df93d..2c9e56dbcf 100644 --- a/cpukit/libfs/src/defaults/default_eval_path.c +++ b/cpukit/libfs/src/defaults/default_eval_path.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_fsync_success.c b/cpukit/libfs/src/defaults/default_fsync_success.c index bbbeb1c8b8..71267fc5cd 100644 --- a/cpukit/libfs/src/defaults/default_fsync_success.c +++ b/cpukit/libfs/src/defaults/default_fsync_success.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_ftruncate_directory.c b/cpukit/libfs/src/defaults/default_ftruncate_directory.c index 33ae65ffcf..94b54064df 100644 --- a/cpukit/libfs/src/defaults/default_ftruncate_directory.c +++ b/cpukit/libfs/src/defaults/default_ftruncate_directory.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_handlers.c b/cpukit/libfs/src/defaults/default_handlers.c index 0e61c5a24c..134d42d81b 100644 --- a/cpukit/libfs/src/defaults/default_handlers.c +++ b/cpukit/libfs/src/defaults/default_handlers.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2010 embedded brains GmbH. All rights reserved. + * Copyright (c) 2010 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_kqfilter.c b/cpukit/libfs/src/defaults/default_kqfilter.c index 3f060845fd..97c61c3391 100644 --- a/cpukit/libfs/src/defaults/default_kqfilter.c +++ b/cpukit/libfs/src/defaults/default_kqfilter.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved. + * Copyright (C) 2013, 2017 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_lock_and_unlock.c b/cpukit/libfs/src/defaults/default_lock_and_unlock.c index 584614a8cf..a45af94a88 100644 --- a/cpukit/libfs/src/defaults/default_lock_and_unlock.c +++ b/cpukit/libfs/src/defaults/default_lock_and_unlock.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_lseek_directory.c b/cpukit/libfs/src/defaults/default_lseek_directory.c index 4b80c164e3..69ef3cc820 100644 --- a/cpukit/libfs/src/defaults/default_lseek_directory.c +++ b/cpukit/libfs/src/defaults/default_lseek_directory.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_lseek_file.c b/cpukit/libfs/src/defaults/default_lseek_file.c index e3901b5f9c..91d48e8cd4 100644 --- a/cpukit/libfs/src/defaults/default_lseek_file.c +++ b/cpukit/libfs/src/defaults/default_lseek_file.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_ops.c b/cpukit/libfs/src/defaults/default_ops.c index 45069e76d7..35290ae6af 100644 --- a/cpukit/libfs/src/defaults/default_ops.c +++ b/cpukit/libfs/src/defaults/default_ops.c @@ -9,7 +9,7 @@ */ /* - * Copyright (C) 2010 embedded brains GmbH + * Copyright (C) 2010 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_poll.c b/cpukit/libfs/src/defaults/default_poll.c index afa09c2c20..5fa94c2de9 100644 --- a/cpukit/libfs/src/defaults/default_poll.c +++ b/cpukit/libfs/src/defaults/default_poll.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved. + * Copyright (C) 2013, 2017 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_readv.c b/cpukit/libfs/src/defaults/default_readv.c index 40db4f3054..797426c9c3 100644 --- a/cpukit/libfs/src/defaults/default_readv.c +++ b/cpukit/libfs/src/defaults/default_readv.c @@ -12,7 +12,7 @@ * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/defaults/default_writev.c b/cpukit/libfs/src/defaults/default_writev.c index 31c1c76da2..908bb7edc4 100644 --- a/cpukit/libfs/src/defaults/default_writev.c +++ b/cpukit/libfs/src/defaults/default_writev.c @@ -12,7 +12,7 @@ * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/dosfs/fat.c b/cpukit/libfs/src/dosfs/fat.c index 9229406dec..88b4a2859b 100644 --- a/cpukit/libfs/src/dosfs/fat.c +++ b/cpukit/libfs/src/dosfs/fat.c @@ -93,7 +93,6 @@ fat_buf_release(fat_fs_info_t *fs_info) uint32_t sec_num = fs_info->c.blk_num; bool sec_of_fat = ((sec_num >= fs_info->vol.fat_loc) && (sec_num < fs_info->vol.rdir_loc)); - uint32_t blk = fat_sector_num_to_block_num(fs_info, sec_num); uint32_t blk_ofs = fat_sector_offset_to_block_offset(fs_info, sec_num, 0); @@ -115,6 +114,7 @@ fat_buf_release(fat_fs_info_t *fs_info) for (i = 1; i < fs_info->vol.fats; i++) { rtems_bdbuf_buffer *bd; + uint32_t blk; sec_num = fs_info->c.blk_num + fs_info->vol.fat_length * i, blk = fat_sector_num_to_block_num(fs_info, sec_num); diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.c b/cpukit/libfs/src/dosfs/fat_fat_operations.c index 24a408f9c7..fe6bbf03ce 100644 --- a/cpukit/libfs/src/dosfs/fat_fat_operations.c +++ b/cpukit/libfs/src/dosfs/fat_fat_operations.c @@ -203,9 +203,9 @@ fat_free_fat_clusters_chain( cur_cln = next_cln; } - fs_info->vol.next_cl = chain; - if (fs_info->vol.free_cls != FAT_UNDEFINED_VALUE) - fs_info->vol.free_cls += freed_cls_cnt; + fs_info->vol.next_cl = chain; + if (fs_info->vol.free_cls != FAT_UNDEFINED_VALUE) + fs_info->vol.free_cls += freed_cls_cnt; fat_buf_release(fs_info); if (rc1 != RC_OK) @@ -356,7 +356,7 @@ fat_set_fat_cluster( if (rc != RC_OK) return rc; - *sec_buf &= 0x00; + *sec_buf = 0x00; *sec_buf |= (uint8_t)((fat16_clv & 0xFF00)>>8); @@ -364,7 +364,7 @@ fat_set_fat_cluster( } else { - *(sec_buf + ofs + 1) &= 0x00; + *(sec_buf + ofs + 1) = 0x00; *(sec_buf + ofs + 1) |= (uint8_t )((fat16_clv & 0xFF00)>>8); } @@ -372,7 +372,7 @@ fat_set_fat_cluster( else { fat16_clv = ((uint16_t )in_val) & FAT_FAT12_MASK; - *(sec_buf + ofs) &= 0x00; + *(sec_buf + ofs) = 0x00; *(sec_buf + ofs) |= (uint8_t)(fat16_clv & 0x00FF); diff --git a/cpukit/libfs/src/dosfs/msdos.h b/cpukit/libfs/src/dosfs/msdos.h index df9600a02d..e6d650c8d5 100644 --- a/cpukit/libfs/src/dosfs/msdos.h +++ b/cpukit/libfs/src/dosfs/msdos.h @@ -11,7 +11,7 @@ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> * * Modifications to support UTF-8 in the file system are - * Copyright (c) 2013 embedded brains GmbH. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -72,12 +72,12 @@ typedef struct msdos_fs_info_s rtems_dosfs_convert_control *converter; } msdos_fs_info_t; -RTEMS_INLINE_ROUTINE void msdos_fs_lock(msdos_fs_info_t *fs_info) +static inline void msdos_fs_lock(msdos_fs_info_t *fs_info) { rtems_recursive_mutex_lock(&fs_info->vol_mutex); } -RTEMS_INLINE_ROUTINE void msdos_fs_unlock(msdos_fs_info_t *fs_info) +static inline void msdos_fs_unlock(msdos_fs_info_t *fs_info) { rtems_recursive_mutex_unlock(&fs_info->vol_mutex); } diff --git a/cpukit/libfs/src/dosfs/msdos_conv.c b/cpukit/libfs/src/dosfs/msdos_conv.c index 7efd5a7e80..ba28a07512 100644 --- a/cpukit/libfs/src/dosfs/msdos_conv.c +++ b/cpukit/libfs/src/dosfs/msdos_conv.c @@ -25,7 +25,7 @@ * October 1992 * * Modifications to support UTF-8 in the file system are - * Copyright (c) 2013 embedded brains GmbH. + * Copyright (c) 2013 embedded brains GmbH & Co. KG */ #ifdef HAVE_CONFIG_H diff --git a/cpukit/libfs/src/dosfs/msdos_conv_default.c b/cpukit/libfs/src/dosfs/msdos_conv_default.c index 2a42738db0..43cff88c6c 100644 --- a/cpukit/libfs/src/dosfs/msdos_conv_default.c +++ b/cpukit/libfs/src/dosfs/msdos_conv_default.c @@ -7,7 +7,7 @@ */ /* - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at diff --git a/cpukit/libfs/src/dosfs/msdos_conv_utf8.c b/cpukit/libfs/src/dosfs/msdos_conv_utf8.c index 332d33d2dc..36027eb2f3 100644 --- a/cpukit/libfs/src/dosfs/msdos_conv_utf8.c +++ b/cpukit/libfs/src/dosfs/msdos_conv_utf8.c @@ -7,7 +7,7 @@ */ /* - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c index 26df0451c9..f71969f787 100644 --- a/cpukit/libfs/src/dosfs/msdos_create.c +++ b/cpukit/libfs/src/dosfs/msdos_create.c @@ -202,7 +202,7 @@ msdos_creat_node(const rtems_filesystem_location_info_t *parent_loc, fs_info->fat.vol.bpc, &unused); if (rc != RC_OK) - goto err; + goto error; /* * dot and dotdot entries are identical to new node except the diff --git a/cpukit/libfs/src/dosfs/msdos_dir.c b/cpukit/libfs/src/dosfs/msdos_dir.c index 093020cbfd..d9585635d9 100644 --- a/cpukit/libfs/src/dosfs/msdos_dir.c +++ b/cpukit/libfs/src/dosfs/msdos_dir.c @@ -11,7 +11,7 @@ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> * * Modifications to support UTF-8 in the file system are - * Copyright (c) 2013 embedded brains GmbH. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -339,14 +339,14 @@ msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count) iop->offset = iop->offset + sizeof(struct dirent); cmpltd += (sizeof(struct dirent)); count -= (sizeof(struct dirent)); + } - /* inode number extracted, close fat-file */ - rc = fat_file_close(&fs_info->fat, tmp_fat_fd); - if (rc != RC_OK) - { - msdos_fs_unlock(fs_info); - return rc; - } + /* inode number extracted, close fat-file */ + rc = fat_file_close(&fs_info->fat, tmp_fat_fd); + if (rc != RC_OK) + { + msdos_fs_unlock(fs_info); + return rc; } } diff --git a/cpukit/libfs/src/dosfs/msdos_file.c b/cpukit/libfs/src/dosfs/msdos_file.c index d142968337..e25f292484 100644 --- a/cpukit/libfs/src/dosfs/msdos_file.c +++ b/cpukit/libfs/src/dosfs/msdos_file.c @@ -179,7 +179,7 @@ msdos_file_ftruncate(rtems_libio_t *iop, off_t length) length, &new_length); if (rc == RC_OK && length != new_length) { - fat_file_truncate(&fs_info->fat, fat_fd, old_length); + (void) fat_file_truncate(&fs_info->fat, fat_fd, old_length); errno = ENOSPC; rc = -1; } @@ -223,6 +223,11 @@ msdos_file_sync(rtems_libio_t *iop) } rc = fat_sync(&fs_info->fat); + if (rc != RC_OK) + { + msdos_fs_unlock(fs_info); + return rc; + } msdos_fs_unlock(fs_info); diff --git a/cpukit/libfs/src/dosfs/msdos_init.c b/cpukit/libfs/src/dosfs/msdos_init.c index 36f1442a26..2ea3c025c7 100644 --- a/cpukit/libfs/src/dosfs/msdos_init.c +++ b/cpukit/libfs/src/dosfs/msdos_init.c @@ -11,10 +11,10 @@ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Modifications to support UTF-8 in the file system are - * Copyright (c) 2013 embedded brains GmbH. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c index a82389bc42..cd59c494ea 100644 --- a/cpukit/libfs/src/dosfs/msdos_misc.c +++ b/cpukit/libfs/src/dosfs/msdos_misc.c @@ -11,7 +11,7 @@ * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru> * * Modifications to support UTF-8 in the file system are - * Copyright (c) 2013 embedded brains GmbH. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -288,7 +288,6 @@ msdos_long_to_short(rtems_dosfs_convert_control *converter, &codepage_name_len); if (eno == EINVAL) { - eno = 0; type = MSDOS_NAME_LONG; } else @@ -1304,7 +1303,6 @@ static int msdos_find_file_in_directory ( const uint8_t *filename_converted, const size_t name_len_for_compare, - const size_t name_len_for_save, const msdos_name_type_t name_type, msdos_fs_info_t *fs_info, fat_file_fd_t *fat_fd, @@ -1747,6 +1745,8 @@ msdos_add_file ( /* Get position of short file name entry */ ret = msdos_get_pos(fs_info, fat_fd, bts2rd, short_file_offset, &dir_pos->sname); + if (ret != RC_OK) + return ret; /* * Handle the entry writes. @@ -1918,7 +1918,6 @@ msdos_find_name_in_fat_file ( retval = msdos_find_file_in_directory ( buffer, name_len_for_compare, - name_len_for_save, name_type, fs_info, fat_fd, diff --git a/cpukit/libfs/src/dosfs/msdos_mknod.c b/cpukit/libfs/src/dosfs/msdos_mknod.c index 934805c5f1..8fe01e4985 100644 --- a/cpukit/libfs/src/dosfs/msdos_mknod.c +++ b/cpukit/libfs/src/dosfs/msdos_mknod.c @@ -44,7 +44,7 @@ int msdos_mknod( ) { int rc = RC_OK; - fat_file_type_t type = 0; + fat_file_type_t type = FAT_DIRECTORY; /* * Figure out what type of msdos node this is. diff --git a/cpukit/libfs/src/ftpfs/ftpfs.c b/cpukit/libfs/src/ftpfs/ftpfs.c index 06d06bc9cc..cec30f9071 100644 --- a/cpukit/libfs/src/ftpfs/ftpfs.c +++ b/cpukit/libfs/src/ftpfs/ftpfs.c @@ -7,7 +7,7 @@ */ /* - * COPYRIGHT (C) 2009-2012 embedded brains GmbH. + * COPYRIGHT (C) 2009-2012 embedded brains GmbH & Co. KG * COPYRIGHT (C) 2002 IMD Ingenieurbuero fuer Microcomputertechnik. * * Redistribution and use in source and binary forms, with or without diff --git a/cpukit/libfs/src/ftpfs/tftpDriver.c b/cpukit/libfs/src/ftpfs/tftpDriver.c index bebe748ca5..5d9fa12000 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 & Co. KG * * 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 /* - * Largest data transfer + * TFTP error codes */ -#define TFTP_BUFSIZE 512 +#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 + +/* + * Special return value for prepare_*_packet_for_sending() functions + * (other return values are the length of the packet to be send) + */ +#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,59 +441,148 @@ 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 + * + * Ignoring result because error packets are sent once and maybe lost. */ - sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to); + (void) sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to); +} + +/* + * 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 data packet + * Wait for a packet */ -static int +static ssize_t getPacket (struct tftpStream *tp, int retryCount) { - int len; + ssize_t len; struct timeval tv; + int flags = 0; - 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; + 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; + /* + * Ignoring result because all possible errors indicate wrong + * arguments and these arguments are OK as tested by test suite. + */ + (void) 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; + /* + * Ignoring result because all possible errors indicate wrong + * arguments and these arguments are OK as tested by test suite. + */ + (void) 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 +600,711 @@ 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; + /* + * Ignoring result because all possible errors indicate wrong + * arguments and these arguments are OK as tested by test suite. + */ + (void) 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. + */ + (void) 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 +1313,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 +1339,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 +1347,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 +1377,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 +1492,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 -) -{ - 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 +void _Tftp_Destroy( + void *tftp_handle ) { - 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..690ff415f6 --- /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 & Co. KG + * + * 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..1fa39c96f2 --- /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 & Co. KG + * + * 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/imfs/imfs_add_node.c b/cpukit/libfs/src/imfs/imfs_add_node.c index 814736284d..2fd25fcc46 100644 --- a/cpukit/libfs/src/imfs/imfs_add_node.c +++ b/cpukit/libfs/src/imfs/imfs_add_node.c @@ -9,7 +9,7 @@ */ /* - * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * Copyright (C) 2020 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> int IMFS_add_node( const char *path, IMFS_jnode_t *node, void *arg ) { diff --git a/cpukit/libfs/src/imfs/imfs_chown.c b/cpukit/libfs/src/imfs/imfs_chown.c index 5a0049ba2d..20210470a1 100644 --- a/cpukit/libfs/src/imfs/imfs_chown.c +++ b/cpukit/libfs/src/imfs/imfs_chown.c @@ -38,7 +38,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> #include <unistd.h> diff --git a/cpukit/libfs/src/imfs/imfs_eval.c b/cpukit/libfs/src/imfs/imfs_eval.c index bb71fe1a3b..e20a85b5d5 100644 --- a/cpukit/libfs/src/imfs/imfs_eval.c +++ b/cpukit/libfs/src/imfs/imfs_eval.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_eval_devfs.c b/cpukit/libfs/src/imfs/imfs_eval_devfs.c index 69c8cc4c0f..1eea1c7dd8 100644 --- a/cpukit/libfs/src/imfs/imfs_eval_devfs.c +++ b/cpukit/libfs/src/imfs/imfs_eval_devfs.c @@ -9,7 +9,7 @@ */ /* - * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * Copyright (C) 2020 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_fchmod.c b/cpukit/libfs/src/imfs/imfs_fchmod.c index 7f885a1987..6f6c57ff23 100644 --- a/cpukit/libfs/src/imfs/imfs_fchmod.c +++ b/cpukit/libfs/src/imfs/imfs_fchmod.c @@ -38,7 +38,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> int IMFS_fchmod( const rtems_filesystem_location_info_t *loc, diff --git a/cpukit/libfs/src/imfs/imfs_fifo.c b/cpukit/libfs/src/imfs/imfs_fifo.c index a3154e18bd..be86187663 100644 --- a/cpukit/libfs/src/imfs/imfs_fifo.c +++ b/cpukit/libfs/src/imfs/imfs_fifo.c @@ -37,7 +37,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> #include <sys/filio.h> diff --git a/cpukit/libfs/src/imfs/imfs_linfile.c b/cpukit/libfs/src/imfs/imfs_linfile.c index 4f961d19e8..c9b1ec9e90 100644 --- a/cpukit/libfs/src/imfs/imfs_linfile.c +++ b/cpukit/libfs/src/imfs/imfs_linfile.c @@ -38,7 +38,7 @@ #include <string.h> -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> static ssize_t IMFS_linfile_read( rtems_libio_t *iop, diff --git a/cpukit/libfs/src/imfs/imfs_link.c b/cpukit/libfs/src/imfs/imfs_link.c index 301a280211..3ea05b0807 100644 --- a/cpukit/libfs/src/imfs/imfs_link.c +++ b/cpukit/libfs/src/imfs/imfs_link.c @@ -38,7 +38,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> static const IMFS_node_control IMFS_node_control_hard_link; diff --git a/cpukit/libfs/src/imfs/imfs_load_tar.c b/cpukit/libfs/src/imfs/imfs_load_tar.c index ec651d820a..18c5366b56 100644 --- a/cpukit/libfs/src/imfs/imfs_load_tar.c +++ b/cpukit/libfs/src/imfs/imfs_load_tar.c @@ -45,7 +45,7 @@ int rtems_tarfs_load( const char *mountpoint, - uint8_t *tar_image, + const void *tar_image, size_t tar_size ) { @@ -57,6 +57,7 @@ int rtems_tarfs_load( size_t len; Untar_HeaderContext ctx; unsigned long ptr; + const uint8_t *image; len = strlen( mountpoint ); if ( len >= sizeof( buf ) - UNTAR_FILE_NAME_SIZE - 2 ) { @@ -82,11 +83,12 @@ int rtems_tarfs_load( } ptr = 0; + image = tar_image; while ( ptr + 512 <= tar_size ) { int retval; - retval = Untar_ProcessHeader( &ctx, (const char *) &tar_image[ ptr ] ); + retval = Untar_ProcessHeader( &ctx, (const char *) &image[ ptr ] ); if ( retval != UNTAR_SUCCESSFUL ) { return -1; } @@ -97,7 +99,7 @@ int rtems_tarfs_load( retval = IMFS_make_linearfile( ctx.file_path, ctx.mode, - &tar_image[ ptr ], + &image[ ptr ], ctx.file_size ); if ( retval != 0 ) { diff --git a/cpukit/libfs/src/imfs/imfs_make_generic_node.c b/cpukit/libfs/src/imfs/imfs_make_generic_node.c index abecf5ec53..73c44e31db 100644 --- a/cpukit/libfs/src/imfs/imfs_make_generic_node.c +++ b/cpukit/libfs/src/imfs/imfs_make_generic_node.c @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> #include <string.h> diff --git a/cpukit/libfs/src/imfs/imfs_make_linfile.c b/cpukit/libfs/src/imfs/imfs_make_linfile.c index 0a81e3848d..30a3e76117 100644 --- a/cpukit/libfs/src/imfs/imfs_make_linfile.c +++ b/cpukit/libfs/src/imfs/imfs_make_linfile.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (C) 2019 embedded brains GmbH + * Copyright (C) 2019 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_memfile.c b/cpukit/libfs/src/imfs/imfs_memfile.c index 769a570ecf..3930fd6ae5 100644 --- a/cpukit/libfs/src/imfs/imfs_memfile.c +++ b/cpukit/libfs/src/imfs/imfs_memfile.c @@ -38,7 +38,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> #include <stdlib.h> #include <string.h> @@ -638,7 +638,6 @@ ssize_t IMFS_memfile_write( fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src ); #endif memcpy( &(*block_ptr)[ 0 ], src, my_length ); - my_length = 0; copied += to_copy; } diff --git a/cpukit/libfs/src/imfs/imfs_mknod.c b/cpukit/libfs/src/imfs/imfs_mknod.c index 17aa1d3802..7c708f9fa8 100644 --- a/cpukit/libfs/src/imfs/imfs_mknod.c +++ b/cpukit/libfs/src/imfs/imfs_mknod.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,7 +41,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> static const IMFS_mknod_control *get_control( const IMFS_mknod_controls *controls, diff --git a/cpukit/libfs/src/imfs/imfs_mount.c b/cpukit/libfs/src/imfs/imfs_mount.c index ba4e258f7a..c35f9f96f4 100644 --- a/cpukit/libfs/src/imfs/imfs_mount.c +++ b/cpukit/libfs/src/imfs/imfs_mount.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_node.c b/cpukit/libfs/src/imfs/imfs_node.c index 91bd89d66c..7d2308fe1d 100644 --- a/cpukit/libfs/src/imfs/imfs_node.c +++ b/cpukit/libfs/src/imfs/imfs_node.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,7 +41,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> IMFS_jnode_t *IMFS_initialize_node( IMFS_jnode_t *node, diff --git a/cpukit/libfs/src/imfs/imfs_node_destroy_default.c b/cpukit/libfs/src/imfs/imfs_node_destroy_default.c index 84f06a30fb..852baaf305 100644 --- a/cpukit/libfs/src/imfs/imfs_node_destroy_default.c +++ b/cpukit/libfs/src/imfs/imfs_node_destroy_default.c @@ -9,7 +9,7 @@ */ /* - * Copyright (C) 2014 embedded brains GmbH (http://www.embedded-brains.de) + * Copyright (C) 2014 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_rename.c b/cpukit/libfs/src/imfs/imfs_rename.c index d925591b32..94876d9217 100644 --- a/cpukit/libfs/src/imfs/imfs_rename.c +++ b/cpukit/libfs/src/imfs/imfs_rename.c @@ -38,7 +38,7 @@ #include "config.h" #endif -#include <rtems/imfs.h> +#include <rtems/imfsimpl.h> #include <string.h> #include <stdlib.h> diff --git a/cpukit/libfs/src/imfs/imfs_rmnod.c b/cpukit/libfs/src/imfs/imfs_rmnod.c index 121283cdd9..ed60baeff3 100644 --- a/cpukit/libfs/src/imfs/imfs_rmnod.c +++ b/cpukit/libfs/src/imfs/imfs_rmnod.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_stat.c b/cpukit/libfs/src/imfs/imfs_stat.c index 260f0ade02..82bf40e07d 100644 --- a/cpukit/libfs/src/imfs/imfs_stat.c +++ b/cpukit/libfs/src/imfs/imfs_stat.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/imfs_unmount.c b/cpukit/libfs/src/imfs/imfs_unmount.c index 448573b63a..b4289e0f9d 100644 --- a/cpukit/libfs/src/imfs/imfs_unmount.c +++ b/cpukit/libfs/src/imfs/imfs_unmount.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/imfs/ioman.c b/cpukit/libfs/src/imfs/ioman.c index a9958b5c82..cc04fd7136 100644 --- a/cpukit/libfs/src/imfs/ioman.c +++ b/cpukit/libfs/src/imfs/ioman.c @@ -13,7 +13,7 @@ * On-Line Applications Research Corporation (OAR). * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/cpukit/libfs/src/jffs2/VERSION b/cpukit/libfs/src/jffs2/VERSION index 7d9c9f41b2..7302f10ef6 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 v6.1..v9.99 -- \ include/uapi/linux/jffs2.h \ fs/jffs2/LICENCE \ fs/jffs2/acl.h \ @@ -26,6 +26,7 @@ generate the relevant patches: fs/jffs2/readinode.c \ fs/jffs2/scan.c \ fs/jffs2/summary.h \ + fs/jffs2/wbuf.c \ fs/jffs2/write.c \ fs/jffs2/xattr.h diff --git a/cpukit/libfs/src/jffs2/include/linux/fs.h b/cpukit/libfs/src/jffs2/include/linux/fs.h index a638e7b6bf..9e40d105dc 100644 --- a/cpukit/libfs/src/jffs2/include/linux/fs.h +++ b/cpukit/libfs/src/jffs2/include/linux/fs.h @@ -34,4 +34,7 @@ struct iattr { time_t ia_ctime; }; +#define SB_RDONLY 1 +#define sb_rdonly(sb) ((sb)->s_flags & SB_RDONLY) + #endif /* __LINUX_FS_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/jffs2.h b/cpukit/libfs/src/jffs2/include/linux/jffs2.h index a18b719f49..637ee4a793 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 */ @@ -128,7 +123,7 @@ struct jffs2_raw_dirent __u8 unused[2]; jint32_t node_crc; jint32_t name_crc; - __u8 name[0]; + __u8 name[]; }; /* The JFFS2 raw inode structure: Used for storage on physical media. */ @@ -160,7 +155,7 @@ struct jffs2_raw_inode jint16_t flags; /* See JFFS2_INO_FLAG_* */ jint32_t data_crc; /* CRC for the (compressed) data. */ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ - __u8 data[0]; + __u8 data[]; }; struct jffs2_raw_xattr { @@ -175,7 +170,7 @@ struct jffs2_raw_xattr { jint16_t value_len; jint32_t data_crc; jint32_t node_crc; - __u8 data[0]; + __u8 data[]; } __attribute__((packed)); struct jffs2_raw_xref @@ -201,7 +196,7 @@ struct jffs2_raw_summary jint32_t padded; /* sum of the size of padding nodes */ jint32_t sum_crc; /* summary information crc */ jint32_t node_crc; /* node crc */ - jint32_t sum[0]; /* inode summary info */ + jint32_t sum[]; /* inode summary info */ }; union jffs2_node_union diff --git a/cpukit/libfs/src/jffs2/include/linux/jiffies.h b/cpukit/libfs/src/jffs2/include/linux/jiffies.h new file mode 100644 index 0000000000..77debb5d27 --- /dev/null +++ b/cpukit/libfs/src/jffs2/include/linux/jiffies.h @@ -0,0 +1 @@ +#include <linux/workqueue.h> diff --git a/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h b/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h index eb5f422116..ed46ce2391 100644 --- a/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h +++ b/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * Copyright 2016 Chris Johns <chrisj@rtems.org> * diff --git a/cpukit/libfs/src/jffs2/include/linux/list.h b/cpukit/libfs/src/jffs2/include/linux/list.h index 4dc8a5a6b7..efb7fb8019 100644 --- a/cpukit/libfs/src/jffs2/include/linux/list.h +++ b/cpukit/libfs/src/jffs2/include/linux/list.h @@ -139,6 +139,14 @@ list_empty( struct list_head *list ) (_ent_) != (_list_); \ (_ent_) = (_ent_)->next ) +/* list_for_each_safe - using _ent_, iterate through list _list_ + while protecting against removal of list elements */ + +#define list_for_each_safe( _ent_, _tmp_, _list_ ) \ + for ( (_ent_) = (_list_)->next, (_tmp_) = (_ent_)->next; \ + (_ent_) != (_list_); \ + (_ent_) = (_tmp_), (_tmp_) = (_ent_)->next ) + /* * list_for_each_entry - this function can be use to iterate over all * items in a list* _list_ with it's head at _head_ and link _item_ diff --git a/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h b/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h index bcf0a9aa70..548201483f 100644 --- a/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h +++ b/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h @@ -6,6 +6,14 @@ #define MTD_FAIL_ADDR_UNKNOWN -1LL +struct mtd_info { + uint32_t oobavail; + uint32_t oobsize; + uint32_t size; + uint32_t erasesize; + uint32_t writesize; +}; + static inline int do_mtd_point(size_t *retlen, void **ebuf) { *retlen = 0; @@ -20,4 +28,33 @@ static inline int do_mtd_point(size_t *retlen, void **ebuf) #define mtd_kmalloc_up_to(a, b) kmalloc(*(b), GFP_KERNEL) +struct mtd_oob_ops { + uint32_t mode; + size_t len; + size_t retlen; + size_t ooblen; + size_t oobretlen; + uint32_t ooboffs; + uint8_t *datbuf; + uint8_t *oobbuf; +}; + +#define EUCLEAN EAGAIN +static inline int mtd_is_bitflip(int err) { return (err == -EUCLEAN); } + +#define mtd_block_isbad(mtd_sp, offset) ({ \ + bool bad; \ + int sc = jffs2_flash_block_is_bad(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), offset, &bad); \ + if (sc) { \ + return sc; \ + } \ + bad; \ +}) +#define mtd_block_markbad(mtd_sp, offset) \ + jffs2_flash_block_mark_bad(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), offset) +#define mtd_write(mtd_sp, ofs, len, retlen, buf) \ + jffs2_flash_direct_write(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), ofs, len, retlen, buf) +#define mtd_read(mtd_sp, ofs, len, retlen, buf) \ + jffs2_flash_direct_read(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), ofs, len, retlen, buf) + #endif /* __LINUX_MTD_MTD_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h b/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h new file mode 100644 index 0000000000..fe47ba6fbf --- /dev/null +++ b/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h @@ -0,0 +1,26 @@ +#ifndef __LINUX_MTD_RAWNAND_H__ +#define __LINUX_MTD_RAWNAND_H__ + +#define mtd_read_oob(mtd_sp, offset, ops) ({ \ + struct jffs2_sb_info *sb_info = RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd); \ + int sc = jffs2_flash_oob_read(sb_info, offset, (ops)->oobbuf, (ops)->ooblen); \ + if (sc) { \ + sc = -EIO; \ + } else { \ + (ops)->oobretlen = (ops)->ooblen; \ + } \ + sc; \ +}) +#define mtd_write_oob(mtd_sp, offset, ops) ({ \ + struct jffs2_sb_info *sb_info = RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd); \ + int sc = jffs2_flash_oob_write(sb_info, offset, (ops)->oobbuf, (ops)->ooblen); \ + if (sc != RTEMS_SUCCESSFUL) { \ + sc = -EIO; \ + } else { \ + (ops)->oobretlen = (ops)->ooblen; \ + } \ + sc; \ +}) +#define MTD_OPS_AUTO_OOB 1 + +#endif /* __LINUX_MTD_RAWNAND_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/mutex.h b/cpukit/libfs/src/jffs2/include/linux/mutex.h index f5e1efb1af..cc82a3f17a 100644 --- a/cpukit/libfs/src/jffs2/include/linux/mutex.h +++ b/cpukit/libfs/src/jffs2/include/linux/mutex.h @@ -26,4 +26,6 @@ static inline void mutex_unlock(struct mutex *m) (void) m; } +#define mutex_is_locked(m) 1 + #endif diff --git a/cpukit/libfs/src/jffs2/include/linux/printk.h b/cpukit/libfs/src/jffs2/include/linux/printk.h new file mode 100644 index 0000000000..515188faa1 --- /dev/null +++ b/cpukit/libfs/src/jffs2/include/linux/printk.h @@ -0,0 +1,6 @@ +#ifndef __LINUX_PRINTK_H__ +#define __LINUX_PRINTK_H__ + +#define no_printk(fmt, ...) do { } while (0) + +#endif /* __LINUX_PRINTK_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/rwsem.h b/cpukit/libfs/src/jffs2/include/linux/rwsem.h index 57bfdf13b7..e59e1cede3 100644 --- a/cpukit/libfs/src/jffs2/include/linux/rwsem.h +++ b/cpukit/libfs/src/jffs2/include/linux/rwsem.h @@ -1,6 +1,16 @@ #ifndef __LINUX_RWSEM_H__ #define __LINUX_RWSEM_H__ -struct rw_semaphore; +struct rw_semaphore {}; + +#define init_rwsem(rwsem) + +#define down_read(rwsem) + +#define down_write(rwsem) + +#define up_read(rwsem) + +#define up_write(rwsem) #endif /* __LINUX_RWSEM_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/slab.h b/cpukit/libfs/src/jffs2/include/linux/slab.h index 532586e210..d664f50dfb 100644 --- a/cpukit/libfs/src/jffs2/include/linux/slab.h +++ b/cpukit/libfs/src/jffs2/include/linux/slab.h @@ -7,6 +7,7 @@ #define kzalloc(x, y) calloc(1, x) #define kmalloc(x, y) malloc(x) +#define kmalloc_array(x, y, z) kmalloc((x * y), z) #define kfree(x) free(x) #define kvfree(x) free(x) #define vmalloc(x) malloc(x) diff --git a/cpukit/libfs/src/jffs2/include/linux/workqueue.h b/cpukit/libfs/src/jffs2/include/linux/workqueue.h index 90d98288af..9811c7cd3e 100644 --- a/cpukit/libfs/src/jffs2/include/linux/workqueue.h +++ b/cpukit/libfs/src/jffs2/include/linux/workqueue.h @@ -1,10 +1,34 @@ #ifndef __LINUX_WORKQUEUE_H__ #define __LINUX_WORKQUEUE_H__ -struct work_struct { } ; +#include <rtems/chain.h> +#include <linux/mutex.h> -#define INIT_WORK(x,y,z) /* */ -#define schedule_work(x) do { } while(0) -#define flush_scheduled_work() do { } while(0) +struct work_struct { rtems_chain_node node; }; + +#define queue_delayed_work(workqueue, delayed_work, delay_ms) ({ \ + jffs2_queue_delayed_work(delayed_work, delay_ms); \ + 0; \ +}) + +#define INIT_DELAYED_WORK(delayed_work, delayed_workqueue_callback) ({ \ + (delayed_work)->callback = delayed_workqueue_callback; \ +}) + +#define msecs_to_jiffies(a) (a) + +typedef void (*work_callback_t)(struct work_struct *work); +struct delayed_work { + struct work_struct work; + struct mutex dw_mutex; + volatile bool pending; + volatile uint64_t execution_time; + work_callback_t callback; + /* Superblock provided for locking */ + struct super_block *sb; +}; + +#define to_delayed_work(work) RTEMS_CONTAINER_OF(work, struct delayed_work, work) +void jffs2_queue_delayed_work(struct delayed_work *work, int delay_ms); #endif /* __LINUX_WORKQUEUE_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/writeback.h b/cpukit/libfs/src/jffs2/include/linux/writeback.h new file mode 100644 index 0000000000..9899205383 --- /dev/null +++ b/cpukit/libfs/src/jffs2/include/linux/writeback.h @@ -0,0 +1 @@ +/* This file intentionally left empty as a stub for wbuf.c */ diff --git a/cpukit/libfs/src/jffs2/src/acl.h b/cpukit/libfs/src/jffs2/src/acl.h index 2e2b5745c3..9d9fb7cf09 100644 --- a/cpukit/libfs/src/jffs2/src/acl.h +++ b/cpukit/libfs/src/jffs2/src/acl.h @@ -22,12 +22,14 @@ 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 -struct posix_acl *jffs2_get_acl(struct inode *inode, int type); -int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type); +struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu); +int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *acl, int type); extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *); extern int jffs2_init_acl_post(struct inode *); diff --git a/cpukit/libfs/src/jffs2/src/build.c b/cpukit/libfs/src/jffs2/src/build.c index d35bc83bbb..12e2ef820b 100644 --- a/cpukit/libfs/src/jffs2/src/build.c +++ b/cpukit/libfs/src/jffs2/src/build.c @@ -417,13 +417,15 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); ret = -EIO; - goto out_free; + goto out_sum_exit; } jffs2_calc_trigger_levels(c); return 0; + out_sum_exit: + jffs2_sum_exit(c); out_free: kvfree(c->blocks); diff --git a/cpukit/libfs/src/jffs2/src/compr.c b/cpukit/libfs/src/jffs2/src/compr.c index a9e821a6a1..19605754de 100644 --- a/cpukit/libfs/src/jffs2/src/compr.c +++ b/cpukit/libfs/src/jffs2/src/compr.c @@ -7,7 +7,7 @@ * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, * University of Szeged, Hungary - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by Arjan van de Ven <arjan@infradead.org> * diff --git a/cpukit/libfs/src/jffs2/src/compr_rtime.c b/cpukit/libfs/src/jffs2/src/compr_rtime.c index c8313ed234..e3c761dcfe 100644 --- a/cpukit/libfs/src/jffs2/src/compr_rtime.c +++ b/cpukit/libfs/src/jffs2/src/compr_rtime.c @@ -44,6 +44,9 @@ uint16_t rtems_jffs2_compressor_rtime_compress( (void) self; + if (*dstlen <= 3) + return JFFS2_COMPR_NONE; + memset(positions,0,sizeof(positions)); while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { diff --git a/cpukit/libfs/src/jffs2/src/debug.h b/cpukit/libfs/src/jffs2/src/debug.h index 6c905b3854..6b4afd4e75 100644 --- a/cpukit/libfs/src/jffs2/src/debug.h +++ b/cpukit/libfs/src/jffs2/src/debug.h @@ -13,6 +13,7 @@ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ +#include <linux/printk.h> #include <linux/sched.h> #ifndef CONFIG_JFFS2_FS_DEBUG @@ -117,73 +118,73 @@ do { \ #ifdef JFFS2_DBG_READINODE_MESSAGES #define dbg_readinode(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_readinode(fmt, ...) +#define dbg_readinode(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif #ifdef JFFS2_DBG_READINODE2_MESSAGES #define dbg_readinode2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_readinode2(fmt, ...) +#define dbg_readinode2(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES #define dbg_fragtree(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fragtree(fmt, ...) +#define dbg_fragtree(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif #ifdef JFFS2_DBG_FRAGTREE2_MESSAGES #define dbg_fragtree2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fragtree2(fmt, ...) +#define dbg_fragtree2(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Directory entry list manilulation debugging messages */ #ifdef JFFS2_DBG_DENTLIST_MESSAGES #define dbg_dentlist(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_dentlist(fmt, ...) +#define dbg_dentlist(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Print the messages about manipulating node_refs */ #ifdef JFFS2_DBG_NODEREF_MESSAGES #define dbg_noderef(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_noderef(fmt, ...) +#define dbg_noderef(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Manipulations with the list of inodes (JFFS2 inocache) */ #ifdef JFFS2_DBG_INOCACHE_MESSAGES #define dbg_inocache(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_inocache(fmt, ...) +#define dbg_inocache(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Summary debugging messages */ #ifdef JFFS2_DBG_SUMMARY_MESSAGES #define dbg_summary(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_summary(fmt, ...) +#define dbg_summary(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* File system build messages */ #ifdef JFFS2_DBG_FSBUILD_MESSAGES #define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fsbuild(fmt, ...) +#define dbg_fsbuild(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Watch the object allocations */ #ifdef JFFS2_DBG_MEMALLOC_MESSAGES #define dbg_memalloc(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_memalloc(fmt, ...) +#define dbg_memalloc(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Watch the XATTR subsystem */ #ifdef JFFS2_DBG_XATTR_MESSAGES #define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_xattr(fmt, ...) +#define dbg_xattr(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* "Sanity" checks */ diff --git a/cpukit/libfs/src/jffs2/src/dir-rtems.c b/cpukit/libfs/src/jffs2/src/dir-rtems.c index 9fa70bcec4..91906bac94 100644 --- a/cpukit/libfs/src/jffs2/src/dir-rtems.c +++ b/cpukit/libfs/src/jffs2/src/dir-rtems.c @@ -6,11 +6,11 @@ * Copyright © 2001-2003 Free Software Foundation, Inc. * Copyright © 2001-2007 Red Hat, Inc. * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -214,6 +214,8 @@ int jffs2_mknod( f = JFFS2_INODE_INFO(inode); + mutex_lock(&f->sem); + inode->i_size = datalen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); diff --git a/cpukit/libfs/src/jffs2/src/erase.c b/cpukit/libfs/src/jffs2/src/erase.c index 9ffffafaea..6966ed9b0a 100644 --- a/cpukit/libfs/src/jffs2/src/erase.c +++ b/cpukit/libfs/src/jffs2/src/erase.c @@ -45,9 +45,9 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n", __func__, jeb->offset, jeb->offset, jeb->offset + c->sector_size); - instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + instr = kzalloc(sizeof(struct erase_info), GFP_KERNEL); if (!instr) { - pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); + pr_warn("kzalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); @@ -59,8 +59,6 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, return; } - memset(instr, 0, sizeof(*instr)); - instr->addr = jeb->offset; instr->len = c->sector_size; @@ -403,7 +401,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/flashio.c b/cpukit/libfs/src/jffs2/src/flashio.c index 7b689a5a7c..6b0a9433fa 100644 --- a/cpukit/libfs/src/jffs2/src/flashio.c +++ b/cpukit/libfs/src/jffs2/src/flashio.c @@ -17,8 +17,23 @@ #include <linux/kernel.h> #include "nodelist.h" +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER int jffs2_flash_read(struct jffs2_sb_info * c, - cyg_uint32 read_buffer_offset, const size_t size, + loff_t read_buffer_offset, const size_t size, + size_t * return_size, unsigned char *write_buffer) +{ + return jffs2_flash_direct_read(c, read_buffer_offset, size, return_size, write_buffer); +} + +int jffs2_flash_write(struct jffs2_sb_info * c, + loff_t write_buffer_offset, size_t size, + size_t * return_size, const unsigned char *read_buffer) +{ + return jffs2_flash_direct_write(c, write_buffer_offset, size, return_size, read_buffer); +} +#endif +int jffs2_flash_direct_read(struct jffs2_sb_info * c, + loff_t read_buffer_offset, const size_t size, size_t * return_size, unsigned char *write_buffer) { const struct super_block *sb = OFNI_BS_2SFFJ(c); @@ -29,9 +44,9 @@ int jffs2_flash_read(struct jffs2_sb_info * c, return (*fc->read)(fc, read_buffer_offset, write_buffer, size); } -int jffs2_flash_write(struct jffs2_sb_info * c, - cyg_uint32 write_buffer_offset, const size_t size, - size_t * return_size, unsigned char *read_buffer) +int jffs2_flash_direct_write(struct jffs2_sb_info * c, + loff_t write_buffer_offset, const size_t size, + size_t * return_size, const unsigned char *read_buffer) { const struct super_block *sb = OFNI_BS_2SFFJ(c); rtems_jffs2_flash_control *fc = sb->s_flash_control; @@ -133,3 +148,55 @@ int jffs2_flash_erase(struct jffs2_sb_info * c, return (*fc->erase)(fc, jeb->offset); } +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER +int jffs2_flash_block_is_bad(struct jffs2_sb_info * c, + cyg_uint32 block_offset, bool *bad) +{ + const struct super_block *sb = OFNI_BS_2SFFJ(c); + rtems_jffs2_flash_control *fc = sb->s_flash_control; + + return (*fc->block_is_bad)(fc, block_offset, bad); +} + +int jffs2_flash_block_mark_bad(struct jffs2_sb_info * c, + cyg_uint32 block_offset) +{ + const struct super_block *sb = OFNI_BS_2SFFJ(c); + rtems_jffs2_flash_control *fc = sb->s_flash_control; + + return (*fc->block_mark_bad)(fc, block_offset); +} + +int jffs2_flash_oob_write(struct jffs2_sb_info * c, + cyg_uint32 block_offset, + uint8_t *oobbuf, + uint32_t ooblen) +{ + const struct super_block *sb = OFNI_BS_2SFFJ(c); + rtems_jffs2_flash_control *fc = sb->s_flash_control; + + return (*fc->oob_write)(fc, block_offset, oobbuf, ooblen); +} + +int jffs2_flash_oob_read(struct jffs2_sb_info * c, + cyg_uint32 block_offset, + uint8_t *oobbuf, + uint32_t ooblen) +{ + const struct super_block *sb = OFNI_BS_2SFFJ(c); + rtems_jffs2_flash_control *fc = sb->s_flash_control; + + return (*fc->oob_read)(fc, block_offset, oobbuf, ooblen); +} + +int jffs2_flash_get_oob_size(struct jffs2_sb_info * c) +{ + const struct super_block *sb = OFNI_BS_2SFFJ(c); + rtems_jffs2_flash_control *fc = sb->s_flash_control; + + if (fc->get_oob_size == NULL) { + return 0; + } + return (*fc->get_oob_size)(fc); +} +#endif diff --git a/cpukit/libfs/src/jffs2/src/fs-rtems.c b/cpukit/libfs/src/jffs2/src/fs-rtems.c index 8bc3d85cc3..029cba6618 100644 --- a/cpukit/libfs/src/jffs2/src/fs-rtems.c +++ b/cpukit/libfs/src/jffs2/src/fs-rtems.c @@ -6,12 +6,12 @@ * Copyright © 2001-2003 Free Software Foundation, Inc. * Copyright © 2001-2007 Red Hat, Inc. * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> - * Copyright © 2013, 2016 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright (C) 2013, 2016 embedded brains GmbH & Co. KG * * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com> * Contributors: David Woodhouse, Nick Garnett, Richard Panton. * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -23,12 +23,14 @@ #include "nodelist.h" #include <linux/pagemap.h> #include <linux/crc32.h> +#include <linux/mtd/mtd.h> #include "compr.h" #include <errno.h> #include <string.h> #include <assert.h> #include <rtems/libio.h> #include <rtems/libio_.h> +#include <rtems/sysinit.h> /* Ensure that the JFFS2 values are identical to the POSIX defines */ @@ -577,6 +579,9 @@ static int rtems_jffs2_ioctl( break; case RTEMS_JFFS2_FORCE_GARBAGE_COLLECTION: eno = -jffs2_garbage_collect_pass(&inode->i_sb->jffs2_sb); + if (!eno) { + eno = -jffs2_flush_wbuf_pad(&inode->i_sb->jffs2_sb); + } break; default: eno = EINVAL; @@ -1049,10 +1054,26 @@ static void rtems_jffs2_freenode(const rtems_filesystem_location_info_t *loc) jffs2_iput(inode); } +static void jffs2_remove_delayed_work(struct delayed_work *dwork); + static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry) { rtems_jffs2_fs_info *fs_info = mt_entry->fs_info; struct _inode *root_i = mt_entry->mt_fs_root->location.node_access; +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + struct jffs2_sb_info *c = JFFS2_SB_INFO(&fs_info->sb); + + /* Remove wbuf delayed work */ + jffs2_remove_delayed_work(&c->wbuf_dwork); + + /* Flush any pending writes */ + if (!sb_rdonly(&fs_info->sb)) { + jffs2_flush_wbuf_gc(c, 0); + jffs2_flush_wbuf_pad(c); + } +#endif + + jffs2_sum_exit(c); icache_evict(root_i, NULL); assert(root_i->i_cache_next == NULL); @@ -1062,6 +1083,12 @@ static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry rtems_jffs2_free_directory_entries(root_i); free(root_i); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + jffs2_nand_flash_cleanup(c); + free(c->mtd); + c->mtd = NULL; +#endif + rtems_jffs2_free_fs_info(fs_info, true); } @@ -1212,6 +1239,127 @@ static int calculate_inocache_hashsize(uint32_t flash_size) return hashsize; } +/* Chain for holding delayed work */ +rtems_chain_control delayed_work_chain; + +/* Lock for protecting the delayed work chain */ +struct mutex delayed_work_mutex; + +/* + * All delayed work structs are initialized and added to the chain during FS + * init. Must be called with no locks held + */ +static void add_delayed_work_to_chain(struct delayed_work *work) +{ + /* Initialize delayed work */ + mutex_init(&work->dw_mutex); + work->pending = false; + _Chain_Initialize_node(&work->work.node); \ + work->callback = NULL; + + mutex_lock(&delayed_work_mutex); + rtems_chain_append_unprotected(&delayed_work_chain, &work->work.node); + mutex_unlock(&delayed_work_mutex); +} + +void jffs2_queue_delayed_work(struct delayed_work *work, int delay_ms) +{ + mutex_lock(&work->dw_mutex); + if (!work->pending) { + work->execution_time = rtems_clock_get_uptime_nanoseconds(); + work->execution_time += delay_ms*1000000; + work->pending = true; + } + mutex_unlock(&work->dw_mutex); +} + +/* Clean up during FS unmount */ +static void jffs2_remove_delayed_work(struct delayed_work *dwork) +{ + mutex_lock(&delayed_work_mutex); + rtems_chain_extract_unprotected(&dwork->work.node); + mutex_unlock(&delayed_work_mutex); + /* Don't run pending delayed work, this will happen during unmount */ +} + +static void process_delayed_work(void) +{ + struct delayed_work* work; + rtems_chain_node* node; + + mutex_lock(&delayed_work_mutex); + + if (rtems_chain_is_empty(&delayed_work_chain)) { + mutex_unlock(&delayed_work_mutex); + return; + } + + node = rtems_chain_first(&delayed_work_chain); + while (!rtems_chain_is_tail(&delayed_work_chain, node)) { + work = (struct delayed_work*) node; + node = rtems_chain_next(node); + + if (!work->pending) { + continue; + } + + if (rtems_clock_get_uptime_nanoseconds() < work->execution_time) { + continue; + } + + mutex_lock(&work->dw_mutex); + work->pending = false; + mutex_unlock(&work->dw_mutex); + + rtems_jffs2_do_lock(work->sb); + work->callback(&work->work); + rtems_jffs2_do_unlock(work->sb); + } + mutex_unlock(&delayed_work_mutex); +} + +/* Task for processing delayed work */ +static rtems_task delayed_work_task( + rtems_task_argument unused +) +{ + (void)unused; + while (1) { + process_delayed_work(); + usleep(1); + } +} + +rtems_id delayed_work_task_id; + +static void jffs2_init_delayed_work_task(void) +{ + /* Initialize chain for delayed work */ + rtems_chain_initialize_empty(&delayed_work_chain); + + /* Initialize lock for delayed work */ + mutex_init(&delayed_work_mutex); + + /* Create task for delayed work */ + rtems_status_code err = rtems_task_create( + rtems_build_name( 'J', 'F', 'F', 'S' ), + jffs2_config.delayed_write_priority, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &delayed_work_task_id + ); + if (err != RTEMS_SUCCESSFUL) { + printk("JFFS2 delayed work task processor creation failed\n"); + } +} + +RTEMS_SYSINIT_ITEM( + jffs2_init_delayed_work_task, + RTEMS_SYSINIT_LIBIO, + RTEMS_SYSINIT_ORDER_MIDDLE +); + int rtems_jffs2_initialize( rtems_filesystem_mount_table_entry_t *mt_entry, const void *data @@ -1235,13 +1383,29 @@ int rtems_jffs2_initialize( err = -ENOMEM; } - sb = &fs_info->sb; - c = JFFS2_SB_INFO(sb); if (err == 0) { + sb = &fs_info->sb; + c = JFFS2_SB_INFO(sb); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + c->wbuf_dwork.sb = sb; + add_delayed_work_to_chain(&c->wbuf_dwork); +#endif + spin_lock_init(&c->erase_completion_lock); + spin_lock_init(&c->inocache_lock); + c->mtd = NULL; rtems_recursive_mutex_init(&sb->s_mutex, RTEMS_FILESYSTEM_TYPE_JFFS2); } + /* Start task for delayed work if it hasn't already been started */ + if (err == 0) { + err = rtems_task_start( delayed_work_task_id, delayed_work_task, 0 ); + /* Task already running from previous mount */ + if (err == RTEMS_INCORRECT_STATE) { + err = 0; + } + } + if (err == 0) { uint32_t blocks = fc->flash_size / fc->block_size; @@ -1263,12 +1427,31 @@ int rtems_jffs2_initialize( sb->s_flash_control = fc; sb->s_compressor_control = jffs2_mount_data->compressor_control; +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + c->mtd = malloc(sizeof(struct mtd_info)); + if (!c->mtd) { + err = -ENOMEM; + } + } + + if (err == 0) { + c->mtd->oobavail = jffs2_flash_get_oob_size(c); + c->mtd->oobsize = c->mtd->oobavail; + c->mtd->size = fc->flash_size; + c->mtd->erasesize = fc->block_size; + c->mtd->writesize = fc->write_size; +#endif c->inocache_hashsize = inocache_hashsize; c->inocache_list = &fs_info->inode_cache[0]; c->sector_size = fc->block_size; c->flash_size = fc->flash_size; c->cleanmarker_size = sizeof(struct jffs2_unknown_node); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + err = jffs2_nand_flash_setup(c); + } + if (err == 0) { +#endif err = jffs2_do_mount_fs(c); } @@ -1296,6 +1479,11 @@ int rtems_jffs2_initialize( return 0; } else { if (fs_info != NULL) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + jffs2_remove_delayed_work(&c->wbuf_dwork); +#endif + free(c->mtd); + c->mtd = NULL; rtems_jffs2_free_fs_info(fs_info, do_mount_fs_was_successful); } else { rtems_jffs2_flash_control_destroy(fc); @@ -1316,30 +1504,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) { @@ -1366,6 +1530,8 @@ static struct _inode *new_inode(struct super_block *sb) inode->i_cache_next = NULL; // Newest inode, about to be cached + mutex_init(&JFFS2_INODE_INFO(inode)->sem); + // Add to the icache for (cached_inode = sb->s_root; cached_inode != NULL; cached_inode = cached_inode->i_cache_next) { @@ -1482,7 +1648,14 @@ void jffs2_iput(struct _inode *i) static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) { - memset(f, 0, sizeof(*f)); + /* These must be set manually to preserve other members */ + f->highest_version = 0; + f->fragtree = RB_ROOT; + f->metadata = NULL; + f->dents = NULL; + f->target = NULL; + f->flags = 0; + f->usercompr = 0; } static void jffs2_clear_inode (struct _inode *inode) @@ -1567,6 +1740,7 @@ static int jffs2_read_inode (struct _inode *inode) c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); + mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); diff --git a/cpukit/libfs/src/jffs2/src/gc.c b/cpukit/libfs/src/jffs2/src/gc.c index f557075ab8..3d0d8c56d9 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_read_folio, NULL); + 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/jffs2_fs_i.h b/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h index 6b6050e9eb..64ff8abc01 100644 --- a/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h +++ b/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h @@ -18,11 +18,11 @@ #include <linux/mutex.h> struct jffs2_inode_info { - /* We need an internal mutex similar to inode->i_mutex. + /* We need an internal mutex similar to inode->i_rwsem. Unfortunately, we can't used the existing one, because either the GC would deadlock, or we'd have to release it before letting GC proceed. Or we'd have to put ugliness - into the GC code so it didn't attempt to obtain the i_mutex + into the GC code so it didn't attempt to obtain the i_rwsem for the inode(s) which are already locked */ struct mutex sem; diff --git a/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h b/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h index 778275f48a..7960f92f85 100644 --- a/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h +++ b/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h @@ -1,3 +1,5 @@ +#include "rtems-jffs2-config.h" + /* * JFFS2 -- Journalling Flash File System, Version 2. * @@ -38,6 +40,7 @@ struct jffs2_mount_opts { * users. This is implemented simply by means of not allowing the * latter users to write to the file system if the amount if the * available space is less then 'rp_size'. */ + bool set_rp_size; unsigned int rp_size; }; @@ -72,6 +75,9 @@ struct jffs2_sb_info { uint32_t bad_size; uint32_t sector_size; uint32_t unchecked_size; +#ifdef __rtems__ + uint32_t obsolete_size; +#endif uint32_t nr_free_blocks; uint32_t nr_erasing_blocks; diff --git a/cpukit/libfs/src/jffs2/src/nodelist.h b/cpukit/libfs/src/jffs2/src/nodelist.h index 143f60dbcb..b30aa5bbf6 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[]; }; /* @@ -351,14 +351,14 @@ static inline struct jffs2_node_frag *frag_last(struct rb_root *root) #define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) -#define frag_erase(frag, list) rb_erase(&frag->rb, list); +#define frag_erase(frag, list) rb_erase(&frag->rb, list) #define tn_next(tn) rb_entry(rb_next(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_prev(tn) rb_entry(rb_prev(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_parent(tn) rb_entry(rb_parent(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_left(tn) rb_entry((tn)->rb.rb_left, struct jffs2_tmp_dnode_info, rb) #define tn_right(tn) rb_entry((tn)->rb.rb_right, struct jffs2_tmp_dnode_info, rb) -#define tn_erase(tn, list) rb_erase(&tn->rb, list); +#define tn_erase(tn, list) rb_erase(&tn->rb, list) #define tn_last(list) rb_entry(rb_last(list), struct jffs2_tmp_dnode_info, rb) #define tn_first(list) rb_entry(rb_first(list), struct jffs2_tmp_dnode_info, rb) diff --git a/cpukit/libfs/src/jffs2/src/os-rtems.h b/cpukit/libfs/src/jffs2/src/os-rtems.h index db1be61e67..4bc6f5df13 100644 --- a/cpukit/libfs/src/jffs2/src/os-rtems.h +++ b/cpukit/libfs/src/jffs2/src/os-rtems.h @@ -2,11 +2,11 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2002-2003 Free Software Foundation, Inc. - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -52,11 +52,10 @@ static inline unsigned int full_name_hash(const void *salt, const unsigned char return hash; } -/* NAND flash not currently supported on RTEMS */ -#define jffs2_can_mark_obsolete(c) (1) +#define container_of(a, b, c) RTEMS_CONTAINER_OF(a, b, c) #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) @@ -101,6 +100,10 @@ struct _inode { struct super_block { struct jffs2_sb_info jffs2_sb; + /* + * If granular locking is ever enabled for JFFS2, the inode cache + * (s_root) needs to be protected due to NAND delayed writes. + */ struct _inode * s_root; rtems_jffs2_flash_control *s_flash_control; rtems_jffs2_compressor_control *s_compressor_control; @@ -108,6 +111,7 @@ struct super_block { unsigned char s_gc_buffer[PAGE_CACHE_SIZE]; // Avoids malloc when user may be under memory pressure rtems_recursive_mutex s_mutex; char s_name_buf[JFFS2_MAX_NAME_LEN]; + uint32_t s_flags; }; #define sleep_on_spinunlock(wq, sl) spin_unlock(sl) @@ -136,9 +140,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) @@ -154,13 +155,15 @@ static inline uint32_t jffs2_to_os_mode (uint32_t jmode) /* flashio.c */ -int jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, - const size_t size, size_t * return_size, unsigned char * write_buffer); -int jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset, - const size_t size, size_t * return_size, unsigned char * read_buffer); +int jffs2_flash_read(struct jffs2_sb_info *c, loff_t read_buffer_offset, + size_t size, size_t * return_size, unsigned char * write_buffer); +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t write_buffer_offset, + size_t size, size_t * return_size, const unsigned char * read_buffer); int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); int jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); +int jffs2_flash_direct_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); // dir-rtems.c struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *name, size_t namelen); @@ -176,8 +179,9 @@ int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsign static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) { } -#ifndef CONFIG_JFFS2_FS_WRITEBUFFER #define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) ) +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER + #define jffs2_can_mark_obsolete(c) (1) #define jffs2_is_writebuffered(c) (0) #define jffs2_cleanmarker_oob(c) (0) @@ -194,9 +198,55 @@ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL #define jffs2_nor_ecc(c) (0) -#else -#error no nand yet -#endif +#else /* CONFIG_JFFS2_FS_WRITEBUFFER */ +/* dirty_writeback_interval is in centiseconds, 500cs == 5s */ +#define dirty_writeback_interval 500 +#define MTD_BIT_WRITEABLE 0x800 +#define jffs2_is_writebuffered(c) (c->wbuf != NULL) + +#define jffs2_can_mark_obsolete(c) (OFNI_BS_2SFFJ(c)->s_flash_control->block_is_bad == NULL) + +#define jffs2_cleanmarker_oob(c) (OFNI_BS_2SFFJ(c)->s_flash_control->block_is_bad != NULL) + +#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) + +/* wbuf.c */ +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); +int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); +int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); +void jffs2_wbuf_timeout(unsigned long data); +void jffs2_wbuf_process(void *data); +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); +int jffs2_nand_flash_setup(struct jffs2_sb_info *c); +void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); +int jffs2_flash_block_is_bad(struct jffs2_sb_info * c, + cyg_uint32 block_offset, + bool *bad); +int jffs2_flash_block_mark_bad(struct jffs2_sb_info * c, + cyg_uint32 block_offset); +int jffs2_flash_oob_write(struct jffs2_sb_info * c, + cyg_uint32 block_offset, + uint8_t *oobbuf, + uint32_t ooblen); +int jffs2_flash_oob_read(struct jffs2_sb_info * c, + cyg_uint32 block_offset, + uint8_t *oobbuf, + uint32_t ooblen); +int jffs2_flash_get_oob_size(struct jffs2_sb_info * c); + +int jffs2_dataflash_setup(struct jffs2_sb_info *c); +void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); +int jffs2_ubivol_setup(struct jffs2_sb_info *c); +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c); + +int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); +void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); +void jffs2_dirty_trigger(struct jffs2_sb_info *c); + +#endif /* CONFIG_JFFS2_FS_WRITEBUFFER */ #ifndef BUG_ON #define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0) diff --git a/cpukit/libfs/src/jffs2/src/readinode.c b/cpukit/libfs/src/jffs2/src/readinode.c index e6c9452c03..9f554f8644 100644 --- a/cpukit/libfs/src/jffs2/src/readinode.c +++ b/cpukit/libfs/src/jffs2/src/readinode.c @@ -693,6 +693,22 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r jffs2_free_full_dirent(fd); return -EIO; } + +#ifdef CONFIG_JFFS2_SUMMARY + /* + * we use CONFIG_JFFS2_SUMMARY because without it, we + * have checked it while mounting + */ + crc = crc32(0, fd->name, rd->nsize); + if (unlikely(crc != je32_to_cpu(rd->name_crc))) { + JFFS2_NOTICE("name CRC failed on dirent node at" + "%#08x: read %#08x,calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + jffs2_mark_node_obsolete(c, ref); + jffs2_free_full_dirent(fd); + return 0; + } +#endif } fd->nhash = full_name_hash(NULL, fd->name, rd->nsize); @@ -1294,7 +1310,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 +1450,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..0e88d81989 100644 --- a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h +++ b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2018 embedded brains Gmbh (http://www.embedded-brains.de) + * Copyright (C) 2018 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,3 +33,5 @@ #define __ECOS 1 #define KBUILD_MODNAME "JFFS2" +#define fallthrough __attribute__((__fallthrough__)) +#define CONFIG_JFFS2_FS_WRITEBUFFER diff --git a/cpukit/libfs/src/jffs2/src/scan.c b/cpukit/libfs/src/jffs2/src/scan.c index 177a0cdd3f..8ac4a40414 100644 --- a/cpukit/libfs/src/jffs2/src/scan.c +++ b/cpukit/libfs/src/jffs2/src/scan.c @@ -139,7 +139,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); ret = -ENOMEM; - goto out; + goto out_buf; } } @@ -264,26 +264,47 @@ 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) ) { +#ifdef __rtems__ + if (c->obsolete_size != c->dirty_size) { +#endif + 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); +#ifdef __rtems__ + pr_notice("nr_erasing_blocks %d, used 0x%x, dirty 0x%x, wasted 0x%x, free 0x%x, erasing 0x%x, bad 0x%x, obsolete 0x%x, unchecked 0x%x\n", + c->nr_erasing_blocks, + c->used_size, + c->dirty_size, + c->wasted_size, + c->free_size, + c->erasing_size, + c->bad_size, + c->obsolete_size, + c->unchecked_size); +#endif ret = -EIO; goto out; } +#ifdef __rtems__ + } +#endif spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); spin_unlock(&c->erase_completion_lock); } ret = 0; out: + jffs2_sum_reset_collected(s); + kfree(s); + out_buf: if (buf_size) kfree(flashbuf); #ifndef __ECOS else mtd_unpoint(c->mtd, 0, c->mtd->size); #endif - kfree(s); return ret; } @@ -530,8 +551,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; + } } } @@ -640,6 +664,9 @@ scan_more: sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node)); +#ifdef __rtems__ + c->obsolete_size += (jeb->offset + c->sector_size - ofs); +#endif if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs))) return err; break; @@ -790,6 +817,9 @@ scan_more: if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); +#ifdef __rtems__ + c->obsolete_size += PAD(je32_to_cpu(node->totlen)); +#endif continue; } @@ -1078,7 +1108,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo memcpy(&fd->name, rd->name, checkedlen); fd->name[checkedlen] = 0; - crc = crc32(0, fd->name, rd->nsize); + crc = crc32(0, fd->name, checkedlen); if (crc != je32_to_cpu(rd->name_crc)) { pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", __func__, ofs, je32_to_cpu(rd->name_crc), crc); diff --git a/cpukit/libfs/src/jffs2/src/summary.h b/cpukit/libfs/src/jffs2/src/summary.h index 60207a2ae9..36d9a12807 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 @@ -194,18 +194,18 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb #define jffs2_sum_active() (0) #define jffs2_sum_init(a) (0) -#define jffs2_sum_exit(a) +#define jffs2_sum_exit(a) do { } while (0) #define jffs2_sum_disable_collecting(a) #define jffs2_sum_is_disabled(a) (0) -#define jffs2_sum_reset_collected(a) +#define jffs2_sum_reset_collected(a) do { } while (0) #define jffs2_sum_add_kvec(a,b,c,d) (0) -#define jffs2_sum_move_collected(a,b) +#define jffs2_sum_move_collected(a,b) do { } while (0) #define jffs2_sum_write_sumnode(a) (0) -#define jffs2_sum_add_padding_mem(a,b) -#define jffs2_sum_add_inode_mem(a,b,c) -#define jffs2_sum_add_dirent_mem(a,b,c) -#define jffs2_sum_add_xattr_mem(a,b,c) -#define jffs2_sum_add_xref_mem(a,b,c) +#define jffs2_sum_add_padding_mem(a,b) do { } while (0) +#define jffs2_sum_add_inode_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_dirent_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_xattr_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_xref_mem(a,b,c) do { } while (0) #define jffs2_sum_scan_sumnode(a,b,c,d,e) (0) #endif /* CONFIG_JFFS2_SUMMARY */ diff --git a/cpukit/libfs/src/jffs2/src/wbuf.c b/cpukit/libfs/src/jffs2/src/wbuf.c new file mode 100644 index 0000000000..04e2b03efc --- /dev/null +++ b/cpukit/libfs/src/jffs2/src/wbuf.c @@ -0,0 +1,1352 @@ +#include "rtems-jffs2-config.h" + +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de> + * + * Created by David Woodhouse <dwmw2@infradead.org> + * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/crc32.h> +#include <linux/mtd/rawnand.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/writeback.h> + +#include "nodelist.h" + +/* For testing write failures */ +#undef BREAKME +#undef BREAKMEHEADER + +#ifdef BREAKME +static unsigned char *brokenbuf; +#endif + +#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) ) +#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) ) + +/* max. erase failures before we mark a block bad */ +#define MAX_ERASE_FAILURES 2 + +struct jffs2_inodirty { + uint32_t ino; + struct jffs2_inodirty *next; +}; + +static struct jffs2_inodirty inodirty_nomem; + +static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) +{ + struct jffs2_inodirty *this = c->wbuf_inodes; + + /* If a malloc failed, consider _everything_ dirty */ + if (this == &inodirty_nomem) + return 1; + + /* If ino == 0, _any_ non-GC writes mean 'yes' */ + if (this && !ino) + return 1; + + /* Look to see if the inode in question is pending in the wbuf */ + while (this) { + if (this->ino == ino) + return 1; + this = this->next; + } + return 0; +} + +static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) +{ + struct jffs2_inodirty *this; + + this = c->wbuf_inodes; + + if (this != &inodirty_nomem) { + while (this) { + struct jffs2_inodirty *next = this->next; + kfree(this); + this = next; + } + } + c->wbuf_inodes = NULL; +} + +static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) +{ + struct jffs2_inodirty *new; + + /* Schedule delayed write-buffer write-out */ + jffs2_dirty_trigger(c); + + if (jffs2_wbuf_pending_for_ino(c, ino)) + return; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + jffs2_dbg(1, "No memory to allocate inodirty. Fallback to all considered dirty\n"); + jffs2_clear_wbuf_ino_list(c); + c->wbuf_inodes = &inodirty_nomem; + return; + } + new->ino = ino; + new->next = c->wbuf_inodes; + c->wbuf_inodes = new; + return; +} + +static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) +{ + struct list_head *this, *next; + static int n; + + if (list_empty(&c->erasable_pending_wbuf_list)) + return; + + list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + jffs2_dbg(1, "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", + jeb->offset); + list_del(this); + if ((jiffies + (n++)) & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + jffs2_dbg(1, "...and adding to erase_pending_list\n"); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + jffs2_dbg(1, "...and adding to erasable_list\n"); + list_add_tail(&jeb->list, &c->erasable_list); + } + } +} + +#define REFILE_NOTEMPTY 0 +#define REFILE_ANYWAY 1 + +static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) +{ + jffs2_dbg(1, "About to refile bad block at %08x\n", jeb->offset); + + /* File the existing block on the bad_used_list.... */ + if (c->nextblock == jeb) + c->nextblock = NULL; + else /* Not sure this should ever happen... need more coffee */ + list_del(&jeb->list); + if (jeb->first_node) { + jffs2_dbg(1, "Refiling block at %08x to bad_used_list\n", + jeb->offset); + list_add(&jeb->list, &c->bad_used_list); + } else { + BUG_ON(allow_empty == REFILE_NOTEMPTY); + /* It has to have had some nodes or we couldn't be here */ + jffs2_dbg(1, "Refiling block at %08x to erase_pending_list\n", + jeb->offset); + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } + + if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { + uint32_t oldfree = jeb->free_size; + + jffs2_link_node_ref(c, jeb, + (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE, + oldfree, NULL); + /* convert to wasted */ + c->wasted_size += oldfree; + jeb->wasted_size += oldfree; + c->dirty_size -= oldfree; + jeb->dirty_size -= oldfree; + } + + jffs2_dbg_dump_block_lists_nolock(c); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); +} + +static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_raw_node_ref *raw, + union jffs2_node_union *node) +{ + struct jffs2_node_frag *frag; + struct jffs2_full_dirent *fd; + + dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n", + node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype)); + + BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 && + je16_to_cpu(node->u.magic) != 0); + + switch (je16_to_cpu(node->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + if (f->metadata && f->metadata->raw == raw) { + dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata); + return &f->metadata->raw; + } + frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset)); + BUG_ON(!frag); + /* Find a frag which refers to the full_dnode we want to modify */ + while (!frag->node || frag->node->raw != raw) { + frag = frag_next(frag); + BUG_ON(!frag); + } + dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node); + return &frag->node->raw; + + case JFFS2_NODETYPE_DIRENT: + for (fd = f->dents; fd; fd = fd->next) { + if (fd->raw == raw) { + dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd); + return &fd->raw; + } + } + BUG(); + + default: + dbg_noderef("Don't care about replacing raw for nodetype %x\n", + je16_to_cpu(node->u.nodetype)); + break; + } + return NULL; +} + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY +static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, + uint32_t ofs) +{ + int ret; + size_t retlen; + char *eccstr; + + ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); + if (ret && ret != -EUCLEAN && ret != -EBADMSG) { + pr_warn("%s(): Read back of page at %08x failed: %d\n", + __func__, c->wbuf_ofs, ret); + return ret; + } else if (retlen != c->wbuf_pagesize) { + pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d\n", + __func__, ofs, retlen, c->wbuf_pagesize); + return -EIO; + } + if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) + return 0; + + if (ret == -EUCLEAN) + eccstr = "corrected"; + else if (ret == -EBADMSG) + eccstr = "correction failed"; + else + eccstr = "OK or unused"; + + pr_warn("Write verify error (ECC %s) at %08x. Wrote:\n", + eccstr, c->wbuf_ofs); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf, c->wbuf_pagesize, 0); + + pr_warn("Read back:\n"); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf_verify, c->wbuf_pagesize, 0); + + return -EIO; +} +#else +#define jffs2_verify_write(c,b,o) (0) +#endif + +/* Recover from failure to write wbuf. Recover the nodes up to the + * wbuf, not the one which we were starting to try to write. */ + +static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb, *new_jeb; + struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL; + size_t retlen; + int ret; + int nr_refile = 0; + unsigned char *buf; + uint32_t start, end, ofs, len; + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + spin_lock(&c->erase_completion_lock); + if (c->wbuf_ofs % c->mtd->erasesize) + jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); + else + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + spin_unlock(&c->erase_completion_lock); + + BUG_ON(!ref_obsolete(jeb->last_node)); + + /* Find the first node to be recovered, by skipping over every + node which ends before the wbuf starts, or which is obsolete. */ + for (next = raw = jeb->first_node; next; raw = next) { + next = ref_next(raw); + + if (ref_obsolete(raw) || + (next && ref_offset(next) <= c->wbuf_ofs)) { + dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw)), + c->wbuf_ofs); + continue; + } + dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw))); + + first_raw = raw; + break; + } + + if (!first_raw) { + /* All nodes were obsolete. Nothing to recover. */ + jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad\n"); + c->wbuf_len = 0; + return; + } + + start = ref_offset(first_raw); + end = ref_offset(jeb->last_node); + nr_refile = 1; + + /* Count the number of refs which need to be copied */ + while ((raw = ref_next(raw)) != jeb->last_node) + nr_refile++; + + dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); + + buf = NULL; + if (start < c->wbuf_ofs) { + /* First affected node was already partially written. + * Attempt to reread the old data into our buffer. */ + + buf = kmalloc(end - start, GFP_KERNEL); + if (!buf) { + pr_crit("Malloc failure in wbuf recovery. Data loss ensues.\n"); + + goto read_failed; + } + + /* Do the read... */ + ret = mtd_read(c->mtd, start, c->wbuf_ofs - start, &retlen, + buf); + + /* ECC recovered ? */ + if ((ret == -EUCLEAN || ret == -EBADMSG) && + (retlen == c->wbuf_ofs - start)) + ret = 0; + + if (ret || retlen != c->wbuf_ofs - start) { + pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.\n"); + + kfree(buf); + buf = NULL; + read_failed: + first_raw = ref_next(first_raw); + nr_refile--; + while (first_raw && ref_obsolete(first_raw)) { + first_raw = ref_next(first_raw); + nr_refile--; + } + + /* If this was the only node to be recovered, give up */ + if (!first_raw) { + c->wbuf_len = 0; + return; + } + + /* It wasn't. Go on and try to recover nodes complete in the wbuf */ + start = ref_offset(first_raw); + dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); + + } else { + /* Read succeeded. Copy the remaining data from the wbuf */ + memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); + } + } + /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. + Either 'buf' contains the data, or we find it in the wbuf */ + + /* ... and get an allocation of space from a shiny new block instead */ + ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.\n"); + kfree(buf); + return; + } + + /* The summary is not recovered, so it must be disabled for this erase block */ + jffs2_sum_disable_collecting(c->summary); + + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile); + if (ret) { + pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.\n"); + kfree(buf); + return; + } + + ofs = write_ofs(c); + + if (end-start >= c->wbuf_pagesize) { + /* Need to do another write immediately, but it's possible + that this is just because the wbuf itself is completely + full, and there's nothing earlier read back from the + flash. Hence 'buf' isn't necessarily what we're writing + from. */ + unsigned char *rewrite_buf = buf?:c->wbuf; + uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); + + jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover\n", + towrite, ofs); + +#ifdef BREAKMEHEADER + static int breakme; + if (breakme++ == 20) { + pr_notice("Faking write error at 0x%08x\n", ofs); + breakme = 0; + mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf); + ret = -EIO; + } else +#endif + ret = mtd_write(c->mtd, ofs, towrite, &retlen, + rewrite_buf); + + if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { + /* Argh. We tried. Really we did. */ + pr_crit("Recovery of wbuf failed due to a second write error\n"); + kfree(buf); + + if (retlen) + jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL); + + return; + } + pr_notice("Recovery of wbuf succeeded to %08x\n", ofs); + + c->wbuf_len = (end - start) - towrite; + c->wbuf_ofs = ofs + towrite; + memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); + /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ + } else { + /* OK, now we're left with the dregs in whichever buffer we're using */ + if (buf) { + memcpy(c->wbuf, buf, end-start); + } else { + memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); + } + c->wbuf_ofs = ofs; + c->wbuf_len = end - start; + } + + /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ + new_jeb = &c->blocks[ofs / c->sector_size]; + + spin_lock(&c->erase_completion_lock); + for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) { + uint32_t rawlen = ref_totlen(c, jeb, raw); + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *new_ref; + struct jffs2_raw_node_ref **adjust_ref = NULL; + struct jffs2_inode_info *f = NULL; + + jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x\n", + rawlen, ref_offset(raw), ref_flags(raw), ofs); + + ic = jffs2_raw_ref_to_ic(raw); + + /* Ick. This XATTR mess should be fixed shortly... */ + if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) { + struct jffs2_xattr_datum *xd = (void *)ic; + BUG_ON(xd->node != raw); + adjust_ref = &xd->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) { + struct jffs2_xattr_datum *xr = (void *)ic; + BUG_ON(xr->node != raw); + adjust_ref = &xr->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) { + struct jffs2_raw_node_ref **p = &ic->nodes; + + /* Remove the old node from the per-inode list */ + while (*p && *p != (void *)ic) { + if (*p == raw) { + (*p) = (raw->next_in_ino); + raw->next_in_ino = NULL; + break; + } + p = &((*p)->next_in_ino); + } + + if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) { + /* If it's an in-core inode, then we have to adjust any + full_dirent or full_dnode structure to point to the + new version instead of the old */ + f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink); + if (IS_ERR(f)) { + /* Should never happen; it _must_ be present */ + JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n", + ic->ino, PTR_ERR(f)); + BUG(); + } + /* We don't lock f->sem. There's a number of ways we could + end up in here with it already being locked, and nobody's + going to modify it on us anyway because we hold the + alloc_sem. We're only changing one ->raw pointer too, + which we can get away with without upsetting readers. */ + adjust_ref = jffs2_incore_replace_raw(c, f, raw, + (void *)(buf?:c->wbuf) + (ref_offset(raw) - start)); + } else if (unlikely(ic->state != INO_STATE_PRESENT && + ic->state != INO_STATE_CHECKEDABSENT && + ic->state != INO_STATE_GC)) { + JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state); + BUG(); + } + } + + new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic); + + if (adjust_ref) { + BUG_ON(*adjust_ref != raw); + *adjust_ref = new_ref; + } + if (f) + jffs2_gc_release_inode(c, f); + + if (!ref_obsolete(raw)) { + jeb->dirty_size += rawlen; + jeb->used_size -= rawlen; + c->dirty_size += rawlen; + c->used_size -= rawlen; + raw->flash_offset = ref_offset(raw) | REF_OBSOLETE; + BUG_ON(raw->next_in_ino); + } + ofs += rawlen; + } + + kfree(buf); + + /* Fix up the original jeb now it's on the bad_list */ + if (first_raw == jeb->first_node) { + jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list\n", + jeb->offset); + list_move(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + + jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); + + spin_unlock(&c->erase_completion_lock); + + jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", + c->wbuf_ofs, c->wbuf_len); + +} + +/* Meaning of pad argument: + 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. + 1: Pad, do not adjust nextblock free_size + 2: Pad, adjust nextblock free_size +*/ +#define NOPAD 0 +#define PAD_NOACCOUNT 1 +#define PAD_ACCOUNTING 2 + +static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) +{ + struct jffs2_eraseblock *wbuf_jeb; + int ret; + size_t retlen; + + /* Nothing to do if not write-buffering the flash. In particular, we shouldn't + del_timer() the timer we never initialised. */ + if (!jffs2_is_writebuffered(c)) + return 0; + + if (!mutex_is_locked(&c->alloc_sem)) { + pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!\n"); + BUG(); + } + + if (!c->wbuf_len) /* already checked c->wbuf above */ + return 0; + + wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1)) + return -ENOMEM; + + /* claim remaining space on the page + this happens, if we have a change to a new block, + or if fsync forces us to flush the writebuffer. + if we have a switch to next page, we will not have + enough remaining space for this. + */ + if (pad ) { + c->wbuf_len = PAD(c->wbuf_len); + + /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR + with 8 byte page size */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); + + if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { + struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); + padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); + padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); + padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); + } + } + /* else jffs2_flash_writev has actually filled in the rest of the + buffer for us, and will deal with the node refs etc. later. */ + +#ifdef BREAKME + static int breakme; + if (breakme++ == 20) { + pr_notice("Faking write error at 0x%08x\n", c->wbuf_ofs); + breakme = 0; + mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, + brokenbuf); + ret = -EIO; + } else +#endif + + ret = mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, + &retlen, c->wbuf); + + if (ret) { + pr_warn("jffs2_flush_wbuf(): Write failed with %d\n", ret); + goto wfail; + } else if (retlen != c->wbuf_pagesize) { + pr_warn("jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", + retlen, c->wbuf_pagesize); + ret = -EIO; + goto wfail; + } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) { + wfail: + jffs2_wbuf_recover(c); + + return ret; + } + + /* Adjust free size of the block if we padded. */ + if (pad) { + uint32_t waste = c->wbuf_pagesize - c->wbuf_len; + + jffs2_dbg(1, "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", + (wbuf_jeb == c->nextblock) ? "next" : "", + wbuf_jeb->offset); + + /* wbuf_pagesize - wbuf_len is the amount of space that's to be + padded. If there is less free space in the block than that, + something screwed up */ + if (wbuf_jeb->free_size < waste) { + pr_crit("jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", + c->wbuf_ofs, c->wbuf_len, waste); + pr_crit("jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", + wbuf_jeb->offset, wbuf_jeb->free_size); + BUG(); + } + + spin_lock(&c->erase_completion_lock); + + jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL); + /* FIXME: that made it count as dirty. Convert to wasted */ + wbuf_jeb->dirty_size -= waste; + c->dirty_size -= waste; + wbuf_jeb->wasted_size += waste; + c->wasted_size += waste; + } else + spin_lock(&c->erase_completion_lock); + + /* Stick any now-obsoleted blocks on the erase_pending_list */ + jffs2_refile_wbuf_blocks(c); + jffs2_clear_wbuf_ino_list(c); + spin_unlock(&c->erase_completion_lock); + + memset(c->wbuf,0xff,c->wbuf_pagesize); + /* adjust write buffer offset, else we get a non contiguous write bug */ + c->wbuf_ofs += c->wbuf_pagesize; + c->wbuf_len = 0; + return 0; +} + +/* Trigger garbage collection to flush the write-buffer. + If ino arg is zero, do it if _any_ real (i.e. not GC) writes are + outstanding. If ino arg non-zero, do it only if a write for the + given inode is outstanding. */ +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) +{ + uint32_t old_wbuf_ofs; + uint32_t old_wbuf_len; + int ret = 0; + + jffs2_dbg(1, "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino); + + if (!c->wbuf) + return 0; + + mutex_lock(&c->alloc_sem); + if (!jffs2_wbuf_pending_for_ino(c, ino)) { + jffs2_dbg(1, "Ino #%d not pending in wbuf. Returning\n", ino); + mutex_unlock(&c->alloc_sem); + return 0; + } + + old_wbuf_ofs = c->wbuf_ofs; + old_wbuf_len = c->wbuf_len; + + if (c->unchecked_size) { + /* GC won't make any progress for a while */ + jffs2_dbg(1, "%s(): padding. Not finished checking\n", + __func__); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); + } else while (old_wbuf_len && + old_wbuf_ofs == c->wbuf_ofs) { + + mutex_unlock(&c->alloc_sem); + + jffs2_dbg(1, "%s(): calls gc pass\n", __func__); + + ret = jffs2_garbage_collect_pass(c); + if (ret) { + /* GC failed. Flush it with padding instead */ + mutex_lock(&c->alloc_sem); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); + break; + } + mutex_lock(&c->alloc_sem); + } + + jffs2_dbg(1, "%s(): ends...\n", __func__); + + mutex_unlock(&c->alloc_sem); + return ret; +} + +/* Pad write-buffer to end and write it, wasting space. */ +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) +{ + int ret; + + if (!c->wbuf) + return 0; + + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + /* retry - maybe wbuf recover left some data in wbuf. */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + up_write(&c->wbuf_sem); + + return ret; +} + +static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf, + size_t len) +{ + if (len && !c->wbuf_len && (len >= c->wbuf_pagesize)) + return 0; + + if (len > (c->wbuf_pagesize - c->wbuf_len)) + len = c->wbuf_pagesize - c->wbuf_len; + memcpy(c->wbuf + c->wbuf_len, buf, len); + c->wbuf_len += (uint32_t) len; + return len; +} + +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, + unsigned long count, loff_t to, size_t *retlen, + uint32_t ino) +{ + struct jffs2_eraseblock *jeb; + size_t wbuf_retlen, donelen = 0; + uint32_t outvec_to = to; + int ret, invec; + + /* If not writebuffered flash, don't bother */ + if (!jffs2_is_writebuffered(c)) + return jffs2_flash_direct_writev(c, invecs, count, to, retlen); + + down_write(&c->wbuf_sem); + + /* If wbuf_ofs is not initialized, set it to target address */ + if (c->wbuf_ofs == 0xFFFFFFFF) { + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + memset(c->wbuf,0xff,c->wbuf_pagesize); + } + + /* + * Sanity checks on target address. It's permitted to write + * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to + * write at the beginning of a new erase block. Anything else, + * and you die. New block starts at xxx000c (0-b = block + * header) + */ + if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { + /* It's a write to a new block */ + if (c->wbuf_len) { + jffs2_dbg(1, "%s(): to 0x%lx causes flush of wbuf at 0x%08x\n", + __func__, (unsigned long)to, c->wbuf_ofs); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + if (ret) + goto outerr; + } + /* set pointer to new block */ + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + } + + if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { + /* We're not writing immediately after the writebuffer. Bad. */ + pr_crit("%s(): Non-contiguous write to %08lx\n", + __func__, (unsigned long)to); + if (c->wbuf_len) + pr_crit("wbuf was previously %08x-%08x\n", + c->wbuf_ofs, c->wbuf_ofs + c->wbuf_len); + BUG(); + } + + /* adjust alignment offset */ + if (c->wbuf_len != PAGE_MOD(to)) { + c->wbuf_len = PAGE_MOD(to); + /* take care of alignment to next page */ + if (!c->wbuf_len) { + c->wbuf_len = c->wbuf_pagesize; + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + } + + for (invec = 0; invec < count; invec++) { + int vlen = invecs[invec].iov_len; + uint8_t *v = invecs[invec].iov_base; + + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); + + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; + donelen += wbuf_retlen; + v += wbuf_retlen; + + if (vlen >= c->wbuf_pagesize) { + ret = mtd_write(c->mtd, outvec_to, PAGE_DIV(vlen), + &wbuf_retlen, v); + if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen)) + goto outfile; + + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; + c->wbuf_ofs = outvec_to; + donelen += wbuf_retlen; + v += wbuf_retlen; + } + + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + + outvec_to += wbuf_retlen; + donelen += wbuf_retlen; + } + + /* + * If there's a remainder in the wbuf and it's a non-GC write, + * remember that the wbuf affects this ino + */ + *retlen = donelen; + + if (jffs2_sum_active()) { + int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to); + if (res) + return res; + } + + if (c->wbuf_len && ino) + jffs2_wbuf_dirties_inode(c, ino); + + ret = 0; + up_write(&c->wbuf_sem); + return ret; + +outfile: + /* + * At this point we have no problem, c->wbuf is empty. However + * refile nextblock to avoid writing again to same address. + */ + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[outvec_to / c->sector_size]; + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + + spin_unlock(&c->erase_completion_lock); + +outerr: + *retlen = 0; + up_write(&c->wbuf_sem); + return ret; +} + +/* + * This is the entry for flash write. + * Check, if we work on NAND FLASH, if so build an kvec and write it via vritev +*/ +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf) +{ + struct kvec vecs[1]; + + if (!jffs2_is_writebuffered(c)) + return jffs2_flash_direct_write(c, ofs, len, retlen, buf); + + vecs[0].iov_base = (unsigned char *) buf; + vecs[0].iov_len = len; + return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); +} + +/* + Handle readback from writebuffer and ECC failure return +*/ +int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +{ + loff_t orbf = 0, owbf = 0, lwbf = 0; + int ret; + + if (!jffs2_is_writebuffered(c)) + return mtd_read(c->mtd, ofs, len, retlen, buf); + + /* Read flash */ + down_read(&c->wbuf_sem); + ret = mtd_read(c->mtd, ofs, len, retlen, buf); + + if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) { + if (ret == -EBADMSG) + pr_warn("mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", + len, ofs); + /* + * We have the raw data without ECC correction in the buffer, + * maybe we are lucky and all data or parts are correct. We + * check the node. If data are corrupted node check will sort + * it out. We keep this block, it will fail on write or erase + * and the we mark it bad. Or should we do that now? But we + * should give him a chance. Maybe we had a system crash or + * power loss before the ecc write or a erase was completed. + * So we return success. :) + */ + ret = 0; + } + + /* if no writebuffer available or write buffer empty, return */ + if (!c->wbuf_pagesize || !c->wbuf_len) + goto exit; + + /* if we read in a different block, return */ + if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) + goto exit; + + if (ofs >= c->wbuf_ofs) { + owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ + if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ + goto exit; + lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ + if (lwbf > len) + lwbf = len; + } else { + orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ + if (orbf > len) /* is write beyond write buffer ? */ + goto exit; + lwbf = len - orbf; /* number of bytes to copy */ + if (lwbf > c->wbuf_len) + lwbf = c->wbuf_len; + } + if (lwbf > 0) + memcpy(buf+orbf,c->wbuf+owbf,lwbf); + +exit: + up_read(&c->wbuf_sem); + return ret; +} + +#define NR_OOB_SCAN_PAGES 4 + +/* For historical reasons we use only 8 bytes for OOB clean marker */ +#define OOB_CM_SIZE 8 + +static const struct jffs2_unknown_node oob_cleanmarker = +{ + .magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = constant_cpu_to_je32(8) +}; + +/* + * Check, if the out of band area is empty. This function knows about the clean + * marker and if it is present in OOB, treats the OOB as empty anyway. + */ +int jffs2_check_oob_empty(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int mode) +{ + int i, ret; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + struct mtd_oob_ops ops = { }; + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; + ops.oobbuf = c->oobbuf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_read_oob(c->mtd, jeb->offset, &ops); + if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) { + pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret || mtd_is_bitflip(ret)) + ret = -EIO; + return ret; + } + + for(i = 0; i < ops.ooblen; i++) { + if (mode && i < cmlen) + /* Yeah, we know about the cleanmarker */ + continue; + + if (ops.oobbuf[i] != 0xFF) { + jffs2_dbg(2, "Found %02x at %x in OOB for " + "%08x\n", ops.oobbuf[i], i, jeb->offset); + return 1; + } + } + + return 0; +} + +/* + * Check for a valid cleanmarker. + * Returns: 0 if a valid cleanmarker was found + * 1 if no cleanmarker was found + * negative error code if an error occurred + */ +int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + struct mtd_oob_ops ops = { }; + int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = cmlen; + ops.oobbuf = c->oobbuf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_read_oob(c->mtd, jeb->offset, &ops); + if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) { + pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret || mtd_is_bitflip(ret)) + ret = -EIO; + return ret; + } + + return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); +} + +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + int ret; + struct mtd_oob_ops ops = { }; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = cmlen; + ops.oobbuf = (uint8_t *)&oob_cleanmarker; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_write_oob(c->mtd, jeb->offset, &ops); + if (ret || ops.oobretlen != ops.ooblen) { + pr_err("cannot write OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; + return ret; + } + + return 0; +} + +/* + * On NAND we try to mark this block bad. If the block was erased more + * than MAX_ERASE_FAILURES we mark it finally bad. + * Don't care about failures. This block remains on the erase-pending + * or badblock list as long as nobody manipulates the flash with + * a bootloader or something like that. + */ + +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) +{ + int ret; + + /* if the count is < max, we try to write the counter to the 2nd page oob area */ + if( ++jeb->bad_count < MAX_ERASE_FAILURES) + return 0; + + pr_warn("marking eraseblock at %08x as bad\n", bad_offset); + ret = mtd_block_markbad(c->mtd, bad_offset); + + if (ret) { + jffs2_dbg(1, "%s(): Write failed for block at %08x: error %d\n", + __func__, jeb->offset, ret); + return ret; + } + return 1; +} + +static struct jffs2_sb_info *work_to_sb(struct work_struct *work) +{ + struct delayed_work *dwork; + + dwork = to_delayed_work(work); + return container_of(dwork, struct jffs2_sb_info, wbuf_dwork); +} + +static void delayed_wbuf_sync(struct work_struct *work) +{ + struct jffs2_sb_info *c = work_to_sb(work); + struct super_block *sb = OFNI_BS_2SFFJ(c); + + if (!sb_rdonly(sb)) { + jffs2_dbg(1, "%s()\n", __func__); + jffs2_flush_wbuf_gc(c, 0); + } +} + +void jffs2_dirty_trigger(struct jffs2_sb_info *c) +{ + struct super_block *sb = OFNI_BS_2SFFJ(c); + unsigned long delay; + + if (sb_rdonly(sb)) + return; + + delay = msecs_to_jiffies(dirty_writeback_interval * 10); + if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay)) + jffs2_dbg(1, "%s()\n", __func__); +} + +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +{ + if (!c->mtd->oobsize) + return 0; + + /* Cleanmarker is out-of-band, so inline size zero */ + c->cleanmarker_size = 0; + + if (c->mtd->oobavail == 0) { + pr_err("inconsistent device description\n"); + return -EINVAL; + } + + jffs2_dbg(1, "using OOB on NAND\n"); + + c->oobavail = c->mtd->oobavail; + + /* Initialise write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + c->oobbuf = kmalloc_array(NR_OOB_SCAN_PAGES, c->oobavail, GFP_KERNEL); + if (!c->oobbuf) { + kfree(c->wbuf); + return -ENOMEM; + } + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif + return 0; +} + +void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) +{ +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); + kfree(c->oobbuf); +} + +int jffs2_dataflash_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; /* No cleanmarkers needed */ + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + c->wbuf_pagesize = c->mtd->erasesize; + + /* Find a suitable c->sector_size + * - Not too much sectors + * - Sectors have to be at least 4 K + some bytes + * - All known dataflashes have erase sizes of 528 or 1056 + * - we take at least 8 eraseblocks and want to have at least 8K size + * - The concatenation should be a power of 2 + */ + + c->sector_size = 8 * c->mtd->erasesize; + + while (c->sector_size < 8192) { + c->sector_size *= 2; + } + + /* It may be necessary to adjust the flash size */ + c->flash_size = c->mtd->size; + + if ((c->flash_size % c->sector_size) != 0) { + c->flash_size = (c->flash_size / c->sector_size) * c->sector_size; + pr_warn("flash size adjusted to %dKiB\n", c->flash_size); + } + + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->wbuf); + return -ENOMEM; + } +#endif + + pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n", + c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); +} + +int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker currently occupies whole programming regions, + * either one or 2 for 8Byte STMicro flashes. */ + c->cleanmarker_size = max(16u, c->mtd->writesize); + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->wbuf); + return -ENOMEM; + } +#endif + return 0; +} + +void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); +} + +int jffs2_ubivol_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; + + if (c->mtd->writesize == 1) + /* We do not need write-buffer */ + return 0; + + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n", + c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} diff --git a/cpukit/libfs/src/rfs/rtems-rfs-rtems.c b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c index 4db48a8422..58c082f2ee 100644 --- a/cpukit/libfs/src/rfs/rtems-rfs-rtems.c +++ b/cpukit/libfs/src/rfs/rtems-rfs-rtems.c @@ -12,7 +12,7 @@ * COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org> * * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. + * Copyright (c) 2012 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions |