On macOS, getxattr() is surprisingly slow, several times slower than running listxattr on the same file. (It seems like getxattr() is doing security checks before determining whether the xattr exists or not).
On macOS 14.5, timing for 30000 calls to the following functions (the target file contains no xattrs): ``` lstat: 29729459 ns getxattr: 110323334 ns listxattr: 14672500 ns ```
And a Wine test that calls FindFirstFile 2500 times with a path of "C:\windows\system32\*.nonexistent": ``` currently: 12119 ms with this patch: 5580 ms ```
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/ntdll/unix/file.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index d1a6573ed69..c6105255330 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -408,8 +408,42 @@ static int xattr_fset( int filedes, const char *name, const void *value, size_t }
+/* On macOS, getxattr() is significantly slower than listxattr() + * (even for files with no extended attributes). + */ +#ifdef __APPLE__ +static BOOL xattr_exists( const char **path, int *filedes, const char *name ) +{ + char xattrs[1024]; + ssize_t i = 0, ret; + + if (path) + ret = listxattr( *path, xattrs, sizeof(xattrs), 0 ); + else + ret = flistxattr( *filedes, xattrs, sizeof(xattrs), 0 ); + if (ret == -1) + return errno == ERANGE ? TRUE : FALSE; + + while (i < ret) + { + if (!strcmp( name, &xattrs[i] )) + return TRUE; + i += strlen(&xattrs[i]) + 1; + } + + errno = ENOATTR; + return FALSE; +} +#endif + + static int xattr_get( const char *path, const char *name, void *value, size_t size ) { +#ifdef __APPLE__ + if (!xattr_exists( &path, NULL, name )) + return -1; +#endif + #ifdef HAVE_SYS_XATTR_H # ifdef XATTR_ADDITIONAL_OPTIONS return getxattr( path, name, value, size, 0, 0 ); @@ -428,6 +462,11 @@ static int xattr_get( const char *path, const char *name, void *value, size_t si
static int xattr_fget( int filedes, const char *name, void *value, size_t size ) { +#ifdef __APPLE__ + if (!xattr_exists( NULL, &filedes, name )) + return -1; +#endif + #ifdef HAVE_SYS_XATTR_H # ifdef XATTR_ADDITIONAL_OPTIONS return fgetxattr( filedes, name, value, size, 0, 0 );