librdmacm: Add support for fcntl64

Add preload interception for fcntl64 so rsocket file descriptors
support the same flag semantics as the glibc fcntl64 API.

Signed-off-by: Batsheva Black <bblack@nvidia.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9165fa2..d694e3e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -449,6 +449,52 @@
   add_definitions("-D_FILE_OFFSET_BITS=64")
 endif()
 
+# librspreload: probe libc for fcntl64; CMake sets RDMA_PRELOAD_WRAP_LFS64 when
+# preload should implement the fcntl64 wrapper (see RDMA_PRELOAD_WRAP_LFS64 below).
+set(CMAKE_REQUIRED_QUIET 1)
+set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+set(CMAKE_REQUIRED_DEFINITIONS "")
+CHECK_C_SOURCE_COMPILES("
+#include <fcntl.h>
+int main(void) {
+  (void)&fcntl64;
+  return 0;
+}
+" RDMA_PRELOAD_LIBC_HAS_FCNTL64_SENDFILE64)
+set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+set(CMAKE_REQUIRED_QUIET 0)
+if(RDMA_PRELOAD_LIBC_HAS_FCNTL64_SENDFILE64)
+  set(RDMA_PRELOAD_HAVE_LFS_WRAPPER_SYMS 1)
+else()
+  set(RDMA_PRELOAD_HAVE_LFS_WRAPPER_SYMS 0)
+endif()
+
+# Single gate for preload.c fcntl64 wrapper: need libc symbol and must not compile
+# preload with _FILE_OFFSET_BITS=64 (CMake adds that when HAVE_LARGE_FILES is false).
+if(RDMA_PRELOAD_HAVE_LFS_WRAPPER_SYMS AND HAVE_LARGE_FILES)
+  set(RDMA_PRELOAD_WRAP_LFS64 1)
+else()
+  set(RDMA_PRELOAD_WRAP_LFS64 0)
+endif()
+
+set(RDMA_PRELOAD_FCNTL64_IN_HEADER 0)
+set(RDMA_PRELOAD_SENDFILE64_IN_HEADER 0)
+if(RDMA_PRELOAD_HAVE_LFS_WRAPPER_SYMS)
+  set(CMAKE_REQUIRED_QUIET 1)
+  set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+  set(CMAKE_REQUIRED_FLAGS "${CMAKE_C_FLAGS}")
+  CHECK_C_SOURCE_COMPILES("
+#define _GNU_SOURCE
+#include <fcntl.h>
+int fcntl64(int socket, int cmd, ...) { (void)socket;(void)cmd; return 0; }
+" RDMA_PRELOAD_FCNTL64_DECLARED_IN_HEADER)
+  if(RDMA_PRELOAD_FCNTL64_DECLARED_IN_HEADER)
+    set(RDMA_PRELOAD_FCNTL64_IN_HEADER 1)
+  endif()
+  set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}")
+  set(CMAKE_REQUIRED_QUIET 0)
+endif()
+
 # Provide a shim if C11 stdatomic.h is not supported.
 if (NOT HAVE_SPARSE)
   CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC)
diff --git a/buildlib/rdma_functions.cmake b/buildlib/rdma_functions.cmake
index ef77d95..8d48612 100644
--- a/buildlib/rdma_functions.cmake
+++ b/buildlib/rdma_functions.cmake
@@ -120,6 +120,18 @@
   install(TARGETS ${DEST} DESTINATION "${CMAKE_INSTALL_LIBDIR}")
 endfunction()
 
+# rsocket LD_PRELOAD module. Requires RDMA_PRELOAD_* variables from top-level
+# CMakeLists.txt (RDMA_PRELOAD_WRAP_LFS64 and fcntl64 header probe).
+function(rdma_rspreload_module DEST VERSION_SCRIPT)
+  add_library(${DEST} MODULE ${ARGN})
+  target_compile_definitions(${DEST} PRIVATE
+    RDMA_PRELOAD_WRAP_LFS64=${RDMA_PRELOAD_WRAP_LFS64}
+    RDMA_PRELOAD_FCNTL64_IN_HEADER=${RDMA_PRELOAD_FCNTL64_IN_HEADER})
+  set_target_properties(${DEST} PROPERTIES LINK_FLAGS ${CMAKE_SHARED_LINKER_FLAGS})
+  set_target_properties(${DEST} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${BUILD_LIB}")
+  rdma_set_library_map(${DEST} ${VERSION_SCRIPT})
+endfunction()
+
 # Create a special provider with exported symbols in it The shared provider
 # exists as a normal system library with the normal shared library SONAME and
 # other convections. The system library is symlinked into the
diff --git a/librdmacm/CMakeLists.txt b/librdmacm/CMakeLists.txt
index ea1d155..cba925a 100644
--- a/librdmacm/CMakeLists.txt
+++ b/librdmacm/CMakeLists.txt
@@ -27,14 +27,10 @@
 
 # The preload library is a bit special, it needs to be open coded
 # Since it is a LD_PRELOAD it has no soname, and is installed in sub dir
-add_library(rspreload MODULE
+rdma_rspreload_module(rspreload librspreload.map
   preload.c
   indexer.c
   )
-# Even though this is a module we still want to use Wl,--no-undefined
-set_target_properties(rspreload PROPERTIES LINK_FLAGS ${CMAKE_SHARED_LINKER_FLAGS})
-set_target_properties(rspreload PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${BUILD_LIB}")
-rdma_set_library_map(rspreload librspreload.map)
 target_link_libraries(rspreload LINK_PRIVATE
   rdmacm
   ${CMAKE_THREAD_LIBS_INIT}
diff --git a/librdmacm/librspreload.map b/librdmacm/librspreload.map
index 23a54d4..4e27671 100644
--- a/librdmacm/librspreload.map
+++ b/librdmacm/librspreload.map
@@ -10,6 +10,7 @@
 		connect;
 		dup2;
 		fcntl;
+		fcntl64;
 		getpeername;
 		getsockname;
 		getsockopt;
diff --git a/librdmacm/preload.c b/librdmacm/preload.c
index 952e546..b8ae0ca 100644
--- a/librdmacm/preload.c
+++ b/librdmacm/preload.c
@@ -59,6 +59,12 @@
 #include "cma.h"
 #include "indexer.h"
 
+#if RDMA_PRELOAD_WRAP_LFS64
+#if !RDMA_PRELOAD_FCNTL64_IN_HEADER
+int fcntl64(int socket, int cmd, ... /* arg */);
+#endif
+#endif
+
 struct socket_calls {
 	int (*socket)(int domain, int type, int protocol);
 	int (*bind)(int socket, const struct sockaddr *addr, socklen_t addrlen);
@@ -88,6 +94,9 @@
 	int (*getsockopt)(int socket, int level, int optname,
 			  void *optval, socklen_t *optlen);
 	int (*fcntl)(int socket, int cmd, ... /* arg */);
+#if RDMA_PRELOAD_WRAP_LFS64
+	int (*fcntl64)(int socket, int cmd, ... /* arg */);
+#endif
 	int (*dup2)(int oldfd, int newfd);
 	ssize_t (*sendfile)(int out_fd, int in_fd, off_t *offset, size_t count);
 	int (*fxstat)(int ver, int fd, struct stat *buf);
@@ -410,6 +419,9 @@
 	real.setsockopt = dlsym(RTLD_NEXT, "setsockopt");
 	real.getsockopt = dlsym(RTLD_NEXT, "getsockopt");
 	real.fcntl = dlsym(RTLD_NEXT, "fcntl");
+#if RDMA_PRELOAD_WRAP_LFS64
+	real.fcntl64 = dlsym(RTLD_NEXT, "fcntl64");
+#endif
 	real.dup2 = dlsym(RTLD_NEXT, "dup2");
 	real.sendfile = dlsym(RTLD_NEXT, "sendfile");
 	real.fxstat = dlsym(RTLD_NEXT, "__fxstat");
@@ -647,7 +659,6 @@
 		if (cur_flags == -1)
 			goto close;
 	}
-
 	return fd;
 close:
 	close(fd);
@@ -1163,6 +1174,48 @@
 	return ret;
 }
 
+#if RDMA_PRELOAD_WRAP_LFS64
+int fcntl64(int socket, int cmd, ... /* arg */)
+{
+	va_list args;
+	long lparam;
+	void *pparam;
+	int fd, ret;
+
+	init_preload();
+	va_start(args, cmd);
+	switch (cmd) {
+	case F_GETFD:
+	case F_GETFL:
+	case F_GETOWN:
+	case F_GETSIG:
+	case F_GETLEASE:
+		ret = (fd_get(socket, &fd) == fd_rsocket) ?
+			rfcntl(fd, cmd) : real.fcntl64(fd, cmd);
+		break;
+	case F_DUPFD:
+	/*case F_DUPFD_CLOEXEC:*/
+	case F_SETFD:
+	case F_SETFL:
+	case F_SETOWN:
+	case F_SETSIG:
+	case F_SETLEASE:
+	case F_NOTIFY:
+		lparam = va_arg(args, long);
+		ret = (fd_get(socket, &fd) == fd_rsocket) ?
+			rfcntl(fd, cmd, lparam) : real.fcntl64(fd, cmd, lparam);
+		break;
+	default:
+		pparam = va_arg(args, void *);
+		ret = (fd_get(socket, &fd) == fd_rsocket) ?
+			rfcntl(fd, cmd, pparam) : real.fcntl64(fd, cmd, pparam);
+		break;
+	}
+	va_end(args);
+	return ret;
+}
+#endif
+
 /*
  * dup2 is not thread safe
  */