ssize_t vfswrap_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, int fromfd, const DATA_BLOB *hdr, SMB_OFF_T offset, size_t n) { ssize_t result; START_PROFILE_BYTES(syscall_sendfile, n); result = sys_sendfile(tofd, fromfd, hdr, offset, n); END_PROFILE(syscall_sendfile); return result; }
/* * Transmit the body of a file using sendfile(). * * Linux at the moment requires the input be page-based -- ie a disk file, and * only on particular filesystems. If the sendfile() call fails in a way that * makes us think that regular IO might work, then we try that instead. For * example, the /tmp filesystem may not support sendfile(). */ int dcc_pump_sendfile(int ofd, int ifd, size_t size) { ssize_t sent; off_t offset = 0; int ret; while (size) { /* Handle possibility of partial transmission, e.g. if * sendfile() is interrupted by a signal. size is decremented * as we go. */ sent = sys_sendfile(ofd, ifd, &offset, size); if (sent == -1) { if ((errno == ENOSYS || errno == EINVAL) && offset == 0) { /* The offset==0 tests is because we may be part way through * the file. We can't just naively go back to read/write * because sendfile() does not update the file pointer: we * would need to lseek() first. That case is not handled at * the moment because it's unlikely that sendfile() would * suddenly be unsupported while we're using it. A failure * halfway through probably indicates a genuine error.*/ rs_log_info("decided to use read/write rather than sendfile"); return dcc_pump_readwrite(ofd, ifd, size); } else if (errno == EAGAIN) { /* Sleep until we're able to write out more data. */ if ((ret = dcc_select_for_write(ofd, dcc_io_timeout)) != 0) return ret; rs_trace("select() returned, continuing to write"); } else if (errno == EINTR) { rs_trace("sendfile() interrupted, continuing"); } else { rs_log_error("sendfile failed: %s", strerror(errno)); return EXIT_IO_ERROR; } } else if (sent == 0) { rs_log_error("sendfile returned 0? can't cope"); return EXIT_IO_ERROR; } else if (sent != (ssize_t) size) { /* offset is automatically updated by sendfile. */ size -= sent; rs_log_notice("sendfile: partial transmission of %ld bytes; retrying %ld @%ld", (long) sent, (long) size, (long) offset); } else { /* normal case, everything was sent. */ break; } } return 0; }
ssize_t dsi_stream_read_file(DSI *dsi, int fromfd, off_t offset, const size_t length) { size_t written; ssize_t len; LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: sending %u bytes", length); if (dsi->flags & DSI_DISCONNECTED) return -1; dsi->in_write++; written = 0; while (written < length) { len = sys_sendfile(dsi->socket, fromfd, &offset, length - written); if (len < 0) { if (errno == EINTR) continue; if (errno == EINVAL || errno == ENOSYS) return -1; if (errno == EAGAIN || errno == EWOULDBLOCK) { if (dsi_peek(dsi)) { /* can't go back to blocking mode, exit, the next read will return with an error and afpd will die. */ break; } continue; } LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno)); break; } else if (!len) { /* afpd is going to exit */ errno = EIO; return -1; /* I think we're at EOF here... */ } else written += len; } dsi->write_count += written; dsi->in_write--; return written; }
/* read from a socket and write to an adouble file */ ssize_t ad_writefile(struct adouble *ad, const int eid, const int sock, off_t off, const int end, const size_t len) { #ifdef __linux__ ssize_t cc; int fd; fd = ad_sendfile_init(ad, eid, &off, end); if ((cc = sys_sendfile(fd, sock, &off, len)) < 0) return -1; if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid))) ad_setentrylen(ad, eid, off); return cc; #endif /* __linux__ */ }
asmlinkage int sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count) { mm_segment_t old_fs = get_fs(); int ret; off_t of; if (offset && get_user(of, offset)) return -EFAULT; set_fs(KERNEL_DS); ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL, count); set_fs(old_fs); if (offset && put_user(of, offset)) return -EFAULT; return ret; }
/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, * with the corresponding cast to a signed int to insure that the * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) * and the register representation of a signed int (msr in 64-bit mode) is performed. */ asmlinkage long compat_sys_sendfile(u32 out_fd, u32 in_fd, compat_off_t __user * offset, u32 count) { mm_segment_t old_fs = get_fs(); int ret; off_t of; off_t __user *up; if (offset && get_user(of, offset)) return -EFAULT; /* The __user pointer cast is valid because of the set_fs() */ set_fs(KERNEL_DS); up = offset ? (off_t __user *) &of : NULL; ret = sys_sendfile((int)out_fd, (int)in_fd, up, count); set_fs(old_fs); if (offset && put_user(of, offset)) return -EFAULT; return ret; }
ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const size_t length, const int err) { int ret = 0; size_t written = 0; size_t total = length; ssize_t len; off_t pos = offset; char block[DSI_BLOCKSIZ]; #ifdef HAVE_SENDFILEV int sfvcnt; struct sendfilevec vec[2]; ssize_t nwritten; #elif defined(FREEBSD) ssize_t nwritten; void *hdrp; struct sf_hdtr hdr; struct iovec iovec; hdr.headers = &iovec; hdr.hdr_cnt = 1; hdr.trailers = NULL; hdr.trl_cnt = 0; hdrp = &hdr; #endif LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length); if (dsi->flags & DSI_DISCONNECTED) return -1; dsi->in_write++; dsi->flags |= DSI_NOREPLY; dsi->header.dsi_flags = DSIFL_REPLY; dsi->header.dsi_len = htonl(length); dsi->header.dsi_code = htonl(err); dsi_header_pack_reply(dsi, block); #ifdef HAVE_SENDFILEV total += DSI_BLOCKSIZ; sfvcnt = 2; vec[0].sfv_fd = SFV_FD_SELF; vec[0].sfv_flag = 0; /* Cast to unsigned long to prevent sign extension of the * pointer value for the LFS case; see Apache PR 39463. */ vec[0].sfv_off = (unsigned long)block; vec[0].sfv_len = DSI_BLOCKSIZ; vec[1].sfv_fd = fromfd; vec[1].sfv_flag = 0; vec[1].sfv_off = offset; vec[1].sfv_len = length; #elif defined(FREEBSD) iovec.iov_base = block; iovec.iov_len = DSI_BLOCKSIZ; #else dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE); #endif while (written < total) { #ifdef HAVE_SENDFILEV nwritten = 0; len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten); #elif defined(FREEBSD) len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0); if (len == 0) len = nwritten; #else len = sys_sendfile(dsi->socket, fromfd, &pos, total - written); #endif if (len < 0) { switch (errno) { case EINTR: case EAGAIN: len = 0; #if defined(HAVE_SENDFILEV) || defined(FREEBSD) len = (size_t)nwritten; #elif defined(SOLARIS) if (pos > offset) { /* we actually have sent sth., adjust counters and keep trying */ len = pos - offset; offset = pos; } #endif /* HAVE_SENDFILEV */ if (dsi_peek(dsi) != 0) { ret = -1; goto exit; } break; default: LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno)); ret = -1; goto exit; } } else if (len == 0) { /* afpd is going to exit */ ret = -1; goto exit; } #ifdef HAVE_SENDFILEV if (sfvcnt == 2 && len >= vec[0].sfv_len) { vec[1].sfv_off += len - vec[0].sfv_len; vec[1].sfv_len -= len - vec[0].sfv_len; vec[0] = vec[1]; sfvcnt = 1; } else { vec[0].sfv_off += len; vec[0].sfv_len -= len; } #elif defined(FREEBSD) if (hdrp) { if (len >= iovec.iov_len) { hdrp = NULL; len -= iovec.iov_len; /* len now contains how much sendfile() actually sent from the file */ } else { iovec.iov_len -= len; iovec.iov_base += len; len = 0; } } pos += len; #endif /* HAVE_SENDFILEV */ LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len); written += len; } #ifdef HAVE_SENDFILEV written -= DSI_BLOCKSIZ; #endif dsi->write_count += written; exit: dsi->in_write--; LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: written: %zd", written); if (ret != 0) return -1; return written; }