diff --git a/.gitattributes b/.gitattributes
index 06b1fc657866a7f011b18d27b66b94401b1af90e..4f36f569fe9663dd782e3ad9028bae2cb9cb0201 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -357,6 +357,7 @@ testfont binary
 /src/modules/files/efuns.c foreign_ident
 /src/modules/files/file.c foreign_ident
 /src/modules/files/file.h foreign_ident
+/src/modules/files/sendfile.c foreign_ident
 /src/modules/files/socket.c foreign_ident
 /src/modules/files/socktest.pike foreign_ident
 /src/modules/files/termios.c foreign_ident
diff --git a/src/modules/files/sendfile.c b/src/modules/files/sendfile.c
new file mode 100644
index 0000000000000000000000000000000000000000..e2b5a095a4a931540ec7eed55bf91312ee17afb6
--- /dev/null
+++ b/src/modules/files/sendfile.c
@@ -0,0 +1,595 @@
+/*
+ * $Id: sendfile.c,v 1.1 1999/04/03 01:54:04 grubba Exp $
+ *
+ * Sends headers + from_fd[off..off+len-1] + trailers to to_fd asyncronously.
+ *
+ * Henrik Grubbström 1999-04-02
+ */
+
+#include "global.h"
+#include "config.h"
+
+#include "fd_control.h"
+#include "object.h"
+#include "array.h"
+#include "threads.h"
+#include "interpret.h"
+#include "svalue.h"
+#include "callback.h"
+#include "backend.h"
+#include "module_support.h"
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+#include <unistd.h>
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif /* HAVE_SYS_UIO_H */
+
+/* #define SF_DEBUG */
+
+#ifdef SF_DEBUG
+#define SF_DFPRINTF(X)	fprintf X
+#else /* !SF_DEBUG */
+#define SF_DFPRINTF(X)
+#endif /* SF_DEBUG */
+
+struct pike_sendfile
+{
+  struct object *self;
+
+  int sent;
+
+  struct array *headers;
+  struct array *trailers;
+
+  struct object *from_file;
+  struct object *to_file;
+
+  struct svalue callback;
+  struct array *args;
+
+  int from_fd;
+  int to_fd;
+
+  INT_TYPE offset;
+  INT_TYPE len;
+
+  struct iovec *hd_iov;
+  struct iovec *tr_iov;
+
+  int hd_cnt;
+  int tr_cnt;
+
+  struct iovec *iovs;
+};
+
+#define THIS	((struct pike_sendfile *)(fp->current_storage))
+
+static struct program *pike_sendfile_prog = NULL;
+
+/*
+ * Struct init code.
+ */
+
+static void init_pike_sendfile(struct object *o)
+{
+  MEMSET(THIS, 0, sizeof(struct pike_sendfile));
+
+  THIS->callback.type = T_INT;
+}
+
+static void exit_pike_sendfile(struct object *o)
+{
+  if (THIS->iovs) {
+    free(THIS->iovs);
+  }
+  if (THIS->headers) {
+    free_array(THIS->headers);
+  }
+  if (THIS->trailers) {
+    free_array(THIS->trailers);
+  }
+  if (THIS->from_file) {
+    free_object(THIS->from_file);
+  }
+  if (THIS->to_file) {
+    free_object(THIS->to_file);
+  }
+  if (THIS->args) {
+    free_array(THIS->args);
+  }
+  if (THIS->self) {
+    /* This can occur if Pike exits before the backend has started. */
+    free_object(THIS->self);
+    THIS->self = NULL;
+  }
+  free_svalue(&(THIS->callback));
+}
+
+#ifdef _REENTRANT
+
+/*
+ * Code called in the threaded case. 
+ */
+
+void call_callback_and_free(struct callback *cb, void *this_, void *arg)
+{
+  struct pike_sendfile *this = this_;
+  int sz;
+
+  SF_DFPRINTF((stderr, "sendfile: Calling callback...\n"));
+
+  remove_callback(cb);
+
+  /* Make sure we get freed in case of error */
+  push_object(this->self);
+  this->self = NULL;
+
+  push_int(this->sent);
+  sz = this->args->size;
+  push_array_items(this->args);
+  this->args = NULL;
+
+  if (this->callback.type != T_INT) {
+    apply_svalue(&this->callback, 1 + sz);
+    pop_stack();
+
+    free_svalue(&this->callback);
+    this->callback.type = T_INT;
+    this->callback.u.integer = 0;
+  }
+
+  /* Free ourselves */
+  pop_stack();
+}
+
+/* writev() without the IOV_MAX limit. */
+int send_iov(int fd, struct iovec *iov, int iovcnt)
+{
+  int sent = 0;
+
+  while (iovcnt) {
+    int bytes;
+    int cnt = iovcnt;
+
+#ifdef IOV_MAX
+    if (cnt > IOV_MAX) cnt = IOV_MAX;
+#endif
+
+#ifdef MAX_IOVEC
+    if (cnt > MAX_IOVEC) cnt = MAX_IOVEC;
+#endif
+
+    bytes = writev(fd, iov, cnt);
+
+    if ((bytes < 0) && (errno == EINTR)) {
+      continue;
+    } else if (bytes <= 0) {
+      /* Error or file closed at other end. */
+      return sent;
+    } else {
+      sent += bytes;
+
+      while (bytes) {
+	if (bytes >= iov->iov_len) {
+	  bytes -= iov->iov_len;
+	  iov++;
+	  iovcnt--;
+	} else {
+	  iov->iov_base = ((char *)iov->iov_base) + bytes;
+	  iov->iov_len -= bytes;
+	  break;
+	}
+      }
+    }
+  }
+  return sent;
+}
+
+void *worker(void *this_)
+{
+  struct pike_sendfile *this = this_;
+
+  /* Make sure we're using blocking I/O */
+  set_nonblocking(this->to_fd, 0);
+
+  SF_DFPRINTF((stderr, "sendfile: Worker started\n"));
+
+  if ((this->from_file) && (this->len)) {
+    struct iovec *iov;
+    int iovcnt;
+
+#ifdef HAVE_FREEBSD_SENDFILE
+    struct sf_hdtr hdtr = { NULL, 0, NULL, 0 };
+    off_t sent = 0;
+    int len = this->len;
+
+    SF_DFPRINTF((stderr, "sendfile: Using FreeBSD-style sendfile()\n"));
+
+    if (this->hd_cnt) {
+      hdtr.headers = this->hd_iov;
+      hdtr.hdr_cnt = this->hd_cnt;
+    }
+    if (this->tr_cnt) {
+      hdtr.trailers = this->tr_iov;
+      hdtr.trl_cnt = this->tr_cnt;
+    }
+
+    if (len < 0) {
+      len = 0;
+    }
+
+    if (sendfile(this->from_fd, this->to_fd, len, this->offset,
+		 &hdtr, &sent, 0) < 0) {
+      switch(errno) {
+      default:
+      case ENOTSOCK:
+      case EINVAL:
+	/* Try doing it by hand instead. */
+	goto fallback;
+	break;
+      case EFAULT:
+	/* Bad arguments */
+	fatal("FreeBSD style sendfile(): EFAULT\n");
+	break;
+      case EBADF:
+      case ENOTCON:
+      case EPIPE:
+      case EIO:
+      case EAGAIN:
+	/* Bad fd's or socket has been closed at other end. */
+	break;
+      }
+    }
+    this->sent += sent;
+
+    goto done;
+
+  fallback:
+#endif /* !HAVE_FREEBSD_SENDFILE */
+
+    SF_DFPRINTF((stderr, "sendfile: Sending headers\n"));
+
+    /* Send headers */
+    if (this->hd_cnt) {
+      this->sent += send_iov(this->to_fd, this->hd_iov, this->hd_cnt);
+    }
+    
+#if defined(HAVE_SENDFILE) && !defined(HAVE_FREEBSD_SENDFILE)
+    SF_DFPRINTF((stderr, "sendfile: Sending file with sendfile()\n"));
+
+    {
+      int fail = sendfile(this->to_fd, this->from_fd,
+			  &this->offset, this->len);
+
+      if (fail < 0) {
+	/* Failed: Try normal... */
+	goto normal;
+      }
+      this->sent += fail;
+      goto send_trailers;
+    }
+  normal:
+#endif /* HAVE_SENDFILE && !HAVE_FREEBSD_SENDFILE */
+    SF_DFPRINTF((stderr, "sendfile: Sending file by hand\n"));
+
+    {
+#define BUF_SIZE 65536
+      char *buffer = malloc(BUF_SIZE);
+      int buflen;
+
+      lseek(this->from_fd, this->offset, SEEK_SET);
+
+      if (buffer) {
+	int len = this->len;
+	if (len > BUF_SIZE) {
+	  len = BUF_SIZE;
+	}
+	while ((buflen = read(this->from_fd, buffer, len)) > 0) {
+	  char *buf = buffer;
+	  this->len -= buflen;
+	  this->offset += buflen;
+	  while (buflen) {
+	    int wrlen = write(this->to_fd, buf, buflen);
+	    if ((wrlen < 0) && (errno == EINTR)) {
+	      continue;
+	    } else if (wrlen <= 0) {
+	      free(buffer);
+	      goto send_trailers;
+	    }
+	    buf += wrlen;
+	    buflen -= wrlen;
+	    this->sent += wrlen;
+	  }
+	  len = this->len;
+	  if (len > BUF_SIZE) {
+	    len = BUF_SIZE;
+	  }
+	}
+	free(buffer);
+#undef BUF_SIZE
+      } else {
+	/* FIXME: Out of memory. */
+      }
+    }
+  send_trailers:
+    SF_DFPRINTF((stderr, "sendfile: Sending trailers.\n"));
+
+    if (this->tr_cnt) {
+      this->sent += send_iov(this->to_fd, this->tr_iov, this->tr_cnt);
+    }
+  } else {
+    /* Only headers & trailers */
+    struct iovec *iov = this->hd_iov;
+    int iovcnt = this->hd_cnt;
+
+    SF_DFPRINTF((stderr, "sendfile: Only headers & trailers.\n"));
+
+    if (!iovcnt) {
+      /* Only trailers */
+      iovcnt = this->tr_cnt;
+      iov = this->tr_iov;
+    } else if (this->tr_cnt) {
+      /* Both headers & trailers */
+      if (iov + this->hd_cnt != this->tr_iov) {
+	/* They are not back-to-back. Fix! */
+	int i;
+	struct iovec *iov_tmp = iov + this->hd_cnt;
+	for (i=0; i < this->tr_cnt; i++) {
+	  iov_tmp[i] = this->tr_iov[i];
+	}
+      }
+      /* They are now back-to-back. */
+      iovcnt += this->tr_cnt;
+    }
+    /* All iovec's are now in iov & iovcnt */
+
+    this->sent += send_iov(this->to_fd, iov, iovcnt);
+  }
+
+ done:
+
+  SF_DFPRINTF((stderr,
+	      "sendfile: Done. Setting up callback\n"
+	      "%d bytes sent\n", this->sent));
+
+  mt_lock(&interpreter_lock);
+
+  /* Neither of the following can be done in our current context
+   * so we do them from a backend callback.
+   * * Call the callback.
+   * * Get rid of extra ref to the object, and free ourselves.
+   */
+  add_backend_callback(call_callback_and_free, this, 0);
+
+  /* Call as soon as possible. */
+  next_timeout.tv_usec = 0;
+  next_timeout.tv_sec = 0;
+
+  /* Wake up the backend */
+  wake_up_backend();
+    
+  mt_unlock(&interpreter_lock);
+
+  /* Die */
+  return NULL;
+}
+
+#endif /* _REENTRANT */
+
+/*
+ * Callable functions
+ */
+
+/* void create(array(string) headers, object from, int offset, int len,
+ *             array(string) trailers, object to, 
+ *             function callback, mixed ... args)
+ */
+static void sf_create(INT32 args)
+{
+  struct pike_sendfile sf;
+  int iovcnt = 0;
+  struct svalue *cb = NULL;
+
+  if (THIS->to_file) {
+    error("sendfile->create(): Called a second time!\n");
+  }
+
+  MEMSET(&sf, 0, sizeof(struct pike_sendfile));
+  sf.callback.type = T_INT;
+
+  get_all_args("sendfile", args, "%A%O%i%i%A%o%*",
+	       &(sf.headers), &(sf.from_file), &(sf.offset),
+	       &(sf.len), &(sf.trailers), &(sf.to_file), &cb);
+  sf.callback = *cb;
+
+  /* Fix the trailing args */
+  push_array(sf.args = aggregate_array(args-7));
+  args = 8;
+
+  /* Do some extra arg checking */
+  sf.hd_cnt = 0;
+  if (sf.headers) {
+    struct array *a = sf.headers;
+    int i;
+
+    for (i=0; i < a->size; i++) {
+      if ((a->item[i].type != T_STRING) || (a->item[i].u.string->size_shift)) {
+	SIMPLE_BAD_ARG_ERROR("sendfile", 1, "array(string)");
+      }
+    }
+    iovcnt = a->size;
+    sf.hd_cnt = a->size;
+  }
+
+  sf.tr_cnt = 0;
+  if (sf.trailers) {
+    struct array *a = sf.trailers;
+    int i;
+
+    for (i=0; i < a->size; i++) {
+      if ((a->item[i].type != T_STRING) || (a->item[i].u.string->size_shift)) {
+	SIMPLE_BAD_ARG_ERROR("sendfile", 5, "array(string)");
+      }
+    }
+    iovcnt += a->size;
+    sf.tr_cnt = a->size;
+  }
+
+  /* Note: No need for safe_apply() */
+  sf.from_fd = -1;
+  if (sf.from_file) {
+    apply(sf.from_file, "query_fd", 0);
+    if (sp[-1].type != T_INT) {
+      pop_stack();
+      bad_arg_error("sendfile", sp-args, args, 2, "object", sp+1-args,
+		    "Bad argument 2 to sendfile(): "
+		    "query_fd() returned non-integer.\n");
+    }
+    sf.from_fd = sp[-1].u.integer;
+    pop_stack();
+
+    /* Fix offset */
+    if (sf.offset < 0) {
+      if (sf.from_fd >= 0) {
+	sf.offset = tell(sf.from_fd);
+      } else {
+	apply(sf.from_file, "tell", 0);
+	if (sp[-1].type != T_INT) {
+	  pop_stack();
+	  bad_arg_error("sendfile", sp-args, args, 2, "object", sp+1-args,
+			"Bad argument 2 to sendfile(): "
+			"tell() returned non-integer.\n");
+	}
+	sf.offset = sp[-1].u.integer;
+	pop_stack();
+      }
+    }
+    if (sf.offset < 0) {
+      sf.offset = 0;
+    }
+  }
+  apply(sf.to_file, "query_fd", 0);
+  if (sp[-1].type != T_INT) {
+    pop_stack();
+    bad_arg_error("sendfile", sp-args, args, 6, "object", sp+5-args,
+		  "Bad argument 2 to sendfile(): "
+		  "query_fd() returned non-integer.\n");
+  }
+  sf.to_fd = sp[-1].u.integer;
+  pop_stack();
+
+  /* Set up the iovec's */
+  if (iovcnt) {
+    sf.iovs = (struct iovec *)xalloc(sizeof(struct iovec) * iovcnt);
+
+    sf.hd_iov = sf.iovs;
+    sf.tr_iov = sf.iovs + sf.hd_cnt;
+
+    if (sf.headers) {
+      int i;
+      for (i = sf.hd_cnt; i--;) {
+	struct pike_string *s;
+	if ((s = sf.headers->item[i].u.string)->len) {
+	  sf.hd_iov[i].iov_base = s->str;
+	  sf.hd_iov[i].iov_len = s->len;
+	} else {
+	  sf.hd_iov++;
+	  sf.hd_cnt--;
+	}
+      }
+    }
+    if (sf.trailers) {
+      int i;
+      for (i = sf.tr_cnt; i--;) {
+	struct pike_string *s;
+	if ((s = sf.trailers->item[i].u.string)->len) {
+	  sf.tr_iov[i].iov_base = s->str;
+	  sf.tr_iov[i].iov_len = s->len;
+	} else {
+	  sf.tr_iov++;
+	  sf.tr_cnt--;
+	}
+      }
+    }
+  }
+
+  /* We need to copy the arrays since the user might do destructive
+   * operations on them, and we need the arrays to keep references to
+   * the strings.
+   */
+  if ((sf.headers) && (sf.headers->refs > 1)) {
+    struct array *a = copy_array(sf.headers);
+    free_array(sf.headers);
+    sf.headers = a;
+  }
+  if ((sf.trailers) && (sf.trailers->refs > 1)) {
+    struct array *a = copy_array(sf.trailers);
+    free_array(sf.trailers);
+    sf.trailers = a;
+  }
+
+  /* Note: we hold a reference to ourselves.
+   * The gc() won't find it, so be carefull.
+   */
+  add_ref(sf.self = fp->current_object);
+
+  /*
+   * Setup done. Note that we keep refs to all refcounted svalues in
+   * our object.
+   */
+  sp -= args;
+  *THIS = sf;
+  args = 0;
+
+#ifdef _REENTRANT
+  if (((sf.from_fd >= 0) || (!sf.from_file)) && (sf.to_fd >= 0)) {
+    THREAD_T th_id;
+    /* The worker will have a ref. */
+    th_create_small(&th_id, worker, THIS);
+  } else {
+#endif /* _REENTRANT */
+    /* Not implemented yet */
+    free_object(THIS->self);
+    THIS->self = NULL;
+  }
+  return;
+}
+
+/*
+ * Module init code
+ */
+void pike_module_init(void)
+{
+  start_new_program();
+  ADD_STORAGE(struct pike_sendfile);
+  map_variable("_args", "array(mixed)", 0, OFFSETOF(pike_sendfile, args),
+	       T_ARRAY);
+  map_variable("_callback", "function(int,mixed...:void)", 0,
+	       OFFSETOF(pike_sendfile, callback), T_MIXED);
+
+  /* function(array(string),object,int,int,array(string),object,function(int,mixed...:void),mixed...:void) */
+  ADD_FUNCTION("create", sf_create,
+	       tFuncV(tArr(tStr) tObj tInt tInt tArr(tStr) tObj
+		      tFuncV(tInt, tMix, tVoid), tMix, tVoid), 0);
+
+  set_init_callback(init_pike_sendfile);
+  set_exit_callback(exit_pike_sendfile);
+
+  pike_sendfile_prog = end_program();
+  add_program_constant("sendfile", pike_sendfile_prog, 0);
+}
+
+void pike_module_exit(void)
+{
+  if (pike_sendfile_prog) {
+    free_program(pike_sendfile_prog);
+    pike_sendfile_prog = NULL;
+  }
+}