This is the mail archive of the
libc-alpha@sources.redhat.com
mailing list for the glibc project.
FreeBSD port (29): sendfile
- From: Bruno Haible <bruno at clisp dot org>
- To: libc-alpha at sources dot redhat dot com
- Date: Fri, 12 Jul 2002 13:36:01 +0200 (CEST)
- Subject: FreeBSD port (29): sendfile
Hi,
Since FreeBSD's native sendfile() system call is not the same as the Linux
one (works only on sockets), here is a patch that adds an emulation of it
using read/write to sysdeps/posix/.
The emulation is multithread-safe if the platform has a pread() system call,
like FreeBSD does, otherwise it is not multithread-safe (but better than
the stub in sysdeps/generic).
2002-07-06 Bruno Haible <bruno@clisp.org>
* sysdeps/posix/sendfile.c: New file.
* sysdeps/posix/sendfile64.c: New file.
diff -r -c3 glibc-20020627.bak/io/sys/sendfile.h glibc-20020627/io/sys/sendfile.h
--- glibc-20020627.bak/io/sys/sendfile.h Wed Jun 5 10:56:16 2002
+++ glibc-20020627/io/sys/sendfile.h Fri Jul 5 11:55:56 2002
@@ -25,8 +25,10 @@
__BEGIN_DECLS
-/* Send COUNT bytes from file associated with IN_FD starting at OFFSET to
- descriptor OUT_FD. */
+/* Send up to COUNT bytes from file associated with IN_FD starting at *OFFSET
+ to descriptor OUT_FD. Set *OFFSET to the IN_FD's file position following
+ the read bytes. If OFFSET is NULL, the normal file position is used
+ instead. Return the number of written bytes, or -1 in case of error. */
#ifndef __USE_FILE_OFFSET64
extern ssize_t sendfile (int __out_fd, int __in_fd, off_t *__offset,
size_t __count) __THROW;
--- /dev/null Fri Jul 12 03:09:05 2002
+++ glibc-20020627/sysdeps/posix/sendfile.c Fri Jul 12 00:08:57 2002
@@ -0,0 +1,210 @@
+/* sendfile -- copy data directly from one file descriptor to another
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Bruno Haible <bruno@clisp.org>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <sys/sendfile.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* You can set USE_PREAD if you have a multithread-safe pread() function
+ and you want a multithread-safe sendfile() function.
+ Of course "multithread-safety" here is meant with respect to IN_FD, not
+ OUT_FD. Multiple threads writing to the same OUT_FD leads to madness
+ anyway. */
+#ifndef USE_PREAD
+# define USE_PREAD 0
+#endif
+
+/* Send up to COUNT bytes from file associated with IN_FD starting at *OFFSET
+ to descriptor OUT_FD. Set *OFFSET to the IN_FD's file position following
+ the read bytes. If OFFSET is NULL, the normal file position is used
+ instead. Return the number of written bytes, or -1 in case of error. */
+ssize_t
+sendfile (int out_fd, int in_fd, off_t *offset, size_t count)
+{
+ char stackbuf[4096];
+ void *buffer;
+ size_t bufsize;
+#if USE_PREAD
+ off_t start_offset;
+#else
+ off_t old_offset;
+#endif
+ ssize_t written;
+ ssize_t notwritten;
+ int saved_errno;
+
+ if (count == 0)
+ return 0;
+
+#if !USE_PREAD
+ old_offset = 0;
+#endif
+ if (offset != NULL)
+ {
+#if USE_PREAD
+ start_offset = *offset;
+#else
+ /* Since we must not change the file pointer preserve the value so that
+ we can restore it later. */
+ old_offset = __libc_lseek (in_fd, 0, SEEK_CUR);
+ if (old_offset == (off_t) -1)
+ return -1;
+
+ /* Set to wanted position. */
+ if (__libc_lseek (in_fd, *offset, SEEK_SET) == (off_t) -1)
+ return -1;
+#endif
+ }
+
+ /* Try to do it all in one swoop, through a malloc'ed buffer. */
+ if (count > sizeof (stackbuf))
+ {
+ bufsize = count;
+ if (bufsize > 512 * 1024 * 1024)
+ bufsize = 512 * 1024 * 1024;
+
+ for (;;)
+ {
+ buffer = malloc (bufsize);
+ if (buffer)
+ break;
+
+ bufsize = bufsize / 2;
+ if (bufsize <= sizeof (stackbuf))
+ {
+ buffer = stackbuf;
+ bufsize = sizeof (stackbuf);
+ break;
+ }
+ }
+ }
+ else
+ {
+ buffer = stackbuf;
+ bufsize = sizeof (stackbuf);
+ }
+
+ written = 0;
+ notwritten = 0;
+ saved_errno = errno;
+ do
+ {
+ /* Read a chunk of data from IN_FD into the buffer, and write it
+ to OUT_FD. */
+ size_t count_this_round = (count <= bufsize ? count : bufsize);
+ ssize_t nbytesread;
+
+#if USE_PREAD
+ if (offset != NULL)
+ nbytesread = __libc_pread (in_fd, buffer, count_this_round,
+ start_offset + written);
+ else
+#endif
+ nbytesread = __libc_read (in_fd, buffer, count_this_round);
+
+ if (nbytesread >= 0)
+ {
+ char *p = (char *) buffer;
+ size_t n = nbytesread;
+
+ while (n > 0)
+ {
+ ssize_t ret = __libc_write (out_fd, p, n);
+
+ if (ret < 0)
+ break;
+ if (ret == 0)
+ abort ();
+ p += ret;
+ n -= ret;
+ written += ret;
+ }
+
+ if (n > 0)
+ {
+ /* Could not write all bytes that were requested. */
+ if (written == 0)
+ {
+ written = -1;
+ saved_errno = errno;
+ }
+ notwritten = n;
+ break;
+ }
+ }
+ else
+ {
+ /* Could not read all bytes that were requested. */
+ if (written == 0)
+ {
+ written = -1;
+ saved_errno = errno;
+ }
+ break;
+ }
+ }
+ while (count > 0);
+
+ if (offset != NULL)
+ {
+#if USE_PREAD
+ /* Store the new position. */
+ *offset = start_offset + (written >= 0 ? written : 0);
+#else
+ /* Store the new position. */
+ off_t new_offset = __libc_lseek (in_fd, 0, SEEK_CUR);
+ if (new_offset == (off_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+ else
+ *offset = new_offset - notwritten;
+
+ /* Now restore the old position. */
+ if (__libc_lseek (in_fd, old_offset, SEEK_SET) == (off_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+#endif
+ }
+ else
+ {
+ if (notwritten > 0)
+ /* We read more bytes than we could write. Move IN_FD's file
+ position back accordingly. */
+ if (__libc_lseek (in_fd, -notwritten, SEEK_CUR) == (off_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+ }
+
+ if (buffer != stackbuf)
+ free (buffer);
+
+ __set_errno (saved_errno);
+ return written;
+}
--- /dev/null Fri Jul 12 03:09:05 2002
+++ glibc-20020627/sysdeps/posix/sendfile64.c Fri Jul 12 00:11:29 2002
@@ -0,0 +1,210 @@
+/* sendfile64 -- copy data directly from one file descriptor to another
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Bruno Haible <bruno@clisp.org>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <sys/sendfile.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* You can set USE_PREAD if you have a multithread-safe pread64() function
+ and you want a multithread-safe sendfile64() function.
+ Of course "multithread-safety" here is meant with respect to IN_FD, not
+ OUT_FD. Multiple threads writing to the same OUT_FD leads to madness
+ anyway. */
+#ifndef USE_PREAD
+# define USE_PREAD 0
+#endif
+
+/* Send up to COUNT bytes from file associated with IN_FD starting at *OFFSET
+ to descriptor OUT_FD. Set *OFFSET to the IN_FD's file position following
+ the read bytes. If OFFSET is NULL, the normal file position is used
+ instead. Return the number of written bytes, or -1 in case of error. */
+ssize_t
+sendfile64 (int out_fd, int in_fd, off64_t *offset, size_t count)
+{
+ char stackbuf[4096];
+ void *buffer;
+ size_t bufsize;
+#if USE_PREAD
+ off64_t start_offset;
+#else
+ off64_t old_offset;
+#endif
+ ssize_t written;
+ ssize_t notwritten;
+ int saved_errno;
+
+ if (count == 0)
+ return 0;
+
+#if !USE_PREAD
+ old_offset = 0;
+#endif
+ if (offset != NULL)
+ {
+#if USE_PREAD
+ start_offset = *offset;
+#else
+ /* Since we must not change the file pointer preserve the value so that
+ we can restore it later. */
+ old_offset = __libc_lseek64 (in_fd, 0, SEEK_CUR);
+ if (old_offset == (off64_t) -1)
+ return -1;
+
+ /* Set to wanted position. */
+ if (__libc_lseek64 (in_fd, *offset, SEEK_SET) == (off64_t) -1)
+ return -1;
+#endif
+ }
+
+ /* Try to do it all in one swoop, through a malloc'ed buffer. */
+ if (count > sizeof (stackbuf))
+ {
+ bufsize = count;
+ if (bufsize > 512 * 1024 * 1024)
+ bufsize = 512 * 1024 * 1024;
+
+ for (;;)
+ {
+ buffer = malloc (bufsize);
+ if (buffer)
+ break;
+
+ bufsize = bufsize / 2;
+ if (bufsize <= sizeof (stackbuf))
+ {
+ buffer = stackbuf;
+ bufsize = sizeof (stackbuf);
+ break;
+ }
+ }
+ }
+ else
+ {
+ buffer = stackbuf;
+ bufsize = sizeof (stackbuf);
+ }
+
+ written = 0;
+ notwritten = 0;
+ saved_errno = errno;
+ do
+ {
+ /* Read a chunk of data from IN_FD into the buffer, and write it
+ to OUT_FD. */
+ size_t count_this_round = (count <= bufsize ? count : bufsize);
+ ssize_t nbytesread;
+
+#if USE_PREAD
+ if (offset != NULL)
+ nbytesread = __libc_pread64 (in_fd, buffer, count_this_round,
+ start_offset + written);
+ else
+#endif
+ nbytesread = __libc_read (in_fd, buffer, count_this_round);
+
+ if (nbytesread >= 0)
+ {
+ char *p = (char *) buffer;
+ size_t n = nbytesread;
+
+ while (n > 0)
+ {
+ ssize_t ret = __libc_write (out_fd, p, n);
+
+ if (ret < 0)
+ break;
+ if (ret == 0)
+ abort ();
+ p += ret;
+ n -= ret;
+ written += ret;
+ }
+
+ if (n > 0)
+ {
+ /* Could not write all bytes that were requested. */
+ if (written == 0)
+ {
+ written = -1;
+ saved_errno = errno;
+ }
+ notwritten = n;
+ break;
+ }
+ }
+ else
+ {
+ /* Could not read all bytes that were requested. */
+ if (written == 0)
+ {
+ written = -1;
+ saved_errno = errno;
+ }
+ break;
+ }
+ }
+ while (count > 0);
+
+ if (offset != NULL)
+ {
+#if USE_PREAD
+ /* Store the new position. */
+ *offset = start_offset + (written >= 0 ? written : 0);
+#else
+ /* Store the new position. */
+ off64_t new_offset = __libc_lseek64 (in_fd, 0, SEEK_CUR);
+ if (new_offset == (off64_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+ else
+ *offset = new_offset - notwritten;
+
+ /* Now restore the old position. */
+ if (__libc_lseek64 (in_fd, old_offset, SEEK_SET) == (off64_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+#endif
+ }
+ else
+ {
+ if (notwritten > 0)
+ /* We read more bytes than we could write. Move IN_FD's file
+ position back accordingly. */
+ if (__libc_lseek64 (in_fd, -notwritten, SEEK_CUR) == (off64_t) -1)
+ {
+ if (written >= 0)
+ saved_errno = errno;
+ written = -1;
+ }
+ }
+
+ if (buffer != stackbuf)
+ free (buffer);
+
+ __set_errno (saved_errno);
+ return written;
+}