From 62ea75a934a9e050e42f2b4c9159d74ca403a0b8 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Thu, 25 Dec 2025 19:19:08 +0100 Subject: [PATCH 1/5] add shims --- Sources/FoundationEssentials/Platform.swift | 4 +- .../include/filemanager_shims.h | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/Sources/FoundationEssentials/Platform.swift b/Sources/FoundationEssentials/Platform.swift index a7aad439e6..1687553f81 100644 --- a/Sources/FoundationEssentials/Platform.swift +++ b/Sources/FoundationEssentials/Platform.swift @@ -164,7 +164,7 @@ extension Platform { } static func gid(forName name: String) -> gid_t? { - withUserGroupBuffer(name, group(), sizeProperty: Int32(_SC_GETGR_R_SIZE_MAX), operation: getgrnam_r) { + withUserGroupBuffer(name, group(), sizeProperty: Int32(_SC_GETGR_R_SIZE_MAX), operation: _filemanager_shims_getgrnam_r) { $0.gr_gid } } @@ -193,7 +193,7 @@ extension Platform { } static func name(forGID gid: gid_t) -> String? { - withUserGroupBuffer(gid, group(), sizeProperty: Int32(_SC_GETGR_R_SIZE_MAX), operation: getgrgid_r) { + withUserGroupBuffer(gid, group(), sizeProperty: Int32(_SC_GETGR_R_SIZE_MAX), operation: _filemanager_shims_getgrgid_r) { // Android's gr_name `char *`` is nullable when it should be non-null. // FIXME: avoid the coerce cast workaround once https://github.com/android/ndk/issues/2098 is fixed. let gr_name: UnsafeMutablePointer? = $0.gr_name diff --git a/Sources/_FoundationCShims/include/filemanager_shims.h b/Sources/_FoundationCShims/include/filemanager_shims.h index 6fd8a7cc88..820a87befe 100644 --- a/Sources/_FoundationCShims/include/filemanager_shims.h +++ b/Sources/_FoundationCShims/include/filemanager_shims.h @@ -14,6 +14,7 @@ #define CSHIMS_FILEMANAGER_H #include "_CShimsMacros.h" +#include "_CShimsTargetConditionals.h" #if __has_include() #include @@ -46,4 +47,81 @@ extern int _mkpath_np(const char *path, mode_t omode, const char **firstdir); #endif +#include + +#if TARGET_OS_ANDROID && __ANDROID_API__ <= 23 +#include +#include +#include + +static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, + char *buf, size_t buflen, struct group **result) { + // Call the non-reentrant version. + // On Android, this uses Thread Local Storage (TLS), + // so it is safe from race conditions with other threads. + struct group *p = getgrgid(gid); + + if (p == NULL) { + *result = NULL; + return errno != 0 ? errno : 0; + } + + size_t name_len = strlen(p->gr_name) + 1; + if (name_len > buflen) { + *result = NULL; + return ERANGE; + } + + strcpy(buf, p->gr_name); + grp->gr_name = buf; + grp->gr_gid = p->gr_gid; + + grp->gr_passwd = (char *)""; + grp->gr_mem = NULL; + + *result = grp; + return 0; +} + +static inline int _filemanager_shims_getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **result) { + // Call the non-reentrant version. + // On Android, this uses Thread Local Storage (TLS), + // so it is safe from race conditions with other threads. + struct group *p = getgrnam(name); + + if (p == NULL) { + *result = NULL; + return errno != 0 ? errno : 0; + } + + size_t name_len = strlen(p->gr_name) + 1; + if (name_len > buflen) { + *result = NULL; + return ERANGE; + } + + strcpy(buf, p->gr_name); + grp->gr_name = buf; + grp->gr_gid = p->gr_gid; + + grp->gr_passwd = (char *)""; + grp->gr_mem = NULL; + + *result = grp; + return 0; +} + +#else +static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, + char *buf, size_t buflen, struct group **result) { + return getgrgid_r(gid, grp, buf, buflen, result); +} + +static inline int _filemanager_shims_getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **result) { + return getgrnam_r(name, grp, buf, buflen, result); +} +#endif + #endif // CSHIMS_FILEMANAGER_H From e30e7fb33c17213b8c9a0a20807e9066d8264a0b Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Thu, 25 Dec 2025 20:46:01 +0100 Subject: [PATCH 2/5] fix errno and target platforms --- .../_FoundationCShims/include/filemanager_shims.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/_FoundationCShims/include/filemanager_shims.h b/Sources/_FoundationCShims/include/filemanager_shims.h index 820a87befe..7348141a15 100644 --- a/Sources/_FoundationCShims/include/filemanager_shims.h +++ b/Sources/_FoundationCShims/include/filemanager_shims.h @@ -47,15 +47,16 @@ extern int _mkpath_np(const char *path, mode_t omode, const char **firstdir); #endif -#include - #if TARGET_OS_ANDROID && __ANDROID_API__ <= 23 +#include #include #include #include static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result) { + errno = 0; + // Call the non-reentrant version. // On Android, this uses Thread Local Storage (TLS), // so it is safe from race conditions with other threads. @@ -63,7 +64,7 @@ static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, if (p == NULL) { *result = NULL; - return errno != 0 ? errno : 0; + return errno; } size_t name_len = strlen(p->gr_name) + 1; @@ -85,6 +86,8 @@ static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, static inline int _filemanager_shims_getgrnam_r(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result) { + errno = 0; + // Call the non-reentrant version. // On Android, this uses Thread Local Storage (TLS), // so it is safe from race conditions with other threads. @@ -92,7 +95,7 @@ static inline int _filemanager_shims_getgrnam_r(const char *name, struct group * if (p == NULL) { *result = NULL; - return errno != 0 ? errno : 0; + return errno; } size_t name_len = strlen(p->gr_name) + 1; @@ -112,7 +115,9 @@ static inline int _filemanager_shims_getgrnam_r(const char *name, struct group * return 0; } -#else +#elif !TARGET_OS_WINDOWS && !TARGET_OS_WASI +#include + static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result) { return getgrgid_r(gid, grp, buf, buflen, result); From 0b13b30578f2a0375814745c2f28ee3d6267f6e4 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Sat, 7 Feb 2026 11:52:47 +0100 Subject: [PATCH 3/5] PR feedback --- .../include/filemanager_shims.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Sources/_FoundationCShims/include/filemanager_shims.h b/Sources/_FoundationCShims/include/filemanager_shims.h index 7348141a15..eedcf8fad9 100644 --- a/Sources/_FoundationCShims/include/filemanager_shims.h +++ b/Sources/_FoundationCShims/include/filemanager_shims.h @@ -67,17 +67,20 @@ static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, return errno; } - size_t name_len = strlen(p->gr_name) + 1; - if (name_len > buflen) { + if (strlcpy(buf, p->gr_name, buflen) >= buflen) { *result = NULL; return ERANGE; } - strcpy(buf, p->gr_name); grp->gr_name = buf; grp->gr_gid = p->gr_gid; + // Android Bionic actually leaves this as NULL, but just use "" for safety. grp->gr_passwd = (char *)""; + + // Android Bionic generates a synthetic list ["groupname", NULL]. + // Replicating that here would require deep copying the strings array. + // Foundation does not use this either, so NULL is sufficient and avoids complexity. grp->gr_mem = NULL; *result = grp; @@ -98,17 +101,20 @@ static inline int _filemanager_shims_getgrnam_r(const char *name, struct group * return errno; } - size_t name_len = strlen(p->gr_name) + 1; - if (name_len > buflen) { + if (strlcpy(buf, p->gr_name, buflen) >= buflen) { *result = NULL; return ERANGE; } - strcpy(buf, p->gr_name); grp->gr_name = buf; grp->gr_gid = p->gr_gid; + // Android Bionic actually leaves this as NULL, but just use "" for safety. grp->gr_passwd = (char *)""; + + // Android Bionic generates a synthetic list ["groupname", NULL]. + // Replicating that here would require deep copying the strings array. + // Foundation does not use this either, so NULL is sufficient and avoids complexity. grp->gr_mem = NULL; *result = grp; From 0d5edb9ff3ea5bb38fd90158257ef064b365381e Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 9 Feb 2026 20:30:08 +0100 Subject: [PATCH 4/5] set passwd to NULL --- Sources/_FoundationCShims/include/filemanager_shims.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/_FoundationCShims/include/filemanager_shims.h b/Sources/_FoundationCShims/include/filemanager_shims.h index eedcf8fad9..c68681515a 100644 --- a/Sources/_FoundationCShims/include/filemanager_shims.h +++ b/Sources/_FoundationCShims/include/filemanager_shims.h @@ -75,8 +75,8 @@ static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp, grp->gr_name = buf; grp->gr_gid = p->gr_gid; - // Android Bionic actually leaves this as NULL, but just use "" for safety. - grp->gr_passwd = (char *)""; + // Android Bionic leaves this as NULL. + grp->gr_passwd = NULL; // Android Bionic generates a synthetic list ["groupname", NULL]. // Replicating that here would require deep copying the strings array. @@ -109,8 +109,8 @@ static inline int _filemanager_shims_getgrnam_r(const char *name, struct group * grp->gr_name = buf; grp->gr_gid = p->gr_gid; - // Android Bionic actually leaves this as NULL, but just use "" for safety. - grp->gr_passwd = (char *)""; + // Android Bionic leaves this as NULL. + grp->gr_passwd = NULL; // Android Bionic generates a synthetic list ["groupname", NULL]. // Replicating that here would require deep copying the strings array. From 87bb8a5b98015b50ea8f8182d657eee0cd64cddf Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Wed, 11 Feb 2026 08:42:37 +0100 Subject: [PATCH 5/5] replace other targets by has_include --- Sources/_FoundationCShims/include/filemanager_shims.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/_FoundationCShims/include/filemanager_shims.h b/Sources/_FoundationCShims/include/filemanager_shims.h index c68681515a..9b45a05608 100644 --- a/Sources/_FoundationCShims/include/filemanager_shims.h +++ b/Sources/_FoundationCShims/include/filemanager_shims.h @@ -121,7 +121,7 @@ static inline int _filemanager_shims_getgrnam_r(const char *name, struct group * return 0; } -#elif !TARGET_OS_WINDOWS && !TARGET_OS_WASI +#elif __has_include() #include static inline int _filemanager_shims_getgrgid_r(gid_t gid, struct group *grp,