From 473aa8093af3ea5a23575217d932f1a3abee37b9 Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:51:10 +1000 Subject: [PATCH] Haiku: Add native library IO support Adds detection for non-portable filesystem and mount APIs and equivalent implementations using the Haiku API. --- src/native/libs/Common/pal_config.h.in | 5 ++ src/native/libs/System.Native/pal_io.c | 50 +++++++++++++++++-- src/native/libs/System.Native/pal_mount.c | 60 +++++++++++++++++++++-- src/native/libs/configure.cmake | 18 +++++++ 4 files changed, 125 insertions(+), 8 deletions(-) diff --git a/src/native/libs/Common/pal_config.h.in b/src/native/libs/Common/pal_config.h.in index c8e43fa37dd6a7..5901173e1c7ea0 100644 --- a/src/native/libs/Common/pal_config.h.in +++ b/src/native/libs/Common/pal_config.h.in @@ -32,6 +32,7 @@ #cmakedefine01 HAVE_MNTINFO #cmakedefine01 HAVE_STATFS_FSTYPENAME #cmakedefine01 HAVE_STATVFS_FSTYPENAME +#cmakedefine01 HAVE_STATVFS_BASETYPE #cmakedefine01 HAVE_NON_LEGACY_STATFS #cmakedefine01 HAVE_STRCPY_S #cmakedefine01 HAVE_STRLCPY @@ -88,6 +89,7 @@ #cmakedefine01 HAVE_NETPACKET_PACKET_H #cmakedefine01 HAVE_NET_IF_ARP_H #cmakedefine01 HAVE_SYS_MNTENT_H +#cmakedefine01 HAVE_MNTENT_H #cmakedefine01 HAVE_NET_IFMEDIA_H #cmakedefine01 HAVE_IOS_NET_IFMEDIA_H #cmakedefine01 HAVE_LINUX_RTNETLINK_H @@ -128,6 +130,7 @@ #cmakedefine01 HAVE_TERMIOS_H #cmakedefine01 HAVE_DLFCN_H #cmakedefine01 HAVE_PTHREAD_H +#cmakedefine01 HAVE_SYS_STATFS_H #cmakedefine01 HAVE_SYS_STATVFS_H #cmakedefine01 HAVE_NET_IF_H #cmakedefine01 HAVE_SYS_PROCINFO_H @@ -141,6 +144,8 @@ #cmakedefine01 HAVE_MAKEDEV_SYSMACROSH #cmakedefine01 HAVE_GETGRGID_R #cmakedefine01 HAVE_TERMIOS2 +#cmakedefine01 HAVE_DIRENT_NAME_SIZE +#cmakedefine01 DIRENT_NAME_SIZE #ifndef HOST_WASI #cmakedefine01 HAVE_GETRUSAGE diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c index 3777ad55151f9d..39bccee01bb045 100644 --- a/src/native/libs/System.Native/pal_io.c +++ b/src/native/libs/System.Native/pal_io.c @@ -46,7 +46,7 @@ #include #elif HAVE_STATFS_MOUNT // BSD #include -#elif HAVE_SYS_STATVFS_H && !HAVE_NON_LEGACY_STATFS // SunOS +#elif HAVE_SYS_STATVFS_H && !HAVE_NON_LEGACY_STATFS && HAVE_STATVFS_BASETYPE // SunOS #include #include #if HAVE_STATFS_VFS @@ -58,6 +58,10 @@ #include #endif +#ifdef TARGET_HAIKU +#include +#endif // TARGET_HAIKU + #ifdef _AIX #include // Somehow, AIX mangles the definition for this behind a C++ def @@ -399,7 +403,7 @@ int32_t SystemNative_IsMemfdSupported(void) } #endif - // Note that the name has no affect on file descriptor behavior. From linux manpage: + // Note that the name has no affect on file descriptor behavior. From linux manpage: // Names do not affect the behavior of the file descriptor, and as such multiple files can have the same name without any side effects. int32_t fd = (int32_t)syscall(__NR_memfd_create, "test", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (fd < 0) return 0; @@ -515,8 +519,13 @@ int32_t SystemNative_GetReadDirRBufferSize(void) #endif // dirent should be under 2k in size assert(result < 2048); +#if HAVE_DIRENT_NAME_SIZE // add some extra space so we can align the buffer to dirent. return (int32_t)(result + dirent_alignment - 1); +#else + // add some extra space for the name. + return sizeof(struct dirent) + NAME_MAX + dirent_alignment - 1; +#endif // HAVE_DIRENT_NAME_SIZE #else return 0; #endif @@ -882,8 +891,14 @@ void SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint { #if !defined(TARGET_WASI) dev_t castedDev = (dev_t)dev; +#if !defined(TARGET_HAIKU) *majorNumber = (uint32_t)major(castedDev); *minorNumber = (uint32_t)minor(castedDev); +#else + // Haiku has no concept of major/minor numbers, but it does have device IDs. + *majorNumber = 0; + *minorNumber = (uint32_t)dev; +#endif // TARGET_HAIKU #else /* TARGET_WASI */ dev_t castedDev = (dev_t)dev; *majorNumber = 0; @@ -894,7 +909,12 @@ void SystemNative_GetDeviceIdentifiers(uint64_t dev, uint32_t* majorNumber, uint int32_t SystemNative_MkNod(const char* pathName, uint32_t mode, uint32_t major, uint32_t minor) { #if !defined(TARGET_WASI) +#if !defined(TARGET_HAIKU) dev_t dev = (dev_t)makedev(major, minor); +#else + (void)major; + dev_t dev = (dev_t)minor; +#endif // !TARGET_HAIKU int32_t result; while ((result = mknod(pathName, (mode_t)mode, dev)) < 0 && errno == EINTR); @@ -1641,7 +1661,7 @@ static int16_t ConvertLockType(int16_t managedLockType) } } -#if !HAVE_NON_LEGACY_STATFS || defined(TARGET_APPLE) || defined(TARGET_FREEBSD) +#if !HAVE_NON_LEGACY_STATFS || defined(TARGET_APPLE) || defined(TARGET_FREEBSD) || defined(TARGET_HAIKU) static uint32_t MapFileSystemNameToEnum(const char* fileSystemName) { uint32_t result = 0; @@ -1800,9 +1820,29 @@ uint32_t SystemNative_GetFileSystemType(intptr_t fd) uint32_t result = (uint32_t)statfsArgs.f_type; return result; #endif +#elif defined(TARGET_HAIKU) + struct stat st; + int fstatRes; + while ((fstatRes = fstat(ToFileDescriptor(fd), &st)) == -1 && errno == EINTR); + if (fstatRes == -1) return 0; + + struct fs_info info; + int fsStatDevRes; + while ((fsStatDevRes = fs_stat_dev(st.st_dev, &info)) == -1 && errno == EINTR); + if (fsStatDevRes == -1) return 0; + + if (strcmp(info.fsh_name, "bfs") == 0) + { + // Haiku names its own BFS filesystem "bfs", but on Linux and some other UNIXes + // it is called "befs" to avoid confusion with Boot File System. + strncpy(info.fsh_name, "befs", sizeof(info.fsh_name) - 1); + info.fsh_name[sizeof(info.fsh_name) - 1] = '\0'; + } + + return MapFileSystemNameToEnum(info.fsh_name); #elif defined(TARGET_WASI) return EINTR; -#elif !HAVE_NON_LEGACY_STATFS +#elif !HAVE_NON_LEGACY_STATFS && HAVE_STATVFS_BASETYPE int statfsRes; struct statvfs statfsArgs; while ((statfsRes = fstatvfs(ToFileDescriptor(fd), &statfsArgs)) == -1 && errno == EINTR) ; @@ -1976,7 +2016,7 @@ static int GetAllowedVectorCount(IOVector* vectors, int32_t vectorCount) // For macOS preadv and pwritev can fail with EINVAL when the total length // of all vectors overflows a 32-bit integer. size_t totalLength = 0; - for (int i = 0; i < allowedCount; i++) + for (int i = 0; i < allowedCount; i++) { assert(INT_MAX >= vectors[i].Count); diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 2453bdfe9ede3f..d68ac8dd761929 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -13,16 +13,24 @@ #if HAVE_MNTINFO #include #else +#if HAVE_SYS_STATFS_H #include +#endif #if HAVE_SYS_MNTENT_H #include #include -#include -#else +#elif HAVE_MNTENT_H #include #endif +#include #define STRING_BUFFER_SIZE 8192 +#ifdef __HAIKU__ +#include +#include +#include +#endif // __HAIKU__ + // Android does not define MNTOPT_RO #ifndef MNTOPT_RO #define MNTOPT_RO "r" @@ -68,7 +76,7 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) return result; } -#else +#elif HAVE_MNTENT_H int result = -1; FILE* fp = setmntent("/proc/mounts", MNTOPT_RO); if (fp != NULL) @@ -91,6 +99,38 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) return result; } +#elif defined(__HAIKU__) + int32 cookie = 0; + dev_t currentDev; + + while ((long)(currentDev = next_dev(&cookie)) >= 0) + { + struct fs_info info; + if (fs_stat_dev(currentDev, &info) != B_OK) + { + continue; + } + + char name[STRING_BUFFER_SIZE]; + // Two bytes for the name as we're storing "." + char buf[sizeof(struct dirent) + 2]; + struct dirent *entry = (struct dirent *)&buf; + strncpy(entry->d_name, ".", 2); + entry->d_pdev = currentDev; + entry->d_pino = info.root; + + if (get_path_for_dirent(entry, name, sizeof(name)) != B_OK) + { + continue; + } + + onFound(context, name); + } + + return 0; +} +#else +#error "Don't know how to enumerate mount points on this platform" #endif int32_t SystemNative_GetSpaceInfoForMountPoint(const char* name, MountPointInformation* mpi) @@ -140,6 +180,9 @@ SystemNative_GetFormatInfoForMountPoint(const char* name, char* formatNameBuffer #if HAVE_NON_LEGACY_STATFS struct statfs stats; int result = statfs(name, &stats); +#elif defined(__HAIKU__) + struct fs_info stats; + int result = fs_stat_dev(dev_for_path(name), &stats); #else struct statvfs stats; int result = statvfs(name, &stats); @@ -166,6 +209,17 @@ SystemNative_GetFormatInfoForMountPoint(const char* name, char* formatNameBuffer assert(formatType != NULL); *formatType = (int64_t)(stats.f_type); SafeStringCopy(formatNameBuffer, Int32ToSizeT(bufferLength), ""); +#elif defined(__HAIKU__) + if (bufferLength < B_OS_NAME_LENGTH) + { + result = ERANGE; + *formatType = 0; + } + else + { + SafeStringCopy(formatNameBuffer, Int32ToSizeT(bufferLength), stats.fsh_name); + *formatType = -1; + } #else *formatType = 0; #endif diff --git a/src/native/libs/configure.cmake b/src/native/libs/configure.cmake index fc7332a54f9fac..bd348980539412 100644 --- a/src/native/libs/configure.cmake +++ b/src/native/libs/configure.cmake @@ -324,7 +324,17 @@ check_struct_has_member( "sys/mount.h" HAVE_STATVFS_FSTYPENAME) +check_struct_has_member( + "struct statvfs" + f_basetype + "sys/statvfs.h" + HAVE_STATVFS_BASETYPE) + set(CMAKE_EXTRA_INCLUDE_FILES dirent.h) +check_type_size( + "((struct dirent*)0)->d_name" + DIRENT_NAME_SIZE) +set(CMAKE_EXTRA_INCLUDE_FILES) # statfs: Find whether this struct exists if (HAVE_STATFS_FSTYPENAME OR HAVE_STATVFS_FSTYPENAME) @@ -882,6 +892,10 @@ check_include_files( "dlfcn.h" HAVE_DLFCN_H) +check_include_files( + "sys/statfs.h" + HAVE_SYS_STATFS_H) + check_include_files( "sys/statvfs.h" HAVE_SYS_STATVFS_H) @@ -955,6 +969,10 @@ check_include_files( "sys/mntent.h" HAVE_SYS_MNTENT_H) +check_include_files( + "mntent.h" + HAVE_MNTENT_H) + check_include_files( "stdint.h;net/if_media.h" HAVE_NET_IFMEDIA_H)