This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]