psxfile01: Add hard link O_NOFOLLOW regression

Extend the existing open() O_NOFOLLOW coverage to verify that a hard
link to a regular file is still resolved to its target, while symbolic
links remain subject to the O_NOFOLLOW check. This is the regression
test for the do_open() fix that uses RTEMS_FS_FOLLOW_HARD_LINK instead
of zero for the O_NOFOLLOW case.

The test writes a known marker to the regular file, creates a hard link
to it, opens the hard link with O_NOFOLLOW, and then checks both the
inode identity and the file contents read back through the descriptor.

Reading the contents is the part that actually distinguishes a correct
resolution from the bug. In the IMFS a hard link is a distinct node
(IMFS_node_control_hard_link) whose fstat handler forwards to the target
(IMFS_stat_hard_link), but whose read and mmap handlers are the default
ones that fail with ENOTSUP. When open() wrongly clears the follow flags
for O_NOFOLLOW, path evaluation stops at the hard link node instead of
resolving it, yet fstat() still reports the target's mode and inode. A
test that only checks st_mode/st_ino therefore passes even with the bug
present. Reading the marker back forces the descriptor to refer to the
real regular file, so the test fails (read returns ENOTSUP) unless the
hard link was actually followed.
This commit is contained in:
Cheng Mingming
2026-07-02 18:30:03 -05:00
committed by Kinsey Moore
parent 4ef704aac4
commit 641f589b37
+51 -1
View File
@@ -78,6 +78,10 @@ rtems_filesystem_operations_table IMFS_ops_no_rename;
static const char somefile[] = "somefile";
static const char hardlink[] = "hardlink";
static const char hardlink_marker[] = "hard link O_NOFOLLOW marker";
static const char somelink[] = "somelink";
/*
@@ -201,13 +205,59 @@ static void test_open_nofollow( void )
int status;
int fd;
struct stat st;
struct stat hardlink_st;
#ifdef O_NOFOLLOW
ssize_t n;
char buf[ sizeof( hardlink_marker ) ];
#endif
fd = open( somefile, O_CREAT, S_IRWXU );
fd = open( somefile, O_CREAT | O_WRONLY, S_IRWXU );
rtems_test_assert( fd >= 0 );
#ifdef O_NOFOLLOW
n = write( fd, hardlink_marker, sizeof( hardlink_marker ) );
rtems_test_assert( n == (ssize_t) sizeof( hardlink_marker ) );
#endif
status = close( fd );
rtems_test_assert( status == 0 );
#ifdef O_NOFOLLOW
status = link( somefile, hardlink );
rtems_test_assert( status == 0 );
status = stat( somefile, &st );
rtems_test_assert( status == 0 );
fd = open( hardlink, O_RDONLY | O_NOFOLLOW );
rtems_test_assert( fd >= 0 );
status = fstat( fd, &hardlink_st );
rtems_test_assert( status == 0 );
rtems_test_assert( S_ISREG( hardlink_st.st_mode ) );
rtems_test_assert( hardlink_st.st_ino == st.st_ino );
/*
* Read the file contents through the hard link. This is what actually
* exercises the fix. A hard link node forwards fstat() to its target,
* so the stat checks above pass even when open() wrongly stops at the
* hard link node instead of resolving it. The hard link node's read
* handler, however, is the default one which fails with ENOTSUP, so the
* read only returns the data when open() resolved the hard link to the
* target regular file.
*/
memset( buf, 0, sizeof( buf ) );
n = read( fd, buf, sizeof( buf ) );
rtems_test_assert( n == (ssize_t) sizeof( hardlink_marker ) );
rtems_test_assert( memcmp( buf, hardlink_marker, sizeof( buf ) ) == 0 );
status = close( fd );
rtems_test_assert( status == 0 );
status = unlink( hardlink );
rtems_test_assert( status == 0 );
#endif
status = symlink( somefile, somelink );
rtems_test_assert( status == 0 );