static ssize_t fiob_writef(struct fiob *f, const char *buf, size_t count) { int fd = f->fd; ssize_t to_write = (ssize_t) count; while (to_write > 0) { ssize_t nwr = write(fd, buf, to_write); if (nwr < 0) { if (errno == EINTR) { errno = 0; continue; } if (errno == EAGAIN || errno == EWOULDBLOCK) return count != to_write ? count - to_write : -1; say_syserror("write, [%s]", f->path); return -1; /* XXX: file position is unspecified */ } if (nwr == 0) break; buf += nwr; to_write -= nwr; } return count - to_write; }
int fio_truncate(int fd, off_t offset) { int rc = ftruncate(fd, offset); if (rc) say_syserror("fio_truncate, [%s]: offset=%jd", fio_filename(fd), (intmax_t) offset); return rc; }
/** * Sets O_NONBLOCK flag in case if lognonblock is set. */ static void log_set_nonblock(struct log *log) { if (!log->nonblock) return; int flags; if ((flags = fcntl(log->fd, F_GETFL, 0)) < 0 || fcntl(log->fd, F_SETFL, flags | O_NONBLOCK) < 0) { say_syserror("fcntl, fd=%i", log->fd); } }
off_t fio_lseek(int fd, off_t offset, int whence) { off_t effective_offset = lseek(fd, offset, whence); if (effective_offset == -1) { say_syserror("lseek, [%s]: offset=%jd, whence=%d", fio_filename(fd), (intmax_t) offset, whence); } else if (whence == SEEK_SET && effective_offset != offset) { say_error("lseek, [%s]: offset set to unexpected value: " "requested %jd effective %jd", fio_filename(fd), (intmax_t)offset, (intmax_t)effective_offset); } return effective_offset; }
ssize_t fio_writev(int fd, struct iovec *iov, int iovcnt) { assert(iov && iovcnt >= 0); ssize_t nwr; restart: nwr = writev(fd, iov, iovcnt); if (nwr < 0) { if (errno == EINTR) { errno = 0; goto restart; } if (errno != EAGAIN && errno != EWOULDBLOCK) say_syserror("writev, [%s]", fio_filename(fd)); } return nwr; }
ssize_t fio_write(int fd, const void *buf, size_t count) { ssize_t to_write = (ssize_t) count; while (to_write > 0) { ssize_t nwr = write(fd, buf, to_write); if (nwr < 0) { if (errno == EINTR) { errno = 0; continue; } if (errno == EAGAIN || errno == EWOULDBLOCK) return count != to_write ? count - to_write : -1; say_syserror("write, [%s]", fio_filename(fd)); return -1; /* XXX: file position is unspecified */ } if (nwr == 0) break; buf += nwr; to_write -= nwr; } return count - to_write; }
ssize_t fio_read(int fd, void *buf, size_t count) { ssize_t to_read = (ssize_t) count; while (to_read > 0) { ssize_t nrd = read(fd, buf, to_read); if (nrd < 0) { if (errno == EINTR) { errno = 0; continue; } if (errno == EAGAIN || errno == EWOULDBLOCK) return count != to_read ? count - to_read : -1; say_syserror("read, [%s]", fio_filename(fd)); return -1; /* XXX: file position is unspecified */ } if (nrd == 0) break; buf += nrd; to_read -= nrd; } return count - to_read; }
static ssize_t fiob_readf(struct fiob *f, char *buf, size_t count) { ssize_t to_read = (ssize_t) count; while (to_read > 0) { ssize_t nrd = read(f->fd, buf, to_read); if (nrd < 0) { if (errno == EINTR) { errno = 0; continue; } if (errno == EAGAIN || errno == EWOULDBLOCK) return count != to_read ? count - to_read : -1; say_syserror("read, [%s]", f->path); return -1; /* XXX: file position is unspecified */ } if (nrd == 0) break; buf += nrd; to_read -= nrd; } return count - to_read; }
static ssize_t fiob_read(void *cookie, char *buf, size_t count) #endif { struct fiob *f = (struct fiob *)cookie; ssize_t to_read = (ssize_t) count; /* The number of starting bytes in f->buf to skip due to alignment */ off_t skip = 0; while (to_read > 0) { /* Align `to_read' FIOB_ALIGN to be <= size of f->buf */ ssize_t to_read_al = MIN(fiob_ceil(to_read), f->bsize); /* * Optimistically try to read aligned size into the aligned * buffer. If the current file position is not aligned then * read(2) returns EINVAL. In this case seek to an aligned * position and try again. This trick saves one extra * syscall for general workflow. */ ssize_t nrd = read(f->fd, f->buf, to_read_al); if (nrd < 0) { if (errno == EINTR) { errno = 0; continue; } else if (errno == EINVAL && skip == 0) { /* * read(2) can return EINVAL only in 3 cases: * 1. read buffer is not aligned - handled in * fiob_open(). * 2. read size is not aligned - handled above * 3. current file position is not aligned - * handled here. */ off_t pos = lseek(f->fd, 0, SEEK_CUR); if (pos < 0) { say_syserror("lseek, [%s]", f->path); return -1; } /* Calculate aligned position */ skip = pos % FIOB_ALIGN; pos -= skip; if (skip == 0) { /* Position is aligned. */ errno = EINVAL; say_error("read, [%s]", f->path); return -1; } /* Seek to the new position */ if (lseek(f->fd, pos, SEEK_SET) != pos) { say_syserror("lseek, [%s]", f->path); return -1; } /* Try to read again. */ continue; } say_syserror("read, [%s]", f->path); return -1; /* XXX: file position is unspecified */ } /* Ignore starting bytes if the position was aligned. */ nrd -= skip; if (nrd == 0) break; if (nrd > to_read) { /* * A few more bytes have been read because `to_read' * is not aligned to FIOB_ALIGN. Set the file position * to the expected libc value and ignore extra bytes. */ if (lseek(f->fd, to_read - nrd, SEEK_CUR) < 0) { say_syserror("lseek, [%s]", f->path); return -1; } nrd = to_read; } memcpy(buf, f->buf + skip, nrd); /* see nrd -= skip */ skip = 0; /* reset alignment offset */ buf += nrd; to_read -= nrd; } return count - to_read; }
/** open file. The same as fopen but receives additional open (2) flags */ FILE * fiob_open(const char *path, const char *mode) { int omode = 0666; int flags = 0; int save_errno; int fd = -1; FILE *file = NULL; #if defined (FIOB_DIRECT) struct fiob *f = NULL; #endif /* defined(FIOB_DIRECT) */ int um = umask(0722); umask(um); omode &= ~um; if (strchr(mode, 'r')) { if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_RDONLY; } else if (strchr(mode, 'w')) { flags |= O_TRUNC | O_CREAT; if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_WRONLY; } else if (strchr(mode, 'a')) { flags |= O_CREAT | O_APPEND; if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_WRONLY; } else { errno = EINVAL; return NULL; } /* O_EXCL */ #ifdef O_EXCL if (strchr(mode, 'x')) flags |= O_EXCL; #endif /* O_SYNC */ if (strchr(mode, 's')) { flags |= WAL_SYNC_FLAG; } fd = open(path, flags, omode); if (fd < 0) goto error; #if defined(FIOB_DIRECT) if (strchr(mode, 'd') == NULL) goto fdopen; /* Try to enable O_DIRECT */ flags = fcntl(fd, F_GETFL); if (flags != -1 && fcntl(fd, F_SETFL, flags | O_DIRECT) != -1) { say_debug("using O_DIRECT for %s", path); } else { #if defined(NDEBUG) /* Don't use opencookie in release mode without O_DIRECT */ goto fdopen; #endif /* defined(NDEBUG) */ } f = (struct fiob *)calloc(1, sizeof(struct fiob)); if (!f) goto error; f->fd = fd; f->bsize = FIOB_BSIZE; if (posix_memalign(&f->buf, FIOB_ALIGN, f->bsize)) goto error; /* for valgrind */ memset(f->buf, 0, f->bsize); f->path = strdup(path); if (!f->path) goto error; f->io.read = fiob_read; f->io.write = fiob_write; f->io.seek = fiob_seek; f->io.close = fiob_close; #ifdef HAVE_FUNOPEN file = funopen(f, f->io.read, f->io.write, f->io.seek, f->io.close); #else file = fopencookie(f, mode, f->io); #endif if (!file) goto error; #ifdef TARGET_OS_LINUX file->_fileno = f->fd; #else file->_file = f->fd; #endif return file; fdopen: #endif /* defined(FIOB_DIRECT) */ /* Fallback to libc implementation */ file = fdopen(fd, mode); if (!file) goto error; return file; error: save_errno = errno; say_syserror("Can't open '%s'", path); if (fd >= 0) close(fd); #if defined(FIOB_DIRECT) if (f) { free(f->buf); free(f->path); free(f); } #endif /* FIOB_DIRECT */ errno = save_errno; return NULL; }
/** open file. The same as fiob_open but receives additional open (2) flags */ FILE * fiob_open(const char *path, const char *mode) { int omode = 0666; int flags = 0; int save_errno; size_t bsize = 0; void *buf = NULL; int um = umask(0722); umask(um); omode &= ~um; if (strchr(mode, 'r')) { if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_RDONLY; } else if (strchr(mode, 'w')) { flags |= O_TRUNC | O_CREAT; if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_WRONLY; } else if (strchr(mode, 'a')) { flags |= O_CREAT | O_APPEND; if (strchr(mode, '+')) flags |= O_RDWR; else flags |= O_WRONLY; } else { errno = EINVAL; return NULL; } /* O_EXCL */ #ifdef O_EXCL if (strchr(mode, 'x')) flags |= O_EXCL; #endif /* O_DIRECT */ if (strchr(mode, 'd')) { #ifdef O_DIRECT flags |= O_DIRECT; #endif bsize = O_DIRECT_BSIZE; posix_memalign(&buf, 4096, bsize); if (!buf) { errno = ENOMEM; return NULL; } /* for valgrind */ memset(buf, 0, bsize); } /* O_SYNC */ if (strchr(mode, 's')) { flags |= WAL_SYNC_FLAG; } struct fiob *f = (struct fiob *)calloc(1, sizeof(struct fiob)); if (!f) { free(buf); errno = ENOMEM; return NULL; } f->path = strdup(path); if (!f->path) { errno = ENOMEM; goto error; } f->buf = buf; f->bsize = bsize; f->fd = open(path, flags, omode); if (f->fd < 0) goto error; f->io.read = fiob_read; f->io.write = fiob_write; f->io.seek = fiob_seek; f->io.close = fiob_close; FILE *file; #ifdef HAVE_FUNOPEN file = funopen(f, f->io.read, f->io.write, f->io.seek, f->io.close); #else file = fopencookie(f, mode, f->io); #endif if (!file) goto error; #ifdef TARGET_OS_LINUX file->_fileno = f->fd; #else file->_file = f->fd; #endif return file; error: save_errno = errno; say_syserror("Can't open '%s'", path); if (f->fd > 0) close(f->fd); free(f->buf); free(f->path); free(f); errno = save_errno; return NULL; }