From ed59263471fda24f42183e08c1036698c4de706f Mon Sep 17 00:00:00 2001 From: Mazen Adel Elmessady Date: Sat, 23 Aug 2025 00:32:14 +0300 Subject: [PATCH] Add posix_getdents() support to RTEMS This commit adds implementation and tests for posix_getdents(). The implementation used getdents() as a reference. Updates rtems/programs/gsoc#69 rtems&24 --- cpukit/posix/src/posix_getdents.c | 78 +++++ spec/build/cpukit/librtemscpu.yml | 1 + spec/build/testsuites/psxtests/grp.yml | 2 + .../testsuites/psxtests/psxgetdents01.yml | 19 ++ testsuites/psxtests/psxgetdents01/init.c | 302 ++++++++++++++++++ .../psxtests/psxgetdents01/psxgetdents01.doc | 45 +++ .../psxtests/psxgetdents01/psxgetdents01.scn | 24 ++ 7 files changed, 471 insertions(+) create mode 100644 cpukit/posix/src/posix_getdents.c create mode 100644 spec/build/testsuites/psxtests/psxgetdents01.yml create mode 100644 testsuites/psxtests/psxgetdents01/init.c create mode 100644 testsuites/psxtests/psxgetdents01/psxgetdents01.doc create mode 100644 testsuites/psxtests/psxgetdents01/psxgetdents01.scn diff --git a/cpukit/posix/src/posix_getdents.c b/cpukit/posix/src/posix_getdents.c new file mode 100644 index 0000000000..f508e500d3 --- /dev/null +++ b/cpukit/posix/src/posix_getdents.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @brief This source file contains the implementation of posix_getdents() + */ + +/* + * Copyright (C) 2025 Mazen Adel Elmessady + * + * 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 + +#include +#include +#include +#include + +ssize_t posix_getdents( int fildes, void *buf, size_t nbyte, int flags ) +{ + rtems_libio_t *iop; + mode_t type; + int result; + + if ( nbyte < sizeof( struct dirent ) ) { + rtems_set_errno_and_return_minus_one( EINVAL ); + } + + /* + * Get the file control block structure associated with the file descriptor + */ + LIBIO_GET_IOP_WITH_ACCESS( fildes, iop, LIBIO_FLAGS_READ, EBADF ); + + /* + * Make sure we are working on a directory + */ + type = rtems_filesystem_location_type( &iop->pathinfo ); + + if ( !S_ISDIR( type ) ) { + rtems_libio_iop_drop( iop ); + rtems_set_errno_and_return_minus_one( ENOTDIR ); + } + + /* + * Return the number of bytes that were actually transferred as a result + * of the read attempt. + */ + result = ( *iop->pathinfo.handlers->read_h )( iop, buf, nbyte ); + rtems_libio_iop_drop( iop ); + flags = 0; + return result; +} diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index 0b60e70cd4..06e4983397 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -1095,6 +1095,7 @@ source: - cpukit/posix/src/pbarrierdestroy.c - cpukit/posix/src/pbarrierinit.c - cpukit/posix/src/pbarrierwait.c +- cpukit/posix/src/posix_getdents.c - cpukit/posix/src/posix_madvise.c - cpukit/posix/src/prwlockdestroy.c - cpukit/posix/src/prwlockinit.c diff --git a/spec/build/testsuites/psxtests/grp.yml b/spec/build/testsuites/psxtests/grp.yml index 4a58494ae1..75026e37a0 100644 --- a/spec/build/testsuites/psxtests/grp.yml +++ b/spec/build/testsuites/psxtests/grp.yml @@ -129,6 +129,8 @@ links: uid: psxftw01 - role: build-dependency uid: psxgetattrnp01 +- role: build-dependency + uid: psxgetdents01 - role: build-dependency uid: psxgetrusage01 - role: build-dependency diff --git a/spec/build/testsuites/psxtests/psxgetdents01.yml b/spec/build/testsuites/psxtests/psxgetdents01.yml new file mode 100644 index 0000000000..2e30d1e409 --- /dev/null +++ b/spec/build/testsuites/psxtests/psxgetdents01.yml @@ -0,0 +1,19 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: test-program +cflags: [] +copyrights: +- Copyright (C) 2025 Mazen Adel Elmessady +cppflags: [] +cxxflags: [] +enabled-by: true +features: c cprogram +includes: [] +ldflags: [] +links: [] +source: +- testsuites/psxtests/psxgetdents01/init.c +stlib: [] +target: testsuites/psxtests/psxgetdents01.exe +type: build +use-after: [] +use-before: [] diff --git a/testsuites/psxtests/psxgetdents01/init.c b/testsuites/psxtests/psxgetdents01/init.c new file mode 100644 index 0000000000..acdd32ef04 --- /dev/null +++ b/testsuites/psxtests/psxgetdents01/init.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2025 Mazen Adel Elmessady + * + * 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 "tmacros.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char rtems_test_name[] = "PSXGETDENTS01"; + +/* Forward declarations */ +void *POSIX_Init( void *unused ); +static void test_posix_getdents_errors( void ); +static void test_posix_getdents_basic( void ); +static void test_posix_getdents_parsing( void ); +static void test_posix_getdents_file_types( void ); + +/* Declare getdents function from libcsupport */ +extern int getdents( int dd_fd, char *dd_buf, int dd_len ); + +static void test_posix_getdents_errors( void ) +{ + char buffer[ 1024 ]; + int fd; + ssize_t result; + + puts( "Testing posix_getdents() error conditions" ); + + /* Test EBADF - Invalid file descriptor */ + puts( " posix_getdents(-1, buffer, size, 0) -- EBADF" ); + result = posix_getdents( -1, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( result == -1 ); + rtems_test_assert( errno == EBADF ); + + puts( " posix_getdents(999, buffer, size, 0) -- EBADF" ); + result = posix_getdents( 999, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( result == -1 ); + rtems_test_assert( errno == EBADF ); + + /* Test EINVAL - Invalid buffer size */ + + fd = open( "/", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + puts( " posix_getdents(fd, buffer, 0, 0) -- EINVAL" ); + result = posix_getdents( fd, buffer, 0, 0 ); + rtems_test_assert( result == -1 ); + rtems_test_assert( errno == EINVAL ); + + puts( " posix_getdents(fd, buffer, small_size, 0) -- EINVAL" ); + result = posix_getdents( fd, buffer, 5, 0 ); + rtems_test_assert( result == -1 ); + rtems_test_assert( errno == EINVAL ); + + close( fd ); + + /* Test ENOTDIR - Regular file instead of directory */ + puts( " posix_getdents(file_fd, buffer, size, 0) -- ENOTDIR" ); + fd = creat( "/tmp/test_file", 0644 ); + if ( fd >= 0 ) { + close( fd ); + fd = open( "/tmp/test_file", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + result = posix_getdents( fd, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( result == -1 ); + rtems_test_assert( errno == ENOTDIR ); + + close( fd ); + unlink( "/tmp/test_file" ); + } +} + +static void test_posix_getdents_basic( void ) +{ + char buffer[ 4096 ]; + int fd; + ssize_t bytes_read; + + puts( "Testing posix_getdents() basic functionality" ); + /* Test reading root directory */ + puts( " Opening and reading root directory" ); + fd = open( "/", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + bytes_read = posix_getdents( fd, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( bytes_read >= 0 ); + printf( " Read %zd bytes from root directory\n", bytes_read ); + + close( fd ); + + /* Test reading /tmp directory */ + puts( " Testing /tmp directory" ); + rtems_test_assert( mkdir( "/tmp", 0755 ) == 0 || errno == EEXIST ); + + fd = open( "/tmp", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + bytes_read = posix_getdents( fd, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( bytes_read >= 0 ); + printf( " Read %zd bytes from /tmp directory\n", bytes_read ); + + close( fd ); +} + +static void test_posix_getdents_parsing( void ) +{ + char buffer[ 4096 ]; + int fd; + ssize_t bytes_read; + struct dirent *entry; + char *ptr, *end; + int entry_count = 0; + + puts( "Testing posix_getdents() directory entry parsing" ); + + /* Create test directory with known contents */ + puts( " Creating test directory structure" ); + rtems_test_assert( mkdir( "/tmp/test_dir", 0755 ) == 0 ); + + /* Create a regular file */ + fd = creat( "/tmp/test_dir/test_file.txt", 0644 ); + rtems_test_assert( fd >= 0 ); + close( fd ); + + /* Create a subdirectory */ + rtems_test_assert( mkdir( "/tmp/test_dir/subdir", 0755 ) == 0 ); + + /* Open test directory */ + fd = open( "/tmp/test_dir", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + bytes_read = getdents( fd, buffer, sizeof( buffer ) ); + rtems_test_assert( bytes_read > 0 ); + + printf( " Parsing %zd bytes of directory entries\n", bytes_read ); + + /* Parse directory entries */ + ptr = buffer; + end = buffer + bytes_read; + + while ( ptr < end ) { + entry = (struct dirent *) ptr; + + /* Validate entry structure */ + rtems_test_assert( entry->d_reclen > 0 ); + rtems_test_assert( entry->d_reclen <= ( end - ptr ) ); + rtems_test_assert( strlen( entry->d_name ) < entry->d_reclen ); + + printf( + " Entry: '%s', type: %d, reclen: %d\n", + entry->d_name, + entry->d_type, + entry->d_reclen + ); + + entry_count++; + ptr += entry->d_reclen; + } + + printf( " Found %d directory entries\n", entry_count ); + + close( fd ); + + /* Cleanup */ + unlink( "/tmp/test_dir/test_file.txt" ); + rmdir( "/tmp/test_dir/subdir" ); + rmdir( "/tmp/test_dir" ); +} + +static void test_posix_getdents_file_types( void ) +{ + char buffer[ 1024 ]; + int fd; + ssize_t bytes_read; + struct dirent *entry; + char *ptr, *end; + bool found_regular = false, found_directory = false; + + puts( "Testing posix_getdents() file type detection" ); + + /* Create test directory with different file types */ + puts( " Creating files of different types" ); + rtems_test_assert( mkdir( "/tmp/type_test", 0755 ) == 0 ); + + /* Create regular file */ + fd = creat( "/tmp/type_test/regular_file", 0644 ); + rtems_test_assert( fd >= 0 ); + close( fd ); + + /* Create directory */ + rtems_test_assert( mkdir( "/tmp/type_test/directory", 0755 ) == 0 ); + + /* Open test directory */ + fd = open( "/tmp/type_test", O_RDONLY ); + rtems_test_assert( fd >= 0 ); + + bytes_read = posix_getdents( fd, buffer, sizeof( buffer ), 0 ); + rtems_test_assert( bytes_read > 0 ); + + /* Parse entries and check file types */ + ptr = buffer; + end = buffer + bytes_read; + + while ( ptr < end ) { + entry = (struct dirent *) ptr; + + printf( " File: '%s', type: %d\n", entry->d_name, entry->d_type ); + + if ( strcmp( entry->d_name, "regular_file" ) == 0 ) { + found_regular = true; + if ( entry->d_type != DT_UNKNOWN ) { + rtems_test_assert( entry->d_type == DT_REG ); + } + } else if ( strcmp( entry->d_name, "directory" ) == 0 ) { + found_directory = true; + if ( entry->d_type != DT_UNKNOWN ) { + rtems_test_assert( entry->d_type == DT_DIR ); + } + } + + ptr += entry->d_reclen; + } + + rtems_test_assert( found_regular ); + rtems_test_assert( found_directory ); + + close( fd ); + + /* Cleanup */ + unlink( "/tmp/type_test/regular_file" ); + rmdir( "/tmp/type_test/directory" ); + rmdir( "/tmp/type_test" ); +} + +void *POSIX_Init( void *unused ) +{ + TEST_BEGIN(); + + /* Ensure /tmp directory exists for tests */ + mkdir( "/tmp", 0755 ); + + test_posix_getdents_errors(); + test_posix_getdents_basic(); + test_posix_getdents_parsing(); + test_posix_getdents_file_types(); + + TEST_END(); + rtems_test_exit( 0 ); + + return NULL; /* just so the compiler thinks we returned something */ +} + +/* configuration information */ +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_MAXIMUM_POSIX_THREADS 1 + +#define CONFIGURE_POSIX_INIT_THREAD_TABLE + +#define CONFIGURE_IMFS_ENABLE_MKFIFO +#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 32 + +#define CONFIGURE_INIT +#include diff --git a/testsuites/psxtests/psxgetdents01/psxgetdents01.doc b/testsuites/psxtests/psxgetdents01/psxgetdents01.doc new file mode 100644 index 0000000000..00e83b725e --- /dev/null +++ b/testsuites/psxtests/psxgetdents01/psxgetdents01.doc @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (C) 2025 Mazen Adel Elmessady +# +# 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. +# + +This file describes the directives and concepts tested by this test set. + +test set name: psxgetdents01 + +directives: + + posix_getdents + +concepts: + ++ Verify error conditions in posix_getdents + ++ Verify directory entry reading functionality + ++ Verify proper handling of invalid file descriptors + ++ Verify proper handling of invalid buffer parameters + ++ Verify directory entry structure format and content diff --git a/testsuites/psxtests/psxgetdents01/psxgetdents01.scn b/testsuites/psxtests/psxgetdents01/psxgetdents01.scn new file mode 100644 index 0000000000..651a2ff123 --- /dev/null +++ b/testsuites/psxtests/psxgetdents01/psxgetdents01.scn @@ -0,0 +1,24 @@ +*** BEGIN OF TEST PSXGETDENTS01 *** +Testing posix_getdents() error conditions + posix_getdents(-1, buffer, size, 0) -- EBADF + posix_getdents(999, buffer, size, 0) -- EBADF + posix_getdents(fd, buffer, 0, 0) -- EINVAL + posix_getdents(fd, buffer, small_size, 0) -- EINVAL + posix_getdents(file_fd, buffer, size, 0) -- ENOTDIR +Testing posix_getdents() basic functionality + Opening and reading root directory + Read 560 bytes from root directory + Testing /tmp directory + Read 0 bytes from /tmp directory +Testing posix_getdents() directory entry parsing + Creating test directory structure + Parsing 560 bytes of directory entries + Entry: 'test_file.txt', type: 8, reclen: 280 + Entry: 'subdir', type: 4, reclen: 280 + Found 2 directory entries +Testing posix_getdents() file type detection + Creating files of different types + File: 'regular_file', type: 8 + File: 'directory', type: 4 +*** END OF TEST PSXGETDENTS01 *** +