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