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