xref: /lighttpd1.4/src/fdevent.c (revision 66698469)
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 	if (!symlinks) flags |= O_NOFOLLOW;
175 #ifdef O_CLOEXEC
176 	return open(pathname, flags | O_CLOEXEC | FDEVENT_O_FLAGS, mode);
177 #else
178 	int fd = open(pathname, flags | FDEVENT_O_FLAGS, mode);
179 #ifdef FD_CLOEXEC
180 	if (fd != -1)
181 		force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
182 #endif
183 	return fd;
184 #endif
185 }
186 
187 
188 int fdevent_open_devnull(void) {
189   #if defined(_WIN32)
190     return fdevent_open_cloexec("nul", 0, O_RDWR, 0);
191   #elif defined(__sun) /* /dev/null is a symlink on Illumos */
192     return fdevent_open_cloexec("/dev/null", 1, O_RDWR, 0);
193   #else
194     return fdevent_open_cloexec("/dev/null", 0, O_RDWR, 0);
195   #endif
196 }
197 
198 
199 int fdevent_open_dirname(char *path, int symlinks) {
200     /*(handle special cases of no dirname or dirname is root directory)*/
201     char * const c = strrchr(path, '/');
202     const char * const dname = (NULL != c ? c == path ? "/" : path : ".");
203     int dfd;
204     int flags = O_RDONLY;
205   #ifdef O_DIRECTORY
206     flags |= O_DIRECTORY;
207   #endif
208     if (NULL != c) *c = '\0';
209     dfd = fdevent_open_cloexec(dname, symlinks, flags, 0);
210     if (NULL != c) *c = '/';
211     return dfd;
212 }
213 
214 
215 #ifdef _WIN32
216 #include <stdio.h>
217 #endif
218 
219 int fdevent_pipe_cloexec (int * const fds, const unsigned int bufsz_hint) {
220  #ifdef _WIN32
221     return _pipe(fds, bufsz_hint, _O_BINARY | _O_NOINHERIT);
222  #else
223   #ifdef HAVE_PIPE2
224     if (0 != pipe2(fds, O_CLOEXEC))
225   #endif
226     {
227         if (0 != pipe(fds)
228          #ifdef FD_CLOEXEC
229          || 0 != fcntl(fds[0], F_SETFD, FD_CLOEXEC)
230          || 0 != fcntl(fds[1], F_SETFD, FD_CLOEXEC)
231          #endif
232            )
233             return -1;
234     }
235   #ifdef F_SETPIPE_SZ
236     if (bufsz_hint > 65536)
237         if (0 != fcntl(fds[1], F_SETPIPE_SZ, bufsz_hint)) { } /*(ignore error)*/
238   #else
239     UNUSED(bufsz_hint);
240   #endif
241     return 0;
242  #endif
243 }
244 
245 
246 int fdevent_mkostemp(char *path, int flags) {
247  #ifdef _WIN32
248     /* future: if _sopen_s() returns EEXIST, might reset template (path) with
249      * trailing "XXXXXX", and then loop to try again */
250     int fd;      /*(flags might have _O_APPEND)*/
251     return (0 == _mktemp_s(path, strlen(path)+1))
252         && (0 == _sopen_s(&fd, path, _O_CREAT  | _O_EXCL   | _O_TEMPORARY
253                                    | flags     | _O_BINARY | _O_NOINHERIT,
254                           _SH_DENYRW, _S_IREAD | _S_IWRITE))
255       ? fd
256       : -1;
257  #elif defined(HAVE_MKOSTEMP)
258     return mkostemp(path, O_CLOEXEC | flags);
259  #else
260   #ifdef __COVERITY__
261     /* POSIX-2008 requires mkstemp create file with 0600 perms */
262     umask(0600);
263   #endif
264     /* coverity[secure_temp : FALSE] */
265     const int fd = mkstemp(path);
266     if (fd < 0) return fd;
267 
268     if (flags && 0 != fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | flags)) {
269         /* (should not happen; fd is regular file) */
270         int errnum = errno;
271         close(fd);
272         errno = errnum;
273         return -1;
274     }
275 
276     fdevent_setfd_cloexec(fd);
277     return fd;
278  #endif
279 }
280 
281 
282 int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) {
283 	int fd;
284 	socklen_t len = (socklen_t) *addrlen;
285 
286       #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
287        #if defined(__NetBSD__)
288 	const int sock_cloexec = 1;
289 	fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
290        #else
291 	int sock_cloexec = use_sock_cloexec;
292 	if (sock_cloexec) {
293 		fd = accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK);
294 		if (fd >= 0) {
295 			if (!use_sock_nonblock) {
296 				if (0 != fdevent_fcntl_set_nb(fd)) {
297 					close(fd);
298 					fd = -1;
299 				}
300 			}
301 		}
302 		else {
303 			switch (errno) {
304 			case ENOSYS:
305 			case ENOTSUP:
306 			case EPERM:
307 				fd = accept(listenfd, addr, &len);
308 				sock_cloexec = 0;
309 				break;
310 			default:
311 				break;
312 			}
313 		}
314 	}
315 	else {
316 		fd = accept(listenfd, addr, &len);
317 	}
318        #endif
319       #else
320 	const int sock_cloexec = 0;
321 	fd = accept(listenfd, addr, &len);
322       #endif
323 
324 	if (fd >= 0) {
325 		*addrlen = (size_t)len;
326 		if (!sock_cloexec && 0 != fdevent_fcntl_set_nb_cloexec(fd)) {
327 			close(fd);
328 			fd = -1;
329 		}
330 	}
331 	return fd;
332 }
333 
334 
335 #ifdef __APPLE__
336 #include <crt_externs.h>
337 #define environ (* _NSGetEnviron())
338 #else
339 extern char **environ;
340 #endif
341 char ** fdevent_environ (void) { return environ; }
342 
343 
344 #ifdef FD_CLOEXEC
345 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) {
346     if (oldfd >= 0) {
347         if (oldfd != newfd) {
348             force_assert(oldfd > STDERR_FILENO);
349             if (newfd != dup2(oldfd, newfd)) return -1;
350         }
351         else {
352             fdevent_clrfd_cloexec(newfd);
353         }
354     }
355     return newfd;
356 }
357 #else
358 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) {
359     if (oldfd >= 0) {
360         if (oldfd != newfd) {
361             force_assert(oldfd > STDERR_FILENO);
362             if (newfd != dup2(oldfd, newfd)) return -1;
363             if (!reuse) close(oldfd);
364         }
365     }
366     return newfd;
367 }
368 #endif
369 
370 
371 int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) {
372   #ifdef FD_CLOEXEC
373     if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO))
374         return -1;
375     if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO))
376         return -1;
377     if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO))
378         return -1;
379   #else
380     if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO,
381                                                          fdin == fdout
382                                                          || fdin == fderr))
383         return -1;
384     if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO,
385                                                           fdout == fderr))
386         return -1;
387     if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO,
388                                                           0))
389         return -1;
390   #endif
391 
392     return 0;
393 }
394 
395 
396 #include <stdio.h>      /* perror() rename() */
397 #include <signal.h>     /* signal() */
398 
399 
400 int fdevent_rename(const char *oldpath, const char *newpath) {
401     return rename(oldpath, newpath);
402 }
403 
404 
405 pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) {
406  #ifdef HAVE_FORK
407 
408     pid_t pid = fork();
409     if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */
410 
411     /* child (0 == pid) */
412 
413     if (-1 != dfd) {
414         if (0 != fchdir(dfd))
415             _exit(errno);
416         close(dfd);
417     }
418 
419     if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno);
420   #ifndef FD_CLOEXEC
421     /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/
422     for (int i = 3; i < 256; ++i) close(i);
423   #endif
424 
425     /* reset_signals which may have been ignored (SIG_IGN) */
426   #ifdef SIGTTOU
427     signal(SIGTTOU, SIG_DFL);
428   #endif
429   #ifdef SIGTTIN
430     signal(SIGTTIN, SIG_DFL);
431   #endif
432   #ifdef SIGTSTP
433     signal(SIGTSTP, SIG_DFL);
434   #endif
435     signal(SIGPIPE, SIG_DFL);
436 
437     execve(name, argv, envp ? envp : environ);
438 
439     int errnum = errno;
440     int argnum =
441       (0 == strcmp(argv[0], "/bin/sh") && argv[1] && 0 == strcmp(argv[1], "-c"))
442       ? 2
443       : 0;
444     perror(argv[argnum]);
445     _exit(errnum);
446 
447  #else
448 
449     UNUSED(name);
450     UNUSED(argv);
451     UNUSED(envp);
452     UNUSED(fdin);
453     UNUSED(fdout);
454     UNUSED(fderr);
455     UNUSED(dfd);
456     return (pid_t)-1;
457 
458  #endif
459 }
460 
461 
462 #ifdef HAVE_SYS_WAIT_H
463 #include <sys/wait.h>
464 #endif
465 
466 int fdevent_waitpid(pid_t pid, int * const status, int nb) {
467     const int flags = nb ? WNOHANG : 0;
468     pid_t rv;
469     do { rv = waitpid(pid, status, flags); } while (-1 == rv && errno == EINTR);
470     return rv;
471 }
472 
473 int fdevent_waitpid_intr(pid_t pid, int * const status) {
474     return waitpid(pid, status, 0);
475 }
476 
477 
478 #ifndef MSG_DONTWAIT
479 #define MSG_DONTWAIT 0
480 #endif
481 #ifndef MSG_NOSIGNAL
482 #define MSG_NOSIGNAL 0
483 #endif
484 
485 
486 ssize_t fdevent_socket_read_discard (int fd, char *buf, size_t sz, int family, int so_type) {
487   #if defined(MSG_TRUNC) && defined(__linux__)
488     if ((family == AF_INET || family == AF_INET6) && so_type == SOCK_STREAM) {
489         ssize_t len = recv(fd, buf, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL);
490         if (len >= 0 || errno != EINVAL) return len;
491     }
492   #else
493     UNUSED(family);
494     UNUSED(so_type);
495   #endif
496     return read(fd, buf, sz);
497 }
498 
499 
500 #include <sys/ioctl.h>
501 #ifdef HAVE_SYS_FILIO_H
502 #include <sys/filio.h>  /* FIONREAD (for illumos (OpenIndiana)) */
503 #endif
504 #ifdef _WIN32
505 #include <winsock2.h>
506 #endif
507 int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread) {
508   #ifdef _WIN32
509     if (fdfmt != S_IFSOCK) { errno = ENOTSOCK; return -1; }
510     return ioctlsocket(fd, FIONREAD, toread);
511   #else
512    #ifdef __CYGWIN__
513     /*(cygwin supports FIONREAD on pipes, not sockets)*/
514     if (fdfmt != S_IFIFO) { errno = EOPNOTSUPP; return -1; }
515    #else
516     UNUSED(fdfmt);
517    #endif
518     return ioctl(fd, FIONREAD, toread);
519   #endif
520 }
521 
522 
523 int fdevent_connect_status(int fd) {
524     /* try to finish the connect() */
525     /*(should be called after connect() only when fd is writable (POLLOUT))*/
526     int opt;
527     socklen_t len = sizeof(opt);
528     return (0 == getsockopt(fd,SOL_SOCKET,SO_ERROR,&opt,&len)) ? opt : errno;
529 }
530 
531 
532 #include <netinet/tcp.h>
533 #if (defined(__APPLE__) && defined(__MACH__)) \
534   || defined(__FreeBSD__) || defined(__NetBSD__) \
535   || defined(__OpenBSD__) || defined(__DragonFly__)
536 #include <netinet/tcp_fsm.h>
537 #endif
538 
539 /* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */
540 int fdevent_is_tcp_half_closed(int fd) {
541   #ifdef TCP_CONNECTION_INFO     /* Darwin */
542     struct tcp_connection_info tcpi;
543     socklen_t tlen = sizeof(tcpi);
544     return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen)
545             && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
546   #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT)
547     /* FreeBSD, NetBSD (not present in OpenBSD or DragonFlyBSD) */
548     struct tcp_info tcpi;
549     socklen_t tlen = sizeof(tcpi);
550     return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen)
551             && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
552   #elif defined(TCP_INFO) && defined(__linux__)
553     /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */
554     struct tcp_info tcpi;
555     socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/
556     return (0 == getsockopt(fd,     SOL_TCP, TCP_INFO, &tcpi, &tlen)
557             && tcpi.tcpi_state == TCP_CLOSE_WAIT);
558   #else
559     UNUSED(fd);
560     /*(0 != getpeername() error might indicate TCP RST, but success
561      * would not differentiate between half-close and full-close)*/
562     return 0; /* false (not half-closed) or TCP state unknown */
563   #endif
564 }
565 
566 
567 int fdevent_set_tcp_nodelay (const int fd, const int opt)
568 {
569     return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
570 }
571 
572 
573 int fdevent_set_so_reuseaddr (const int fd, const int opt)
574 {
575     return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
576 }
577 
578 
579 #include <sys/stat.h>
580 #include "ck.h"
581 __attribute_cold__ /*(convenience routine for use at config at startup)*/
582 char *
583 fdevent_load_file (const char * const fn, off_t *lim, log_error_st *errh, void *(malloc_fn)(size_t), void(free_fn)(void *))
584 {
585     int fd;
586     off_t sz = 0;
587     char *buf = NULL;
588     do {
589         fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
590         if (fd < 0) break;
591 
592         struct stat st;
593         if (0 != fstat(fd, &st)) break;
594         if ((sizeof(off_t) > sizeof(size_t) && st.st_size >= (off_t)~(size_t)0u)
595             || (*lim != 0 && st.st_size >= *lim)) {
596             errno = EOVERFLOW;
597             break;
598         }
599 
600         sz = st.st_size;
601         buf = malloc_fn((size_t)sz+1);/*+1 trailing '\0' for str funcs on data*/
602         if (NULL == buf) break;
603 
604         if (sz) {
605             ssize_t rd = 0;
606             off_t off = 0;
607             do {
608                 rd = read(fd, buf+off, (size_t)(sz-off));
609             } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
610             if (off != sz) { /*(file truncated?)*/
611                 if (rd >= 0) errno = EIO;
612                 break;
613             }
614         }
615 
616         buf[sz] = '\0';
617         *lim = sz;
618         close(fd);
619         return buf;
620     } while (0);
621     int errnum = errno;
622     if (errh)
623         log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
624     if (fd >= 0) close(fd);
625     if (buf) {
626         ck_memzero(buf, (size_t)sz);
627         free_fn(buf);
628     }
629     *lim = 0;
630     errno = errnum;
631     return NULL;
632 }
633 
634 
635 int
636 fdevent_load_file_bytes (char * const buf, const off_t sz, off_t off, const char * const fn, log_error_st *errh)
637 {
638     int fd;
639     do {
640         fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
641         if (fd < 0) break;
642 
643         if (0 != off && (off_t)-1 == lseek(fd, off, SEEK_SET)) break;
644         off = 0;
645 
646         ssize_t rd = 0;
647         do {
648             rd = read(fd, buf+off, (size_t)(sz-off));
649         } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
650         if (off != sz) { /*(file truncated? or incorrect sz requested)*/
651             if (rd >= 0) errno = EIO;
652             break;
653         }
654 
655         close(fd);
656         return 0;
657     } while (0);
658     int errnum = errno;
659     if (errh)
660         log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
661     if (fd >= 0) close(fd);
662     ck_memzero(buf, (size_t)sz);
663     errno = errnum;
664     return -1;
665 }
666