This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new aaa7f2bfc6f libs/libc/unistd: Add copy_file_range().
aaa7f2bfc6f is described below
commit aaa7f2bfc6f2febe883f58e7a7e5f4116ea60249
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Oct 13 19:05:51 2026 +0200
libs/libc/unistd: Add copy_file_range().
copy_file_range() copies a range of one file to another file, at given
offsets or at the file offsets. glibc, musl and FreeBSD have it, and
programs that use it, for example to copy file contents into an
archive, did not build.
Copy through a small buffer with read() and write(), or pread() and
pwrite() when an offset is given. Like other implementations, return
the number of bytes copied, which can be less than asked, and 0 at the
end of the input.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <[email protected]>
---
include/unistd.h | 2 +
libs/libc/unistd/CMakeLists.txt | 1 +
libs/libc/unistd/Make.defs | 1 +
libs/libc/unistd/lib_copy_file_range.c | 137 +++++++++++++++++++++++++++++++++
4 files changed, 141 insertions(+)
diff --git a/include/unistd.h b/include/unistd.h
index b17c8e6f933..a929f02fa65 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -392,6 +392,8 @@ ssize_t read(int fd, FAR void *buf, size_t nbytes);
ssize_t write(int fd, FAR const void *buf, size_t nbytes);
ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset);
+ssize_t copy_file_range(int infd, FAR off_t *inoff, int outfd,
+ FAR off_t *outoff, size_t len, unsigned int flags);
int ftruncate(int fd, off_t length);
int fchown(int fd, uid_t owner, gid_t group);
int lockf(int fd, int cmd, off_t len);
diff --git a/libs/libc/unistd/CMakeLists.txt b/libs/libc/unistd/CMakeLists.txt
index 75bc4f69931..7cf686154d7 100644
--- a/libs/libc/unistd/CMakeLists.txt
+++ b/libs/libc/unistd/CMakeLists.txt
@@ -22,6 +22,7 @@
set(SRCS
lib_access.c
+ lib_copy_file_range.c
lib_daemon.c
lib_swab.c
lib_pathconf.c
diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs
index 207376a3bfb..67abbc43963 100644
--- a/libs/libc/unistd/Make.defs
+++ b/libs/libc/unistd/Make.defs
@@ -23,6 +23,7 @@
# Add the unistd C files to the build
CSRCS += lib_access.c lib_daemon.c lib_swab.c lib_pathconf.c lib_sysconf.c
+CSRCS += lib_copy_file_range.c
CSRCS += lib_getcwd.c lib_getentropy.c lib_getopt_common.c lib_getopt.c
CSRCS += lib_getopt_long.c lib_getopt_longonly.c lib_getoptvars.c
lib_getoptargp.c
CSRCS += lib_getopterrp.c lib_getoptindp.c lib_getoptoptp.c lib_times.c
diff --git a/libs/libc/unistd/lib_copy_file_range.c
b/libs/libc/unistd/lib_copy_file_range.c
new file mode 100644
index 00000000000..d1956ce19e1
--- /dev/null
+++ b/libs/libc/unistd/lib_copy_file_range.c
@@ -0,0 +1,137 @@
+/****************************************************************************
+ * libs/libc/unistd/lib_copy_file_range.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define COPY_BUFSIZE 512
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: copy_file_range
+ *
+ * Description:
+ * Copy up to len bytes from the file infd to the file outfd. If inoff
+ * is NULL, the data is read at the file offset of infd, which advances;
+ * if not, it is read at *inoff, which advances, and the file offset of
+ * infd does not change. outoff works the same way for outfd.
+ *
+ * Input Parameters:
+ * infd - The file to read
+ * inoff - The offset to read at, or NULL
+ * outfd - The file to write
+ * outoff - The offset to write at, or NULL
+ * len - The number of bytes to copy
+ * flags - Must be zero
+ *
+ * Returned Value:
+ * The number of bytes copied, which can be less than len, or zero at the
+ * end of infd. On failure, -1 (ERROR) with errno set.
+ *
+ ****************************************************************************/
+
+ssize_t copy_file_range(int infd, FAR off_t *inoff, int outfd,
+ FAR off_t *outoff, size_t len, unsigned int flags)
+{
+ char buf[COPY_BUFSIZE];
+ ssize_t total = 0;
+
+ if (flags != 0)
+ {
+ set_errno(EINVAL);
+ return ERROR;
+ }
+
+ while (len > 0)
+ {
+ size_t size = len < sizeof(buf) ? len : sizeof(buf);
+ ssize_t nread;
+ ssize_t done = 0;
+
+ nread = inoff ? pread(infd, buf, size, *inoff) :
+ read(infd, buf, size);
+ if (nread <= 0)
+ {
+ return nread < 0 && total == 0 ? ERROR : total;
+ }
+
+ while (done < nread)
+ {
+ ssize_t nwritten;
+
+ nwritten = outoff ? pwrite(outfd, buf + done, nread - done,
+ *outoff) :
+ write(outfd, buf + done, nread - done);
+ if (nwritten <= 0)
+ {
+ /* Leave the input after the last byte written */
+
+ if (inoff)
+ {
+ *inoff += done;
+ }
+ else
+ {
+ lseek(infd, done - nread, SEEK_CUR);
+ }
+
+ return total == 0 && done == 0 ? ERROR : total + done;
+ }
+
+ if (outoff)
+ {
+ *outoff += nwritten;
+ }
+
+ done += nwritten;
+ }
+
+ if (inoff)
+ {
+ *inoff += nread;
+ }
+
+ total += nread;
+ len -= nread;
+
+ if ((size_t)nread < size)
+ {
+ break;
+ }
+ }
+
+ return total;
+}