forked from Imagelibrary/rtems
cpukit/libio: Support close with IOP references held
- Provide an option for a file system to support close wtih references held. This can happen in more complex file systems and file descriptor handling with more complete reference handling implementations where an fd can hold other fds and close can be call on any fd and succeed. - Fix open IOP leaks in the error paths. - Provide better definition of the IOP flags to help clarify the code. Fixes #5201
This commit is contained in:
committed by
Kinsey Moore
parent
fb1d9c8aca
commit
df1d85c0f8
@@ -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:
|
||||
@@ -1363,18 +1365,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)
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -1383,6 +1390,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.
|
||||
*
|
||||
@@ -1413,6 +1434,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.
|
||||
*
|
||||
@@ -1423,6 +1454,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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user