[libc-commits] [libc] [libc] Implement lockf in unistd (#230856) (PR #231145)

Aman Maurya via libc-commits libc-commits at lists.llvm.org
Wed Oct 14 01:03:48 PDT 2026


https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/231145

>From 82bd60345cd1e132cd5f3c9bcf6f29bd0f82dd7b Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Mon, 12 Oct 2026 18:00:51 +0530
Subject: [PATCH 1/3] [libc] Implement lockf in unistd (#230856)

---
 libc/config/linux/aarch64/entrypoints.txt     |   1 +
 libc/config/linux/arm/entrypoints.txt         |   1 +
 libc/config/linux/riscv/entrypoints.txt       |   1 +
 libc/config/linux/x86_64/entrypoints.txt      |   1 +
 libc/hdr/CMakeLists.txt                       |   1 +
 libc/hdr/unistd_macros.h                      |   1 +
 .../llvm-libc-macros/linux/unistd-macros.h    |   6 +
 libc/include/unistd.yaml                      |  16 +++
 libc/src/unistd/CMakeLists.txt                |   7 ++
 libc/src/unistd/linux/CMakeLists.txt          |  19 ++++
 libc/src/unistd/linux/lockf.cpp               |  81 ++++++++++++++
 libc/src/unistd/lockf.h                       |  26 +++++
 libc/test/src/unistd/CMakeLists.txt           |  19 ++++
 libc/test/src/unistd/lockf_test.cpp           | 103 ++++++++++++++++++
 14 files changed, 283 insertions(+)
 create mode 100644 libc/src/unistd/linux/lockf.cpp
 create mode 100644 libc/src/unistd/lockf.h
 create mode 100644 libc/test/src/unistd/lockf_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index b76cc76c30bcd..38c6166dca757 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -486,6 +486,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.unistd.isatty
     libc.src.unistd.link
     libc.src.unistd.linkat
+    libc.src.unistd.lockf
     libc.src.unistd.lseek
     libc.src.unistd.nice
     libc.src.unistd.pathconf
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 4e535fe5111d6..4438d0de33591 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -282,6 +282,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.unistd.isatty
     libc.src.unistd.link
     libc.src.unistd.linkat
+    libc.src.unistd.lockf
     libc.src.unistd.lseek
     libc.src.unistd.nice
     libc.src.unistd.pause
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 0a0932bcb3e42..4399c0514fcfb 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -506,6 +506,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.unistd.isatty
     libc.src.unistd.link
     libc.src.unistd.linkat
+    libc.src.unistd.lockf
     libc.src.unistd.lseek
     libc.src.unistd.nice
     libc.src.unistd.pathconf
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 85bb01f416219..026648e21438f 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -526,6 +526,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.unistd.isatty
     libc.src.unistd.link
     libc.src.unistd.linkat
+    libc.src.unistd.lockf
     libc.src.unistd.lseek
     libc.src.unistd.nice
     libc.src.unistd.pathconf
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index cf8b1f36d349e..10ac84340523d 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -320,6 +320,7 @@ add_proxy_header_library(
   DEPENDS
     .unistd_overlay
   FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-macros.file_seek_macros
     libc.include.llvm-libc-macros.unistd_macros
 )
 
diff --git a/libc/hdr/unistd_macros.h b/libc/hdr/unistd_macros.h
index bda16224608bf..b1a63b2dd1e7d 100644
--- a/libc/hdr/unistd_macros.h
+++ b/libc/hdr/unistd_macros.h
@@ -11,6 +11,7 @@
 
 #ifdef LIBC_FULL_BUILD
 
+#include "include/llvm-libc-macros/file-seek-macros.h"
 #include "include/llvm-libc-macros/unistd-macros.h"
 
 #else // Overlay mode
diff --git a/libc/include/llvm-libc-macros/linux/unistd-macros.h b/libc/include/llvm-libc-macros/linux/unistd-macros.h
index 0a9facd82fcb2..e14d44960de0e 100644
--- a/libc/include/llvm-libc-macros/linux/unistd-macros.h
+++ b/libc/include/llvm-libc-macros/linux/unistd-macros.h
@@ -20,6 +20,12 @@
 #define W_OK 2
 #define R_OK 4
 
+// Values for command argument to the lockf(...) function.
+#define F_ULOCK 0
+#define F_LOCK 1
+#define F_TLOCK 2
+#define F_TEST 3
+
 #define _SC_ARG_MAX 0
 #define _SC_CLK_TCK 2
 #define _SC_OPEN_MAX 4
diff --git a/libc/include/unistd.yaml b/libc/include/unistd.yaml
index 160b909f3af7b..3f7a362fa5def 100644
--- a/libc/include/unistd.yaml
+++ b/libc/include/unistd.yaml
@@ -88,6 +88,14 @@ macros:
     macro_header: unistd-macros.h
   - macro_name: _POSIX_THREAD_SPORADIC_SERVER
     macro_header: unistd-macros.h
+  - macro_name: F_ULOCK
+    macro_header: unistd-macros.h
+  - macro_name: F_LOCK
+    macro_header: unistd-macros.h
+  - macro_name: F_TLOCK
+    macro_header: unistd-macros.h
+  - macro_name: F_TEST
+    macro_header: unistd-macros.h
 types:
   - type_name: uid_t
   - type_name: gid_t
@@ -414,6 +422,14 @@ functions:
       - type: int
       - type: const char *
       - type: int
+  - name: lockf
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: int
+      - type: int
+      - type: off_t
   - name: lseek
     standards:
       - posix
diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 795224f0e9df4..91a6b55c28aef 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -294,6 +294,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.linkat
 )
 
+add_entrypoint_object(
+  lockf
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.lockf
+)
+
 add_entrypoint_object(
   lseek
   ALIAS
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 3624f4e1891f2..6921a468e295d 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -482,6 +482,25 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  lockf
+  SRCS
+    lockf.cpp
+  HDRS
+    ../lockf.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fcntl_macros
+    libc.hdr.types.off_t
+    libc.hdr.types.struct_flock
+    libc.hdr.unistd_macros
+    libc.src.__support.OSUtil.linux.syscall_wrappers.fcntl
+    libc.src.__support.OSUtil.linux.syscall_wrappers.getpid
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+)
+
 add_entrypoint_object(
   lseek
   SRCS
diff --git a/libc/src/unistd/linux/lockf.cpp b/libc/src/unistd/linux/lockf.cpp
new file mode 100644
index 0000000000000..ecfed291ab59e
--- /dev/null
+++ b/libc/src/unistd/linux/lockf.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 implementation of lockf.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/unistd/lockf.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/types/off_t.h"
+#include "hdr/types/struct_flock.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fcntl.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, lockf, (int fd, int cmd, off_t len)) {
+  struct flock fl = {};
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_CUR;
+  fl.l_start = 0;
+  fl.l_len = len;
+  fl.l_pid = 0;
+
+  switch (cmd) {
+  case F_TEST: {
+    fl.l_type = F_RDLCK;
+    auto result = linux_syscalls::fcntl(fd, F_GETLK, &fl);
+    if (!result.has_value()) {
+      libc_errno = result.error();
+      return -1;
+    }
+    if (fl.l_type == F_UNLCK || fl.l_pid == linux_syscalls::getpid())
+      return 0;
+    libc_errno = EACCES;
+    return -1;
+  }
+  case F_ULOCK: {
+    fl.l_type = F_UNLCK;
+    auto result = linux_syscalls::fcntl(fd, F_SETLK, &fl);
+    if (!result.has_value()) {
+      libc_errno = result.error();
+      return -1;
+    }
+    return 0;
+  }
+  case F_LOCK: {
+    auto result = linux_syscalls::fcntl(fd, F_SETLKW, &fl);
+    if (!result.has_value()) {
+      libc_errno = result.error();
+      return -1;
+    }
+    return 0;
+  }
+  case F_TLOCK: {
+    auto result = linux_syscalls::fcntl(fd, F_SETLK, &fl);
+    if (!result.has_value()) {
+      libc_errno = result.error();
+      return -1;
+    }
+    return 0;
+  }
+  default:
+    libc_errno = EINVAL;
+    return -1;
+  }
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/unistd/lockf.h b/libc/src/unistd/lockf.h
new file mode 100644
index 0000000000000..27a9949753fe5
--- /dev/null
+++ b/libc/src/unistd/lockf.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 header for lockf.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UNISTD_LOCKF_H
+#define LLVM_LIBC_SRC_UNISTD_LOCKF_H
+
+#include "hdr/types/off_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int lockf(int fd, int cmd, off_t len);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UNISTD_LOCKF_H
diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt
index 89d6dcece5e10..bab290eccccfa 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -972,6 +972,25 @@ add_libc_test(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_test(
+  lockf_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    lockf_test.cpp
+  DEPENDS
+    libc.hdr.fcntl_macros
+    libc.hdr.sys_stat_macros
+    libc.hdr.unistd_macros
+    libc.src.errno.errno
+    libc.src.fcntl.open
+    libc.src.unistd.close
+    libc.src.unistd.lockf
+    libc.src.unistd.unlink
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 if(LLVM_LIBC_FULL_BUILD)
   add_libc_test(
     _exit_test
diff --git a/libc/test/src/unistd/lockf_test.cpp b/libc/test/src/unistd/lockf_test.cpp
new file mode 100644
index 0000000000000..a2b14c6e6ad86
--- /dev/null
+++ b/libc/test/src/unistd/lockf_test.cpp
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Unittest for lockf.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/unistd_macros.h"
+#include "src/fcntl/open.h"
+#include "src/unistd/close.h"
+#include "src/unistd/lockf.h"
+#include "src/unistd/unlink.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcLockfTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcLockfTest, BadFd) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(-1, F_LOCK, 0), Fails(EBADF));
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(-1, F_TLOCK, 0), Fails(EBADF));
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(-1, F_ULOCK, 0), Fails(EBADF));
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(-1, F_TEST, 0), Fails(EBADF));
+}
+
+TEST_F(LlvmLibcLockfTest, InvalidCmd) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_FILE_NAME = "testdata/lockf_invalid_cmd.test";
+  auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(fd, 0);
+
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, 999, 0), Fails(EINVAL));
+
+  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+}
+
+TEST_F(LlvmLibcLockfTest, LockUnlockAndTest) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_FILE_NAME = "testdata/lockf_basic.test";
+  auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(fd, 0);
+
+  // Section is not locked: F_TEST should succeed (return 0).
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TEST, 0), Succeeds(0));
+
+  // Lock the entire file: F_LOCK should succeed.
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_LOCK, 0), Succeeds(0));
+
+  // When locked by calling process, F_TEST still succeeds (POSIX requirement).
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TEST, 0), Succeeds(0));
+
+  // Unlock the entire file: F_ULOCK should succeed.
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_ULOCK, 0), Succeeds(0));
+
+  // Non-blocking lock: F_TLOCK should succeed.
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TLOCK, 100), Succeeds(0));
+
+  // Unlock the section: F_ULOCK should succeed.
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_ULOCK, 100), Succeeds(0));
+
+  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+}
+
+TEST_F(LlvmLibcLockfTest, ReadOnlyFd) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+  constexpr const char *TEST_FILE_NAME = "testdata/lockf_rdonly.test";
+  auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
+
+  // Create the file first so we can open it read-only.
+  int create_fd =
+      LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(create_fd, 0);
+  EXPECT_THAT(LIBC_NAMESPACE::close(create_fd), Succeeds(0));
+
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_RDONLY, 0);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(fd, 0);
+
+  // F_LOCK on a read-only fd should fail with EBADF.
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_LOCK, 0), Fails(EBADF));
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TLOCK, 0), Fails(EBADF));
+
+  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+}

>From 8ad2432a2accf06780e6322b5a1c27c718c44a1e Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Tue, 13 Oct 2026 20:20:37 +0530
Subject: [PATCH 2/3] [libc] Address review feedback on lockf (#230856)

---
 libc/src/unistd/linux/CMakeLists.txt          |  1 +
 libc/src/unistd/linux/lockf.cpp               | 60 +++++++--------
 .../integration/src/unistd/CMakeLists.txt     | 22 ++++++
 .../integration/src/unistd/lockf_test.cpp     | 75 +++++++++++++++++++
 libc/test/src/unistd/CMakeLists.txt           |  1 +
 libc/test/src/unistd/lockf_test.cpp           | 34 ++++-----
 6 files changed, 142 insertions(+), 51 deletions(-)
 create mode 100644 libc/test/integration/src/unistd/lockf_test.cpp

diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 6921a468e295d..27d9cf9d9cec5 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -497,6 +497,7 @@ add_entrypoint_object(
     libc.src.__support.OSUtil.linux.syscall_wrappers.fcntl
     libc.src.__support.OSUtil.linux.syscall_wrappers.getpid
     libc.src.__support.common
+    libc.src.__support.error_or
     libc.src.__support.libc_errno
     libc.src.__support.macros.config
 )
diff --git a/libc/src/unistd/linux/lockf.cpp b/libc/src/unistd/linux/lockf.cpp
index ecfed291ab59e..81dcb52fdcb10 100644
--- a/libc/src/unistd/linux/lockf.cpp
+++ b/libc/src/unistd/linux/lockf.cpp
@@ -21,61 +21,53 @@
 #include "src/__support/OSUtil/linux/syscall_wrappers/fcntl.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
 #include "src/__support/common.h"
+#include "src/__support/error_or.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, lockf, (int fd, int cmd, off_t len)) {
+namespace {
+
+LIBC_INLINE ErrorOr<int> lockf_impl(int fd, int cmd, off_t len) {
   struct flock fl = {};
-  fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_CUR;
   fl.l_start = 0;
   fl.l_len = len;
-  fl.l_pid = 0;
 
   switch (cmd) {
   case F_TEST: {
     fl.l_type = F_RDLCK;
     auto result = linux_syscalls::fcntl(fd, F_GETLK, &fl);
-    if (!result.has_value()) {
-      libc_errno = result.error();
-      return -1;
-    }
+    if (!result.has_value())
+      return Error(result.error());
     if (fl.l_type == F_UNLCK || fl.l_pid == linux_syscalls::getpid())
       return 0;
-    libc_errno = EACCES;
-    return -1;
+    return Error(EACCES);
   }
-  case F_ULOCK: {
+  case F_ULOCK:
     fl.l_type = F_UNLCK;
-    auto result = linux_syscalls::fcntl(fd, F_SETLK, &fl);
-    if (!result.has_value()) {
-      libc_errno = result.error();
-      return -1;
-    }
-    return 0;
-  }
-  case F_LOCK: {
-    auto result = linux_syscalls::fcntl(fd, F_SETLKW, &fl);
-    if (!result.has_value()) {
-      libc_errno = result.error();
-      return -1;
-    }
-    return 0;
-  }
-  case F_TLOCK: {
-    auto result = linux_syscalls::fcntl(fd, F_SETLK, &fl);
-    if (!result.has_value()) {
-      libc_errno = result.error();
-      return -1;
-    }
-    return 0;
-  }
+    return linux_syscalls::fcntl(fd, F_SETLK, &fl);
+  case F_LOCK:
+    fl.l_type = F_WRLCK;
+    return linux_syscalls::fcntl(fd, F_SETLKW, &fl);
+  case F_TLOCK:
+    fl.l_type = F_WRLCK;
+    return linux_syscalls::fcntl(fd, F_SETLK, &fl);
   default:
-    libc_errno = EINVAL;
+    return Error(EINVAL);
+  }
+}
+
+} // anonymous namespace
+
+LLVM_LIBC_FUNCTION(int, lockf, (int fd, int cmd, off_t len)) {
+  auto result = lockf_impl(fd, cmd, len);
+  if (!result.has_value()) {
+    libc_errno = result.error();
     return -1;
   }
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 069385a532543..5d42ce8753f2c 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -38,6 +38,28 @@ add_integration_test(
     libc.include.sys_syscall
 )
 
+add_integration_test(
+  lockf_test
+  SUITE
+    unistd-integration-tests
+  SRCS
+    lockf_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fcntl_macros
+    libc.hdr.sys_stat_macros
+    libc.hdr.sys_wait_macros
+    libc.hdr.unistd_macros
+    libc.src.errno.errno
+    libc.src.fcntl.open
+    libc.src.stdlib.exit
+    libc.src.sys.wait.waitpid
+    libc.src.unistd.close
+    libc.src.unistd.fork
+    libc.src.unistd.lockf
+    libc.src.unistd.unlink
+)
+
 if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_ARCHITECTURE_IS_X86}))
   add_integration_test(
     stack_smashing_test
diff --git a/libc/test/integration/src/unistd/lockf_test.cpp b/libc/test/integration/src/unistd/lockf_test.cpp
new file mode 100644
index 0000000000000..7c7ee64bba47e
--- /dev/null
+++ b/libc/test/integration/src/unistd/lockf_test.cpp
@@ -0,0 +1,75 @@
+//===-- Integration test for lockf ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_stat_macros.h"
+#include "hdr/sys_wait_macros.h"
+#include "hdr/unistd_macros.h"
+#include "src/fcntl/open.h"
+#include "src/stdlib/exit.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/close.h"
+#include "src/unistd/fork.h"
+#include "src/unistd/lockf.h"
+#include "src/unistd/unlink.h"
+
+#include "test/IntegrationTest/test.h"
+
+#include <errno.h>
+
+TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
+          [[maybe_unused]] char **envp) {
+  constexpr const char TEST_FILE[] = "lockf_integration.test";
+
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+  ASSERT_TRUE(fd > 0);
+
+  // Acquire an exclusive lock on the file in the parent process.
+  ASSERT_EQ(LIBC_NAMESPACE::lockf(fd, F_LOCK, 0), 0);
+
+  pid_t pid = LIBC_NAMESPACE::fork();
+  ASSERT_TRUE(pid >= 0);
+
+  if (pid == 0) {
+    // Child process:
+    int child_fd = LIBC_NAMESPACE::open(TEST_FILE, O_RDWR, 0);
+    if (child_fd < 0)
+      LIBC_NAMESPACE::exit(1);
+
+    // F_TEST should fail because the section is locked by another process
+    // (parent).
+    if (LIBC_NAMESPACE::lockf(child_fd, F_TEST, 0) == 0)
+      LIBC_NAMESPACE::exit(2);
+    if (errno != EACCES && errno != EAGAIN)
+      LIBC_NAMESPACE::exit(3);
+
+    // F_TLOCK should fail because non-blocking lock cannot be acquired.
+    if (LIBC_NAMESPACE::lockf(child_fd, F_TLOCK, 0) == 0)
+      LIBC_NAMESPACE::exit(4);
+    if (errno != EACCES && errno != EAGAIN)
+      LIBC_NAMESPACE::exit(5);
+
+    LIBC_NAMESPACE::close(child_fd);
+    LIBC_NAMESPACE::exit(0);
+  }
+
+  // Parent process waits for child.
+  int status = 0;
+  pid_t cpid = LIBC_NAMESPACE::waitpid(pid, &status, 0);
+  ASSERT_EQ(cpid, pid);
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(WEXITSTATUS(status), 0);
+
+  // Release lock and clean up file.
+  ASSERT_EQ(LIBC_NAMESPACE::lockf(fd, F_ULOCK, 0), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::close(fd), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::unlink(TEST_FILE), 0);
+
+  return 0;
+}
diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt
index bab290eccccfa..77f69bb238569 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -982,6 +982,7 @@ add_libc_test(
     libc.hdr.fcntl_macros
     libc.hdr.sys_stat_macros
     libc.hdr.unistd_macros
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.fcntl.open
     libc.src.unistd.close
diff --git a/libc/test/src/unistd/lockf_test.cpp b/libc/test/src/unistd/lockf_test.cpp
index a2b14c6e6ad86..d2b264c71c5b6 100644
--- a/libc/test/src/unistd/lockf_test.cpp
+++ b/libc/test/src/unistd/lockf_test.cpp
@@ -14,6 +14,7 @@
 #include "hdr/fcntl_macros.h"
 #include "hdr/sys_stat_macros.h"
 #include "hdr/unistd_macros.h"
+#include "src/__support/CPP/scope.h"
 #include "src/fcntl/open.h"
 #include "src/unistd/close.h"
 #include "src/unistd/lockf.h"
@@ -41,10 +42,12 @@ TEST_F(LlvmLibcLockfTest, InvalidCmd) {
   ASSERT_ERRNO_SUCCESS();
   ASSERT_GT(fd, 0);
 
-  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, 999, 0), Fails(EINVAL));
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+  });
 
-  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+  EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, 999, 0), Fails(EINVAL));
 }
 
 TEST_F(LlvmLibcLockfTest, LockUnlockAndTest) {
@@ -55,6 +58,11 @@ TEST_F(LlvmLibcLockfTest, LockUnlockAndTest) {
   ASSERT_ERRNO_SUCCESS();
   ASSERT_GT(fd, 0);
 
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+  });
+
   // Section is not locked: F_TEST should succeed (return 0).
   EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TEST, 0), Succeeds(0));
 
@@ -72,9 +80,6 @@ TEST_F(LlvmLibcLockfTest, LockUnlockAndTest) {
 
   // Unlock the section: F_ULOCK should succeed.
   EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_ULOCK, 100), Succeeds(0));
-
-  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
 }
 
 TEST_F(LlvmLibcLockfTest, ReadOnlyFd) {
@@ -83,21 +88,16 @@ TEST_F(LlvmLibcLockfTest, ReadOnlyFd) {
   constexpr const char *TEST_FILE_NAME = "testdata/lockf_rdonly.test";
   auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
 
-  // Create the file first so we can open it read-only.
-  int create_fd =
-      LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU);
-  ASSERT_ERRNO_SUCCESS();
-  ASSERT_GT(create_fd, 0);
-  EXPECT_THAT(LIBC_NAMESPACE::close(create_fd), Succeeds(0));
-
-  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_RDONLY, 0);
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_RDONLY, S_IRWXU);
   ASSERT_ERRNO_SUCCESS();
   ASSERT_GT(fd, 0);
 
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+    EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
+  });
+
   // F_LOCK on a read-only fd should fail with EBADF.
   EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_LOCK, 0), Fails(EBADF));
   EXPECT_THAT(LIBC_NAMESPACE::lockf(fd, F_TLOCK, 0), Fails(EBADF));
-
-  EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
-  EXPECT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
 }

>From 6ef8f1ee74521c641f1f0795ce36e947596390b3 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 14 Oct 2026 13:32:59 +0530
Subject: [PATCH 3/3] [libc] Address review feedback on lockf

---
 libc/src/unistd/linux/lockf.cpp                 |  6 +-----
 libc/test/integration/src/unistd/CMakeLists.txt |  1 +
 libc/test/integration/src/unistd/lockf_test.cpp | 11 ++++++-----
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libc/src/unistd/linux/lockf.cpp b/libc/src/unistd/linux/lockf.cpp
index 81dcb52fdcb10..e2fc005df9f01 100644
--- a/libc/src/unistd/linux/lockf.cpp
+++ b/libc/src/unistd/linux/lockf.cpp
@@ -27,9 +27,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-namespace {
-
-LIBC_INLINE ErrorOr<int> lockf_impl(int fd, int cmd, off_t len) {
+static ErrorOr<int> lockf_impl(int fd, int cmd, off_t len) {
   struct flock fl = {};
   fl.l_whence = SEEK_CUR;
   fl.l_start = 0;
@@ -59,8 +57,6 @@ LIBC_INLINE ErrorOr<int> lockf_impl(int fd, int cmd, off_t len) {
   }
 }
 
-} // anonymous namespace
-
 LLVM_LIBC_FUNCTION(int, lockf, (int fd, int cmd, off_t len)) {
   auto result = lockf_impl(fd, cmd, len);
   if (!result.has_value()) {
diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 5d42ce8753f2c..1cb2e7b6d335f 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -50,6 +50,7 @@ add_integration_test(
     libc.hdr.sys_stat_macros
     libc.hdr.sys_wait_macros
     libc.hdr.unistd_macros
+    libc.src.__support.CPP.scope
     libc.src.errno.errno
     libc.src.fcntl.open
     libc.src.stdlib.exit
diff --git a/libc/test/integration/src/unistd/lockf_test.cpp b/libc/test/integration/src/unistd/lockf_test.cpp
index 7c7ee64bba47e..504b1fc22f667 100644
--- a/libc/test/integration/src/unistd/lockf_test.cpp
+++ b/libc/test/integration/src/unistd/lockf_test.cpp
@@ -11,6 +11,7 @@
 #include "hdr/sys_stat_macros.h"
 #include "hdr/sys_wait_macros.h"
 #include "hdr/unistd_macros.h"
+#include "src/__support/CPP/scope.h"
 #include "src/fcntl/open.h"
 #include "src/stdlib/exit.h"
 #include "src/sys/wait/waitpid.h"
@@ -30,6 +31,11 @@ TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
   int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
   ASSERT_TRUE(fd > 0);
 
+  LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+    ASSERT_EQ(LIBC_NAMESPACE::close(fd), 0);
+    ASSERT_EQ(LIBC_NAMESPACE::unlink(TEST_FILE), 0);
+  });
+
   // Acquire an exclusive lock on the file in the parent process.
   ASSERT_EQ(LIBC_NAMESPACE::lockf(fd, F_LOCK, 0), 0);
 
@@ -66,10 +72,5 @@ TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
   ASSERT_TRUE(WIFEXITED(status));
   ASSERT_EQ(WEXITSTATUS(status), 0);
 
-  // Release lock and clean up file.
-  ASSERT_EQ(LIBC_NAMESPACE::lockf(fd, F_ULOCK, 0), 0);
-  ASSERT_EQ(LIBC_NAMESPACE::close(fd), 0);
-  ASSERT_EQ(LIBC_NAMESPACE::unlink(TEST_FILE), 0);
-
   return 0;
 }



More information about the libc-commits mailing list