1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup streaming Streams
36 * @ingroup dnscore
37 * @brief
38 *
39 *
40 *
41 * @{
42 *
43 *----------------------------------------------------------------------------*/
44 #include "dnscore/dnscore-config.h"
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51
52 #include "dnscore/file_output_stream.h"
53 #include "dnscore/fdtools.h"
54
55 /*
56 * This structure is supposed to match the output_stream one
57 * It helps using the void* data as an int without a INT_AT(x) kind of macro
58 */
59
60 typedef struct file_output_stream file_output_stream;
61
62 struct file_output_stream
63 {
64
65 union
66 {
67 void *voidp;
68 int fd;
69 } data;
70
71 const output_stream_vtbl* vtbl;
72 };
73
74 static ya_result
file_output_stream_write(output_stream * stream_,const u8 * buffer,u32 len)75 file_output_stream_write(output_stream* stream_, const u8* buffer, u32 len)
76 {
77 const file_output_stream* stream = (file_output_stream*)stream_;
78
79 const u8* start = buffer;
80
81 while(len > 0)
82 {
83 ssize_t ret = write(stream->data.fd, buffer, len);
84
85 if(ret <= 0)
86 {
87 int err = errno;
88
89 if(err == EINTR)
90 {
91 continue;
92 }
93
94 if(err == EAGAIN)
95 {
96 #if __FreeBSD__ || __OpenBSD__ || __APPLE__
97 int oldflags = fcntl (stream->data.fd, F_GETFL, 0);
98 if(oldflags < 0)
99 {
100 return MAKE_ERRNO_ERROR(err);
101 }
102 #endif
103 continue;
104 }
105
106 /* error */
107 return MAKE_ERRNO_ERROR(err);
108 }
109
110 buffer += ret;
111 len -= (u32)ret;
112 }
113
114 return (ya_result)(buffer - start);
115 }
116
117 static ya_result
file_output_stream_writefully(output_stream * stream_,const u8 * buffer,u32 len)118 file_output_stream_writefully(output_stream* stream_, const u8* buffer, u32 len)
119 {
120 const file_output_stream* stream = (file_output_stream*)stream_;
121
122 const u8* start = buffer;
123
124 while(len > 0)
125 {
126 ssize_t ret = write(stream->data.fd, buffer, len);
127
128 if(ret <= 0)
129 {
130 int err = errno;
131
132 if(err == EINTR)
133 {
134 continue;
135 }
136
137 if(err == EAGAIN)
138 {
139 #if __FreeBSD__ || __OpenBSD__ || __APPLE__
140 int oldflags = fcntl (stream->data.fd, F_GETFL, 0);
141 if(oldflags < 0)
142 {
143 return MAKE_ERRNO_ERROR(err);
144 }
145 #endif
146 continue;
147 }
148
149 if(err == ENOSPC)
150 {
151 // the disk is full : wait a bit, hope the admin catches it, try again later
152 sleep((rand()&7) + 1);
153 continue;
154 }
155
156 /* error */
157 return MAKE_ERRNO_ERROR(err);
158 }
159
160 buffer += ret;
161 len -= (u32)ret;
162 }
163
164 return (ya_result)(buffer - start);
165 }
166
167 static ya_result
file_output_stream_flush(output_stream * stream_)168 file_output_stream_flush(output_stream* stream_)
169 {
170 file_output_stream* stream = (file_output_stream*)stream_;
171
172 if(fsync_ex(stream->data.fd) == 0) /* or fdatasync ... maybe it would be slightly better */
173 {
174 return SUCCESS;
175 }
176
177 return ERRNO_ERROR;
178 }
179
180 static void
file_output_stream_close(output_stream * stream_)181 file_output_stream_close(output_stream* stream_)
182 {
183 file_output_stream* stream = (file_output_stream*)stream_;
184
185 /* don't, it's only for a test that I did this assert((stream->data.fd < 0)||(stream->data.fd >2)); */
186
187 if(stream->data.fd != -1) /* harmless close but still ... */
188 {
189 close_ex(stream->data.fd);
190 }
191
192 output_stream_set_void(stream_);
193 }
194
195 static void
file_output_stream_noclose(output_stream * stream_)196 file_output_stream_noclose(output_stream* stream_)
197 {
198 file_output_stream* stream = (file_output_stream*)stream_;
199 stream->data.fd = -1;
200 output_stream_set_void(stream_);
201 }
202
203 static const output_stream_vtbl file_output_stream_noclose_vtbl ={
204 file_output_stream_write,
205 file_output_stream_flush,
206 file_output_stream_noclose,
207 "file_output_stream-noclose",
208 };
209
210 static const output_stream_vtbl file_output_stream_vtbl ={
211 file_output_stream_write,
212 file_output_stream_flush,
213 file_output_stream_close,
214 "file_output_stream",
215 };
216
217 static const output_stream_vtbl file_full_output_stream_vtbl ={
218 file_output_stream_writefully,
219 file_output_stream_flush,
220 file_output_stream_close,
221 "file_output_stream",
222 };
223
224 ya_result
file_output_stream_open(output_stream * stream,const char * filename)225 file_output_stream_open(output_stream* stream, const char* filename)
226 {
227 ya_result ret;
228 ret = file_output_stream_open_ex(stream, filename, O_RDWR|O_CLOEXEC, 0600);
229 return ret;
230 }
231
232 ya_result
file_output_stream_create(output_stream * stream,const char * filename,mode_t mode)233 file_output_stream_create(output_stream* stream, const char* filename, mode_t mode)
234 {
235 ya_result ret;
236 ret = file_output_stream_open_ex(stream, filename, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
237 return ret;
238 }
239
240 ya_result
file_output_stream_create_excl(output_stream * stream,const char * filename,mode_t mode)241 file_output_stream_create_excl(output_stream* stream, const char* filename, mode_t mode)
242 {
243 ya_result ret;
244 ret = file_output_stream_open_ex(stream, filename, O_RDWR | O_CREAT | O_TRUNC | O_EXCL | O_CLOEXEC, mode);
245 return ret;
246 }
247
248 ya_result
file_output_stream_set_full_writes(output_stream * stream,bool full_writes)249 file_output_stream_set_full_writes(output_stream* stream, bool full_writes)
250 {
251 if(is_fd_output_stream(stream))
252 {
253 if(full_writes)
254 {
255 stream->vtbl = &file_full_output_stream_vtbl;
256 }
257 else
258 {
259 stream->vtbl = &file_output_stream_vtbl;
260 }
261 return SUCCESS;
262 }
263 else
264 {
265 return INVALID_STATE_ERROR;
266 }
267 }
268
269 ya_result
file_output_stream_open_ex(output_stream * stream_,const char * filename,int flags,mode_t mode)270 file_output_stream_open_ex(output_stream* stream_, const char* filename, int flags, mode_t mode)
271 {
272 yassert(sizeof(void*) >= sizeof(int));
273
274 int fd = open_create_ex(filename, flags, mode);
275
276 if(fd < 0)
277 {
278 return ERRNO_ERROR;
279 }
280
281 #if (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) && !defined(__gnu__hurd__)
282 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
283 #endif
284
285 file_output_stream* stream = (file_output_stream*)stream_;
286 stream->data.fd = fd;
287
288 stream->vtbl = &file_output_stream_vtbl;
289
290 return SUCCESS;
291 }
292
293 ya_result
file_output_stream_open_ex_nolog(output_stream * stream_,const char * filename,int flags,mode_t mode)294 file_output_stream_open_ex_nolog(output_stream* stream_, const char* filename, int flags, mode_t mode)
295 {
296 yassert(sizeof(void*) >= sizeof(int));
297
298 int fd = open_create_ex_nolog(filename, flags, mode);
299
300 if(fd < 0)
301 {
302 return ERRNO_ERROR;
303 }
304
305 #if (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) && !defined(__gnu__hurd__)
306 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
307 #endif
308
309 file_output_stream* stream = (file_output_stream*)stream_;
310 stream->data.fd = fd;
311
312 stream->vtbl = &file_output_stream_vtbl;
313
314 return SUCCESS;
315 }
316
317 void
file_output_stream_close_nolog(output_stream * stream_)318 file_output_stream_close_nolog(output_stream* stream_)
319 {
320 file_output_stream* stream = (file_output_stream*)stream_;
321
322 /* don't, it's only for a test that I did this assert((stream->data.fd < 0)||(stream->data.fd >2)); */
323
324 if(stream->data.fd != -1) /* harmless close but still ... */
325 {
326 close_ex(stream->data.fd);
327 stream->data.fd = -1;
328 }
329
330 output_stream_set_void(stream_);
331 }
332
333 ya_result
fd_output_stream_attach(output_stream * stream_,int fd)334 fd_output_stream_attach(output_stream* stream_, int fd)
335 {
336 yassert(sizeof(void*) >= sizeof(int));
337
338 file_output_stream* stream = (file_output_stream*)stream_;
339 stream->data.fd = fd;
340
341 stream->vtbl = &file_output_stream_vtbl;
342
343 return SUCCESS;
344 }
345
346 ya_result
fd_output_stream_attach_noclose(output_stream * stream_,int fd)347 fd_output_stream_attach_noclose(output_stream* stream_, int fd)
348 {
349 yassert(sizeof(void*) >= sizeof(int));
350
351 file_output_stream* stream = (file_output_stream*)stream_;
352 stream->data.fd = fd;
353
354 stream->vtbl = &file_output_stream_noclose_vtbl;
355
356 return SUCCESS;
357 }
358
359 void
fd_output_stream_detach(output_stream * stream_)360 fd_output_stream_detach(output_stream* stream_)
361 {
362 yassert(sizeof(void*) >= sizeof(int));
363
364 file_output_stream* stream = (file_output_stream*)stream_;
365 stream->data.fd = -1;
366 }
367
368 ya_result
fd_output_stream_get_filedescriptor(output_stream * stream)369 fd_output_stream_get_filedescriptor(output_stream* stream)
370 {
371 file_output_stream *fos = (file_output_stream*)stream;
372 return fos->data.fd;
373 }
374
375 bool
is_fd_output_stream(output_stream * stream_)376 is_fd_output_stream(output_stream* stream_)
377 {
378 file_output_stream* stream = (file_output_stream*)stream_;
379 return (stream != NULL) && ((stream->vtbl == &file_output_stream_vtbl) || (stream->vtbl == &file_full_output_stream_vtbl));
380 }
381
fd_output_stream_get_size(output_stream * stream_)382 s64 fd_output_stream_get_size(output_stream* stream_)
383 {
384 file_output_stream* stream = (file_output_stream*)stream_;
385
386 struct stat s;
387
388 if(fstat(stream->data.fd, &s) >= 0)
389 {
390 if(S_ISREG(s.st_mode))
391 {
392 return s.st_size;
393 }
394 }
395
396 return (s64)ERRNO_ERROR;
397 }
398
file_output_steam_advise_sequential(output_stream * stream_)399 void file_output_steam_advise_sequential(output_stream* stream_)
400 {
401 file_output_stream* stream = (file_output_stream*)stream_;
402
403 #if (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) && !defined(__gnu__hurd__)
404 if(stream->data.fd >= 0)
405 {
406 posix_fadvise(stream->data.fd, 0, 0, POSIX_FADV_SEQUENTIAL);
407 }
408 #endif
409 }
410
411 /** @} */
412