[libc-commits] [libc] [libc] Add the get and set sys/xattr.h entrypoints. (PR #224729)

Alex Strelnikov via libc-commits libc-commits at lists.llvm.org
Thu Sep 24 07:45:05 PDT 2026


https://github.com/strel-12 updated https://github.com/llvm/llvm-project/pull/224729

>From 8ee513607279a518bc14b5a5f5d2e06a3cbfb39b Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Fri, 18 Sep 2026 19:54:59 +0000
Subject: [PATCH 1/6] Add the get and set sys/xattr.h entrypoints.

---
 libc/config/linux/aarch64/entrypoints.txt     |   6 +
 libc/config/linux/x86_64/entrypoints.txt      |   6 +
 libc/hdr/CMakeLists.txt                       |   8 +
 libc/hdr/sys_xattr_macros.h                   |  27 +++
 libc/include/CMakeLists.txt                   |   1 +
 libc/include/llvm-libc-macros/CMakeLists.txt  |   6 +
 .../llvm-libc-macros/linux/CMakeLists.txt     |   6 +
 .../llvm-libc-macros/linux/sys-xattr-macros.h |  20 ++
 .../llvm-libc-macros/sys-xattr-macros.h       |  21 ++
 libc/include/sys/xattr.yaml                   |  63 +++++-
 .../linux/syscall_wrappers/CMakeLists.txt     |  72 +++++++
 .../OSUtil/linux/syscall_wrappers/fgetxattr.h |  34 ++++
 .../OSUtil/linux/syscall_wrappers/fsetxattr.h |  34 ++++
 .../OSUtil/linux/syscall_wrappers/getxattr.h  |  34 ++++
 .../OSUtil/linux/syscall_wrappers/lgetxattr.h |  34 ++++
 .../OSUtil/linux/syscall_wrappers/lsetxattr.h |  34 ++++
 .../OSUtil/linux/syscall_wrappers/setxattr.h  |  34 ++++
 libc/src/sys/xattr/CMakeLists.txt             |  42 ++++
 libc/src/sys/xattr/fgetxattr.h                |  27 +++
 libc/src/sys/xattr/fsetxattr.h                |  27 +++
 libc/src/sys/xattr/getxattr.h                 |  27 +++
 libc/src/sys/xattr/lgetxattr.h                |  27 +++
 libc/src/sys/xattr/linux/CMakeLists.txt       |  90 ++++++++
 libc/src/sys/xattr/linux/fgetxattr.cpp        |  35 ++++
 libc/src/sys/xattr/linux/fsetxattr.cpp        |  38 ++++
 libc/src/sys/xattr/linux/getxattr.cpp         |  37 ++++
 libc/src/sys/xattr/linux/lgetxattr.cpp        |  37 ++++
 libc/src/sys/xattr/linux/lsetxattr.cpp        |  39 ++++
 libc/src/sys/xattr/linux/setxattr.cpp         |  39 ++++
 libc/src/sys/xattr/lsetxattr.h                |  27 +++
 libc/src/sys/xattr/setxattr.h                 |  27 +++
 libc/test/src/sys/xattr/CMakeLists.txt        | 134 ++++++++++++
 libc/test/src/sys/xattr/fgetxattr_test.cpp    | 123 +++++++++++
 libc/test/src/sys/xattr/fsetxattr_test.cpp    | 158 ++++++++++++++
 libc/test/src/sys/xattr/getxattr_test.cpp     | 158 ++++++++++++++
 libc/test/src/sys/xattr/lgetxattr_test.cpp    | 148 ++++++++++++++
 libc/test/src/sys/xattr/lsetxattr_test.cpp    | 164 +++++++++++++++
 libc/test/src/sys/xattr/setxattr_test.cpp     | 192 ++++++++++++++++++
 38 files changed, 2035 insertions(+), 1 deletion(-)
 create mode 100644 libc/hdr/sys_xattr_macros.h
 create mode 100644 libc/include/llvm-libc-macros/linux/sys-xattr-macros.h
 create mode 100644 libc/include/llvm-libc-macros/sys-xattr-macros.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
 create mode 100644 libc/src/sys/xattr/fgetxattr.h
 create mode 100644 libc/src/sys/xattr/fsetxattr.h
 create mode 100644 libc/src/sys/xattr/getxattr.h
 create mode 100644 libc/src/sys/xattr/lgetxattr.h
 create mode 100644 libc/src/sys/xattr/linux/fgetxattr.cpp
 create mode 100644 libc/src/sys/xattr/linux/fsetxattr.cpp
 create mode 100644 libc/src/sys/xattr/linux/getxattr.cpp
 create mode 100644 libc/src/sys/xattr/linux/lgetxattr.cpp
 create mode 100644 libc/src/sys/xattr/linux/lsetxattr.cpp
 create mode 100644 libc/src/sys/xattr/linux/setxattr.cpp
 create mode 100644 libc/src/sys/xattr/lsetxattr.h
 create mode 100644 libc/src/sys/xattr/setxattr.h
 create mode 100644 libc/test/src/sys/xattr/fgetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/fsetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/getxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/lgetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/lsetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/setxattr_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index fe1c2965f778b..20cff8a4b8f2d 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -385,6 +385,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.wait.waitpid
 
     # sys/xattr.h entrypoints
+    libc.src.sys.xattr.fgetxattr
+    libc.src.sys.xattr.getxattr
+    libc.src.sys.xattr.lgetxattr
+    libc.src.sys.xattr.fsetxattr
+    libc.src.sys.xattr.setxattr
+    libc.src.sys.xattr.lsetxattr
     libc.src.sys.xattr.flistxattr
     libc.src.sys.xattr.listxattr
     libc.src.sys.xattr.llistxattr
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index f21d48d6ef9e7..2e5b944eb2852 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -424,6 +424,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.wait.waitpid
 
     # sys/xattr.h entrypoints
+    libc.src.sys.xattr.fgetxattr
+    libc.src.sys.xattr.getxattr
+    libc.src.sys.xattr.lgetxattr
+    libc.src.sys.xattr.fsetxattr
+    libc.src.sys.xattr.setxattr
+    libc.src.sys.xattr.lsetxattr
     libc.src.sys.xattr.flistxattr
     libc.src.sys.xattr.listxattr
     libc.src.sys.xattr.llistxattr
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index fa01bc55785da..58f1b55946514 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -274,6 +274,14 @@ add_proxy_header_library(
     libc.include.llvm-libc-macros.sys_wait_macros
 )
 
+add_proxy_header_library(
+  sys_xattr_macros
+  HDRS
+    sys_xattr_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-macros.sys_xattr_macros
+)
+
 add_proxy_header_library(
   termios_macros
   HDRS
diff --git a/libc/hdr/sys_xattr_macros.h b/libc/hdr/sys_xattr_macros.h
new file mode 100644
index 0000000000000..b65fa7958511c
--- /dev/null
+++ b/libc/hdr/sys_xattr_macros.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for macro values defined in sys/xattr.h.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_SYS_XATTR_MACROS_H
+#define LLVM_LIBC_HDR_SYS_XATTR_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-xattr-macros.h"
+
+#else // Overlay mode
+
+#include <sys/xattr.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS_XATTR_MACROS_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index a45026de31ee5..805b97ed9017a 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -1103,6 +1103,7 @@ add_header_macro(
   sys/xattr.h
   DEPENDS
     .llvm_libc_common_h
+    .llvm-libc-macros.sys_xattr_macros
     .llvm-libc-types.size_t
     .llvm-libc-types.ssize_t
 )
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 9e9559056dae4..5600fcdca21a1 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -383,6 +383,12 @@ add_macro_header(
     sys-wait-macros.h
 )
 
+add_macro_header(
+  sys_xattr_macros
+  HDR
+    sys-xattr-macros.h
+)
+
 add_macro_header(
   sys_sysmacros_macros
   HDR
diff --git a/libc/include/llvm-libc-macros/linux/CMakeLists.txt b/libc/include/llvm-libc-macros/linux/CMakeLists.txt
index 5a032252d9893..a634b0a9eb9e9 100644
--- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt
@@ -100,6 +100,12 @@ add_header(
     sys-wait-macros.h
 )
 
+add_header(
+  sys_xattr_macros
+  HDR
+    sys-xattr-macros.h
+)
+
 add_header(
   sys_sysmacros_macros
   HDR
diff --git a/libc/include/llvm-libc-macros/linux/sys-xattr-macros.h b/libc/include/llvm-libc-macros/linux/sys-xattr-macros.h
new file mode 100644
index 0000000000000..37fcb5b47a2bb
--- /dev/null
+++ b/libc/include/llvm-libc-macros/linux/sys-xattr-macros.h
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux specific declarations of macros from sys/xattr.h.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_MACROS_LINUX_SYS_XATTR_MACROS_H
+#define LLVM_LIBC_MACROS_LINUX_SYS_XATTR_MACROS_H
+
+#define XATTR_CREATE 1
+#define XATTR_REPLACE 2
+
+#endif // LLVM_LIBC_MACROS_LINUX_SYS_XATTR_MACROS_H
diff --git a/libc/include/llvm-libc-macros/sys-xattr-macros.h b/libc/include/llvm-libc-macros/sys-xattr-macros.h
new file mode 100644
index 0000000000000..37eba86749621
--- /dev/null
+++ b/libc/include/llvm-libc-macros/sys-xattr-macros.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Macros defined in sys/xattr.h.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_MACROS_SYS_XATTR_MACROS_H
+#define LLVM_LIBC_MACROS_SYS_XATTR_MACROS_H
+
+#ifdef __linux__
+#include "linux/sys-xattr-macros.h"
+#endif
+
+#endif // LLVM_LIBC_MACROS_SYS_XATTR_MACROS_H
diff --git a/libc/include/sys/xattr.yaml b/libc/include/sys/xattr.yaml
index 5f8b6174af025..0f680630c34a6 100644
--- a/libc/include/sys/xattr.yaml
+++ b/libc/include/sys/xattr.yaml
@@ -1,13 +1,74 @@
 header: sys/xattr.h
 standards:
   - linux
-macros: []
+macros:
+  - macro_name: XATTR_CREATE
+    macro_header: sys-xattr-macros.h
+  - macro_name: XATTR_REPLACE
+    macro_header: sys-xattr-macros.h
 types:
   - type_name: size_t
   - type_name: ssize_t
 enums: []
 objects: []
 functions:
+  - name: getxattr
+    standards:
+      - linux
+    return_type: ssize_t
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: void *
+      - type: size_t
+  - name: lgetxattr
+    standards:
+      - linux
+    return_type: ssize_t
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: void *
+      - type: size_t
+  - name: fgetxattr
+    standards:
+      - linux
+    return_type: ssize_t
+    arguments:
+      - type: int
+      - type: const char *
+      - type: void *
+      - type: size_t
+  - name: setxattr
+    standards:
+      - linux
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: const void *
+      - type: size_t
+      - type: int
+  - name: lsetxattr
+    standards:
+      - linux
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: const void *
+      - type: size_t
+      - type: int
+  - name: fsetxattr
+    standards:
+      - linux
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: const void *
+      - type: size_t
+      - type: int
   - name: listxattr
     standards:
       - linux
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index f5f28f51f49f6..99d1792c85696 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -215,6 +215,78 @@ add_header_library(
     libc.include.sys_syscall
 )
 
+add_header_library(
+  fgetxattr
+  HDRS
+    fgetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  getxattr
+  HDRS
+    getxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  lgetxattr
+  HDRS
+    lgetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  fsetxattr
+  HDRS
+    fsetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  setxattr
+  HDRS
+    setxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  lsetxattr
+  HDRS
+    lsetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
 add_header_library(
   flistxattr
   HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
new file mode 100644
index 0000000000000..e37b9c787f22b
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for fgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FGETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FGETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<ssize_t> fgetxattr(int fd, const char *name, void *value,
+                                       size_t size) {
+  return syscall_checked<ssize_t>(SYS_fgetxattr, fd, name, value, size);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FGETXATTR_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
new file mode 100644
index 0000000000000..cd818266d59a1
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for fsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> fsetxattr(int fd, const char *name, const void *value,
+                                   size_t size, int flags) {
+  return syscall_checked<int>(SYS_fsetxattr, fd, name, value, size, flags);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSETXATTR_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
new file mode 100644
index 0000000000000..cfc532eadfb4a
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for getxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<ssize_t> getxattr(const char *path, const char *name,
+                                      void *value, size_t size) {
+  return syscall_checked<ssize_t>(SYS_getxattr, path, name, value, size);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETXATTR_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
new file mode 100644
index 0000000000000..1975732642df4
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for lgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LGETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LGETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<ssize_t> lgetxattr(const char *path, const char *name,
+                                       void *value, size_t size) {
+  return syscall_checked<ssize_t>(SYS_lgetxattr, path, name, value, size);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LGETXATTR_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
new file mode 100644
index 0000000000000..2eb2663cbd3ec
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for lsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LSETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LSETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> lsetxattr(const char *path, const char *name,
+                                   const void *value, size_t size, int flags) {
+  return syscall_checked<int>(SYS_lsetxattr, path, name, value, size, flags);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LSETXATTR_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
new file mode 100644
index 0000000000000..c10a8455fd92b
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for setxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETXATTR_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETXATTR_H
+
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> setxattr(const char *path, const char *name,
+                                  const void *value, size_t size, int flags) {
+  return syscall_checked<int>(SYS_setxattr, path, name, value, size, flags);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETXATTR_H
diff --git a/libc/src/sys/xattr/CMakeLists.txt b/libc/src/sys/xattr/CMakeLists.txt
index d008639bddf0d..b02fd40ea7de3 100644
--- a/libc/src/sys/xattr/CMakeLists.txt
+++ b/libc/src/sys/xattr/CMakeLists.txt
@@ -2,6 +2,48 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
 endif()
 
+add_entrypoint_object(
+  fgetxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.fgetxattr
+)
+
+add_entrypoint_object(
+  getxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.getxattr
+)
+
+add_entrypoint_object(
+  lgetxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.lgetxattr
+)
+
+add_entrypoint_object(
+  fsetxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.fsetxattr
+)
+
+add_entrypoint_object(
+  setxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.setxattr
+)
+
+add_entrypoint_object(
+  lsetxattr
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.lsetxattr
+)
+
 add_entrypoint_object(
   flistxattr
   ALIAS
diff --git a/libc/src/sys/xattr/fgetxattr.h b/libc/src/sys/xattr/fgetxattr.h
new file mode 100644
index 0000000000000..68f24e1af28f0
--- /dev/null
+++ b/libc/src/sys/xattr/fgetxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of fgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+ssize_t fgetxattr(int fd, const char *name, void *value, size_t size);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
diff --git a/libc/src/sys/xattr/fsetxattr.h b/libc/src/sys/xattr/fsetxattr.h
new file mode 100644
index 0000000000000..75eb85df31f13
--- /dev/null
+++ b/libc/src/sys/xattr/fsetxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of fsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int fsetxattr(int fd, const char *name, const void *value, size_t size,
+              int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
diff --git a/libc/src/sys/xattr/getxattr.h b/libc/src/sys/xattr/getxattr.h
new file mode 100644
index 0000000000000..45668baa4bea2
--- /dev/null
+++ b/libc/src/sys/xattr/getxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of getxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+ssize_t getxattr(const char *path, const char *name, void *value, size_t size);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
diff --git a/libc/src/sys/xattr/lgetxattr.h b/libc/src/sys/xattr/lgetxattr.h
new file mode 100644
index 0000000000000..525dd5c7c1603
--- /dev/null
+++ b/libc/src/sys/xattr/lgetxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of lgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
diff --git a/libc/src/sys/xattr/linux/CMakeLists.txt b/libc/src/sys/xattr/linux/CMakeLists.txt
index 0d80394c7f8f1..e9a48a8cc6581 100644
--- a/libc/src/sys/xattr/linux/CMakeLists.txt
+++ b/libc/src/sys/xattr/linux/CMakeLists.txt
@@ -1,3 +1,93 @@
+add_entrypoint_object(
+  fgetxattr
+  SRCS
+    fgetxattr.cpp
+  HDRS
+    ../fgetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.fgetxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  getxattr
+  SRCS
+    getxattr.cpp
+  HDRS
+    ../getxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.getxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  lgetxattr
+  SRCS
+    lgetxattr.cpp
+  HDRS
+    ../lgetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.lgetxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  fsetxattr
+  SRCS
+    fsetxattr.cpp
+  HDRS
+    ../fsetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.fsetxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  setxattr
+  SRCS
+    setxattr.cpp
+  HDRS
+    ../setxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.setxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  lsetxattr
+  SRCS
+    lsetxattr.cpp
+  HDRS
+    ../lsetxattr.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.lsetxattr
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   flistxattr
   SRCS
diff --git a/libc/src/sys/xattr/linux/fgetxattr.cpp b/libc/src/sys/xattr/linux/fgetxattr.cpp
new file mode 100644
index 0000000000000..b7ee5c9fe2e78
--- /dev/null
+++ b/libc/src/sys/xattr/linux/fgetxattr.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of fgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/fgetxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(ssize_t, fgetxattr,
+                   (int fd, const char *name, void *value, size_t size)) {
+  LIBC_CRASH_ON_NULLPTR(name);
+
+  ErrorOr<ssize_t> ret = linux_syscalls::fgetxattr(fd, name, value, size);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/linux/fsetxattr.cpp b/libc/src/sys/xattr/linux/fsetxattr.cpp
new file mode 100644
index 0000000000000..d63fb51201878
--- /dev/null
+++ b/libc/src/sys/xattr/linux/fsetxattr.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of fsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/fsetxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fsetxattr,
+                   (int fd, const char *name, const void *value, size_t size,
+                    int flags)) {
+  LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
+
+  ErrorOr<int> ret = linux_syscalls::fsetxattr(fd, name, value, size, flags);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/linux/getxattr.cpp b/libc/src/sys/xattr/linux/getxattr.cpp
new file mode 100644
index 0000000000000..d4f9361b8613b
--- /dev/null
+++ b/libc/src/sys/xattr/linux/getxattr.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of getxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/getxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(ssize_t, getxattr,
+                   (const char *path, const char *name, void *value,
+                    size_t size)) {
+  LIBC_CRASH_ON_NULLPTR(path);
+  LIBC_CRASH_ON_NULLPTR(name);
+
+  ErrorOr<ssize_t> ret = linux_syscalls::getxattr(path, name, value, size);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/linux/lgetxattr.cpp b/libc/src/sys/xattr/linux/lgetxattr.cpp
new file mode 100644
index 0000000000000..0e9885f1e5cad
--- /dev/null
+++ b/libc/src/sys/xattr/linux/lgetxattr.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of lgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/lgetxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(ssize_t, lgetxattr,
+                   (const char *path, const char *name, void *value,
+                    size_t size)) {
+  LIBC_CRASH_ON_NULLPTR(path);
+  LIBC_CRASH_ON_NULLPTR(name);
+
+  ErrorOr<ssize_t> ret = linux_syscalls::lgetxattr(path, name, value, size);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/linux/lsetxattr.cpp b/libc/src/sys/xattr/linux/lsetxattr.cpp
new file mode 100644
index 0000000000000..aea81c5842387
--- /dev/null
+++ b/libc/src/sys/xattr/linux/lsetxattr.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of lsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/lsetxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, lsetxattr,
+                   (const char *path, const char *name, const void *value,
+                    size_t size, int flags)) {
+  LIBC_CRASH_ON_NULLPTR(path);
+  LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
+
+  ErrorOr<int> ret = linux_syscalls::lsetxattr(path, name, value, size, flags);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/linux/setxattr.cpp b/libc/src/sys/xattr/linux/setxattr.cpp
new file mode 100644
index 0000000000000..f0158645329f5
--- /dev/null
+++ b/libc/src/sys/xattr/linux/setxattr.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of setxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/xattr/setxattr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/setxattr.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, setxattr,
+                   (const char *path, const char *name, const void *value,
+                    size_t size, int flags)) {
+  LIBC_CRASH_ON_NULLPTR(path);
+  LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
+
+  ErrorOr<int> ret = linux_syscalls::setxattr(path, name, value, size, flags);
+  if (!ret) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return *ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/xattr/lsetxattr.h b/libc/src/sys/xattr/lsetxattr.h
new file mode 100644
index 0000000000000..6e6170bc896de
--- /dev/null
+++ b/libc/src/sys/xattr/lsetxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of lsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int lsetxattr(const char *path, const char *name, const void *value,
+              size_t size, int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
diff --git a/libc/src/sys/xattr/setxattr.h b/libc/src/sys/xattr/setxattr.h
new file mode 100644
index 0000000000000..87e1bfe5f2d14
--- /dev/null
+++ b/libc/src/sys/xattr/setxattr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Function declaration of setxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
+
+#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int setxattr(const char *path, const char *name, const void *value, size_t size,
+             int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
diff --git a/libc/test/src/sys/xattr/CMakeLists.txt b/libc/test/src/sys/xattr/CMakeLists.txt
index 240cb2cc9c7a7..80adc0ec66268 100644
--- a/libc/test/src/sys/xattr/CMakeLists.txt
+++ b/libc/test/src/sys/xattr/CMakeLists.txt
@@ -1,5 +1,70 @@
 add_custom_target(libc_sys_xattr_unittests)
 
+add_libc_test(
+  fgetxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    fgetxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.fgetxattr
+    libc.src.unistd.close
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+  getxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    getxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.getxattr
+    libc.src.unistd.close
+    libc.src.unistd.symlink
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+  lgetxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    lgetxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.lgetxattr
+    libc.src.unistd.close
+    libc.src.unistd.symlink
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_test(
   flistxattr_test
   SUITE
@@ -21,6 +86,75 @@ add_libc_test(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_test(
+  setxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    setxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.hdr.sys_xattr_macros
+    libc.hdr.types.ssize_t
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.setxattr
+    libc.src.unistd.close
+    libc.src.unistd.symlink
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+  lsetxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    lsetxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.hdr.sys_xattr_macros
+    libc.hdr.types.ssize_t
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.lsetxattr
+    libc.src.unistd.close
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+  fsetxattr_test
+  SUITE
+    libc_sys_xattr_unittests
+  SRCS
+    fsetxattr_test.cpp
+  DEPENDS
+    libc.hdr.sys_stat_macros
+    libc.hdr.sys_xattr_macros
+    libc.include.sys_xattr
+    libc.src.__support.CPP.scope
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.creat
+    libc.src.sys.xattr.fsetxattr
+    libc.src.unistd.close
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_test(
   listxattr_test
   SUITE
diff --git a/libc/test/src/sys/xattr/fgetxattr_test.cpp b/libc/test/src/sys/xattr/fgetxattr_test.cpp
new file mode 100644
index 0000000000000..28846f0d722d0
--- /dev/null
+++ b/libc/test/src/sys/xattr/fgetxattr_test.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for fgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/fgetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcFgetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+TEST_F(LlvmLibcFgetxattrTest, WithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
+                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
+                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
+                   /* flags = */ 0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), nullptr, 0),
+              Succeeds<ssize_t>(XATTR_VALUE.size()));
+
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(
+        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+        Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+
+  // Call with insufficient buffer size and missing attribute to check failing
+  // errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    EXPECT_THAT(
+        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+        Fails<ssize_t>(ERANGE));
+    EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, "user.missing_test_attr", buffer,
+                                          BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::fgetxattr(fd, nullptr, buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcFgetxattrTest, CrashOnNullBufferNonZeroSize) {
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] { LIBC_NAMESPACE::fgetxattr(fd, "user.attr", nullptr, 32); },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/fsetxattr_test.cpp b/libc/test/src/sys/xattr/fsetxattr_test.cpp
new file mode 100644
index 0000000000000..4a5d78f18d54f
--- /dev/null
+++ b/libc/test/src/sys/xattr/fsetxattr_test.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for fsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/fsetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_default_flags.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ 0),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_nonzero_flags.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute exists.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        NEW_XATTR_VALUE.data(),
+                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::fsetxattr(fd, nullptr, buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] { LIBC_NAMESPACE::fsetxattr(fd, "user.attr", nullptr, 32); },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/getxattr_test.cpp b/libc/test/src/sys/xattr/getxattr_test.cpp
new file mode 100644
index 0000000000000..f9ce87af5e448
--- /dev/null
+++ b/libc/test/src/sys/xattr/getxattr_test.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for getxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/getxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/symlink.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcGetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+int recreate_test_symlink(const char *target, const char *linkpath) {
+  LIBC_NAMESPACE::unlink(linkpath);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::symlink(target, linkpath);
+}
+
+TEST_F(LlvmLibcGetxattrTest, WithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/getxattr.txt");
+  constexpr const char *TEST_SYMLINK_TARGET = "getxattr.txt";
+  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
+      libc_make_test_file_path("testdata/getxattr_symlink.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
+              Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
+                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
+                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
+                   /* flags = */ 0));
+
+  // Call with the test file path.
+  {
+    EXPECT_THAT(
+        LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(), nullptr, 0),
+        Succeeds<ssize_t>(XATTR_VALUE.size()));
+
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+
+  // Call with the symlink path to verify the correct syscall is used
+  // internally; getxattr instead of lgetxattr.
+  {
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                         nullptr, 0),
+                Succeeds<ssize_t>(XATTR_VALUE.size()));
+
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+
+  // Call with insufficient buffer size and missing attribute to check failing
+  // errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME,
+                                         "user.missing_test_attr", buffer,
+                                         BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcGetxattrTest, CrashOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::getxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcGetxattrTest, CrashOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::getxattr("testdata/file.txt", nullptr, buffer,
+                                 BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcGetxattrTest, CrashOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::getxattr("testdata/file.txt", "user.attr", nullptr, 32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/lgetxattr_test.cpp b/libc/test/src/sys/xattr/lgetxattr_test.cpp
new file mode 100644
index 0000000000000..fb0273f45b49d
--- /dev/null
+++ b/libc/test/src/sys/xattr/lgetxattr_test.cpp
@@ -0,0 +1,148 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for lgetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/lgetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/symlink.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcLGetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+int recreate_test_symlink(const char *target, const char *linkpath) {
+  LIBC_NAMESPACE::unlink(linkpath);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::symlink(target, linkpath);
+}
+
+TEST_F(LlvmLibcLGetxattrTest, WithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lgetxattr.txt");
+  constexpr const char *TEST_SYMLINK_TARGET = "lgetxattr.txt";
+  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
+      libc_make_test_file_path("testdata/lgetxattr_symlink.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
+              Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
+                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
+                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
+                   /* flags = */ 0));
+
+  // Call with the test file path.
+  {
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          nullptr, 0),
+                Succeeds<ssize_t>(XATTR_VALUE.size()));
+
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+
+  // Call with the symlink path to verify the correct syscall is used
+  // internally; lgetxattr instead of getxattr.
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+
+  // Call with insufficient buffer size to check failing errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+
+    char buffer[BUFFER_SIZE] = {};
+
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+  }
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcLGetxattrTest, CrashOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lgetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLGetxattrTest, CrashOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", nullptr, buffer,
+                                  BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLGetxattrTest, CrashOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", "user.attr", nullptr,
+                                  32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/lsetxattr_test.cpp b/libc/test/src/sys/xattr/lsetxattr_test.cpp
new file mode 100644
index 0000000000000..fa770c8e7e2fc
--- /dev/null
+++ b/libc/test/src/sys/xattr/lsetxattr_test.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for lsetxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/lsetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lsetxattr_default_flags.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  // Set an attribute through the test file name.
+  {
+    string_view XATTR_NAME = "user.test_attr_through_file";
+    string_view XATTR_VALUE = "test_value_through_file";
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          XATTR_VALUE.data(),
+                                          XATTR_VALUE.size(),
+                                          /* flags = */ 0),
+                Succeeds(0));
+
+    ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+              LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
+    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+  }
+
+  // TODO: Figure out if/how to test `lsetxattr` with a symlink path. The "user"
+  // namespace extended attributes appear restricted to regular files and
+  // directories. Attempting to set a "user" extended attribute on a symlink
+  // results in EPERM.
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lsetxattr_nonzero_flags.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute exists.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        NEW_XATTR_VALUE.data(),
+                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lsetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", nullptr, buffer,
+                                  BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr,
+                                  32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/setxattr_test.cpp b/libc/test/src/sys/xattr/setxattr_test.cpp
new file mode 100644
index 0000000000000..1e31cbfc4b28a
--- /dev/null
+++ b/libc/test/src/sys/xattr/setxattr_test.cpp
@@ -0,0 +1,192 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for setxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/OSUtil/linux/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/setxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/symlink.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <sys/syscall.h>
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+int recreate_test_symlink(const char *target, const char *linkpath) {
+  LIBC_NAMESPACE::unlink(linkpath);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::symlink(target, linkpath);
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/setxattr_default_flags.txt");
+  constexpr const char *TEST_SYMLINK_TARGET = "setxattr_default_flags.txt";
+  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
+      libc_make_test_file_path("testdata/setxattr_default_flags_symlink.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
+              Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  // Set an attribute through the test file name.
+  {
+    string_view XATTR_NAME = "user.test_attr_through_file";
+    string_view XATTR_VALUE = "test_value_through_file";
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                         XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                         /* flags = */ 0),
+                Succeeds(0));
+
+    ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+              LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
+    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+  }
+
+  // Set another attribute through the symlink to verify the correct syscall is
+  // used internally; setxattr instead of lsetxattr.
+  {
+    string_view XATTR_NAME = "user.test_attr_through_symlink";
+    string_view XATTR_VALUE = "test_value_through_symlink";
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                         XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                         /* flags = */ 0),
+                Succeeds(0));
+
+    EXPECT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+              LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
+    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+  }
+}
+
+TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/setxattr_nonzero_flags.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute exists.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       NEW_XATTR_VALUE.data(),
+                                       NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
+            LIBC_NAMESPACE::syscall_impl<ssize_t>(
+                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
+                XATTR_NAME.data(), buffer, BUFFER_SIZE));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::setxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::setxattr("testdata/file.txt", nullptr, buffer,
+                                 BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace

>From a54839c13a61dfff202ffd7de966b10cd5ab2098 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Fri, 18 Sep 2026 20:08:09 +0000
Subject: [PATCH 2/6] Fix missing null checks

---
 libc/src/sys/xattr/linux/fgetxattr.cpp | 2 ++
 libc/src/sys/xattr/linux/getxattr.cpp  | 2 ++
 libc/src/sys/xattr/linux/lgetxattr.cpp | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/libc/src/sys/xattr/linux/fgetxattr.cpp b/libc/src/sys/xattr/linux/fgetxattr.cpp
index b7ee5c9fe2e78..cf4da7f98cbf6 100644
--- a/libc/src/sys/xattr/linux/fgetxattr.cpp
+++ b/libc/src/sys/xattr/linux/fgetxattr.cpp
@@ -23,6 +23,8 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(ssize_t, fgetxattr,
                    (int fd, const char *name, void *value, size_t size)) {
   LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
 
   ErrorOr<ssize_t> ret = linux_syscalls::fgetxattr(fd, name, value, size);
   if (!ret) {
diff --git a/libc/src/sys/xattr/linux/getxattr.cpp b/libc/src/sys/xattr/linux/getxattr.cpp
index d4f9361b8613b..1080158465800 100644
--- a/libc/src/sys/xattr/linux/getxattr.cpp
+++ b/libc/src/sys/xattr/linux/getxattr.cpp
@@ -25,6 +25,8 @@ LLVM_LIBC_FUNCTION(ssize_t, getxattr,
                     size_t size)) {
   LIBC_CRASH_ON_NULLPTR(path);
   LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
 
   ErrorOr<ssize_t> ret = linux_syscalls::getxattr(path, name, value, size);
   if (!ret) {
diff --git a/libc/src/sys/xattr/linux/lgetxattr.cpp b/libc/src/sys/xattr/linux/lgetxattr.cpp
index 0e9885f1e5cad..da3ef91e01a81 100644
--- a/libc/src/sys/xattr/linux/lgetxattr.cpp
+++ b/libc/src/sys/xattr/linux/lgetxattr.cpp
@@ -25,6 +25,8 @@ LLVM_LIBC_FUNCTION(ssize_t, lgetxattr,
                     size_t size)) {
   LIBC_CRASH_ON_NULLPTR(path);
   LIBC_CRASH_ON_NULLPTR(name);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(value);
 
   ErrorOr<ssize_t> ret = linux_syscalls::lgetxattr(path, name, value, size);
   if (!ret) {

>From e189225fa3afbb2a170d6e5856315c46cb562a98 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Fri, 18 Sep 2026 20:15:58 +0000
Subject: [PATCH 3/6] Fix up test names

---
 libc/test/src/sys/xattr/fgetxattr_test.cpp |  4 ++--
 libc/test/src/sys/xattr/fsetxattr_test.cpp | 10 +++++-----
 libc/test/src/sys/xattr/getxattr_test.cpp  |  6 +++---
 libc/test/src/sys/xattr/lgetxattr_test.cpp | 10 +++++-----
 libc/test/src/sys/xattr/lsetxattr_test.cpp | 12 ++++++------
 libc/test/src/sys/xattr/setxattr_test.cpp  | 12 ++++++------
 6 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/libc/test/src/sys/xattr/fgetxattr_test.cpp b/libc/test/src/sys/xattr/fgetxattr_test.cpp
index 28846f0d722d0..a4fde52fb8634 100644
--- a/libc/test/src/sys/xattr/fgetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/fgetxattr_test.cpp
@@ -88,7 +88,7 @@ TEST_F(LlvmLibcFgetxattrTest, WithUserExtendedAttribute) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
@@ -105,7 +105,7 @@ TEST(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcFgetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcFgetxattrTest, CrashOnNullBufferNonZeroSize) {
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
diff --git a/libc/test/src/sys/xattr/fsetxattr_test.cpp b/libc/test/src/sys/xattr/fsetxattr_test.cpp
index 4a5d78f18d54f..436042c7f2605 100644
--- a/libc/test/src/sys/xattr/fsetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/fsetxattr_test.cpp
@@ -29,7 +29,7 @@
 namespace {
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LlvmLibcFsetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using LIBC_NAMESPACE::cpp::scope_exit;
 using LIBC_NAMESPACE::cpp::string_view;
 
@@ -39,7 +39,7 @@ int recreate_test_file(const char *path) {
   return LIBC_NAMESPACE::creat(path, S_IRWXU);
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+TEST_F(LlvmLibcFsetxattrTest, SetAttributeDefaultFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/fsetxattr_default_flags.txt");
 
@@ -68,7 +68,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
   EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+TEST_F(LlvmLibcFsetxattrTest, SetAttributeWithNonzeroFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/fsetxattr_nonzero_flags.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
@@ -123,7 +123,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcFsetxattrTest, CrashOnNullAttributeName) {
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
@@ -140,7 +140,7 @@ TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcFsetxattrTest, CrashOnNullBufferNonZeroSize) {
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
diff --git a/libc/test/src/sys/xattr/getxattr_test.cpp b/libc/test/src/sys/xattr/getxattr_test.cpp
index f9ce87af5e448..73df59d85e30b 100644
--- a/libc/test/src/sys/xattr/getxattr_test.cpp
+++ b/libc/test/src/sys/xattr/getxattr_test.cpp
@@ -124,7 +124,7 @@ TEST_F(LlvmLibcGetxattrTest, WithUserExtendedAttribute) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcGetxattrTest, CrashOnNullPath) {
+TEST_F(LlvmLibcGetxattrTest, CrashOnNullPath) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -134,7 +134,7 @@ TEST(LlvmLibcGetxattrTest, CrashOnNullPath) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcGetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcGetxattrTest, CrashOnNullAttributeName) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -145,7 +145,7 @@ TEST(LlvmLibcGetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcGetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcGetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
         LIBC_NAMESPACE::getxattr("testdata/file.txt", "user.attr", nullptr, 32);
diff --git a/libc/test/src/sys/xattr/lgetxattr_test.cpp b/libc/test/src/sys/xattr/lgetxattr_test.cpp
index fb0273f45b49d..dbddcbe435a24 100644
--- a/libc/test/src/sys/xattr/lgetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/lgetxattr_test.cpp
@@ -28,7 +28,7 @@
 namespace {
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLGetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LlvmLibcLgetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using LIBC_NAMESPACE::cpp::scope_exit;
 using LIBC_NAMESPACE::cpp::string_view;
 
@@ -44,7 +44,7 @@ int recreate_test_symlink(const char *target, const char *linkpath) {
   return LIBC_NAMESPACE::symlink(target, linkpath);
 }
 
-TEST_F(LlvmLibcLGetxattrTest, WithUserExtendedAttribute) {
+TEST_F(LlvmLibcLgetxattrTest, WithUserExtendedAttribute) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/lgetxattr.txt");
   constexpr const char *TEST_SYMLINK_TARGET = "lgetxattr.txt";
@@ -113,7 +113,7 @@ TEST_F(LlvmLibcLGetxattrTest, WithUserExtendedAttribute) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcLGetxattrTest, CrashOnNullPath) {
+TEST_F(LlvmLibcLgetxattrTest, CrashOnNullPath) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -123,7 +123,7 @@ TEST(LlvmLibcLGetxattrTest, CrashOnNullPath) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLGetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcLgetxattrTest, CrashOnNullAttributeName) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -134,7 +134,7 @@ TEST(LlvmLibcLGetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLGetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcLgetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
         LIBC_NAMESPACE::lgetxattr("testdata/file.txt", "user.attr", nullptr,
diff --git a/libc/test/src/sys/xattr/lsetxattr_test.cpp b/libc/test/src/sys/xattr/lsetxattr_test.cpp
index fa770c8e7e2fc..57f95ba75cab5 100644
--- a/libc/test/src/sys/xattr/lsetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/lsetxattr_test.cpp
@@ -29,7 +29,7 @@
 namespace {
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LlvmLibcLsetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using LIBC_NAMESPACE::cpp::scope_exit;
 using LIBC_NAMESPACE::cpp::string_view;
 
@@ -39,7 +39,7 @@ int recreate_test_file(const char *path) {
   return LIBC_NAMESPACE::creat(path, S_IRWXU);
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+TEST_F(LlvmLibcLsetxattrTest, SetAttributeDefaultFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/lsetxattr_default_flags.txt");
 
@@ -77,7 +77,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
   // results in EPERM.
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+TEST_F(LlvmLibcLsetxattrTest, SetAttributeWithNonzeroFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/lsetxattr_nonzero_flags.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
@@ -129,7 +129,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
+TEST_F(LlvmLibcLsetxattrTest, CrashOnNullPath) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -139,7 +139,7 @@ TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcLsetxattrTest, CrashOnNullAttributeName) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -150,7 +150,7 @@ TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcLsetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
         LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr,
diff --git a/libc/test/src/sys/xattr/setxattr_test.cpp b/libc/test/src/sys/xattr/setxattr_test.cpp
index 1e31cbfc4b28a..c8a0cbe428baa 100644
--- a/libc/test/src/sys/xattr/setxattr_test.cpp
+++ b/libc/test/src/sys/xattr/setxattr_test.cpp
@@ -30,7 +30,7 @@
 namespace {
 
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LlvmLibcSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
 using LIBC_NAMESPACE::cpp::scope_exit;
 using LIBC_NAMESPACE::cpp::string_view;
 
@@ -46,7 +46,7 @@ int recreate_test_symlink(const char *target, const char *linkpath) {
   return LIBC_NAMESPACE::symlink(target, linkpath);
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
+TEST_F(LlvmLibcSetxattrTest, SetAttributeDefaultFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/setxattr_default_flags.txt");
   constexpr const char *TEST_SYMLINK_TARGET = "setxattr_default_flags.txt";
@@ -106,7 +106,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeDefaultFlags) {
   }
 }
 
-TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
+TEST_F(LlvmLibcSetxattrTest, SetAttributeWithNonzeroFlags) {
   const LIBC_NAMESPACE::CString TEST_FILE_NAME =
       libc_make_test_file_path("testdata/setxattr_nonzero_flags.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
@@ -158,7 +158,7 @@ TEST_F(LlvmLibcLSetxattrTest, SetAttributeWithNonzeroFlags) {
 
 #if defined(LIBC_ADD_NULL_CHECKS)
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
+TEST_F(LlvmLibcSetxattrTest, CrashOnNullPath) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -168,7 +168,7 @@ TEST(LlvmLibcLSetxattrTest, CrashOnNullPath) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
+TEST_F(LlvmLibcSetxattrTest, CrashOnNullAttributeName) {
   EXPECT_DEATH(
       [] {
         constexpr size_t BUFFER_SIZE = 32;
@@ -179,7 +179,7 @@ TEST(LlvmLibcLSetxattrTest, CrashOnNullAttributeName) {
       WITH_SIGNAL(-1));
 }
 
-TEST(LlvmLibcLSetxattrTest, CrashOnNullBufferNonZeroSize) {
+TEST_F(LlvmLibcSetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
         LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32);

>From 8216b9b2f01a7d797b8825ef926be1d29b8dcbbc Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Tue, 22 Sep 2026 14:06:50 +0000
Subject: [PATCH 4/6] Update with review fixes.

- Fix fsetxattr arguments in xattr.yaml
- Add missing deps in syscal_wrappers/
- Fix stale header guards in src/sys/xattr/
- Add missing string_view dep in tests
- Fix null check tests
- Split file and symlink scope_cleanups, and add missing ones
---
 libc/include/sys/xattr.yaml                    |  2 +-
 .../linux/syscall_wrappers/CMakeLists.txt      |  9 +++++++++
 .../OSUtil/linux/syscall_wrappers/fgetxattr.h  |  2 ++
 .../OSUtil/linux/syscall_wrappers/fsetxattr.h  |  1 +
 .../OSUtil/linux/syscall_wrappers/getxattr.h   |  2 ++
 .../OSUtil/linux/syscall_wrappers/lgetxattr.h  |  2 ++
 .../OSUtil/linux/syscall_wrappers/lsetxattr.h  |  1 +
 .../OSUtil/linux/syscall_wrappers/setxattr.h   |  1 +
 libc/src/sys/xattr/fgetxattr.h                 |  6 +++---
 libc/src/sys/xattr/flistxattr.h                |  6 +++---
 libc/src/sys/xattr/fsetxattr.h                 |  6 +++---
 libc/src/sys/xattr/getxattr.h                  |  6 +++---
 libc/src/sys/xattr/lgetxattr.h                 |  6 +++---
 libc/src/sys/xattr/listxattr.h                 |  6 +++---
 libc/src/sys/xattr/llistxattr.h                |  6 +++---
 libc/src/sys/xattr/lsetxattr.h                 |  6 +++---
 libc/src/sys/xattr/setxattr.h                  |  6 +++---
 libc/test/src/sys/xattr/CMakeLists.txt         |  7 +++++++
 libc/test/src/sys/xattr/fgetxattr_test.cpp     |  5 +++++
 libc/test/src/sys/xattr/fsetxattr_test.cpp     | 13 +++++++++++--
 libc/test/src/sys/xattr/getxattr_test.cpp      |  7 +++++--
 libc/test/src/sys/xattr/lgetxattr_test.cpp     |  7 +++++--
 libc/test/src/sys/xattr/lsetxattr_test.cpp     | 17 +++++++++++------
 libc/test/src/sys/xattr/setxattr_test.cpp      | 18 +++++++++++++-----
 24 files changed, 103 insertions(+), 45 deletions(-)

diff --git a/libc/include/sys/xattr.yaml b/libc/include/sys/xattr.yaml
index 0f680630c34a6..3d1f30bd43370 100644
--- a/libc/include/sys/xattr.yaml
+++ b/libc/include/sys/xattr.yaml
@@ -64,7 +64,7 @@ functions:
       - linux
     return_type: int
     arguments:
-      - type: const char *
+      - type: int
       - type: const char *
       - type: const void *
       - type: size_t
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 99d1792c85696..f50c656bdf585 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -220,6 +220,8 @@ add_header_library(
   HDRS
     fgetxattr.h
   DEPENDS
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
@@ -232,6 +234,8 @@ add_header_library(
   HDRS
     getxattr.h
   DEPENDS
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
@@ -244,6 +248,8 @@ add_header_library(
   HDRS
     lgetxattr.h
   DEPENDS
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
@@ -256,6 +262,7 @@ add_header_library(
   HDRS
     fsetxattr.h
   DEPENDS
+    libc.hdr.types.size_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
@@ -268,6 +275,7 @@ add_header_library(
   HDRS
     setxattr.h
   DEPENDS
+    libc.hdr.types.size_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
@@ -280,6 +288,7 @@ add_header_library(
   HDRS
     lsetxattr.h
   DEPENDS
+    libc.hdr.types.size_t
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.error_or
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
index e37b9c787f22b..f5c30e1fcd6b6 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fgetxattr.h
@@ -14,6 +14,8 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FGETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FGETXATTR_H
 
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
index cd818266d59a1..81c90978d6204 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fsetxattr.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSETXATTR_H
 
+#include "hdr/types/size_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
index cfc532eadfb4a..2e90f4aab0728 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getxattr.h
@@ -14,6 +14,8 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETXATTR_H
 
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
index 1975732642df4..fec3700c4b387 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/lgetxattr.h
@@ -14,6 +14,8 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LGETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LGETXATTR_H
 
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
index 2eb2663cbd3ec..776a806986403 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/lsetxattr.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LSETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_LSETXATTR_H
 
+#include "hdr/types/size_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
index c10a8455fd92b..dda33e6a0e1ae 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/setxattr.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETXATTR_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETXATTR_H
 
+#include "hdr/types/size_t.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
diff --git a/libc/src/sys/xattr/fgetxattr.h b/libc/src/sys/xattr/fgetxattr.h
index 68f24e1af28f0..0f94a5355cbf5 100644
--- a/libc/src/sys/xattr/fgetxattr.h
+++ b/libc/src/sys/xattr/fgetxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_FGETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_FGETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t fgetxattr(int fd, const char *name, void *value, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_FGETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_FGETXATTR_H
diff --git a/libc/src/sys/xattr/flistxattr.h b/libc/src/sys/xattr/flistxattr.h
index 6493a413fc447..e27fc6814c528 100644
--- a/libc/src/sys/xattr/flistxattr.h
+++ b/libc/src/sys/xattr/flistxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_FLISTXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_FLISTXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_FLISTXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_FLISTXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t flistxattr(int fd, char *list, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_FLISTXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_FLISTXATTR_H
diff --git a/libc/src/sys/xattr/fsetxattr.h b/libc/src/sys/xattr/fsetxattr.h
index 75eb85df31f13..70458df410a3c 100644
--- a/libc/src/sys/xattr/fsetxattr.h
+++ b/libc/src/sys/xattr/fsetxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_FSETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_FSETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "src/__support/macros/config.h"
@@ -24,4 +24,4 @@ int fsetxattr(int fd, const char *name, const void *value, size_t size,
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_FSETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_FSETXATTR_H
diff --git a/libc/src/sys/xattr/getxattr.h b/libc/src/sys/xattr/getxattr.h
index 45668baa4bea2..30f21fabfcf4d 100644
--- a/libc/src/sys/xattr/getxattr.h
+++ b/libc/src/sys/xattr/getxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_GETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_GETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t getxattr(const char *path, const char *name, void *value, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_GETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_GETXATTR_H
diff --git a/libc/src/sys/xattr/lgetxattr.h b/libc/src/sys/xattr/lgetxattr.h
index 525dd5c7c1603..efd9670b7fabc 100644
--- a/libc/src/sys/xattr/lgetxattr.h
+++ b/libc/src/sys/xattr/lgetxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LGETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LGETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LGETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LGETXATTR_H
diff --git a/libc/src/sys/xattr/listxattr.h b/libc/src/sys/xattr/listxattr.h
index 535c8e6ce9186..d66c835860ef4 100644
--- a/libc/src/sys/xattr/listxattr.h
+++ b/libc/src/sys/xattr/listxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LISTXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LISTXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LISTXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LISTXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t listxattr(const char *path, char *list, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LISTXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LISTXATTR_H
diff --git a/libc/src/sys/xattr/llistxattr.h b/libc/src/sys/xattr/llistxattr.h
index 8dabae04fd93a..d8c207f06999b 100644
--- a/libc/src/sys/xattr/llistxattr.h
+++ b/libc/src/sys/xattr/llistxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LLISTXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LLISTXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LLISTXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LLISTXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "hdr/types/ssize_t.h"
@@ -24,4 +24,4 @@ ssize_t llistxattr(const char *path, char *list, size_t size);
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LLISTXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LLISTXATTR_H
diff --git a/libc/src/sys/xattr/lsetxattr.h b/libc/src/sys/xattr/lsetxattr.h
index 6e6170bc896de..38902fbb22d45 100644
--- a/libc/src/sys/xattr/lsetxattr.h
+++ b/libc/src/sys/xattr/lsetxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_LSETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_LSETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "src/__support/macros/config.h"
@@ -24,4 +24,4 @@ int lsetxattr(const char *path, const char *name, const void *value,
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_LSETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_LSETXATTR_H
diff --git a/libc/src/sys/xattr/setxattr.h b/libc/src/sys/xattr/setxattr.h
index 87e1bfe5f2d14..c615ebca25231 100644
--- a/libc/src/sys/xattr/setxattr.h
+++ b/libc/src/sys/xattr/setxattr.h
@@ -11,8 +11,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
-#define LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
+#ifndef LLVM_LIBC_SRC_SYS_XATTR_SETXATTR_H
+#define LLVM_LIBC_SRC_SYS_XATTR_SETXATTR_H
 
 #include "hdr/types/size_t.h"
 #include "src/__support/macros/config.h"
@@ -24,4 +24,4 @@ int setxattr(const char *path, const char *name, const void *value, size_t size,
 
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_SYS_XATTR_LINUX_SETXATTR_H
+#endif // LLVM_LIBC_SRC_SYS_XATTR_SETXATTR_H
diff --git a/libc/test/src/sys/xattr/CMakeLists.txt b/libc/test/src/sys/xattr/CMakeLists.txt
index 80adc0ec66268..e856de637c735 100644
--- a/libc/test/src/sys/xattr/CMakeLists.txt
+++ b/libc/test/src/sys/xattr/CMakeLists.txt
@@ -10,6 +10,7 @@ add_libc_test(
     libc.hdr.sys_stat_macros
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -31,6 +32,7 @@ add_libc_test(
     libc.hdr.sys_stat_macros
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -53,6 +55,7 @@ add_libc_test(
     libc.hdr.sys_stat_macros
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -75,6 +78,7 @@ add_libc_test(
     libc.hdr.sys_stat_macros
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -98,6 +102,7 @@ add_libc_test(
     libc.hdr.types.ssize_t
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -122,6 +127,7 @@ add_libc_test(
     libc.hdr.types.ssize_t
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
@@ -144,6 +150,7 @@ add_libc_test(
     libc.hdr.sys_xattr_macros
     libc.include.sys_xattr
     libc.src.__support.CPP.scope
+    libc.src.__support.CPP.string_view
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
diff --git a/libc/test/src/sys/xattr/fgetxattr_test.cpp b/libc/test/src/sys/xattr/fgetxattr_test.cpp
index a4fde52fb8634..6bf25f6c566af 100644
--- a/libc/test/src/sys/xattr/fgetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/fgetxattr_test.cpp
@@ -13,6 +13,7 @@
 
 #include "hdr/sys_stat_macros.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -89,6 +90,8 @@ TEST_F(LlvmLibcFgetxattrTest, WithUserExtendedAttribute) {
 #if defined(LIBC_ADD_NULL_CHECKS)
 
 TEST_F(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr_null_attribute.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
@@ -106,6 +109,8 @@ TEST_F(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
 }
 
 TEST_F(LlvmLibcFgetxattrTest, CrashOnNullBufferNonZeroSize) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr_null_buffer.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
diff --git a/libc/test/src/sys/xattr/fsetxattr_test.cpp b/libc/test/src/sys/xattr/fsetxattr_test.cpp
index 436042c7f2605..3e6477d6d0281 100644
--- a/libc/test/src/sys/xattr/fsetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/fsetxattr_test.cpp
@@ -15,6 +15,7 @@
 #include "hdr/sys_xattr_macros.h"
 #include "hdr/types/ssize_t.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -124,6 +125,8 @@ TEST_F(LlvmLibcFsetxattrTest, SetAttributeWithNonzeroFlags) {
 #if defined(LIBC_ADD_NULL_CHECKS)
 
 TEST_F(LlvmLibcFsetxattrTest, CrashOnNullAttributeName) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_null_attribute.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
@@ -135,12 +138,15 @@ TEST_F(LlvmLibcFsetxattrTest, CrashOnNullAttributeName) {
       [fd] {
         constexpr size_t BUFFER_SIZE = 32;
         char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::fsetxattr(fd, nullptr, buffer, BUFFER_SIZE);
+        LIBC_NAMESPACE::fsetxattr(fd, nullptr, buffer, BUFFER_SIZE,
+                                  /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
 
 TEST_F(LlvmLibcFsetxattrTest, CrashOnNullBufferNonZeroSize) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_null_buffer.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
   scope_exit cleanup([&] {
@@ -149,7 +155,10 @@ TEST_F(LlvmLibcFsetxattrTest, CrashOnNullBufferNonZeroSize) {
   });
 
   EXPECT_DEATH(
-      [fd] { LIBC_NAMESPACE::fsetxattr(fd, "user.attr", nullptr, 32); },
+      [fd] {
+        LIBC_NAMESPACE::fsetxattr(fd, "user.attr", nullptr, 32,
+                                  /* flags = */ 0);
+      },
       WITH_SIGNAL(-1));
 }
 
diff --git a/libc/test/src/sys/xattr/getxattr_test.cpp b/libc/test/src/sys/xattr/getxattr_test.cpp
index 73df59d85e30b..005298e1358fe 100644
--- a/libc/test/src/sys/xattr/getxattr_test.cpp
+++ b/libc/test/src/sys/xattr/getxattr_test.cpp
@@ -13,6 +13,7 @@
 
 #include "hdr/sys_stat_macros.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -53,13 +54,15 @@ TEST_F(LlvmLibcGetxattrTest, WithUserExtendedAttribute) {
 
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
+  scope_exit unlink_file([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
               Succeeds(0));
-  scope_exit cleanup([&] {
+  scope_exit unlink_symlink([&] {
     ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
   });
 
   string_view XATTR_NAME = "user.test_attr";
diff --git a/libc/test/src/sys/xattr/lgetxattr_test.cpp b/libc/test/src/sys/xattr/lgetxattr_test.cpp
index dbddcbe435a24..4e4257c5f432e 100644
--- a/libc/test/src/sys/xattr/lgetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/lgetxattr_test.cpp
@@ -13,6 +13,7 @@
 
 #include "hdr/sys_stat_macros.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -53,13 +54,15 @@ TEST_F(LlvmLibcLgetxattrTest, WithUserExtendedAttribute) {
 
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
+  scope_exit unlink_file([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
               Succeeds(0));
-  scope_exit cleanup([&] {
+  scope_exit unlink_symlink([&] {
     ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
   });
 
   string_view XATTR_NAME = "user.test_attr";
diff --git a/libc/test/src/sys/xattr/lsetxattr_test.cpp b/libc/test/src/sys/xattr/lsetxattr_test.cpp
index 57f95ba75cab5..8edc6b4c70fe0 100644
--- a/libc/test/src/sys/xattr/lsetxattr_test.cpp
+++ b/libc/test/src/sys/xattr/lsetxattr_test.cpp
@@ -15,6 +15,7 @@
 #include "hdr/sys_xattr_macros.h"
 #include "hdr/types/ssize_t.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -44,11 +45,11 @@ TEST_F(LlvmLibcLsetxattrTest, SetAttributeDefaultFlags) {
       libc_make_test_file_path("testdata/lsetxattr_default_flags.txt");
 
   int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
   scope_exit cleanup([&] {
     ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
   });
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   // Set an attribute through the test file name.
   {
@@ -82,6 +83,9 @@ TEST_F(LlvmLibcLsetxattrTest, SetAttributeWithNonzeroFlags) {
       libc_make_test_file_path("testdata/lsetxattr_nonzero_flags.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   string_view XATTR_NAME = "user.test_attr";
@@ -134,7 +138,8 @@ TEST_F(LlvmLibcLsetxattrTest, CrashOnNullPath) {
       [] {
         constexpr size_t BUFFER_SIZE = 32;
         char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::lsetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+        LIBC_NAMESPACE::lsetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
+                                  /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
@@ -145,7 +150,7 @@ TEST_F(LlvmLibcLsetxattrTest, CrashOnNullAttributeName) {
         constexpr size_t BUFFER_SIZE = 32;
         char buffer[BUFFER_SIZE] = {};
         LIBC_NAMESPACE::lsetxattr("testdata/file.txt", nullptr, buffer,
-                                  BUFFER_SIZE);
+                                  BUFFER_SIZE, /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
@@ -153,8 +158,8 @@ TEST_F(LlvmLibcLsetxattrTest, CrashOnNullAttributeName) {
 TEST_F(LlvmLibcLsetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
-        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr,
-                                  32);
+        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr, 32,
+                                  /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
diff --git a/libc/test/src/sys/xattr/setxattr_test.cpp b/libc/test/src/sys/xattr/setxattr_test.cpp
index c8a0cbe428baa..ab402fe0b40dc 100644
--- a/libc/test/src/sys/xattr/setxattr_test.cpp
+++ b/libc/test/src/sys/xattr/setxattr_test.cpp
@@ -15,6 +15,7 @@
 #include "hdr/sys_xattr_macros.h"
 #include "hdr/types/ssize_t.h"
 #include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall.h"
 #include "src/__support/libc_errno.h"
 #include "src/fcntl/creat.h"
@@ -55,13 +56,15 @@ TEST_F(LlvmLibcSetxattrTest, SetAttributeDefaultFlags) {
 
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
+  scope_exit unlink_file([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
               Succeeds(0));
-  scope_exit cleanup([&] {
+  scope_exit unlink_symlink([&] {
     ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
   });
 
   // Set an attribute through the test file name.
@@ -111,6 +114,9 @@ TEST_F(LlvmLibcSetxattrTest, SetAttributeWithNonzeroFlags) {
       libc_make_test_file_path("testdata/setxattr_nonzero_flags.txt");
   int fd = recreate_test_file(TEST_FILE_NAME);
   ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
   ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
 
   string_view XATTR_NAME = "user.test_attr";
@@ -163,7 +169,8 @@ TEST_F(LlvmLibcSetxattrTest, CrashOnNullPath) {
       [] {
         constexpr size_t BUFFER_SIZE = 32;
         char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::setxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+        LIBC_NAMESPACE::setxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
+                                 /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
@@ -174,7 +181,7 @@ TEST_F(LlvmLibcSetxattrTest, CrashOnNullAttributeName) {
         constexpr size_t BUFFER_SIZE = 32;
         char buffer[BUFFER_SIZE] = {};
         LIBC_NAMESPACE::setxattr("testdata/file.txt", nullptr, buffer,
-                                 BUFFER_SIZE);
+                                 BUFFER_SIZE, /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }
@@ -182,7 +189,8 @@ TEST_F(LlvmLibcSetxattrTest, CrashOnNullAttributeName) {
 TEST_F(LlvmLibcSetxattrTest, CrashOnNullBufferNonZeroSize) {
   EXPECT_DEATH(
       [] {
-        LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32);
+        LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32,
+                                 /* flags = */ 0);
       },
       WITH_SIGNAL(-1));
 }

>From b7ab3ff5fd93b4e3835316f0395ff2f48aae27fc Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Thu, 24 Sep 2026 14:40:34 +0000
Subject: [PATCH 5/6] Consolidate xattr tests into three files.

---
 libc/src/sys/xattr/linux/CMakeLists.txt     |   1 +
 libc/src/sys/xattr/linux/flistxattr.cpp     |   4 +
 libc/src/sys/xattr/linux/listxattr.cpp      |   2 +
 libc/src/sys/xattr/linux/llistxattr.cpp     |   2 +
 libc/test/src/sys/xattr/CMakeLists.txt      | 163 +---------
 libc/test/src/sys/xattr/fgetxattr_test.cpp  | 128 --------
 libc/test/src/sys/xattr/flistxattr_test.cpp | 102 ------
 libc/test/src/sys/xattr/fsetxattr_test.cpp  | 167 ----------
 libc/test/src/sys/xattr/fxattr_test.cpp     | 273 ++++++++++++++++
 libc/test/src/sys/xattr/getxattr_test.cpp   | 161 ----------
 libc/test/src/sys/xattr/lgetxattr_test.cpp  | 151 ---------
 libc/test/src/sys/xattr/listxattr_test.cpp  | 150 ---------
 libc/test/src/sys/xattr/llistxattr_test.cpp | 148 ---------
 libc/test/src/sys/xattr/lsetxattr_test.cpp  | 169 ----------
 libc/test/src/sys/xattr/lxattr_test.cpp     | 302 ++++++++++++++++++
 libc/test/src/sys/xattr/setxattr_test.cpp   | 200 ------------
 libc/test/src/sys/xattr/xattr_test.cpp      | 336 ++++++++++++++++++++
 17 files changed, 937 insertions(+), 1522 deletions(-)
 delete mode 100644 libc/test/src/sys/xattr/fgetxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/flistxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/fsetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/fxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/getxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/lgetxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/listxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/llistxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/lsetxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/lxattr_test.cpp
 delete mode 100644 libc/test/src/sys/xattr/setxattr_test.cpp
 create mode 100644 libc/test/src/sys/xattr/xattr_test.cpp

diff --git a/libc/src/sys/xattr/linux/CMakeLists.txt b/libc/src/sys/xattr/linux/CMakeLists.txt
index e9a48a8cc6581..0a946abb24635 100644
--- a/libc/src/sys/xattr/linux/CMakeLists.txt
+++ b/libc/src/sys/xattr/linux/CMakeLists.txt
@@ -99,6 +99,7 @@ add_entrypoint_object(
     libc.src.__support.common
     libc.src.__support.libc_errno
     libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
     libc.src.errno.errno
 )
 
diff --git a/libc/src/sys/xattr/linux/flistxattr.cpp b/libc/src/sys/xattr/linux/flistxattr.cpp
index 68119436aa29e..e384074e26383 100644
--- a/libc/src/sys/xattr/linux/flistxattr.cpp
+++ b/libc/src/sys/xattr/linux/flistxattr.cpp
@@ -16,10 +16,14 @@
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(ssize_t, flistxattr, (int fd, char *list, size_t size)) {
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(list);
+
   ErrorOr<ssize_t> ret = linux_syscalls::flistxattr(fd, list, size);
   if (!ret) {
     libc_errno = ret.error();
diff --git a/libc/src/sys/xattr/linux/listxattr.cpp b/libc/src/sys/xattr/linux/listxattr.cpp
index d0ef1558ce75c..2d269b24e785c 100644
--- a/libc/src/sys/xattr/linux/listxattr.cpp
+++ b/libc/src/sys/xattr/linux/listxattr.cpp
@@ -23,6 +23,8 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(ssize_t, listxattr,
                    (const char *path, char *list, size_t size)) {
   LIBC_CRASH_ON_NULLPTR(path);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(list);
 
   ErrorOr<ssize_t> ret = linux_syscalls::listxattr(path, list, size);
   if (!ret) {
diff --git a/libc/src/sys/xattr/linux/llistxattr.cpp b/libc/src/sys/xattr/linux/llistxattr.cpp
index 12d073f19deac..6170d48f7f6e0 100644
--- a/libc/src/sys/xattr/linux/llistxattr.cpp
+++ b/libc/src/sys/xattr/linux/llistxattr.cpp
@@ -23,6 +23,8 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(ssize_t, llistxattr,
                    (const char *path, char *list, size_t size)) {
   LIBC_CRASH_ON_NULLPTR(path);
+  if (size != 0)
+    LIBC_CRASH_ON_NULLPTR(list);
 
   ErrorOr<ssize_t> ret = linux_syscalls::llistxattr(path, list, size);
   if (!ret) {
diff --git a/libc/test/src/sys/xattr/CMakeLists.txt b/libc/test/src/sys/xattr/CMakeLists.txt
index e856de637c735..43d9956de50c7 100644
--- a/libc/test/src/sys/xattr/CMakeLists.txt
+++ b/libc/test/src/sys/xattr/CMakeLists.txt
@@ -1,89 +1,24 @@
 add_custom_target(libc_sys_xattr_unittests)
 
 add_libc_test(
-  fgetxattr_test
+  fxattr_test
   SUITE
     libc_sys_xattr_unittests
   SRCS
-    fgetxattr_test.cpp
+    fxattr_test.cpp
   DEPENDS
     libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
+    libc.hdr.sys_xattr_macros
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
     libc.src.__support.CPP.scope
     libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
     libc.src.fcntl.creat
     libc.src.sys.xattr.fgetxattr
-    libc.src.unistd.close
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  getxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    getxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
-    libc.src.sys.xattr.getxattr
-    libc.src.unistd.close
-    libc.src.unistd.symlink
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  lgetxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    lgetxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
-    libc.src.sys.xattr.lgetxattr
-    libc.src.unistd.close
-    libc.src.unistd.symlink
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  flistxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    flistxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
     libc.src.sys.xattr.flistxattr
+    libc.src.sys.xattr.fsetxattr
     libc.src.unistd.close
     libc.src.unistd.unlink
     libc.test.UnitTest.ErrnoCheckingTest
@@ -91,23 +26,24 @@ add_libc_test(
 )
 
 add_libc_test(
-  setxattr_test
+  lxattr_test
   SUITE
     libc_sys_xattr_unittests
   SRCS
-    setxattr_test.cpp
+    lxattr_test.cpp
   DEPENDS
     libc.hdr.sys_stat_macros
     libc.hdr.sys_xattr_macros
+    libc.hdr.types.size_t
     libc.hdr.types.ssize_t
-    libc.include.sys_xattr
     libc.src.__support.CPP.scope
     libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
     libc.src.fcntl.creat
-    libc.src.sys.xattr.setxattr
+    libc.src.sys.xattr.lgetxattr
+    libc.src.sys.xattr.llistxattr
+    libc.src.sys.xattr.lsetxattr
     libc.src.unistd.close
     libc.src.unistd.symlink
     libc.src.unistd.unlink
@@ -116,89 +52,24 @@ add_libc_test(
 )
 
 add_libc_test(
-  lsetxattr_test
+  xattr_test
   SUITE
     libc_sys_xattr_unittests
   SRCS
-    lsetxattr_test.cpp
+    xattr_test.cpp
   DEPENDS
     libc.hdr.sys_stat_macros
     libc.hdr.sys_xattr_macros
+    libc.hdr.types.size_t
     libc.hdr.types.ssize_t
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
-    libc.src.sys.xattr.lsetxattr
-    libc.src.unistd.close
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  fsetxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    fsetxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.hdr.sys_xattr_macros
-    libc.include.sys_xattr
     libc.src.__support.CPP.scope
     libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
-    libc.src.sys.xattr.fsetxattr
-    libc.src.unistd.close
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  listxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    listxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
     libc.src.errno.errno
     libc.src.fcntl.creat
+    libc.src.sys.xattr.getxattr
     libc.src.sys.xattr.listxattr
-    libc.src.unistd.close
-    libc.src.unistd.symlink
-    libc.src.unistd.unlink
-    libc.test.UnitTest.ErrnoCheckingTest
-    libc.test.UnitTest.ErrnoSetterMatcher
-)
-
-add_libc_test(
-  llistxattr_test
-  SUITE
-    libc_sys_xattr_unittests
-  SRCS
-    llistxattr_test.cpp
-  DEPENDS
-    libc.hdr.sys_stat_macros
-    libc.include.sys_xattr
-    libc.src.__support.CPP.scope
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.libc_errno
-    libc.src.errno.errno
-    libc.src.fcntl.creat
-    libc.src.sys.xattr.llistxattr
+    libc.src.sys.xattr.setxattr
     libc.src.unistd.close
     libc.src.unistd.symlink
     libc.src.unistd.unlink
diff --git a/libc/test/src/sys/xattr/fgetxattr_test.cpp b/libc/test/src/sys/xattr/fgetxattr_test.cpp
deleted file mode 100644
index 6bf25f6c566af..0000000000000
--- a/libc/test/src/sys/xattr/fgetxattr_test.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for fgetxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/fgetxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcFgetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-TEST_F(LlvmLibcFgetxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fgetxattr.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
-                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
-                   /* flags = */ 0));
-
-  EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), nullptr, 0),
-              Succeeds<ssize_t>(XATTR_VALUE.size()));
-
-  {
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(
-        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
-        Succeeds(XATTR_VALUE.size()));
-    string_view result_str(buffer, XATTR_VALUE.size());
-    EXPECT_EQ(result_str, XATTR_VALUE);
-  }
-
-  // Call with insufficient buffer size and missing attribute to check failing
-  // errno.
-  {
-    constexpr size_t BUFFER_SIZE = 8;
-    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(
-        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
-        Fails<ssize_t>(ERANGE));
-    EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, "user.missing_test_attr", buffer,
-                                          BUFFER_SIZE),
-                Fails<ssize_t>(ENODATA));
-  }
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcFgetxattrTest, CrashOnNullAttributeName) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fgetxattr_null_attribute.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_DEATH(
-      [fd] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::fgetxattr(fd, nullptr, buffer, BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcFgetxattrTest, CrashOnNullBufferNonZeroSize) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fgetxattr_null_buffer.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_DEATH(
-      [fd] { LIBC_NAMESPACE::fgetxattr(fd, "user.attr", nullptr, 32); },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/flistxattr_test.cpp b/libc/test/src/sys/xattr/flistxattr_test.cpp
deleted file mode 100644
index 474d0ae81d559..0000000000000
--- a/libc/test/src/sys/xattr/flistxattr_test.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for flistxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/flistxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcListxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-TEST_F(LlvmLibcListxattrTest, NoExtendedAttributes) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/flistxattr_no_xattrs.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, nullptr, 0), Succeeds<ssize_t>(0));
-
-  constexpr size_t BUFFER_SIZE = 32;
-  char buffer[BUFFER_SIZE] = {};
-  EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
-              Succeeds<ssize_t>(0));
-}
-
-TEST_F(LlvmLibcListxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/flistxattr_with_user_xattr.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_fsetxattr, fd, XATTR_NAME.data(), XATTR_VALUE.data(),
-                   XATTR_VALUE.size(),
-                   /* flags = */ 0));
-  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
-
-  ASSERT_THAT(LIBC_NAMESPACE::flistxattr(fd, nullptr, 0),
-              Succeeds<ssize_t>(xattr_name_null_terminated_len));
-
-  {
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
-                Succeeds(xattr_name_null_terminated_len));
-    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
-    EXPECT_EQ(result_str, XATTR_NAME);
-  }
-
-  // Call with insufficient buffer size to check failing errno.
-  {
-    constexpr size_t BUFFER_SIZE = 14;
-    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ERANGE));
-  }
-}
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/fsetxattr_test.cpp b/libc/test/src/sys/xattr/fsetxattr_test.cpp
deleted file mode 100644
index 3e6477d6d0281..0000000000000
--- a/libc/test/src/sys/xattr/fsetxattr_test.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for fsetxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "hdr/sys_xattr_macros.h"
-#include "hdr/types/ssize_t.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/fsetxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcFsetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-TEST_F(LlvmLibcFsetxattrTest, SetAttributeDefaultFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fsetxattr_default_flags.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  constexpr size_t BUFFER_SIZE = 32;
-  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-  char buffer[BUFFER_SIZE] = {};
-
-  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        /* flags = */ 0),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-}
-
-TEST_F(LlvmLibcFsetxattrTest, SetAttributeWithNonzeroFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fsetxattr_nonzero_flags.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  constexpr size_t BUFFER_SIZE = 32;
-  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-  char buffer[BUFFER_SIZE] = {};
-
-  // Try replace-only before attribute exists.
-  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        XATTR_REPLACE),
-              Fails(ENODATA));
-
-  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        /* flags = */ XATTR_CREATE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-
-  // Replace-only now that the attribute exists.
-  string_view NEW_XATTR_VALUE = "new_test_value";
-  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
-                                        NEW_XATTR_VALUE.data(),
-                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
-
-  // Try create-only when the attribute already exists.
-  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        XATTR_CREATE),
-              Fails(EEXIST));
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcFsetxattrTest, CrashOnNullAttributeName) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fsetxattr_null_attribute.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_DEATH(
-      [fd] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::fsetxattr(fd, nullptr, buffer, BUFFER_SIZE,
-                                  /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcFsetxattrTest, CrashOnNullBufferNonZeroSize) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/fsetxattr_null_buffer.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_DEATH(
-      [fd] {
-        LIBC_NAMESPACE::fsetxattr(fd, "user.attr", nullptr, 32,
-                                  /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/fxattr_test.cpp b/libc/test/src/sys/xattr/fxattr_test.cpp
new file mode 100644
index 0000000000000..10200236da5dd
--- /dev/null
+++ b/libc/test/src/sys/xattr/fxattr_test.cpp
@@ -0,0 +1,273 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for fgetxattr, fsetxattr, and, flistxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/fgetxattr.h"
+#include "src/sys/xattr/flistxattr.h"
+#include "src/sys/xattr/fsetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcFxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+TEST_F(LlvmLibcFxattrTest, GetAndListWithNoAttributeSet) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fxattr_no_xattrs.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  constexpr size_t BUFFER_SIZE = 32;
+  char buffer[BUFFER_SIZE] = {};
+
+  EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, "user.missing_test_attr", buffer,
+                                        BUFFER_SIZE),
+              Fails<ssize_t>(ENODATA));
+
+  EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, nullptr, 0), Succeeds<ssize_t>(0));
+  EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(0));
+}
+
+TEST_F(LlvmLibcFxattrTest, SetGetAndListWithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fxattr_testfile.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ 0),
+              Succeeds(0));
+  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
+
+  // Get and list with null and sufficient buffer size.
+  EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), nullptr, 0),
+              Succeeds<ssize_t>(XATTR_VALUE.size()));
+  EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, nullptr, 0),
+              Succeeds<ssize_t>(xattr_name_null_terminated_len));
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_NAME.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(
+        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+        Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
+                Succeeds(xattr_name_null_terminated_len));
+    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
+    EXPECT_EQ(result_str, XATTR_NAME);
+  }
+
+  // Get and list with insufficient buffer size, and get with missing attribute
+  // to check failing errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    EXPECT_THAT(
+        LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+        Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::flistxattr(fd, buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::fgetxattr(fd, "user.missing_test_attr", buffer,
+                                          BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+}
+
+TEST_F(LlvmLibcFxattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fxattr_nonzero_flags_testfile.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  EXPECT_THAT(
+      LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+      Succeeds<ssize_t>(XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute does exist.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        NEW_XATTR_VALUE.data(),
+                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  EXPECT_THAT(
+      LIBC_NAMESPACE::fgetxattr(fd, XATTR_NAME.data(), buffer, BUFFER_SIZE),
+      Succeeds<ssize_t>(NEW_XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::fsetxattr(fd, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if 1 // defined(LIBC_ADD_NULL_CHECKS)
+
+TEST_F(LlvmLibcFxattrTest, SetCrashesOnNullAttributeName) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_null_attribute.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::fsetxattr(fd, nullptr, buffer, BUFFER_SIZE,
+                                  /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcFxattrTest, SetCrashesOnNullBufferNonZeroSize) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fsetxattr_null_buffer.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] {
+        LIBC_NAMESPACE::fsetxattr(fd, "user.attr", nullptr, 32,
+                                  /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcFxattrTest, GetCrashesOnNullAttributeName) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr_null_attribute.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::fgetxattr(fd, nullptr, buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcFxattrTest, GetCrashesOnNullBufferNonZeroSize) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr_null_buffer.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH(
+      [fd] { LIBC_NAMESPACE::fgetxattr(fd, "user.attr", nullptr, 32); },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcFxattrTest, ListCrashesOnNullBufferNonZeroSize) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/fgetxattr_null_buffer.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  EXPECT_DEATH([fd] { LIBC_NAMESPACE::flistxattr(fd, nullptr, 32); },
+               WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/getxattr_test.cpp b/libc/test/src/sys/xattr/getxattr_test.cpp
deleted file mode 100644
index 005298e1358fe..0000000000000
--- a/libc/test/src/sys/xattr/getxattr_test.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for getxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/getxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/symlink.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcGetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-int recreate_test_symlink(const char *target, const char *linkpath) {
-  LIBC_NAMESPACE::unlink(linkpath);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::symlink(target, linkpath);
-}
-
-TEST_F(LlvmLibcGetxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/getxattr.txt");
-  constexpr const char *TEST_SYMLINK_TARGET = "getxattr.txt";
-  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
-      libc_make_test_file_path("testdata/getxattr_symlink.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit unlink_file([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
-              Succeeds(0));
-  scope_exit unlink_symlink([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
-                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
-                   /* flags = */ 0));
-
-  // Call with the test file path.
-  {
-    EXPECT_THAT(
-        LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(), nullptr, 0),
-        Succeeds<ssize_t>(XATTR_VALUE.size()));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                         buffer, BUFFER_SIZE),
-                Succeeds(XATTR_VALUE.size()));
-    string_view result_str(buffer, XATTR_VALUE.size());
-    EXPECT_EQ(result_str, XATTR_VALUE);
-  }
-
-  // Call with the symlink path to verify the correct syscall is used
-  // internally; getxattr instead of lgetxattr.
-  {
-    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
-                                         nullptr, 0),
-                Succeeds<ssize_t>(XATTR_VALUE.size()));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
-                                         buffer, BUFFER_SIZE),
-                Succeeds(XATTR_VALUE.size()));
-    string_view result_str(buffer, XATTR_VALUE.size());
-    EXPECT_EQ(result_str, XATTR_VALUE);
-  }
-
-  // Call with insufficient buffer size and missing attribute to check failing
-  // errno.
-  {
-    constexpr size_t BUFFER_SIZE = 8;
-    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-
-    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                         buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ERANGE));
-    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME,
-                                         "user.missing_test_attr", buffer,
-                                         BUFFER_SIZE),
-                Fails<ssize_t>(ENODATA));
-  }
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcGetxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::getxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcGetxattrTest, CrashOnNullAttributeName) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::getxattr("testdata/file.txt", nullptr, buffer,
-                                 BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcGetxattrTest, CrashOnNullBufferNonZeroSize) {
-  EXPECT_DEATH(
-      [] {
-        LIBC_NAMESPACE::getxattr("testdata/file.txt", "user.attr", nullptr, 32);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/lgetxattr_test.cpp b/libc/test/src/sys/xattr/lgetxattr_test.cpp
deleted file mode 100644
index 4e4257c5f432e..0000000000000
--- a/libc/test/src/sys/xattr/lgetxattr_test.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for lgetxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/lgetxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/symlink.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLgetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-int recreate_test_symlink(const char *target, const char *linkpath) {
-  LIBC_NAMESPACE::unlink(linkpath);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::symlink(target, linkpath);
-}
-
-TEST_F(LlvmLibcLgetxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/lgetxattr.txt");
-  constexpr const char *TEST_SYMLINK_TARGET = "lgetxattr.txt";
-  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
-      libc_make_test_file_path("testdata/lgetxattr_symlink.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit unlink_file([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
-              Succeeds(0));
-  scope_exit unlink_symlink([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
-                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
-                   /* flags = */ 0));
-
-  // Call with the test file path.
-  {
-    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                          nullptr, 0),
-                Succeeds<ssize_t>(XATTR_VALUE.size()));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                          buffer, BUFFER_SIZE),
-                Succeeds(XATTR_VALUE.size()));
-    string_view result_str(buffer, XATTR_VALUE.size());
-    EXPECT_EQ(result_str, XATTR_VALUE);
-  }
-
-  // Call with the symlink path to verify the correct syscall is used
-  // internally; lgetxattr instead of getxattr.
-  {
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
-                                          buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ENODATA));
-  }
-
-  // Call with insufficient buffer size to check failing errno.
-  {
-    constexpr size_t BUFFER_SIZE = 8;
-    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
-
-    char buffer[BUFFER_SIZE] = {};
-
-    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                          buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ERANGE));
-  }
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcLgetxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::lgetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcLgetxattrTest, CrashOnNullAttributeName) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", nullptr, buffer,
-                                  BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcLgetxattrTest, CrashOnNullBufferNonZeroSize) {
-  EXPECT_DEATH(
-      [] {
-        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", "user.attr", nullptr,
-                                  32);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/listxattr_test.cpp b/libc/test/src/sys/xattr/listxattr_test.cpp
deleted file mode 100644
index da72fd0602fcf..0000000000000
--- a/libc/test/src/sys/xattr/listxattr_test.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for listxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/listxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/symlink.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcListxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-int recreate_test_symlink(const char *target, const char *linkpath) {
-  LIBC_NAMESPACE::unlink(linkpath);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::symlink(target, linkpath);
-}
-
-TEST_F(LlvmLibcListxattrTest, NoExtendedAttributes) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/listxattr_no_xattrs.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, nullptr, 0),
-              Succeeds<ssize_t>(0));
-
-  constexpr size_t BUFFER_SIZE = 32;
-  char buffer[BUFFER_SIZE] = {};
-  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-              Succeeds<ssize_t>(0));
-}
-
-TEST_F(LlvmLibcListxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/listxattr_with_user_xattr.txt");
-  constexpr const char *TEST_SYMLINK_TARGET = "listxattr_with_user_xattr.txt";
-  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
-      libc_make_test_file_path("testdata/listxattr_symlink.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
-              Succeeds(0));
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
-                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
-                   /* flags = */ 0));
-  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
-
-  // Call with the test file path.
-  {
-    ASSERT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, nullptr, 0),
-                Succeeds<ssize_t>(xattr_name_null_terminated_len));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-                Succeeds(xattr_name_null_terminated_len));
-    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
-    EXPECT_EQ(result_str, XATTR_NAME);
-  }
-
-  // Call with the symlink path to verify the correct syscall is used
-  // internally; listxattr instead of llistxattr.
-  {
-    ASSERT_THAT(LIBC_NAMESPACE::listxattr(TEST_SYMLINK_NAME, nullptr, 0),
-                Succeeds<ssize_t>(xattr_name_null_terminated_len));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(
-        LIBC_NAMESPACE::listxattr(TEST_SYMLINK_NAME, buffer, BUFFER_SIZE),
-        Succeeds(xattr_name_null_terminated_len));
-    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
-    EXPECT_EQ(result_str, XATTR_NAME);
-  }
-
-  // Call with insufficient buffer size to check failing errno.
-  {
-    constexpr size_t BUFFER_SIZE = 14;
-    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ERANGE));
-  }
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST(LlvmLibcListxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::listxattr(nullptr, buffer, BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/llistxattr_test.cpp b/libc/test/src/sys/xattr/llistxattr_test.cpp
deleted file mode 100644
index bddb5516d4cd3..0000000000000
--- a/libc/test/src/sys/xattr/llistxattr_test.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for llistxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/llistxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/symlink.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLlistxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-int recreate_test_symlink(const char *target, const char *linkpath) {
-  LIBC_NAMESPACE::unlink(linkpath);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::symlink(target, linkpath);
-}
-
-TEST_F(LlvmLibcLlistxattrTest, NoExtendedAttributes) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/llistxattr_no_xattrs.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-
-  EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, nullptr, 0),
-              Succeeds<ssize_t>(0));
-
-  constexpr size_t BUFFER_SIZE = 32;
-  char buffer[BUFFER_SIZE] = {};
-  ASSERT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-              Succeeds<ssize_t>(0));
-}
-
-TEST_F(LlvmLibcLlistxattrTest, WithUserExtendedAttribute) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/llistxattr_with_user_xattr.txt");
-  constexpr const char *TEST_SYMLINK_TARGET = "llistxattr_with_user_xattr.txt";
-  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
-      libc_make_test_file_path("testdata/llistxattr_symlink.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
-              Succeeds(0));
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-  });
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  ASSERT_EQ(0, LIBC_NAMESPACE::syscall_impl<int>(
-                   SYS_setxattr, static_cast<const char *>(TEST_FILE_NAME),
-                   XATTR_NAME.data(), XATTR_VALUE.data(), XATTR_VALUE.size(),
-                   /* flags = */ 0));
-  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
-
-  // Call with the test file path.
-  {
-    ASSERT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, nullptr, 0),
-                Succeeds<ssize_t>(xattr_name_null_terminated_len));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    ASSERT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-                Succeeds(xattr_name_null_terminated_len));
-    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
-    EXPECT_EQ(result_str, XATTR_NAME);
-  }
-
-  // Call with the symlink path to verify the correct syscall is used
-  // internally; llistxattr instead of listxattr.
-  {
-    EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_SYMLINK_NAME, nullptr, 0),
-                Succeeds<ssize_t>(0));
-
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(
-        LIBC_NAMESPACE::llistxattr(TEST_SYMLINK_NAME, buffer, BUFFER_SIZE),
-        Succeeds<ssize_t>(0));
-  }
-
-  // Call with insufficient buffer size to check failing errno.
-  {
-    constexpr size_t BUFFER_SIZE = 14;
-    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
-
-    char buffer[BUFFER_SIZE] = {};
-    EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
-                Fails<ssize_t>(ERANGE));
-  }
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST(LlvmLibcLlistxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::llistxattr(nullptr, buffer, BUFFER_SIZE);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/lsetxattr_test.cpp b/libc/test/src/sys/xattr/lsetxattr_test.cpp
deleted file mode 100644
index 8edc6b4c70fe0..0000000000000
--- a/libc/test/src/sys/xattr/lsetxattr_test.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for lsetxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "hdr/sys_xattr_macros.h"
-#include "hdr/types/ssize_t.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/lsetxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcLsetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-TEST_F(LlvmLibcLsetxattrTest, SetAttributeDefaultFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/lsetxattr_default_flags.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  // Set an attribute through the test file name.
-  {
-    string_view XATTR_NAME = "user.test_attr_through_file";
-    string_view XATTR_VALUE = "test_value_through_file";
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-    char buffer[BUFFER_SIZE] = {};
-
-    ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                          XATTR_VALUE.data(),
-                                          XATTR_VALUE.size(),
-                                          /* flags = */ 0),
-                Succeeds(0));
-
-    ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-              LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
-    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-  }
-
-  // TODO: Figure out if/how to test `lsetxattr` with a symlink path. The "user"
-  // namespace extended attributes appear restricted to regular files and
-  // directories. Attempting to set a "user" extended attribute on a symlink
-  // results in EPERM.
-}
-
-TEST_F(LlvmLibcLsetxattrTest, SetAttributeWithNonzeroFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/lsetxattr_nonzero_flags.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  constexpr size_t BUFFER_SIZE = 32;
-  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-  char buffer[BUFFER_SIZE] = {};
-
-  // Try replace-only before attribute exists.
-  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        XATTR_REPLACE),
-              Fails(ENODATA));
-
-  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        /* flags = */ XATTR_CREATE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-
-  // Replace-only now that the attribute exists.
-  string_view NEW_XATTR_VALUE = "new_test_value";
-  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                        NEW_XATTR_VALUE.data(),
-                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
-
-  // Try create-only when the attribute already exists.
-  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                        XATTR_CREATE),
-              Fails(EEXIST));
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcLsetxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::lsetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
-                                  /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcLsetxattrTest, CrashOnNullAttributeName) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", nullptr, buffer,
-                                  BUFFER_SIZE, /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcLsetxattrTest, CrashOnNullBufferNonZeroSize) {
-  EXPECT_DEATH(
-      [] {
-        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr, 32,
-                                  /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/lxattr_test.cpp b/libc/test/src/sys/xattr/lxattr_test.cpp
new file mode 100644
index 0000000000000..f7e11b340db7b
--- /dev/null
+++ b/libc/test/src/sys/xattr/lxattr_test.cpp
@@ -0,0 +1,302 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for lgetxattr, lsetxattr, and, llistxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/lgetxattr.h"
+#include "src/sys/xattr/llistxattr.h"
+#include "src/sys/xattr/lsetxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/symlink.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcLxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+int recreate_test_symlink(const char *target, const char *linkpath) {
+  LIBC_NAMESPACE::unlink(linkpath);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::symlink(target, linkpath);
+}
+
+TEST_F(LlvmLibcLxattrTest, GetAndListWithNoAttributeSet) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lxattr_unset_testfile.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  constexpr size_t BUFFER_SIZE = 32;
+  char buffer[BUFFER_SIZE] = {};
+
+  EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME,
+                                        "user.missing_test_attr", buffer,
+                                        BUFFER_SIZE),
+              Fails<ssize_t>(ENODATA));
+
+  EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, nullptr, 0),
+              Succeeds<ssize_t>(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(0));
+}
+
+TEST_F(LlvmLibcLxattrTest, SetGetAndListWithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lxattr_testfile.txt");
+  constexpr const char *TEST_SYMLINK_TARGET = "lxattr_testfile.txt";
+  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
+      libc_make_test_file_path("testdata/lxattr_testsymlink.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit unlink_file([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
+              Succeeds(0));
+  scope_exit unlink_symlink([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ 0),
+              Succeeds(0));
+  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
+
+  // Get and list using the test file path.
+  EXPECT_THAT(
+      LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(), nullptr, 0),
+      Succeeds<ssize_t>(XATTR_VALUE.size()));
+  EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, nullptr, 0),
+              Succeeds<ssize_t>(xattr_name_null_terminated_len));
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_NAME.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+                Succeeds(xattr_name_null_terminated_len));
+    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
+    EXPECT_EQ(result_str, XATTR_NAME);
+  }
+
+  // Get and list using the symlink path to verify the correct syscall is used
+  // internally; lgetxattr or llistxattr, and not getxattr or listxattr.
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
+
+    char buffer[BUFFER_SIZE] = {};
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+    EXPECT_THAT(
+        LIBC_NAMESPACE::llistxattr(TEST_SYMLINK_NAME, buffer, BUFFER_SIZE),
+        Succeeds<ssize_t>(0));
+  }
+
+  // Get and list with insufficient buffer size, and get with missing attribute
+  // on the file to check failing errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                          buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::llistxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME,
+                                          "user.missing_test_attr", buffer,
+                                          BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+}
+
+TEST_F(LlvmLibcLxattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/lxattr_nonzero_flags_testfile.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute does exist.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        NEW_XATTR_VALUE.data(),
+                                        NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::lgetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(NEW_XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::lsetxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                        XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                        XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if 1 // defined(LIBC_ADD_NULL_CHECKS)
+
+TEST_F(LlvmLibcLxattrTest, SetCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lsetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
+                                  /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, SetCrashesOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", nullptr, buffer,
+                                  BUFFER_SIZE, /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, SetCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::lsetxattr("testdata/file.txt", "user.attr", nullptr, 32,
+                                  /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, GetCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lgetxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, GetCrashesOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", nullptr, buffer,
+                                  BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, GetCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::lgetxattr("testdata/file.txt", "user.attr", nullptr,
+                                  32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcLxattrTest, ListCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::llistxattr(nullptr, buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcLxattrTest, ListCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] { LIBC_NAMESPACE::llistxattr("testdata/file.txt", nullptr, 32); },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace
diff --git a/libc/test/src/sys/xattr/setxattr_test.cpp b/libc/test/src/sys/xattr/setxattr_test.cpp
deleted file mode 100644
index ab402fe0b40dc..0000000000000
--- a/libc/test/src/sys/xattr/setxattr_test.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Unit tests for setxattr.
-///
-//===----------------------------------------------------------------------===//
-
-#include "hdr/sys_stat_macros.h"
-#include "hdr/sys_xattr_macros.h"
-#include "hdr/types/ssize_t.h"
-#include "src/__support/CPP/scope.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall.h"
-#include "src/__support/libc_errno.h"
-#include "src/fcntl/creat.h"
-#include "src/sys/xattr/setxattr.h"
-#include "src/unistd/close.h"
-#include "src/unistd/symlink.h"
-#include "src/unistd/unlink.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
-#include "test/UnitTest/ErrnoSetterMatcher.h"
-#include "test/UnitTest/Test.h"
-#include <sys/syscall.h>
-
-namespace {
-
-using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
-using LlvmLibcSetxattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-using LIBC_NAMESPACE::cpp::scope_exit;
-using LIBC_NAMESPACE::cpp::string_view;
-
-int recreate_test_file(const char *path) {
-  LIBC_NAMESPACE::unlink(path);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::creat(path, S_IRWXU);
-}
-
-int recreate_test_symlink(const char *target, const char *linkpath) {
-  LIBC_NAMESPACE::unlink(linkpath);
-  LIBC_NAMESPACE::libc_errno = 0;
-  return LIBC_NAMESPACE::symlink(target, linkpath);
-}
-
-TEST_F(LlvmLibcSetxattrTest, SetAttributeDefaultFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/setxattr_default_flags.txt");
-  constexpr const char *TEST_SYMLINK_TARGET = "setxattr_default_flags.txt";
-  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
-      libc_make_test_file_path("testdata/setxattr_default_flags_symlink.txt");
-
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit unlink_file([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
-              Succeeds(0));
-  scope_exit unlink_symlink([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
-  });
-
-  // Set an attribute through the test file name.
-  {
-    string_view XATTR_NAME = "user.test_attr_through_file";
-    string_view XATTR_VALUE = "test_value_through_file";
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-    char buffer[BUFFER_SIZE] = {};
-
-    ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                         XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                         /* flags = */ 0),
-                Succeeds(0));
-
-    ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-              LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
-    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-  }
-
-  // Set another attribute through the symlink to verify the correct syscall is
-  // used internally; setxattr instead of lsetxattr.
-  {
-    string_view XATTR_NAME = "user.test_attr_through_symlink";
-    string_view XATTR_VALUE = "test_value_through_symlink";
-    constexpr size_t BUFFER_SIZE = 32;
-    ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-    char buffer[BUFFER_SIZE] = {};
-
-    ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
-                                         XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                         /* flags = */ 0),
-                Succeeds(0));
-
-    EXPECT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-              LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                  SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                  XATTR_NAME.data(), buffer, BUFFER_SIZE));
-    EXPECT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-  }
-}
-
-TEST_F(LlvmLibcSetxattrTest, SetAttributeWithNonzeroFlags) {
-  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
-      libc_make_test_file_path("testdata/setxattr_nonzero_flags.txt");
-  int fd = recreate_test_file(TEST_FILE_NAME);
-  ASSERT_ERRNO_SUCCESS();
-  scope_exit cleanup([&] {
-    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
-  });
-  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-
-  string_view XATTR_NAME = "user.test_attr";
-  string_view XATTR_VALUE = "test_value";
-  constexpr size_t BUFFER_SIZE = 32;
-  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
-  char buffer[BUFFER_SIZE] = {};
-
-  // Try replace-only before attribute exists.
-  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                       XATTR_REPLACE),
-              Fails(ENODATA));
-
-  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                       /* flags = */ XATTR_CREATE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
-
-  // Replace-only now that the attribute exists.
-  string_view NEW_XATTR_VALUE = "new_test_value";
-  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                       NEW_XATTR_VALUE.data(),
-                                       NEW_XATTR_VALUE.size(), XATTR_REPLACE),
-              Succeeds(0));
-
-  ASSERT_EQ(static_cast<ssize_t>(NEW_XATTR_VALUE.size()),
-            LIBC_NAMESPACE::syscall_impl<ssize_t>(
-                SYS_getxattr, static_cast<const char *>(TEST_FILE_NAME),
-                XATTR_NAME.data(), buffer, BUFFER_SIZE));
-  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
-
-  // Try create-only when the attribute already exists.
-  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
-                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
-                                       XATTR_CREATE),
-              Fails(EEXIST));
-}
-
-#if defined(LIBC_ADD_NULL_CHECKS)
-
-TEST_F(LlvmLibcSetxattrTest, CrashOnNullPath) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::setxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
-                                 /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcSetxattrTest, CrashOnNullAttributeName) {
-  EXPECT_DEATH(
-      [] {
-        constexpr size_t BUFFER_SIZE = 32;
-        char buffer[BUFFER_SIZE] = {};
-        LIBC_NAMESPACE::setxattr("testdata/file.txt", nullptr, buffer,
-                                 BUFFER_SIZE, /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-TEST_F(LlvmLibcSetxattrTest, CrashOnNullBufferNonZeroSize) {
-  EXPECT_DEATH(
-      [] {
-        LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32,
-                                 /* flags = */ 0);
-      },
-      WITH_SIGNAL(-1));
-}
-
-#endif // LIBC_ADD_NULL_CHECKS
-
-} // namespace
diff --git a/libc/test/src/sys/xattr/xattr_test.cpp b/libc/test/src/sys/xattr/xattr_test.cpp
new file mode 100644
index 0000000000000..f14ea03816617
--- /dev/null
+++ b/libc/test/src/sys/xattr/xattr_test.cpp
@@ -0,0 +1,336 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for getxattr, setxattr, and, listxattr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_xattr_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "src/__support/CPP/scope.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/creat.h"
+#include "src/sys/xattr/getxattr.h"
+#include "src/sys/xattr/listxattr.h"
+#include "src/sys/xattr/setxattr.h"
+#include "src/unistd/close.h"
+#include "src/unistd/symlink.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcXattrTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LIBC_NAMESPACE::cpp::scope_exit;
+using LIBC_NAMESPACE::cpp::string_view;
+
+int recreate_test_file(const char *path) {
+  LIBC_NAMESPACE::unlink(path);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::creat(path, S_IRWXU);
+}
+
+int recreate_test_symlink(const char *target, const char *linkpath) {
+  LIBC_NAMESPACE::unlink(linkpath);
+  LIBC_NAMESPACE::libc_errno = 0;
+  return LIBC_NAMESPACE::symlink(target, linkpath);
+}
+
+TEST_F(LlvmLibcXattrTest, GetAndListWithNoAttributeSet) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/xattr_unset_testfile.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+
+  constexpr size_t BUFFER_SIZE = 32;
+  char buffer[BUFFER_SIZE] = {};
+
+  EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, "user.missing_test_attr",
+                                       buffer, BUFFER_SIZE),
+              Fails<ssize_t>(ENODATA));
+
+  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, nullptr, 0),
+              Succeeds<ssize_t>(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(0));
+}
+
+TEST_F(LlvmLibcXattrTest, SetGetAndListWithUserExtendedAttribute) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/xattr_testfile.txt");
+  constexpr const char *TEST_SYMLINK_TARGET = "xattr_testfile.txt";
+  const LIBC_NAMESPACE::CString TEST_SYMLINK_NAME =
+      libc_make_test_file_path("testdata/xattr_testsymlink.txt");
+
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit unlink_file([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  ASSERT_THAT(recreate_test_symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME),
+              Succeeds(0));
+  scope_exit unlink_symlink([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK_NAME), Succeeds(0));
+  });
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       /* flags = */ 0),
+              Succeeds(0));
+  size_t xattr_name_null_terminated_len = XATTR_NAME.size() + 1;
+
+  // Get and list using the test file path.
+  EXPECT_THAT(
+      LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(), nullptr, 0),
+      Succeeds<ssize_t>(XATTR_VALUE.size()));
+  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, nullptr, 0),
+              Succeeds<ssize_t>(xattr_name_null_terminated_len));
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_NAME.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+                Succeeds(xattr_name_null_terminated_len));
+    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
+    EXPECT_EQ(result_str, XATTR_NAME);
+  }
+
+  // Get and list using the symlink path to verify the correct syscall is used
+  // internally; getxattr or listxattr, and not lgetxattr or llistxattr.
+  EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                       nullptr, 0),
+              Succeeds<ssize_t>(XATTR_VALUE.size()));
+  EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_SYMLINK_NAME, nullptr, 0),
+              Succeeds<ssize_t>(xattr_name_null_terminated_len));
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, XATTR_NAME.size());
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(LIBC_NAMESPACE::getxattr(TEST_SYMLINK_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Succeeds(XATTR_VALUE.size()));
+    string_view result_str(buffer, XATTR_VALUE.size());
+    EXPECT_EQ(result_str, XATTR_VALUE);
+  }
+  {
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, xattr_name_null_terminated_len);
+
+    char buffer[BUFFER_SIZE] = {};
+    ASSERT_THAT(
+        LIBC_NAMESPACE::listxattr(TEST_SYMLINK_NAME, buffer, BUFFER_SIZE),
+        Succeeds(xattr_name_null_terminated_len));
+    string_view result_str(buffer, xattr_name_null_terminated_len - 1);
+    EXPECT_EQ(result_str, XATTR_NAME);
+  }
+
+  // Set another attribute through the symlink to verify the correct syscall is
+  // used internally; setxattr instead of lsetxattr.
+  {
+    string_view NEW_XATTR_NAME = "user.test_attr_through_symlink";
+    string_view NEW_XATTR_VALUE = "test_value_through_symlink";
+    constexpr size_t BUFFER_SIZE = 32;
+    ASSERT_GE(BUFFER_SIZE, NEW_XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    ASSERT_THAT(
+        LIBC_NAMESPACE::setxattr(TEST_SYMLINK_NAME, NEW_XATTR_NAME.data(),
+                                 NEW_XATTR_VALUE.data(), NEW_XATTR_VALUE.size(),
+                                 /* flags = */ 0),
+        Succeeds(0));
+
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, NEW_XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Succeeds<ssize_t>(NEW_XATTR_VALUE.size()));
+    ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+  }
+
+  // Get and list with insufficient buffer size, and get with missing attribute
+  // to check failing errno.
+  {
+    constexpr size_t BUFFER_SIZE = 8;
+    ASSERT_LT(BUFFER_SIZE, xattr_name_null_terminated_len);
+    ASSERT_LT(BUFFER_SIZE, XATTR_VALUE.size());
+    char buffer[BUFFER_SIZE] = {};
+
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                         buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::listxattr(TEST_FILE_NAME, buffer, BUFFER_SIZE),
+                Fails<ssize_t>(ERANGE));
+
+    EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME,
+                                         "user.missing_test_attr", buffer,
+                                         BUFFER_SIZE),
+                Fails<ssize_t>(ENODATA));
+  }
+}
+
+TEST_F(LlvmLibcXattrTest, SetAttributeWithNonzeroFlags) {
+  const LIBC_NAMESPACE::CString TEST_FILE_NAME =
+      libc_make_test_file_path("testdata/xattr_nonzero_flags_testfile.txt");
+  int fd = recreate_test_file(TEST_FILE_NAME);
+  ASSERT_ERRNO_SUCCESS();
+  scope_exit cleanup([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE_NAME), Succeeds(0));
+  });
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+  string_view XATTR_NAME = "user.test_attr";
+  string_view XATTR_VALUE = "test_value";
+  constexpr size_t BUFFER_SIZE = 32;
+  ASSERT_GE(BUFFER_SIZE, XATTR_VALUE.size());
+  char buffer[BUFFER_SIZE] = {};
+
+  // Try replace-only before attribute exists.
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       XATTR_REPLACE),
+              Fails(ENODATA));
+
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       /* flags = */ XATTR_CREATE),
+              Succeeds(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, XATTR_VALUE.size()), XATTR_VALUE);
+
+  // Replace-only now that the attribute does exist.
+  string_view NEW_XATTR_VALUE = "new_test_value";
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       NEW_XATTR_VALUE.data(),
+                                       NEW_XATTR_VALUE.size(), XATTR_REPLACE),
+              Succeeds(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::getxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       buffer, BUFFER_SIZE),
+              Succeeds<ssize_t>(NEW_XATTR_VALUE.size()));
+  ASSERT_EQ(string_view(buffer, NEW_XATTR_VALUE.size()), NEW_XATTR_VALUE);
+
+  // Try create-only when the attribute already exists.
+  ASSERT_THAT(LIBC_NAMESPACE::setxattr(TEST_FILE_NAME, XATTR_NAME.data(),
+                                       XATTR_VALUE.data(), XATTR_VALUE.size(),
+                                       XATTR_CREATE),
+              Fails(EEXIST));
+}
+
+#if 1 // defined(LIBC_ADD_NULL_CHECKS)
+
+TEST_F(LlvmLibcXattrTest, SetCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::setxattr(nullptr, "user.attr", buffer, BUFFER_SIZE,
+                                 /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, SetCrashesOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::setxattr("testdata/file.txt", nullptr, buffer,
+                                 BUFFER_SIZE, /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, SetCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::setxattr("testdata/file.txt", "user.attr", nullptr, 32,
+                                 /* flags = */ 0);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, GetCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::getxattr(nullptr, "user.attr", buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, GetCrashesOnNullAttributeName) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::getxattr("testdata/file.txt", nullptr, buffer,
+                                 BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, GetCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] {
+        LIBC_NAMESPACE::getxattr("testdata/file.txt", "user.attr", nullptr, 32);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST(LlvmLibcXattrTest, ListCrashesOnNullPath) {
+  EXPECT_DEATH(
+      [] {
+        constexpr size_t BUFFER_SIZE = 32;
+        char buffer[BUFFER_SIZE] = {};
+        LIBC_NAMESPACE::listxattr(nullptr, buffer, BUFFER_SIZE);
+      },
+      WITH_SIGNAL(-1));
+}
+
+TEST_F(LlvmLibcXattrTest, ListCrashesOnNullBufferNonZeroSize) {
+  EXPECT_DEATH(
+      [] { LIBC_NAMESPACE::listxattr("testdata/file.txt", nullptr, 32); },
+      WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
+
+} // namespace

>From dc8e0353bab90f0a62bfb858d27750219a58292e Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Thu, 24 Sep 2026 14:44:18 +0000
Subject: [PATCH 6/6] Sort entrypoint lists

---
 libc/config/linux/aarch64/entrypoints.txt | 8 ++++----
 libc/config/linux/x86_64/entrypoints.txt  | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 20cff8a4b8f2d..b5b80f8863b38 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -386,14 +386,14 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # sys/xattr.h entrypoints
     libc.src.sys.xattr.fgetxattr
+    libc.src.sys.xattr.flistxattr
+    libc.src.sys.xattr.fsetxattr
     libc.src.sys.xattr.getxattr
     libc.src.sys.xattr.lgetxattr
-    libc.src.sys.xattr.fsetxattr
-    libc.src.sys.xattr.setxattr
-    libc.src.sys.xattr.lsetxattr
-    libc.src.sys.xattr.flistxattr
     libc.src.sys.xattr.listxattr
     libc.src.sys.xattr.llistxattr
+    libc.src.sys.xattr.lsetxattr
+    libc.src.sys.xattr.setxattr
 
     # sys/prctl.h entrypoints
     libc.src.sys.prctl.prctl
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 2e5b944eb2852..9da65debef0cf 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -425,14 +425,14 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # sys/xattr.h entrypoints
     libc.src.sys.xattr.fgetxattr
+    libc.src.sys.xattr.flistxattr
+    libc.src.sys.xattr.fsetxattr
     libc.src.sys.xattr.getxattr
     libc.src.sys.xattr.lgetxattr
-    libc.src.sys.xattr.fsetxattr
-    libc.src.sys.xattr.setxattr
-    libc.src.sys.xattr.lsetxattr
-    libc.src.sys.xattr.flistxattr
     libc.src.sys.xattr.listxattr
     libc.src.sys.xattr.llistxattr
+    libc.src.sys.xattr.lsetxattr
+    libc.src.sys.xattr.setxattr
 
     # sys/prctl.h entrypoints
     libc.src.sys.prctl.prctl



More information about the libc-commits mailing list