1d059297112922cabb0c674840589be8db821fd9aAdam Langley/* $OpenBSD: atomicio.c,v 1.27 2015/01/16 06:40:12 deraadt Exp $ */
2bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/*
3bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Copyright (c) 2006 Damien Miller. All rights reserved.
4bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
5bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
6bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * All rights reserved.
7bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
8bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Redistribution and use in source and binary forms, with or without
9bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * modification, are permitted provided that the following conditions
10bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * are met:
11bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 1. Redistributions of source code must retain the above copyright
12bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer.
13bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 2. Redistributions in binary form must reproduce the above copyright
14bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer in the
15bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    documentation and/or other materials provided with the distribution.
16bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
17bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman */
28bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
29bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "includes.h"
30bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
31bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <sys/param.h>
32bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <sys/uio.h>
33bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
34bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <errno.h>
35bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifdef HAVE_POLL_H
36bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <poll.h>
37bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#else
38bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# ifdef HAVE_SYS_POLL_H
39bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#  include <sys/poll.h>
40bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# endif
41bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
42bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <string.h>
43bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <unistd.h>
44d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <limits.h>
45bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
46bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "atomicio.h"
47bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
48bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/*
49bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * ensure all of data on socket comes through. f==read || f==vwrite
50bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman */
51bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmansize_t
52bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanatomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
53bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman    int (*cb)(void *, size_t), void *cb_arg)
54bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
55bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *s = _s;
56bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	size_t pos = 0;
57bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	ssize_t res;
58bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	struct pollfd pfd;
59bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
60d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifndef BROKEN_READ_COMPARISON
61bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	pfd.fd = fd;
62bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	pfd.events = f == read ? POLLIN : POLLOUT;
63d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
64bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	while (n > pos) {
65bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		res = (f) (fd, s + pos, n - pos);
66bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		switch (res) {
67bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		case -1:
68bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (errno == EINTR)
69bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
70bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (errno == EAGAIN || errno == EWOULDBLOCK) {
71d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifndef BROKEN_READ_COMPARISON
72bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				(void)poll(&pfd, 1, -1);
73d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
74bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
75bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
76bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return 0;
77bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		case 0:
78bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			errno = EPIPE;
79bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return pos;
80bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		default:
81bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			pos += (size_t)res;
82bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
83bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				errno = EINTR;
84bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				return pos;
85bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
86bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
87bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
88bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return pos;
89bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
90bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
91bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmansize_t
92bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanatomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
93bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
94bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return atomicio6(f, fd, _s, n, NULL, NULL);
95bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
96bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
97bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/*
98bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * ensure all of data on socket comes through. f==readv || f==writev
99bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman */
100bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmansize_t
101bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanatomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
102bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman    const struct iovec *_iov, int iovcnt,
103bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman    int (*cb)(void *, size_t), void *cb_arg)
104bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
105bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	size_t pos = 0, rem;
106bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	ssize_t res;
107bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	struct iovec iov_array[IOV_MAX], *iov = iov_array;
108bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	struct pollfd pfd;
109bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
110bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (iovcnt > IOV_MAX) {
111bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		errno = EINVAL;
112bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
113bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
114bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* Make a copy of the iov array because we may modify it below */
115bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	memcpy(iov, _iov, iovcnt * sizeof(*_iov));
116bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
117bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifndef BROKEN_READV_COMPARISON
118bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	pfd.fd = fd;
119bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	pfd.events = f == readv ? POLLIN : POLLOUT;
120bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
121bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (; iovcnt > 0 && iov[0].iov_len > 0;) {
122bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		res = (f) (fd, iov, iovcnt);
123bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		switch (res) {
124bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		case -1:
125bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (errno == EINTR)
126bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
127bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (errno == EAGAIN || errno == EWOULDBLOCK) {
128bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifndef BROKEN_READV_COMPARISON
129bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				(void)poll(&pfd, 1, -1);
130bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
131bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
132bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
133bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return 0;
134bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		case 0:
135bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			errno = EPIPE;
136bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return pos;
137bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		default:
138bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			rem = (size_t)res;
139bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			pos += rem;
140bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/* skip completed iov entries */
141bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			while (iovcnt > 0 && rem >= iov[0].iov_len) {
142bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				rem -= iov[0].iov_len;
143bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				iov++;
144bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				iovcnt--;
145bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
146bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/* This shouldn't happen... */
147bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (rem > 0 && (iovcnt <= 0 || rem > iov[0].iov_len)) {
148bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				errno = EFAULT;
149bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				return 0;
150bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
151bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (iovcnt == 0)
152bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				break;
153bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/* update pointer in partially complete iov */
154bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			iov[0].iov_base = ((char *)iov[0].iov_base) + rem;
155bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			iov[0].iov_len -= rem;
156bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
157bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
158bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			errno = EINTR;
159bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return pos;
160bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
161bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
162bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return pos;
163bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
164bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
165bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmansize_t
166bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanatomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
167bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman    const struct iovec *_iov, int iovcnt)
168bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
169bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL);
170bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
171