Keep backwards compatibility through unions.
The compatibility macros like
#define st_atime st_atim.tv_sec
as documented in stat(3type) don't work for nolibc because it would break with other stat-like structures that contain the field st_atime.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- tools/include/nolibc/sys.h | 29 +++++++++++++++------------- tools/include/nolibc/types.h | 6 +++--- tools/testing/selftests/nolibc/nolibc-test.c | 23 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 7874062bea95..11b354e26861 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -1187,19 +1187,22 @@ int sys_stat(const char *path, struct stat *buf) #else #error Neither __NR_newfstatat nor __NR_stat defined, cannot implement sys_stat() #endif - buf->st_dev = stat.st_dev; - buf->st_ino = stat.st_ino; - buf->st_mode = stat.st_mode; - buf->st_nlink = stat.st_nlink; - buf->st_uid = stat.st_uid; - buf->st_gid = stat.st_gid; - buf->st_rdev = stat.st_rdev; - buf->st_size = stat.st_size; - buf->st_blksize = stat.st_blksize; - buf->st_blocks = stat.st_blocks; - buf->st_atime = stat.st_atime; - buf->st_mtime = stat.st_mtime; - buf->st_ctime = stat.st_ctime; + buf->st_dev = stat.st_dev; + buf->st_ino = stat.st_ino; + buf->st_mode = stat.st_mode; + buf->st_nlink = stat.st_nlink; + buf->st_uid = stat.st_uid; + buf->st_gid = stat.st_gid; + buf->st_rdev = stat.st_rdev; + buf->st_size = stat.st_size; + buf->st_blksize = stat.st_blksize; + buf->st_blocks = stat.st_blocks; + buf->st_atim.tv_sec = stat.st_atime; + buf->st_atim.tv_nsec = stat.st_atime_nsec; + buf->st_mtim.tv_sec = stat.st_mtime; + buf->st_mtim.tv_nsec = stat.st_mtime_nsec; + buf->st_ctim.tv_sec = stat.st_ctime; + buf->st_ctim.tv_nsec = stat.st_ctime_nsec; return ret; } #endif diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 15b0baffd336..f96e28bff4ba 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -198,9 +198,9 @@ struct stat { off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ - time_t st_atime; /* time of last access */ - time_t st_mtime; /* time of last modification */ - time_t st_ctime; /* time of last status change */ + union { time_t st_atime; struct timespec st_atim; }; /* time of last access */ + union { time_t st_mtime; struct timespec st_mtim; }; /* time of last modification */ + union { time_t st_ctime; struct timespec st_ctim; }; /* time of last status change */ };
/* WARNING, it only deals with the 4096 first majors and 256 first minors */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 6e0a4dbe321e..8de72c87b7b7 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -500,6 +500,28 @@ static int test_fork(void) } }
+static int test_stat_timestamps(void) +{ + struct stat st; + + if (sizeof(st.st_atim.tv_sec) != sizeof(st.st_atime)) + return 1; + + if (stat("/proc/self/", &st)) + return 1; + + if (st.st_atim.tv_sec != st.st_atime || st.st_atim.tv_nsec > 1000000000) + return 1; + + if (st.st_mtim.tv_sec != st.st_mtime || st.st_mtim.tv_nsec > 1000000000) + return 1; + + if (st.st_ctim.tv_sec != st.st_ctime || st.st_ctim.tv_nsec > 1000000000) + return 1; + + return 0; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -587,6 +609,7 @@ int run_syscall(int min, int max) CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break; CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break; CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; + CASE_TEST(stat_timestamps); EXPECT_SYSZR(1, test_stat_timestamps()); break; CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break; CASE_TEST(unlink_blah); EXPECT_SYSER(1, unlink("/proc/self/blah"), -1, ENOENT); break;
--- base-commit: 1974a2b5fd434812b32952b09df7b79fdee8104d change-id: 20230527-nolibc-stat-nanoseconds-35de1467a430
Best regards,
On Sat, May 27, 2023 at 02:56:42PM +0200, Thomas Weißschuh wrote:
Keep backwards compatibility through unions.
The compatibility macros like
#define st_atime st_atim.tv_sec
as documented in stat(3type) don't work for nolibc because it would break with other stat-like structures that contain the field st_atime.
Ah, good idea, I like this approach. Generally speaking I hate defining macros to access some struct members, even though it has been done for decades in various places of standard libs. Your solution making use of anonymous unions is an elegant alternative to this.
Thanks! Willy
linux-kselftest-mirror@lists.linaro.org