/* ------------------------------ * write raw data. return actual bytes read. checks against EINTR * aren't necessary if all of the signals have SA_RESTART * specified. */ ssize_t dsi_stream_write(DSI *dsi, void *data, const size_t length, int mode) { size_t written; ssize_t len; unsigned int flags; dsi->in_write++; written = 0; LOG(log_maxdebug, logtype_dsi, "dsi_stream_write(send: %zd bytes): START", length); if (dsi->flags & DSI_DISCONNECTED) return -1; if (mode & DSI_MSG_MORE) flags = MSG_MORE; else flags = 0; while (written < length) { len = send(dsi->socket, (uint8_t *) data + written, length - written, flags); if (len >= 0) { written += len; continue; } if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { LOG(log_debug, logtype_dsi, "dsi_stream_write: send: %s", strerror(errno)); if (mode == DSI_NOWAIT && written == 0) { /* DSI_NOWAIT is used by attention give up in this case. */ written = -1; goto exit; } /* Try to read sth. in order to break up possible deadlock */ if (dsi_peek(dsi) != 0) { written = -1; goto exit; } /* Now try writing again */ continue; } LOG(log_error, logtype_dsi, "dsi_stream_write: %s", strerror(errno)); written = -1; goto exit; } dsi->write_count += written; LOG(log_maxdebug, logtype_dsi, "dsi_stream_write(send: %zd bytes): END", length); exit: dsi->in_write--; return written; }
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; }
/* --------------------------------------- * write data. 0 on failure. this assumes that dsi_len will never * cause an overflow in the data buffer. */ int dsi_stream_send(DSI *dsi, void *buf, size_t length) { char block[DSI_BLOCKSIZ]; struct iovec iov[2]; int iovecs = 2; size_t towrite; ssize_t len; LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): START", length); if (dsi->flags & DSI_DISCONNECTED) return 0; dsi_header_pack_reply(dsi, block); if (!length) { /* just write the header */ LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): DSI header, no data", sizeof(block)); length = (dsi_stream_write(dsi, block, sizeof(block), 0) == sizeof(block)); return length; /* really 0 on failure, 1 on success */ } /* block signals */ block_sig(dsi); iov[0].iov_base = block; iov[0].iov_len = sizeof(block); iov[1].iov_base = buf; iov[1].iov_len = length; towrite = sizeof(block) + length; dsi->write_count += towrite; while (towrite > 0) { if (((len = writev(dsi->socket, iov, iovecs)) == -1 && errno == EINTR) || (len == 0)) continue; if ((size_t)len == towrite) /* wrote everything out */ break; else if (len < 0) { /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { if (dsi_peek(dsi) == 0) { continue; } } LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno)); unblock_sig(dsi); return 0; } towrite -= len; if (towrite > length) { /* skip part of header */ iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } else { /* skip to data */ if (iovecs == 2) { iovecs = 1; len -= iov[0].iov_len; iov[0] = iov[1]; } iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } } LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): END", length); unblock_sig(dsi); return 1; }
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; }
/* --------------------------------------- * write data. 0 on failure. this assumes that dsi_len will never * cause an overflow in the data buffer. */ int dsi_stream_send(DSI *dsi, void *buf, size_t length) { char block[DSI_BLOCKSIZ]; struct iovec iov[2]; size_t towrite; ssize_t len; LOG(log_maxdebug, logtype_dsi, "dsi_stream_send: %u bytes", length ? length : sizeof(block)); if (dsi->flags & DSI_DISCONNECTED) return 0; block[0] = dsi->header.dsi_flags; block[1] = dsi->header.dsi_command; memcpy(block + 2, &dsi->header.dsi_requestID, sizeof(dsi->header.dsi_requestID)); memcpy(block + 4, &dsi->header.dsi_code, sizeof(dsi->header.dsi_code)); memcpy(block + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len)); memcpy(block + 12, &dsi->header.dsi_reserved, sizeof(dsi->header.dsi_reserved)); if (!length) { /* just write the header */ length = (dsi_stream_write(dsi, block, sizeof(block), 0) == sizeof(block)); return length; /* really 0 on failure, 1 on success */ } /* block signals */ block_sig(dsi); iov[0].iov_base = block; iov[0].iov_len = sizeof(block); iov[1].iov_base = buf; iov[1].iov_len = length; towrite = sizeof(block) + length; dsi->write_count += towrite; while (towrite > 0) { if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || (len == 0)) continue; if ((size_t)len == towrite) /* wrote everything out */ break; else if (len < 0) { /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { if (!dsi_peek(dsi)) { continue; } } LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno)); unblock_sig(dsi); return 0; } towrite -= len; if (towrite > length) { /* skip part of header */ iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } else { /* skip to data */ if (iov[0].iov_len) { len -= iov[0].iov_len; iov[0].iov_len = 0; } iov[1].iov_base = (char *) iov[1].iov_base + len; iov[1].iov_len -= len; } } unblock_sig(dsi); return 1; }