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