diff --git a/cpukit/include/rtems/libio.h b/cpukit/include/rtems/libio.h index 5424a2a03c..e73aa01fe2 100644 --- a/cpukit/include/rtems/libio.h +++ b/cpukit/include/rtems/libio.h @@ -18,6 +18,8 @@ * Modifications to support reference counting in the file system are * Copyright (C) 2012 embedded brains GmbH & Co. KG * + * Copyright (C) 2025 Contemporary Software + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -1378,18 +1380,23 @@ typedef struct { } rtems_libio_ioctl_args_t; /** - * @name Flag Values + * @name Flag Values and Masks */ /**@{**/ -#define LIBIO_FLAGS_NO_DELAY 0x0001U /* return immediately if no data */ -#define LIBIO_FLAGS_READ 0x0002U /* reading */ -#define LIBIO_FLAGS_WRITE 0x0004U /* writing */ -#define LIBIO_FLAGS_OPEN 0x0100U /* device is open */ -#define LIBIO_FLAGS_APPEND 0x0200U /* all writes append */ -#define LIBIO_FLAGS_CLOSE_ON_EXEC 0x0800U /* close on process exec() */ -#define LIBIO_FLAGS_READ_WRITE (LIBIO_FLAGS_READ | LIBIO_FLAGS_WRITE) -#define LIBIO_FLAGS_REFERENCE_INC 0x1000U +#define LIBIO_FLAGS_FREE 0x0001U /* on the free list */ +#define LIBIO_FLAGS_NO_DELAY 0x0002U /* return immediately if no data */ +#define LIBIO_FLAGS_READ 0x0004U /* reading */ +#define LIBIO_FLAGS_WRITE 0x0008U /* writing */ +#define LIBIO_FLAGS_OPEN 0x0100U /* device is open */ +#define LIBIO_FLAGS_APPEND 0x0200U /* all writes append */ +#define LIBIO_FLAGS_CLOSE_BUSY 0x0400U /* close with refs held */ +#define LIBIO_FLAGS_CLOSE_ON_EXEC 0x0800U /* close on process exec() */ +#define LIBIO_FLAGS_REFERENCE_INC 0x1000U +/* masks */ +#define LIBIO_FLAGS_READ_WRITE (LIBIO_FLAGS_READ | LIBIO_FLAGS_WRITE) +#define LIBIO_FLAGS_FLAGS_MASK (LIBIO_FLAGS_REFERENCE_INC - 1U) +#define LIBIO_FLAGS_REFERENCE_MASK (~LIBIO_FLAGS_FLAGS_MASK) /** @} */ @@ -1398,6 +1405,20 @@ static inline unsigned int rtems_libio_iop_flags( const rtems_libio_t *iop ) return _Atomic_Load_uint( &iop->flags, ATOMIC_ORDER_RELAXED ); } +/** + * @brief Returns true if the iop is a bad file descriptor, otherwise + * returns false. A bad file descriptor means free or not open. + * + * @param[in] flags The flags. + */ +static inline unsigned int rtems_libio_iop_flags_bad_fd( + const unsigned int flags +) +{ + return ( ( flags & LIBIO_FLAGS_FREE ) != 0 ) + || ( ( flags & LIBIO_FLAGS_OPEN ) == 0 ); +} + /** * @brief Returns true if this is a no delay iop, otherwise returns false. * @@ -1428,6 +1449,16 @@ static inline bool rtems_libio_iop_is_writeable( const rtems_libio_t *iop ) return ( rtems_libio_iop_flags( iop ) & LIBIO_FLAGS_WRITE ) != 0; } +/** + * @brief Returns true if the iop is open, otherwise returns false. + * + * @param[in] iop The iop. + */ +static inline bool rtems_libio_iop_is_open( const rtems_libio_t *iop ) +{ + return ( rtems_libio_iop_flags( iop ) & LIBIO_FLAGS_OPEN ) != 0; +} + /** * @brief Returns true if this is an append iop, otherwise returns false. * @@ -1438,6 +1469,28 @@ static inline bool rtems_libio_iop_is_append( const rtems_libio_t *iop ) return ( rtems_libio_iop_flags( iop ) & LIBIO_FLAGS_APPEND ) != 0; } +/** + * @brief Returns true if the iop is held, otherwise returns false. + * + * @param[in] iop The iop. + */ +static inline bool rtems_libio_iop_is_held( const rtems_libio_t *iop ) +{ + const unsigned int ref_count = + rtems_libio_iop_flags( iop ) & LIBIO_FLAGS_REFERENCE_MASK; + return ref_count != 0; +} + +/** + * @brief Returns true if the iop is free, otherwise returns false. + * + * @param[in] iop The iop. + */ +static inline bool rtems_libio_iop_is_free( const rtems_libio_t *iop ) +{ + return ( rtems_libio_iop_flags( iop ) & LIBIO_FLAGS_FREE ) != 0; +} + /** * @name External I/O Handlers */ diff --git a/cpukit/include/rtems/libio_.h b/cpukit/include/rtems/libio_.h index cc72fb4074..21a57cdc4f 100644 --- a/cpukit/include/rtems/libio_.h +++ b/cpukit/include/rtems/libio_.h @@ -103,6 +103,63 @@ extern rtems_filesystem_mount_table_entry_t rtems_filesystem_null_mt_entry; */ extern rtems_filesystem_global_location_t rtems_filesystem_global_location_null; +/* + * File Descriptor Routine Prototypes + */ + +/** + * This routine searches the IOP Table for an unused entry. If it + * finds one, it returns it. Otherwise, it returns NULL. + */ +rtems_libio_t *rtems_libio_allocate(void); + +/** + * Convert UNIX fnctl(2) flags to ones that RTEMS drivers understand + */ +unsigned int rtems_libio_from_fcntl_flags( int fcntl_flags ); + +/** + * Convert RTEMS internal flags to UNIX fnctl(2) flags + */ +int rtems_libio_to_fcntl_flags( unsigned int flags ); + +/** + * This routine frees the resources associated with an IOP (file + * descriptor) and clears the slot in the IOP Table. No checks are + * made on the state of the IOP. + */ +void rtems_libio_free_iop( + rtems_libio_t *iop +); + +/** + * This routine frees the resources associated with an IOP (file + * descriptor) and clears the slot in the IOP Table. The IOP has to + * close (open flag not set) and no references held or the call will + * ignore the request. + */ +static inline void rtems_libio_free( + rtems_libio_t *iop +) +{ + /* + * The IOP cannot be open and there can be no references held for it + * to be returned to the free list. + * + * Note, the open flag indicates the user owns the fd that indexes + * the iop so consider it an indirect reference. We cannot return + * the iop to the free list while the user owns the fd. + * + * Read the flags once as it is an atomic and we need to test 2 + * flags. No convenience call as this is the only case we have. + */ + const unsigned int flags = rtems_libio_iop_flags( iop ); + if ( ( ( flags & LIBIO_FLAGS_OPEN ) == 0 ) + && ( ( flags & LIBIO_FLAGS_REFERENCE_MASK ) == 0 ) ) { + rtems_libio_free_iop( iop ); + } +} + /** * @brief Sets the specified flags in the iop. * @@ -184,6 +241,7 @@ static inline void rtems_libio_iop_drop( rtems_libio_t *iop ) _Assert( flags >= LIBIO_FLAGS_REFERENCE_INC ); desired = flags - LIBIO_FLAGS_REFERENCE_INC; + success = _Atomic_Compare_exchange_uint( &iop->flags, &flags, @@ -199,6 +257,8 @@ static inline void rtems_libio_iop_drop( rtems_libio_t *iop ) ATOMIC_ORDER_RELEASE ); #endif + /* free the IOP is not open or held */ + rtems_libio_free( iop ); } /* @@ -218,56 +278,83 @@ static inline void rtems_libio_iop_drop( rtems_libio_t *iop ) */ #define rtems_libio_check_is_open(_iop) \ - do { \ - if ((rtems_libio_iop_flags(_iop) & LIBIO_FLAGS_OPEN) == 0) { \ - errno = EBADF; \ - return -1; \ - } \ + do { \ + if (rtems_libio_iop_is_open(_iop)) { \ + errno = EBADF; \ + return -1; \ + } \ } while (0) /** - * @brief Macro to get the iop for the specified file descriptor. + * @brief Function to get the iop for the specified file descriptor. * * Checks that the file descriptor is in the valid range and open. */ +static inline int rtems_libio_get_iop( int fd, rtems_libio_t **iop ) +{ + unsigned int flags; + if ( (uint32_t) ( fd ) >= rtems_libio_number_iops ) { + return EBADF; + } + *iop = rtems_libio_iop( fd ); + flags = rtems_libio_iop_hold( *iop ); + if ( rtems_libio_iop_flags_bad_fd( flags ) ) { + rtems_libio_iop_drop( *iop ); + return EBADF; + } + return 0; +} + +/** + * @brief Macro to get the iop for the specified file descriptor. + */ #define LIBIO_GET_IOP( _fd, _iop ) \ do { \ - unsigned int _flags; \ - if ( (uint32_t) ( _fd ) >= rtems_libio_number_iops ) { \ - rtems_set_errno_and_return_minus_one( EBADF ); \ - } \ - _iop = rtems_libio_iop( _fd ); \ - _flags = rtems_libio_iop_hold( _iop ); \ - if ( ( _flags & LIBIO_FLAGS_OPEN ) == 0 ) { \ - rtems_libio_iop_drop( _iop ); \ - rtems_set_errno_and_return_minus_one( EBADF ); \ + int _error = rtems_libio_get_iop( _fd, &_iop ); \ + if ( _error != 0 ) { \ + rtems_set_errno_and_return_minus_one( _error ); \ } \ } while ( 0 ) /** - * @brief Macro to get the iop for the specified file descriptor with access - * flags and error. + * @brief Function to get the iop for the specified file descriptor + * with access flags and error. * * Checks that the file descriptor is in the valid range and open. */ +static inline int rtems_libio_get_iop_with_access( + int fd, rtems_libio_t **iop, unsigned int access_flags, int access_error +) +{ + const unsigned int mandatory = LIBIO_FLAGS_OPEN | access_flags ; + unsigned int flags; + if ( (uint32_t) ( fd ) >= rtems_libio_number_iops ) { + return EBADF; + } + *iop = rtems_libio_iop( fd ); + flags = rtems_libio_iop_hold( *iop ); + if ( ( flags & mandatory ) != mandatory ) { + rtems_libio_iop_drop( *iop ); + *iop = NULL; + if ( ( flags & LIBIO_FLAGS_OPEN ) == 0 ) { + return EBADF; + } else { + return access_error; + } + } + return 0; +} + +/** + * @brief Macro to wrap the function to allow returning the IOP and + * using the error set and return macro. + */ #define LIBIO_GET_IOP_WITH_ACCESS( _fd, _iop, _access_flags, _access_error ) \ do { \ - unsigned int _flags; \ - unsigned int _mandatory; \ - if ( (uint32_t) ( _fd ) >= rtems_libio_number_iops ) { \ - rtems_set_errno_and_return_minus_one( EBADF ); \ - } \ - _iop = rtems_libio_iop( _fd ); \ - _flags = rtems_libio_iop_hold( _iop ); \ - _mandatory = LIBIO_FLAGS_OPEN | ( _access_flags ); \ - if ( ( _flags & _mandatory ) != _mandatory ) { \ - int _error; \ - rtems_libio_iop_drop( _iop ); \ - if ( ( _flags & LIBIO_FLAGS_OPEN ) == 0 ) { \ - _error = EBADF; \ - } else { \ - _error = _access_error; \ - } \ + int _error = rtems_libio_get_iop_with_access( \ + _fd, &_iop, _access_flags, _access_error \ + ); \ + if ( _error != 0 ) { \ rtems_set_errno_and_return_minus_one( _error ); \ } \ } while ( 0 ) @@ -435,34 +522,6 @@ int rtems_filesystem_utime_update( struct timespec new_times[2] ); -/* - * File Descriptor Routine Prototypes - */ - -/** - * This routine searches the IOP Table for an unused entry. If it - * finds one, it returns it. Otherwise, it returns NULL. - */ -rtems_libio_t *rtems_libio_allocate(void); - -/** - * Convert UNIX fnctl(2) flags to ones that RTEMS drivers understand - */ -unsigned int rtems_libio_fcntl_flags( int fcntl_flags ); - -/** - * Convert RTEMS internal flags to UNIX fnctl(2) flags - */ -int rtems_libio_to_fcntl_flags( unsigned int flags ); - -/** - * This routine frees the resources associated with an IOP (file descriptor) - * and clears the slot in the IOP Table. - */ -void rtems_libio_free( - rtems_libio_t *iop -); - /** * Return the number of open iop descriptors */ diff --git a/cpukit/libcsupport/src/close.c b/cpukit/libcsupport/src/close.c index 5a6fb30163..fcb51b9fc9 100644 --- a/cpukit/libcsupport/src/close.c +++ b/cpukit/libcsupport/src/close.c @@ -63,8 +63,11 @@ int close( rtems_set_errno_and_return_minus_one( EBADF ); } - /* The expected flags */ - flags &= LIBIO_FLAGS_REFERENCE_INC - 1U; + /* The expected flags depends on close when busy flag. If set + * there can be references held when calling the close handler */ + if ( ( flags & LIBIO_FLAGS_CLOSE_BUSY ) == 0 ) { + flags &= LIBIO_FLAGS_FLAGS_MASK; + } desired = flags & ~LIBIO_FLAGS_OPEN; success = _Atomic_Compare_exchange_uint( @@ -79,7 +82,7 @@ int close( break; } - if ( ( flags & ~( LIBIO_FLAGS_REFERENCE_INC - 1U ) ) != 0 ) { + if ( ( flags & LIBIO_FLAGS_REFERENCE_MASK ) != 0 ) { rtems_set_errno_and_return_minus_one( EBUSY ); } } diff --git a/cpukit/libcsupport/src/fcntl.c b/cpukit/libcsupport/src/fcntl.c index c8c0ed1cca..43e202c055 100644 --- a/cpukit/libcsupport/src/fcntl.c +++ b/cpukit/libcsupport/src/fcntl.c @@ -58,7 +58,7 @@ static int duplicate_iop( rtems_libio_t *iop ) rtems_filesystem_location_clone( &diop->pathinfo, &iop->pathinfo ); rtems_filesystem_instance_unlock( &iop->pathinfo ); - rtems_libio_iop_flags_set( diop, rtems_libio_fcntl_flags( oflag ) ); + rtems_libio_iop_flags_set( diop, rtems_libio_from_fcntl_flags( oflag ) ); /* * XXX: We call the open handler here to have a proper open and close pair. * @@ -99,7 +99,7 @@ static int duplicate2_iop( rtems_libio_t *iop, int fd2 ) if (rv == 0) { oflag = rtems_libio_to_fcntl_flags( rtems_libio_iop_flags( iop ) ); - rtems_libio_iop_flags_set( iop2, rtems_libio_fcntl_flags( oflag ) ); + rtems_libio_iop_flags_set( iop2, rtems_libio_from_fcntl_flags( oflag ) ); rtems_filesystem_instance_lock( &iop->pathinfo ); rtems_filesystem_location_clone( &iop2->pathinfo, &iop->pathinfo ); @@ -177,7 +177,7 @@ static int vfcntl( break; case F_SETFL: - flags = rtems_libio_fcntl_flags( va_arg( ap, int ) ); + flags = rtems_libio_from_fcntl_flags( va_arg( ap, int ) ); mask = LIBIO_FLAGS_NO_DELAY | LIBIO_FLAGS_APPEND; /* @@ -226,7 +226,10 @@ static int vfcntl( if (ret >= 0) { int err = (*iop->pathinfo.handlers->fcntl_h)( iop, cmd ); - if (err) { + if (err == 0 && !rtems_libio_iop_is_open( iop ) ) { + err = EBADF; + } + if (err != 0) { errno = err; ret = -1; } diff --git a/cpukit/libcsupport/src/freenode.c b/cpukit/libcsupport/src/freenode.c index 1e9fd29297..f37b8fae5f 100644 --- a/cpukit/libcsupport/src/freenode.c +++ b/cpukit/libcsupport/src/freenode.c @@ -43,8 +43,10 @@ void rtems_filesystem_location_free( rtems_filesystem_location_info_t *loc ) { - rtems_filesystem_instance_lock( loc ); - (*loc->mt_entry->ops->freenod_h)( loc ); - rtems_filesystem_instance_unlock( loc ); - rtems_filesystem_location_remove_from_mt_entry( loc ); + if ( loc->mt_entry != NULL ) { + rtems_filesystem_instance_lock( loc ); + (*loc->mt_entry->ops->freenod_h)( loc ); + rtems_filesystem_instance_unlock( loc ); + rtems_filesystem_location_remove_from_mt_entry( loc ); + } } diff --git a/cpukit/libcsupport/src/fstat.c b/cpukit/libcsupport/src/fstat.c index 56f8f84624..3f6fb2bb4d 100644 --- a/cpukit/libcsupport/src/fstat.c +++ b/cpukit/libcsupport/src/fstat.c @@ -65,6 +65,10 @@ int fstat( memset( sbuf, 0, sizeof(struct stat) ); rv = (*iop->pathinfo.handlers->fstat_h)( &iop->pathinfo, sbuf ); + if (rv == 0 && !rtems_libio_iop_is_open( iop ) ) { + errno = EBADF; + rv = -1; + } rtems_libio_iop_drop( iop ); return rv; } diff --git a/cpukit/libcsupport/src/libio.c b/cpukit/libcsupport/src/libio.c index 9cef2a6fb6..0a0c238d43 100644 --- a/cpukit/libcsupport/src/libio.c +++ b/cpukit/libcsupport/src/libio.c @@ -77,7 +77,7 @@ static const rtems_assoc_t status_flags_assoc[] = { { 0, 0, 0 }, }; -unsigned int rtems_libio_fcntl_flags( int fcntl_flags ) +unsigned int rtems_libio_from_fcntl_flags( int fcntl_flags ) { unsigned int flags = 0; uint32_t access_modes; @@ -136,6 +136,8 @@ rtems_libio_t *rtems_libio_allocate( void ) if ( iop != NULL ) { void *next; + rtems_libio_iop_flags_clear( iop, LIBIO_FLAGS_FREE ); + next = iop->data1; rtems_libio_iop_free_head = next; @@ -149,30 +151,32 @@ rtems_libio_t *rtems_libio_allocate( void ) return iop; } -void rtems_libio_free( +void rtems_libio_free_iop( rtems_libio_t *iop ) { size_t zero; - rtems_filesystem_location_free( &iop->pathinfo ); - rtems_libio_lock(); - /* - * Clear everything except the reference count part. At this point in time - * there may be still some holders of this file descriptor. - */ - rtems_libio_iop_flags_clear( iop, LIBIO_FLAGS_REFERENCE_INC - 1U ); - zero = offsetof( rtems_libio_t, offset ); - memset( (char *) iop + zero, 0, sizeof( *iop ) - zero ); + if ( !rtems_libio_iop_is_free( iop ) ) { + /* + * Clear the flags. All references should have been dropped. + */ + _Atomic_Store_uint( &iop->flags, LIBIO_FLAGS_FREE, ATOMIC_ORDER_RELAXED ); - /* - * Append it to the free list. This increases the likelihood that a use - * after close is detected. - */ - *rtems_libio_iop_free_tail = iop; - rtems_libio_iop_free_tail = &iop->data1; + rtems_filesystem_location_free( &iop->pathinfo ); + + zero = offsetof( rtems_libio_t, offset ); + memset( (char *) iop + zero, 0, sizeof( *iop ) - zero ); + + /* + * Append it to the free list. This increases the likelihood that + * a use after close is detected. + */ + *rtems_libio_iop_free_tail = iop; + rtems_libio_iop_free_tail = &iop->data1; + } rtems_libio_unlock(); } diff --git a/cpukit/libcsupport/src/libio_init.c b/cpukit/libcsupport/src/libio_init.c index 250112d9d8..9a47319335 100644 --- a/cpukit/libcsupport/src/libio_init.c +++ b/cpukit/libcsupport/src/libio_init.c @@ -72,8 +72,10 @@ static void rtems_libio_init( void ) if (rtems_libio_number_iops > 0) { iop = rtems_libio_iop_free_head = &rtems_libio_iops[0]; - for (i = 0 ; (i + 1) < rtems_libio_number_iops ; i++, iop++) + for (i = 0 ; (i + 1) < rtems_libio_number_iops ; i++, iop++) { + rtems_libio_iop_flags_set( iop, LIBIO_FLAGS_FREE ); iop->data1 = iop + 1; + } iop->data1 = NULL; rtems_libio_iop_free_tail = &iop->data1; } diff --git a/cpukit/libcsupport/src/open.c b/cpukit/libcsupport/src/open.c index 95de789cd2..319bdd1e8b 100644 --- a/cpukit/libcsupport/src/open.c +++ b/cpukit/libcsupport/src/open.c @@ -137,7 +137,7 @@ static int do_open( rtems_filesystem_eval_path_extract_currentloc( &ctx, &iop->pathinfo ); rtems_filesystem_eval_path_cleanup( &ctx ); - rtems_libio_iop_flags_set( iop, rtems_libio_fcntl_flags( oflag ) ); + rtems_libio_iop_flags_set( iop, rtems_libio_from_fcntl_flags( oflag ) ); rv = (*iop->pathinfo.handlers->open_h)( iop, path, oflag, mode ); @@ -168,10 +168,6 @@ static int do_open( } } - if ( rv < 0 ) { - rtems_libio_free( iop ); - } - return rv; } @@ -192,6 +188,9 @@ int open( const char *path, int oflag, ... ) iop = rtems_libio_allocate(); if ( iop != NULL ) { rv = do_open( iop, path, oflag, mode ); + if ( rv < 0 ) { + rtems_libio_free( iop ); + } } else { errno = ENFILE; rv = -1; diff --git a/testsuites/fstests/fsclose01/init.c b/testsuites/fstests/fsclose01/init.c index c425fa67c2..e80d2a8c33 100644 --- a/testsuites/fstests/fsclose01/init.c +++ b/testsuites/fstests/fsclose01/init.c @@ -1,6 +1,8 @@ /* * Copyright (C) 2012, 2020 embedded brains GmbH & Co. KG * + * Copyright (C) 2025 Contemporary Software + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -44,6 +46,7 @@ const char rtems_test_name[] = "FSCLOSE 1"; typedef enum { ACTION_CLOSE, + ACTION_CLOSE_BUSY, ACTION_FCNTL, ACTION_FDATASYNC, ACTION_FCHDIR, @@ -67,6 +70,7 @@ typedef struct { test_action action; bool wait_in_close; bool wait_in_fstat; + bool test_close_busy; int close_count; int fcntl_count; int fdatasync_count; @@ -84,6 +88,16 @@ typedef struct { static test_context test_instance; +static size_t free_iops(void) { + size_t count = 0; + rtems_libio_t *iop = rtems_libio_iop_free_head; + while (iop != NULL) { + ++count; + iop = iop->data1; + } + return count; +} + static void wait(void) { rtems_status_code sc; @@ -112,6 +126,10 @@ static int handler_open( ctx = IMFS_generic_get_context_by_iop(iop); ++ctx->open_count; + if (ctx->action == ACTION_CLOSE_BUSY) { + iop->flags |= LIBIO_FLAGS_CLOSE_BUSY; + } + return 0; } @@ -344,6 +362,12 @@ static void worker_task(rtems_task_argument arg) rv = close(ctx->fd); rtems_test_assert(rv == 0); break; + case ACTION_CLOSE_BUSY: + ctx->wait_in_fstat = true; + rv = fstat(ctx->fd, &st); + rtems_test_assert(rv == -1); + rtems_test_assert(errno == EBADF); + break; case ACTION_FCNTL: rv = fcntl(ctx->fd, F_GETFD); rtems_test_assert(rv >= 0); @@ -418,6 +442,7 @@ static void worker_task(rtems_task_argument arg) static void test_fd_free_fifo(const char *path) { + const size_t iops_free = free_iops(); int a; int b; int rv; @@ -435,10 +460,13 @@ static void test_fd_free_fifo(const char *path) rtems_test_assert(rv == 0); rtems_test_assert(a != b); + + rtems_test_assert(iops_free == free_iops()); } static void test_close(test_context *ctx) { + const size_t iops_free = free_iops(); const char *path = "generic"; int rv; rtems_status_code sc; @@ -483,40 +511,47 @@ static void test_close(test_context *ctx) wakeup_worker(ctx); rv = close(ctx->fd); - rtems_test_assert(rv == -1); - if (ac == ACTION_CLOSE) { - rtems_test_assert(errno == EBADF); - - flags = rtems_libio_iop_hold(iop); - expected_flags = LIBIO_FLAGS_READ_WRITE; - rtems_test_assert(flags == expected_flags); - flags = rtems_libio_iop_flags(iop); - expected_flags = LIBIO_FLAGS_REFERENCE_INC | LIBIO_FLAGS_READ_WRITE; - rtems_test_assert(flags == expected_flags); - } else { - rtems_test_assert(errno == EBUSY); + switch (ac) { + case ACTION_CLOSE: + rtems_test_assert(rv == -1); + rtems_test_assert(errno == EBADF); + flags = rtems_libio_iop_flags(iop); + expected_flags = LIBIO_FLAGS_READ_WRITE; + rtems_test_assert(flags == expected_flags); + rtems_test_assert((iops_free - 1) == free_iops()); + break; + case ACTION_CLOSE_BUSY: + rtems_test_assert(rv == 0); + break; + default: + rtems_test_assert(rv == -1); + rtems_test_assert(errno == EBUSY); + break; } wakeup_worker(ctx); - if (ac == ACTION_CLOSE) { - flags = rtems_libio_iop_flags(iop); - expected_flags = LIBIO_FLAGS_REFERENCE_INC; - rtems_test_assert(flags == expected_flags); - rtems_libio_iop_drop(iop); - flags = rtems_libio_iop_flags(iop); - expected_flags = 0; - rtems_test_assert(flags == expected_flags); + switch (ac) { + case ACTION_CLOSE: + rtems_test_assert(rtems_libio_iop_is_free(iop)); + default: + break; } rv = close(ctx->fd); - if (ac == ACTION_CLOSE) { - rtems_test_assert(rv == -1); - rtems_test_assert(errno == EBADF); - } else { - rtems_test_assert(rv == 0); + rtems_test_assert(iops_free == free_iops()); + + switch (ac) { + case ACTION_CLOSE: + case ACTION_CLOSE_BUSY: + rtems_test_assert(rv == -1); + rtems_test_assert(errno == EBADF); + break; + default: + rtems_test_assert(rv == 0); + break; } } @@ -526,21 +561,81 @@ static void test_close(test_context *ctx) rv = unlink(path); rtems_test_assert(rv == 0); - rtems_test_assert(ctx->close_count == 17); + rtems_test_assert(ctx->close_count == 18); rtems_test_assert(ctx->fcntl_count == 1); rtems_test_assert(ctx->fdatasync_count == 1); - rtems_test_assert(ctx->fstat_count == 42); + rtems_test_assert(ctx->fstat_count == 45); rtems_test_assert(ctx->fsync_count == 1); rtems_test_assert(ctx->ftruncate_count == 1); rtems_test_assert(ctx->ioctl_count == 1); rtems_test_assert(ctx->lseek_count == 1); - rtems_test_assert(ctx->open_count == 17); + rtems_test_assert(ctx->open_count == 18); rtems_test_assert(ctx->read_count == 1); rtems_test_assert(ctx->readv_count == 1); rtems_test_assert(ctx->write_count == 1); rtems_test_assert(ctx->writev_count == 1); } +static void test_iop(test_context *ctx) { + const size_t iops_free = free_iops(); + rtems_libio_t *iop; + rtems_libio_t *iop2; + unsigned int flags; + unsigned int expected; + /* test allocatior */ + iop = rtems_libio_allocate(); + rtems_test_assert(iop != NULL); + rtems_test_assert(iop->flags == 0); + iop2 = rtems_libio_allocate(); + rtems_test_assert(iop2 != NULL); + rtems_test_assert(rtems_libio_allocate() == NULL); + rtems_libio_free(iop2); + rtems_test_assert(iop2->flags == LIBIO_FLAGS_FREE); + /* test flags, flag set/clear and bit tests */ + rtems_test_assert(!rtems_libio_iop_is_open(iop)); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_OPEN); + rtems_test_assert(rtems_libio_iop_is_open(iop)); + rtems_test_assert(!rtems_libio_iop_is_no_delay(iop)); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_NO_DELAY); + rtems_test_assert(rtems_libio_iop_is_no_delay(iop)); + rtems_test_assert(!rtems_libio_iop_is_readable(iop)); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_READ); + rtems_test_assert(rtems_libio_iop_is_readable(iop)); + rtems_test_assert(!rtems_libio_iop_is_writeable(iop)); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_WRITE); + rtems_test_assert(rtems_libio_iop_is_writeable(iop)); + rtems_test_assert(!rtems_libio_iop_is_append(iop)); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_APPEND); + rtems_test_assert(rtems_libio_iop_is_append(iop)); + /* test hold and drop and if drop frees the iop */ + expected = + LIBIO_FLAGS_OPEN | LIBIO_FLAGS_NO_DELAY | LIBIO_FLAGS_READ | + LIBIO_FLAGS_WRITE | LIBIO_FLAGS_APPEND; + flags = rtems_libio_iop_flags(iop); + rtems_test_assert((flags & LIBIO_FLAGS_FLAGS_MASK) == expected); + rtems_test_assert((flags & LIBIO_FLAGS_REFERENCE_MASK) == 0); + flags = rtems_libio_iop_hold(iop); + rtems_test_assert((flags & LIBIO_FLAGS_FLAGS_MASK) == expected); + flags = rtems_libio_iop_flags(iop); + rtems_test_assert( + (flags & LIBIO_FLAGS_REFERENCE_MASK) == LIBIO_FLAGS_REFERENCE_INC); + rtems_libio_free(iop); + flags = rtems_libio_iop_flags(iop); + rtems_test_assert((flags & LIBIO_FLAGS_FLAGS_MASK) == expected); + rtems_test_assert( + (flags & LIBIO_FLAGS_REFERENCE_MASK) == LIBIO_FLAGS_REFERENCE_INC); + rtems_libio_iop_drop(iop); + flags = rtems_libio_iop_flags(iop); + rtems_test_assert((flags & LIBIO_FLAGS_FLAGS_MASK) == expected); + rtems_libio_iop_flags_clear(iop, LIBIO_FLAGS_OPEN); + rtems_libio_iop_hold(iop); + rtems_libio_iop_drop(iop); + flags = rtems_libio_iop_flags(iop); + rtems_test_assert(flags == LIBIO_FLAGS_FREE); + rtems_test_assert(iops_free == free_iops()); +} + + static void test_tmpfile(test_context *ctx) { rtems_resource_snapshot before; @@ -566,6 +661,7 @@ static void test_tmpfile(test_context *ctx) static void Init(rtems_task_argument arg) { TEST_BEGIN(); + test_iop(&test_instance); test_close(&test_instance); test_tmpfile(&test_instance); TEST_END(); diff --git a/testsuites/fstests/support/fstest.h b/testsuites/fstests/support/fstest.h index 2b3daa4d16..016587df2a 100644 --- a/testsuites/fstests/support/fstest.h +++ b/testsuites/fstests/support/fstest.h @@ -35,7 +35,7 @@ #define FS_PASS() do {puts("PASS");} while (0) #define FS_FAIL() do {\ - printf( "FAIL %s: %d \n", __FILE__, __LINE__ );\ + printf( "FAIL errno=%s %s: %d \n", strerror(errno), __FILE__, __LINE__ );\ fs_test_notify_failure(); \ } while (0)