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