diff options
Diffstat (limited to 'src/common/safe_io.c')
-rw-r--r-- | src/common/safe_io.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/common/safe_io.c b/src/common/safe_io.c new file mode 100644 index 000000000..80921d0f2 --- /dev/null +++ b/src/common/safe_io.c @@ -0,0 +1,297 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "common/safe_io.h" +#include "include/compat.h" + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/socket.h> + +ssize_t safe_read(int fd, void *buf, size_t count) +{ + size_t cnt = 0; + + while (cnt < count) { + ssize_t r = read(fd, buf, count - cnt); + if (r <= 0) { + if (r == 0) { + // EOF + return cnt; + } + if (errno == EINTR) + continue; + return -errno; + } + cnt += r; + buf = (char *)buf + r; + } + return cnt; +} + +#ifdef _WIN32 +// "read" doesn't work with Windows sockets. +ssize_t safe_recv(int fd, void *buf, size_t count) +{ + size_t cnt = 0; + + while (cnt < count) { + ssize_t r = recv(fd, (SOCKOPT_VAL_TYPE)buf, count - cnt, 0); + if (r <= 0) { + if (r == 0) { + // EOF + return cnt; + } + int err = ceph_sock_errno(); + if (err == EAGAIN || err == EINTR) { + continue; + } + return -err; + } + cnt += r; + buf = (char *)buf + r; + } + return cnt; +} +#else +ssize_t safe_recv(int fd, void *buf, size_t count) +{ + // We'll use "safe_read" so that this can work with any type of + // file descriptor. + return safe_read(fd, buf, count); +} +#endif /* _WIN32 */ + +ssize_t safe_read_exact(int fd, void *buf, size_t count) +{ + ssize_t ret = safe_read(fd, buf, count); + if (ret < 0) + return ret; + if ((size_t)ret != count) + return -EDOM; + return 0; +} + +ssize_t safe_recv_exact(int fd, void *buf, size_t count) +{ + ssize_t ret = safe_recv(fd, buf, count); + if (ret < 0) + return ret; + if ((size_t)ret != count) + return -EDOM; + return 0; +} + +ssize_t safe_write(int fd, const void *buf, size_t count) +{ + while (count > 0) { + ssize_t r = write(fd, buf, count); + if (r < 0) { + if (errno == EINTR) + continue; + return -errno; + } + count -= r; + buf = (char *)buf + r; + } + return 0; +} + +#ifdef _WIN32 +ssize_t safe_send(int fd, const void *buf, size_t count) +{ + while (count > 0) { + ssize_t r = send(fd, (SOCKOPT_VAL_TYPE)buf, count, 0); + if (r < 0) { + int err = ceph_sock_errno(); + if (err == EINTR || err == EAGAIN) { + continue; + } + return -err; + } + count -= r; + buf = (char *)buf + r; + } + return 0; +} +#else +ssize_t safe_send(int fd, const void *buf, size_t count) +{ + return safe_write(fd, buf, count); +} +#endif /* _WIN32 */ + +ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset) +{ + size_t cnt = 0; + char *b = (char*)buf; + + while (cnt < count) { + ssize_t r = pread(fd, b + cnt, count - cnt, offset + cnt); + if (r <= 0) { + if (r == 0) { + // EOF + return cnt; + } + if (errno == EINTR) + continue; + return -errno; + } + + cnt += r; + } + return cnt; +} + +ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ret = safe_pread(fd, buf, count, offset); + if (ret < 0) + return ret; + if ((size_t)ret != count) + return -EDOM; + return 0; +} + +ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + while (count > 0) { + ssize_t r = pwrite(fd, buf, count, offset); + if (r < 0) { + if (errno == EINTR) + continue; + return -errno; + } + count -= r; + buf = (char *)buf + r; + offset += r; + } + return 0; +} + +#ifdef CEPH_HAVE_SPLICE +ssize_t safe_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, + size_t len, unsigned int flags) +{ + size_t cnt = 0; + + while (cnt < len) { + ssize_t r = splice(fd_in, off_in, fd_out, off_out, len - cnt, flags); + if (r <= 0) { + if (r == 0) { + // EOF + return cnt; + } + if (errno == EINTR) + continue; + if (errno == EAGAIN) + break; + return -errno; + } + cnt += r; + } + return cnt; +} + +ssize_t safe_splice_exact(int fd_in, off_t *off_in, int fd_out, + off_t *off_out, size_t len, unsigned int flags) +{ + ssize_t ret = safe_splice(fd_in, off_in, fd_out, off_out, len, flags); + if (ret < 0) + return ret; + if ((size_t)ret != len) + return -EDOM; + return 0; +} +#endif + +int safe_write_file(const char *base, const char *file, + const char *val, size_t vallen, + unsigned mode) +{ + int ret; + char fn[PATH_MAX]; + char tmp[PATH_MAX]; + int fd; + + // does the file already have correct content? + char oldval[80]; + ret = safe_read_file(base, file, oldval, sizeof(oldval)); + if (ret == (int)vallen && memcmp(oldval, val, vallen) == 0) + return 0; // yes. + + snprintf(fn, sizeof(fn), "%s/%s", base, file); + snprintf(tmp, sizeof(tmp), "%s/%s.tmp", base, file); + fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, mode); + if (fd < 0) { + ret = errno; + return -ret; + } + ret = safe_write(fd, val, vallen); + if (ret) { + VOID_TEMP_FAILURE_RETRY(close(fd)); + return ret; + } + + ret = fsync(fd); + if (ret < 0) ret = -errno; + VOID_TEMP_FAILURE_RETRY(close(fd)); + if (ret < 0) { + unlink(tmp); + return ret; + } + ret = rename(tmp, fn); + if (ret < 0) { + ret = -errno; + unlink(tmp); + return ret; + } + + fd = open(base, O_RDONLY|O_BINARY); + if (fd < 0) { + ret = -errno; + return ret; + } + ret = fsync(fd); + if (ret < 0) ret = -errno; + VOID_TEMP_FAILURE_RETRY(close(fd)); + + return ret; +} + +int safe_read_file(const char *base, const char *file, + char *val, size_t vallen) +{ + char fn[PATH_MAX]; + int fd, len; + + snprintf(fn, sizeof(fn), "%s/%s", base, file); + fd = open(fn, O_RDONLY|O_BINARY); + if (fd < 0) { + return -errno; + } + len = safe_read(fd, val, vallen); + if (len < 0) { + VOID_TEMP_FAILURE_RETRY(close(fd)); + return len; + } + // close sometimes returns errors, but only after write() + VOID_TEMP_FAILURE_RETRY(close(fd)); + + return len; +} |