diff options
Diffstat (limited to 'src/nxt_file.c')
-rw-r--r-- | src/nxt_file.c | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/src/nxt_file.c b/src/nxt_file.c new file mode 100644 index 00000000..b2cab337 --- /dev/null +++ b/src/nxt_file.c @@ -0,0 +1,601 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +nxt_int_t +nxt_file_open(nxt_file_t *file, nxt_uint_t mode, nxt_uint_t create, + nxt_file_access_t access) +{ + nxt_thread_debug(thr); + +#ifdef __CYGWIN__ + mode |= O_BINARY; +#endif + + /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */ + mode |= (O_NONBLOCK | create); + + file->fd = open((char *) file->name, mode, access); + + file->error = (file->fd == -1) ? nxt_errno : 0; + + nxt_thread_time_debug_update(thr); + + nxt_log_debug(thr->log, "open(\"%FN\", 0x%uXi, 0x%uXi): %FD err:%d", + file->name, mode, access, file->fd, file->error); + + if (file->fd != -1) { + return NXT_OK; + } + + if (file->log_level != 0) { + nxt_thread_log_error(file->log_level, "open(\"%FN\") failed %E", + file->name, file->error); + } + + return NXT_ERROR; +} + + +void +nxt_file_close(nxt_file_t *file) +{ + nxt_thread_log_debug("close(%FD)", file->fd); + + if (close(file->fd) != 0) { + nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD, \"%FN\") failed %E", + file->fd, file->name, nxt_errno); + } +} + + +ssize_t +nxt_file_write(nxt_file_t *file, const u_char *buf, size_t size, + nxt_off_t offset) +{ + ssize_t n; + + nxt_thread_debug(thr); + + n = pwrite(file->fd, buf, size, offset); + + file->error = (n < 0) ? nxt_errno : 0; + + nxt_thread_time_debug_update(thr); + + nxt_log_debug(thr->log, "pwrite(%FD, %p, %uz, %O): %z", + file->fd, buf, size, offset, n); + + if (nxt_fast_path(n >= 0)) { + return n; + } + + nxt_thread_log_error(NXT_LOG_CRIT, + "pwrite(%FD, \"%FN\", %p, %uz, %O) failed %E", + file->fd, file->name, buf, size, + offset, file->error); + + return NXT_ERROR; +} + + +ssize_t +nxt_file_read(nxt_file_t *file, u_char *buf, size_t size, nxt_off_t offset) +{ + ssize_t n; + + nxt_thread_debug(thr); + + n = pread(file->fd, buf, size, offset); + + file->error = (n <= 0) ? nxt_errno : 0; + + nxt_thread_time_debug_update(thr); + + nxt_log_debug(thr->log, "pread(%FD, %p, %uz, %O): %z", + file->fd, buf, size, offset, n); + + if (nxt_fast_path(n >= 0)) { + return n; + } + + nxt_thread_log_error(NXT_LOG_CRIT, + "pread(%FD, \"%FN\", %p, %uz, %O) failed %E", + file->fd, file->name, buf, size, + offset, file->error); + + return NXT_ERROR; +} + + +#if (NXT_HAVE_READAHEAD) + +/* FreeBSD 8.0 fcntl(F_READAHEAD, size) enables read ahead up to the size. */ + +void +nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size) +{ + int ret; + u_char buf; + + ret = fcntl(file->fd, F_READAHEAD, (int) size); + + nxt_thread_log_debug("fcntl(%FD, F_READAHEAD, %uz): %d", + file->fd, size, ret); + + if (nxt_fast_path(ret != -1)) { + (void) nxt_file_read(file, &buf, 1, offset); + return; + } + + nxt_thread_log_error(NXT_LOG_CRIT, + "fcntl(%FD, \"%FN\", F_READAHEAD, %uz) failed %E", + file->fd, file->name, size, nxt_errno); +} + +#elif (NXT_HAVE_POSIX_FADVISE) + +/* + * POSIX_FADV_SEQUENTIAL + * Linux doubles the default readahead window size of a backing device + * which is usually 128K. + * + * FreeBSD does nothing. + * + * POSIX_FADV_WILLNEED + * Linux preloads synchronously up to 2M of specified file region in + * the kernel page cache. Linux-specific readahead(2) syscall does + * the same. Both operations are blocking despite posix_fadvise(2) + * claims the opposite. + * + * FreeBSD does nothing. + */ + +void +nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size) +{ + nxt_err_t err; + + err = posix_fadvise(file->fd, offset, size, POSIX_FADV_WILLNEED); + + nxt_thread_log_debug("posix_fadvise(%FD, \"%FN\", %O, %uz, %d): %d", + file->fd, file->name, offset, size, + POSIX_FADV_WILLNEED, err); + + if (nxt_fast_path(err == 0)) { + return; + } + + nxt_thread_log_error(NXT_LOG_CRIT, + "posix_fadvise(%FD, \"%FN\", %O, %uz, %d) failed %E", + file->fd, file->name, offset, size, + POSIX_FADV_WILLNEED, err); +} + +#elif (NXT_HAVE_RDAHEAD) + +/* MacOSX fcntl(F_RDAHEAD). */ + +void +nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size) +{ + int ret; + u_char buf; + + ret = fcntl(file->fd, F_RDAHEAD, 1); + + nxt_thread_log_debug("fcntl(%FD, F_RDAHEAD, 1): %d", file->fd, ret); + + if (nxt_fast_path(ret != -1)) { + (void) nxt_file_read(file, &buf, 1, offset); + return; + } + + nxt_thread_log_error(NXT_LOG_CRIT, + "fcntl(%FD, \"%FN\", F_RDAHEAD, 1) failed %E", + file->fd, file->name, nxt_errno); +} + +#else + +void +nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size) +{ + u_char buf; + + (void) nxt_file_read(file, &buf, 1, offset); +} + +#endif + + +nxt_int_t +nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi) +{ + int n; + + if (file->fd == NXT_FILE_INVALID) { + n = stat((char *) file->name, fi); + + file->error = (n != 0) ? nxt_errno : 0; + + nxt_thread_log_debug("stat(\"%FN)\": %d", file->name, n); + + if (n == 0) { + return NXT_OK; + } + + if (file->log_level != 0) { + nxt_thread_log_error(file->log_level, "stat(\"%FN\") failed %E", + file->name, file->error); + } + + return NXT_ERROR; + + } else { + n = fstat(file->fd, fi); + + file->error = (n != 0) ? nxt_errno : 0; + + nxt_thread_log_debug("fstat(%FD): %d", file->fd, n); + + if (n == 0) { + return NXT_OK; + } + + /* Use NXT_LOG_CRIT because fstat() error on open file is strange. */ + + nxt_thread_log_error(NXT_LOG_CRIT, "fstat(%FD, \"%FN\") failed %E", + file->fd, file->name, file->error); + + return NXT_ERROR; + } +} + + +nxt_int_t +nxt_file_delete(nxt_file_name_t *name) +{ + nxt_thread_log_debug("unlink(\"%FN\")", name); + + if (nxt_fast_path(unlink((char *) name) == 0)) { + return NXT_OK; + } + + nxt_thread_log_alert("unlink(\"%FN\") failed %E", name, nxt_errno); + + return NXT_ERROR; +} + + +nxt_int_t +nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access) +{ + if (nxt_fast_path(chmod((char *) name, access) == 0)) { + return NXT_OK; + } + + nxt_thread_log_alert("chmod(\"%FN\") failed %E", name, nxt_errno); + + return NXT_ERROR; +} + + +nxt_int_t +nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name) +{ + int ret; + + nxt_thread_log_debug("rename(\"%FN\", \"%FN\")", old_name, new_name); + + ret = rename((char *) old_name, (char *) new_name); + if (nxt_fast_path(ret == 0)) { + return NXT_OK; + } + + nxt_thread_log_alert("rename(\"%FN\", \"%FN\") failed %E", + old_name, new_name, nxt_errno); + + return NXT_ERROR; +} + + +/* + * ioctl(FIONBIO) sets a non-blocking mode using one syscall, + * thereas fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state + * using fcntl(F_GETFL). + * + * ioctl() and fcntl() are syscalls at least in Linux 2.2, FreeBSD 2.x, + * and Solaris 7. + * + * Linux 2.4 uses BKL for ioctl() and fcntl(F_SETFL). + * Linux 2.6 does not use BKL. + */ + +#if (NXT_HAVE_FIONBIO) + +nxt_int_t +nxt_fd_nonblocking(nxt_fd_t fd) +{ + int nb; + + nb = 1; + + if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) { + return NXT_OK; + } + + nxt_thread_log_alert("ioctl(%d, FIONBIO) failed %E", fd, nxt_errno); + + return NXT_ERROR; + +} + + +nxt_int_t +nxt_fd_blocking(nxt_fd_t fd) +{ + int nb; + + nb = 0; + + if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) { + return NXT_OK; + } + + nxt_thread_log_alert("ioctl(%d, !FIONBIO) failed %E", fd, nxt_errno); + + return NXT_ERROR; +} + +#else /* !(NXT_HAVE_FIONBIO) */ + +nxt_int_t +nxt_fd_nonblocking(nxt_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (nxt_slow_path(flags == -1)) { + nxt_thread_log_alert("fcntl(%d, F_GETFL) failed %E", fd, nxt_errno); + return NXT_ERROR; + } + + flags |= O_NONBLOCK; + + if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) { + nxt_thread_log_alert("fcntl(%d, F_SETFL, O_NONBLOCK) failed %E", + fd, nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} + + +nxt_int_t +nxt_fd_blocking(nxt_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (nxt_slow_path(flags == -1)) { + nxt_thread_log_alert("fcntl(%d, F_GETFL) failed %E", + fd, nxt_errno); + return NXT_ERROR; + } + + flags &= O_NONBLOCK; + + if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) { + nxt_thread_log_alert("fcntl(%d, F_SETFL, !O_NONBLOCK) failed %E", + fd, nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} + +#endif /* NXT_HAVE_FIONBIO */ + + +ssize_t +nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size) +{ + ssize_t n; + nxt_err_t err; + + n = write(fd, buf, size); + + err = (n == -1) ? nxt_errno : 0; + + nxt_thread_log_debug("write(%FD, %p, %uz): %z", fd, buf, size, n); + + if (nxt_slow_path(n <= 0)) { + nxt_thread_log_alert("write(%FD) failed %E", fd, err); + } + + return n; +} + + +ssize_t +nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size) +{ + ssize_t n; + nxt_err_t err; + + n = read(fd, buf, size); + + err = (n == -1) ? nxt_errno : 0; + + nxt_thread_log_debug("read(%FD, %p, %uz): %z", fd, buf, size, n); + + if (nxt_slow_path(n <= 0)) { + + if (err == NXT_EAGAIN) { + return 0; + } + + nxt_thread_log_alert("read(%FD) failed %E", fd, err); + } + + return n; +} + + +void +nxt_fd_close(nxt_fd_t fd) +{ + nxt_thread_log_debug("close(%FD)", fd); + + if (nxt_slow_path(close(fd) != 0)) { + nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD) failed %E", + fd, nxt_errno); + } +} + + +/* + * nxt_file_redirect() redirects the file to the fd descriptor. + * Then the fd descriptor is closed. + */ + +nxt_int_t +nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd) +{ + nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", fd, file->fd, file->name); + + if (dup2(fd, file->fd) == -1) { + nxt_thread_log_error(NXT_LOG_CRIT, "dup2(%FD, %FD, \"%FN\") failed %E", + fd, file->fd, file->name, nxt_errno); + return NXT_ERROR; + } + + if (close(fd) != 0) { + nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD, \"%FN\") failed %E", + fd, file->name, nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} + + +/* nxt_file_stderr() redirects the stderr descriptor to the file. */ + +nxt_int_t +nxt_file_stderr(nxt_file_t *file) +{ + nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", + file->fd, STDERR_FILENO, file->name); + + if (dup2(file->fd, STDERR_FILENO) != -1) { + return NXT_OK; + } + + nxt_thread_log_error(NXT_LOG_CRIT, "dup2(%FD, %FD, \"%FN\") failed %E", + file->fd, STDERR_FILENO, file->name); + + return NXT_ERROR; +} + + +nxt_int_t +nxt_stderr_start(void) +{ + int flags, fd; + + flags = fcntl(nxt_stderr, F_GETFL); + + if (flags != -1) { + /* + * If the stderr output of a multithreaded application is + * redirected to a file: + * Linux, Solaris and MacOSX do not write atomically to the output; + * MacOSX besides adds zeroes to the output. + * O_APPEND fixes this. + */ + (void) fcntl(nxt_stderr, F_SETFL, flags | O_APPEND); + + } else { + /* + * The stderr descriptor is closed before application start. + * Reserve the stderr descriptor for future use. Errors are + * ignored because anyway they could be written nowhere. + */ + fd = open("/dev/null", O_WRONLY | O_APPEND); + + if (fd != -1) { + (void) dup2(fd, nxt_stderr); + + if (fd != nxt_stderr) { + (void) close(fd); + } + } + } + + return flags; +} + + +nxt_int_t +nxt_pipe_create(nxt_fd_t *pp, nxt_bool_t nbread, nxt_bool_t nbwrite) +{ + if (pipe(pp) != 0) { + nxt_thread_log_alert("pipe() failed %E", nxt_errno); + + return NXT_ERROR; + } + + nxt_thread_log_debug("pipe(): %FD:%FD", pp[0], pp[1]); + + if (nbread) { + if (nxt_fd_nonblocking(pp[0]) != NXT_OK) { + return NXT_ERROR; + } + } + + if (nbwrite) { + if (nxt_fd_nonblocking(pp[1]) != NXT_OK) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +void +nxt_pipe_close(nxt_fd_t *pp) +{ + nxt_thread_log_debug("pipe close(%FD:%FD)", pp[0], pp[1]); + + if (close(pp[0]) != 0) { + nxt_thread_log_alert("pipe close (%FD) failed %E", pp[0], nxt_errno); + } + + if (close(pp[1]) != 0) { + nxt_thread_log_alert("pipe close(%FD) failed %E", pp[1], nxt_errno); + } +} + + +size_t +nxt_dir_current(char *buf, size_t len) +{ + if (nxt_fast_path(getcwd(buf, len) != NULL)) { + return nxt_strlen(buf); + } + + nxt_thread_log_alert("getcwd(%uz) failed %E", len, nxt_errno); + + return 0; +} |