1 #include "first.h"
2
3 #include "fdevent.h"
4
5 #include <sys/types.h>
6 #include "sys-socket.h"
7
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <fcntl.h>
13
14 #ifdef _WIN32
15 #include <sys/stat.h> /* _S_IREAD _S_IWRITE */
16 #include <io.h>
17 #include <share.h> /* _SH_DENYRW */
18 #include <winsock2.h>
19 #endif
20
21 #include "ck.h"
22 #define force_assert(x) ck_assert(x)
23
24 #ifdef SOCK_CLOEXEC
25 static int use_sock_cloexec;
26 #endif
27 #ifdef SOCK_NONBLOCK
28 static int use_sock_nonblock;
29 #endif
30
fdevent_socket_nb_cloexec_init(void)31 void fdevent_socket_nb_cloexec_init (void)
32 {
33 #ifdef SOCK_CLOEXEC
34 /* Test if SOCK_CLOEXEC is supported by kernel.
35 * Linux kernels < 2.6.27 might return EINVAL if SOCK_CLOEXEC used
36 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529929
37 * http://www.linksysinfo.org/index.php?threads/lighttpd-no-longer-starts-toastman-1-28-0510-7.73132/
38 * Test if SOCK_NONBLOCK is ignored by kernel on sockets.
39 * (reported on Android running a custom ROM)
40 * https://redmine.lighttpd.net/issues/2883
41 */
42 #ifdef SOCK_NONBLOCK
43 int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
44 #else
45 int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
46 #endif
47 if (fd >= 0) {
48 int flags = fcntl(fd, F_GETFL, 0);
49 #ifdef SOCK_NONBLOCK
50 use_sock_nonblock = (-1 != flags && (flags & O_NONBLOCK));
51 #endif
52 use_sock_cloexec = 1;
53 close(fd);
54 }
55 #endif
56 }
57
fdevent_setfd_cloexec(int fd)58 void fdevent_setfd_cloexec(int fd) {
59 #ifdef FD_CLOEXEC
60 if (fd < 0) return;
61 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
62 #else
63 UNUSED(fd);
64 #endif
65 }
66
fdevent_clrfd_cloexec(int fd)67 void fdevent_clrfd_cloexec(int fd) {
68 #ifdef FD_CLOEXEC
69 if (fd >= 0) force_assert(-1 != fcntl(fd, F_SETFD, 0));
70 #else
71 UNUSED(fd);
72 #endif
73 }
74
fdevent_fcntl_set_nb(int fd)75 int fdevent_fcntl_set_nb(int fd) {
76 #ifdef O_NONBLOCK
77 return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
78 #else
79 UNUSED(fd);
80 return 0;
81 #endif
82 }
83
fdevent_fcntl_set_nb_cloexec(int fd)84 int fdevent_fcntl_set_nb_cloexec(int fd) {
85 fdevent_setfd_cloexec(fd);
86 return fdevent_fcntl_set_nb(fd);
87 }
88
fdevent_fcntl_set_nb_cloexec_sock(int fd)89 int fdevent_fcntl_set_nb_cloexec_sock(int fd) {
90 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
91 if (use_sock_cloexec && use_sock_nonblock)
92 return 0;
93 #endif
94 return fdevent_fcntl_set_nb_cloexec(fd);
95 }
96
fdevent_socket_cloexec(int domain,int type,int protocol)97 int fdevent_socket_cloexec(int domain, int type, int protocol) {
98 int fd;
99 #ifdef SOCK_CLOEXEC
100 if (use_sock_cloexec)
101 return socket(domain, type | SOCK_CLOEXEC, protocol);
102 #endif
103 if (-1 != (fd = socket(domain, type, protocol))) {
104 #ifdef FD_CLOEXEC
105 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
106 #endif
107 }
108 return fd;
109 }
110
fdevent_socket_nb_cloexec(int domain,int type,int protocol)111 int fdevent_socket_nb_cloexec(int domain, int type, int protocol) {
112 int fd;
113 #ifdef SOCK_CLOEXEC
114 #ifdef SOCK_NONBLOCK
115 if (use_sock_cloexec && use_sock_nonblock)
116 return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol);
117 #else
118 if (use_sock_cloexec) {
119 fd = socket(domain, type | SOCK_CLOEXEC, protocol);
120 #ifdef O_NONBLOCK
121 if (-1 != fd) force_assert(-1 != fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR));
122 #endif
123 return fd;
124 }
125 #endif
126 #endif
127 if (-1 != (fd = socket(domain, type, protocol))) {
128 #ifdef FD_CLOEXEC
129 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
130 #endif
131 #ifdef O_NONBLOCK
132 force_assert(-1 != fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR));
133 #endif
134 }
135 return fd;
136 }
137
fdevent_dup_cloexec(int fd)138 int fdevent_dup_cloexec (int fd) {
139 #ifdef F_DUPFD_CLOEXEC
140 return fcntl(fd, F_DUPFD_CLOEXEC, 3);
141 #else
142 const int newfd = fcntl(fd, F_DUPFD, 3);
143 if (newfd >= 0) fdevent_setfd_cloexec(newfd);
144 return newfd;
145 #endif
146 }
147
148 #ifndef O_BINARY
149 #define O_BINARY 0
150 #endif
151 #ifndef O_LARGEFILE
152 #define O_LARGEFILE 0
153 #endif
154 #ifndef O_NOCTTY
155 #define O_NOCTTY 0
156 #endif
157 #ifndef O_NOFOLLOW
158 #define O_NOFOLLOW 0
159 #endif
160
161 /*(O_NOFOLLOW is not handled here)*/
162 /*(Note: O_NOFOLLOW affects only the final path segment, the target file,
163 * not any intermediate symlinks along the path)*/
164
165 /* O_CLOEXEC handled further below, if defined) */
166 #ifdef O_NONBLOCK
167 #define FDEVENT_O_FLAGS \
168 (O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK)
169 #else
170 #define FDEVENT_O_FLAGS \
171 (O_BINARY | O_LARGEFILE | O_NOCTTY )
172 #endif
173
fdevent_open_cloexec(const char * pathname,int symlinks,int flags,mode_t mode)174 int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode) {
175 if (!symlinks) flags |= O_NOFOLLOW;
176 #ifdef O_CLOEXEC
177 return open(pathname, flags | O_CLOEXEC | FDEVENT_O_FLAGS, mode);
178 #else
179 int fd = open(pathname, flags | FDEVENT_O_FLAGS, mode);
180 #ifdef FD_CLOEXEC
181 if (fd != -1)
182 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
183 #endif
184 return fd;
185 #endif
186 }
187
188
fdevent_open_devnull(void)189 int fdevent_open_devnull(void) {
190 #if defined(_WIN32)
191 return fdevent_open_cloexec("nul", 0, O_RDWR, 0);
192 #elif defined(__sun) /* /dev/null is a symlink on Illumos */
193 return fdevent_open_cloexec("/dev/null", 1, O_RDWR, 0);
194 #else
195 return fdevent_open_cloexec("/dev/null", 0, O_RDWR, 0);
196 #endif
197 }
198
199
fdevent_open_dirname(char * path,int symlinks)200 int fdevent_open_dirname(char *path, int symlinks) {
201 /*(handle special cases of no dirname or dirname is root directory)*/
202 char * const c = strrchr(path, '/');
203 const char * const dname = (NULL != c ? c == path ? "/" : path : ".");
204 int dfd;
205 int flags = O_RDONLY;
206 #ifdef O_DIRECTORY
207 flags |= O_DIRECTORY;
208 #endif
209 if (NULL != c) *c = '\0';
210 dfd = fdevent_open_cloexec(dname, symlinks, flags, 0);
211 if (NULL != c) *c = '/';
212 return dfd;
213 }
214
215
216 #ifdef _WIN32
217 #include <stdio.h>
218 #endif
219
fdevent_pipe_cloexec(int * const fds,const unsigned int bufsz_hint)220 int fdevent_pipe_cloexec (int * const fds, const unsigned int bufsz_hint) {
221 #ifdef _WIN32
222 return _pipe(fds, bufsz_hint, _O_BINARY | _O_NOINHERIT);
223 #else
224 #ifdef HAVE_PIPE2
225 if (0 != pipe2(fds, O_CLOEXEC))
226 #endif
227 {
228 if (0 != pipe(fds)
229 #ifdef FD_CLOEXEC
230 || 0 != fcntl(fds[0], F_SETFD, FD_CLOEXEC)
231 || 0 != fcntl(fds[1], F_SETFD, FD_CLOEXEC)
232 #endif
233 )
234 return -1;
235 }
236 #ifdef F_SETPIPE_SZ
237 if (bufsz_hint > 65536)
238 if (0 != fcntl(fds[1], F_SETPIPE_SZ, bufsz_hint)) { } /*(ignore error)*/
239 #else
240 UNUSED(bufsz_hint);
241 #endif
242 return 0;
243 #endif
244 }
245
246
fdevent_mkostemp(char * path,int flags)247 int fdevent_mkostemp(char *path, int flags) {
248 #ifdef _WIN32
249 /* future: if _sopen_s() returns EEXIST, might reset template (path) with
250 * trailing "XXXXXX", and then loop to try again */
251 int fd; /*(flags might have _O_APPEND)*/
252 return (0 == _mktemp_s(path, strlen(path)+1))
253 && (0 == _sopen_s(&fd, path, _O_CREAT | _O_EXCL | _O_TEMPORARY
254 | flags | _O_BINARY | _O_NOINHERIT,
255 _SH_DENYRW, _S_IREAD | _S_IWRITE))
256 ? fd
257 : -1;
258 #elif defined(HAVE_MKOSTEMP)
259 return mkostemp(path, O_CLOEXEC | flags);
260 #else
261 #ifdef __COVERITY__
262 /* POSIX-2008 requires mkstemp create file with 0600 perms */
263 umask(0600);
264 #endif
265 /* coverity[secure_temp : FALSE] */
266 const int fd = mkstemp(path);
267 if (fd < 0) return fd;
268
269 if (flags && 0 != fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | flags)) {
270 /* (should not happen; fd is regular file) */
271 int errnum = errno;
272 close(fd);
273 errno = errnum;
274 return -1;
275 }
276
277 fdevent_setfd_cloexec(fd);
278 return fd;
279 #endif
280 }
281
282
283 /* accept4() added in Linux x86 in kernel 2.6.28, but not in arm until 2.6.36
284 * https://lwn.net/Articles/789961/ */
285 #if defined(__linux__) \
286 && (defined(__arm__) || defined(__thumb__) || defined(__arm64__))
287 #ifdef __has_include
288 #if __has_include(<sys/syscall.h>)
289 #include <sys/syscall.h>
290 #endif
291 #endif
292 #ifndef SYS_accept4
293 #define accept4(a,b,c,d) ((errno = ENOTSUP), -1)
294 #endif
295 #endif
296
fdevent_accept_listenfd(int listenfd,struct sockaddr * addr,size_t * addrlen)297 int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) {
298 int fd;
299 socklen_t len = (socklen_t) *addrlen;
300
301 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
302 #if defined(__NetBSD__)
303 const int sock_cloexec = 1;
304 fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
305 #else
306 int sock_cloexec = use_sock_cloexec;
307 if (sock_cloexec) {
308 fd = accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK);
309 if (fd >= 0) {
310 if (!use_sock_nonblock) {
311 if (0 != fdevent_fcntl_set_nb(fd)) {
312 close(fd);
313 fd = -1;
314 }
315 }
316 }
317 else {
318 switch (errno) {
319 case ENOSYS:
320 case ENOTSUP:
321 case EPERM:
322 fd = accept(listenfd, addr, &len);
323 sock_cloexec = 0;
324 break;
325 default:
326 break;
327 }
328 }
329 }
330 else {
331 fd = accept(listenfd, addr, &len);
332 }
333 #endif
334 #else
335 const int sock_cloexec = 0;
336 fd = accept(listenfd, addr, &len);
337 #endif
338
339 if (fd >= 0) {
340 *addrlen = (size_t)len;
341 if (!sock_cloexec && 0 != fdevent_fcntl_set_nb_cloexec(fd)) {
342 close(fd);
343 fd = -1;
344 }
345 }
346 return fd;
347 }
348
349
350 #ifdef __APPLE__
351 #include <crt_externs.h>
352 #define environ (* _NSGetEnviron())
353 #else
354 extern char **environ;
355 #endif
fdevent_environ(void)356 char ** fdevent_environ (void) { return environ; }
357
358
359 #ifdef FD_CLOEXEC
fdevent_dup2_close_clrfd_cloexec(int oldfd,int newfd)360 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) {
361 if (oldfd >= 0) {
362 if (oldfd != newfd) {
363 force_assert(oldfd > STDERR_FILENO);
364 if (newfd != dup2(oldfd, newfd)) return -1;
365 }
366 else {
367 fdevent_clrfd_cloexec(newfd);
368 }
369 }
370 return newfd;
371 }
372 #else
fdevent_dup2_close_clrfd_cloexec(int oldfd,int newfd,int reuse)373 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) {
374 if (oldfd >= 0) {
375 if (oldfd != newfd) {
376 force_assert(oldfd > STDERR_FILENO);
377 if (newfd != dup2(oldfd, newfd)) return -1;
378 if (!reuse) close(oldfd);
379 }
380 }
381 return newfd;
382 }
383 #endif
384
385
fdevent_set_stdin_stdout_stderr(int fdin,int fdout,int fderr)386 int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) {
387 #ifdef FD_CLOEXEC
388 if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO))
389 return -1;
390 if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO))
391 return -1;
392 if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO))
393 return -1;
394 #else
395 if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO,
396 fdin == fdout
397 || fdin == fderr))
398 return -1;
399 if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO,
400 fdout == fderr))
401 return -1;
402 if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO,
403 0))
404 return -1;
405 #endif
406
407 return 0;
408 }
409
410
411 #include <stdio.h> /* perror() rename() */
412 #include <signal.h> /* signal() */
413
414
fdevent_rename(const char * oldpath,const char * newpath)415 int fdevent_rename(const char *oldpath, const char *newpath) {
416 return rename(oldpath, newpath);
417 }
418
419
fdevent_fork_execve(const char * name,char * argv[],char * envp[],int fdin,int fdout,int fderr,int dfd)420 pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) {
421 #ifdef HAVE_FORK
422
423 pid_t pid = fork();
424 if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */
425
426 /* child (0 == pid) */
427
428 if (-1 != dfd) {
429 if (0 != fchdir(dfd))
430 _exit(errno);
431 close(dfd);
432 }
433
434 if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno);
435 #ifndef FD_CLOEXEC
436 /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/
437 for (int i = 3; i < 256; ++i) close(i);
438 #endif
439
440 /* reset_signals which may have been ignored (SIG_IGN) */
441 #ifdef SIGTTOU
442 signal(SIGTTOU, SIG_DFL);
443 #endif
444 #ifdef SIGTTIN
445 signal(SIGTTIN, SIG_DFL);
446 #endif
447 #ifdef SIGTSTP
448 signal(SIGTSTP, SIG_DFL);
449 #endif
450 signal(SIGPIPE, SIG_DFL);
451 signal(SIGUSR1, SIG_DFL);
452
453 execve(name, argv, envp ? envp : environ);
454
455 int errnum = errno;
456 int argnum =
457 (0 == strcmp(argv[0], "/bin/sh") && argv[1] && 0 == strcmp(argv[1], "-c"))
458 ? 2
459 : 0;
460 perror(argv[argnum]);
461 _exit(errnum);
462
463 #else
464
465 UNUSED(name);
466 UNUSED(argv);
467 UNUSED(envp);
468 UNUSED(fdin);
469 UNUSED(fdout);
470 UNUSED(fderr);
471 UNUSED(dfd);
472 return (pid_t)-1;
473
474 #endif
475 }
476
477
478 #ifdef HAVE_SYS_WAIT_H
479 #include <sys/wait.h>
480 #endif
481
fdevent_waitpid(pid_t pid,int * const status,int nb)482 int fdevent_waitpid(pid_t pid, int * const status, int nb) {
483 const int flags = nb ? WNOHANG : 0;
484 pid_t rv;
485 do { rv = waitpid(pid, status, flags); } while (-1 == rv && errno == EINTR);
486 return rv;
487 }
488
fdevent_waitpid_intr(pid_t pid,int * const status)489 int fdevent_waitpid_intr(pid_t pid, int * const status) {
490 return waitpid(pid, status, 0);
491 }
492
493
494 #ifndef MSG_DONTWAIT
495 #define MSG_DONTWAIT 0
496 #endif
497 #ifndef MSG_NOSIGNAL
498 #define MSG_NOSIGNAL 0
499 #endif
500
501
fdevent_socket_read_discard(int fd,char * buf,size_t sz,int family,int so_type)502 ssize_t fdevent_socket_read_discard (int fd, char *buf, size_t sz, int family, int so_type) {
503 #if defined(MSG_TRUNC) && defined(__linux__)
504 if ((family == AF_INET || family == AF_INET6) && so_type == SOCK_STREAM) {
505 ssize_t len = recv(fd, buf, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL);
506 if (len >= 0 || errno != EINVAL) return len;
507 }
508 #else
509 UNUSED(family);
510 UNUSED(so_type);
511 #endif
512 return read(fd, buf, sz);
513 }
514
515
516 #include <sys/ioctl.h>
517 #ifdef HAVE_SYS_FILIO_H
518 #include <sys/filio.h> /* FIONREAD (for illumos (OpenIndiana)) */
519 #endif
520 #ifdef _WIN32
521 #include <winsock2.h>
522 #endif
fdevent_ioctl_fionread(int fd,int fdfmt,int * toread)523 int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread) {
524 #ifdef _WIN32
525 if (fdfmt != S_IFSOCK) { errno = ENOTSOCK; return -1; }
526 return ioctlsocket(fd, FIONREAD, toread);
527 #else
528 #ifdef __CYGWIN__
529 /*(cygwin supports FIONREAD on pipes, not sockets)*/
530 if (fdfmt != S_IFIFO) { errno = EOPNOTSUPP; return -1; }
531 #else
532 UNUSED(fdfmt);
533 #endif
534 return ioctl(fd, FIONREAD, toread);
535 #endif
536 }
537
538
fdevent_connect_status(int fd)539 int fdevent_connect_status(int fd) {
540 /* try to finish the connect() */
541 /*(should be called after connect() only when fd is writable (POLLOUT))*/
542 int opt;
543 socklen_t len = sizeof(opt);
544 return (0 == getsockopt(fd,SOL_SOCKET,SO_ERROR,&opt,&len)) ? opt : errno;
545 }
546
547
548 #include <netinet/tcp.h>
549 #if defined(__FreeBSD__) || defined(__NetBSD__) \
550 || defined(__OpenBSD__) || defined(__DragonFly__)
551 #include <netinet/tcp_fsm.h>
552 #endif
553 #if defined(__APPLE__) && defined(__MACH__)
554 #include <TargetConditionals.h> /* TARGET_OS_IPHONE, TARGET_OS_MAC */
555 #if TARGET_OS_IPHONE /* iOS, tvOS, or watchOS device */
556 /*#define TCPS_CLOSE_WAIT 5*/ /* ??? which header contains this, if any ??? */
557 #elif TARGET_OS_MAC /* MacOS */
558 #include <netinet/tcp_fsm.h>
559 #endif
560 #endif
561
562 /* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */
fdevent_is_tcp_half_closed(int fd)563 int fdevent_is_tcp_half_closed(int fd) {
564 #ifdef TCP_CONNECTION_INFO /* Darwin */
565 struct tcp_connection_info tcpi;
566 socklen_t tlen = sizeof(tcpi);
567 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen)
568 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
569 #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT)
570 /* FreeBSD, NetBSD, OpenBSD (not present in DragonFlyBSD) */
571 struct tcp_info tcpi;
572 socklen_t tlen = sizeof(tcpi);
573 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen)
574 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
575 #elif defined(TCP_INFO) && defined(__linux__)
576 /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */
577 struct tcp_info tcpi;
578 socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/
579 return (0 == getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &tlen)
580 && tcpi.tcpi_state == TCP_CLOSE_WAIT);
581 #else
582 UNUSED(fd);
583 /*(0 != getpeername() error might indicate TCP RST, but success
584 * would not differentiate between half-close and full-close)*/
585 return 0; /* false (not half-closed) or TCP state unknown */
586 #endif
587 }
588
589
fdevent_set_tcp_nodelay(const int fd,const int opt)590 int fdevent_set_tcp_nodelay (const int fd, const int opt)
591 {
592 return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
593 }
594
595
fdevent_set_so_reuseaddr(const int fd,const int opt)596 int fdevent_set_so_reuseaddr (const int fd, const int opt)
597 {
598 return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
599 }
600
601
602 #include <sys/stat.h>
603 #include "ck.h"
604 #include "log.h"
605 __attribute_cold__ /*(convenience routine for use at config at startup)*/
606 char *
fdevent_load_file(const char * const fn,off_t * lim,log_error_st * errh,void * (malloc_fn)(size_t),void (free_fn)(void *))607 fdevent_load_file (const char * const fn, off_t *lim, log_error_st *errh, void *(malloc_fn)(size_t), void(free_fn)(void *))
608 {
609 int fd;
610 off_t sz = 0;
611 char *buf = NULL;
612 do {
613 fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
614 if (fd < 0) break;
615
616 struct stat st;
617 if (0 != fstat(fd, &st)) break;
618 if ((sizeof(off_t) > sizeof(size_t) && st.st_size >= (off_t)~(size_t)0u)
619 || (*lim != 0 && st.st_size >= *lim)) {
620 errno = EOVERFLOW;
621 break;
622 }
623
624 sz = st.st_size;
625 buf = malloc_fn((size_t)sz+1);/*+1 trailing '\0' for str funcs on data*/
626 if (NULL == buf) break;
627
628 if (sz) {
629 ssize_t rd = 0;
630 off_t off = 0;
631 do {
632 rd = read(fd, buf+off, (size_t)(sz-off));
633 } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
634 if (off != sz) { /*(file truncated?)*/
635 if (rd >= 0) errno = EIO;
636 break;
637 }
638 }
639
640 buf[sz] = '\0';
641 *lim = sz;
642 close(fd);
643 return buf;
644 } while (0);
645 int errnum = errno;
646 if (errh)
647 log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
648 if (fd >= 0) close(fd);
649 if (buf) {
650 ck_memzero(buf, (size_t)sz);
651 free_fn(buf);
652 }
653 *lim = 0;
654 errno = errnum;
655 return NULL;
656 }
657
658
659 int
fdevent_load_file_bytes(char * const buf,const off_t sz,off_t off,const char * const fn,log_error_st * errh)660 fdevent_load_file_bytes (char * const buf, const off_t sz, off_t off, const char * const fn, log_error_st *errh)
661 {
662 int fd;
663 do {
664 fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
665 if (fd < 0) break;
666
667 if (0 != off && (off_t)-1 == lseek(fd, off, SEEK_SET)) break;
668 off = 0;
669
670 ssize_t rd = 0;
671 do {
672 rd = read(fd, buf+off, (size_t)(sz-off));
673 } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
674 if (off != sz) { /*(file truncated? or incorrect sz requested)*/
675 if (rd >= 0) errno = EIO;
676 break;
677 }
678
679 close(fd);
680 return 0;
681 } while (0);
682 int errnum = errno;
683 if (errh)
684 log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
685 if (fd >= 0) close(fd);
686 ck_memzero(buf, (size_t)sz);
687 errno = errnum;
688 return -1;
689 }
690