diff options
author | David Zafman <david.zafman@inktank.com> | 2013-09-26 17:42:13 -0700 |
---|---|---|
committer | David Zafman <david.zafman@inktank.com> | 2013-09-26 17:42:13 -0700 |
commit | 4d54c73b7013b772005e9677f629ed2c8c12c6f2 (patch) | |
tree | 73b8399ac8c85d63441e5515530b3ab0b5ce43dc | |
parent | bab72ed394161feb47637f9d2d07ff421e97726c (diff) | |
download | ceph-4d54c73b7013b772005e9677f629ed2c8c12c6f2.tar.gz |
common, os, osd: Use common functions for safe file reading and writing
Add new safe_read_file() and safe_write_file() to update files atomically
Based on OSD::read_meta(), OSD::write_meta() which now call new funcs
Used by read_superblock() and write_superblock()
Used by write_version_stamp() and version_stamp_is_valid()
Fixes: 6422
Signed-off-by: David Zafman <david.zafman@inktank.com>
-rw-r--r-- | src/common/safe_io.c | 77 | ||||
-rw-r--r-- | src/common/safe_io.h | 9 | ||||
-rw-r--r-- | src/os/FileStore.cc | 68 | ||||
-rw-r--r-- | src/osd/OSD.cc | 76 |
4 files changed, 107 insertions, 123 deletions
diff --git a/src/common/safe_io.c b/src/common/safe_io.c index ac99db04ad3..5880bc1bc75 100644 --- a/src/common/safe_io.c +++ b/src/common/safe_io.c @@ -14,8 +14,12 @@ #define _XOPEN_SOURCE 500 +#include <stdio.h> +#include <string.h> #include <unistd.h> #include <errno.h> +#include <fcntl.h> +#include <limits.h> #include "common/safe_io.h" @@ -112,3 +116,76 @@ ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset) } return 0; } + +int safe_write_file(const char *base, const char *file, + const char *val, size_t vallen) +{ + 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, 0644); + if (fd < 0) { + ret = errno; + return -ret; + } + ret = safe_write(fd, val, vallen); + if (ret) { + TEMP_FAILURE_RETRY(close(fd)); + return ret; + } + + ret = fsync(fd); + TEMP_FAILURE_RETRY(close(fd)); + if (ret) { + unlink(tmp); + return ret; + } + ret = rename(tmp, fn); + if (ret) { + unlink(tmp); + return ret; + } + + fd = open(base, O_RDONLY); + if (fd < 0) { + ret = errno; + return -ret; + } + fsync(fd); + TEMP_FAILURE_RETRY(close(fd)); + + return 0; +} + +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); + if (fd < 0) { + return -errno; + } + len = safe_read(fd, val, vallen); + if (len < 0) { + TEMP_FAILURE_RETRY(close(fd)); + return len; + } + // close sometimes returns errors, but only after write() + TEMP_FAILURE_RETRY(close(fd)); + + val[len] = 0; + return len; +} diff --git a/src/common/safe_io.h b/src/common/safe_io.h index 4c2991fe6e8..a4c9bc7a72f 100644 --- a/src/common/safe_io.h +++ b/src/common/safe_io.h @@ -45,6 +45,15 @@ extern "C" { ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset) WARN_UNUSED_RESULT; + + /* + * Safe functions to read and write an entire file. + */ + int safe_write_file(const char *base, const char *file, + const char *val, size_t vallen); + int safe_read_file(const char *base, const char *file, + char *val, size_t vallen); + #ifdef __cplusplus } #endif diff --git a/src/os/FileStore.cc b/src/os/FileStore.cc index 343fb25c0e4..583147fb631 100644 --- a/src/os/FileStore.cc +++ b/src/os/FileStore.cc @@ -946,43 +946,25 @@ int FileStore::_sanity_check_fs() int FileStore::write_superblock() { - char fn[PATH_MAX]; - snprintf(fn, sizeof(fn), "%s/superblock", basedir.c_str()); - int fd = ::open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd < 0) - return -errno; bufferlist bl; ::encode(superblock, bl); - - int ret = safe_write(fd, bl.c_str(), bl.length()); - if (ret < 0) - goto out; - ret = ::fsync(fd); - if (ret < 0) - ret = -errno; - // XXX: fsync() man page says I need to sync containing directory -out: - TEMP_FAILURE_RETRY(::close(fd)); - return ret; + return safe_write_file(basedir.c_str(), "superblock", + bl.c_str(), bl.length()); } int FileStore::read_superblock() { - char fn[PATH_MAX]; - snprintf(fn, sizeof(fn), "%s/superblock", basedir.c_str()); - int fd = ::open(fn, O_RDONLY, 0644); - if (fd < 0) { - if (errno == ENOENT) { + bufferptr bp(PATH_MAX); + int ret = safe_read_file(basedir.c_str(), "superblock", + bp.c_str(), bp.length()); + if (ret < 0) { + if (ret == -ENOENT) { // If the file doesn't exist write initial CompatSet return write_superblock(); - } else - return -errno; - } - bufferptr bp(PATH_MAX); - int ret = safe_read(fd, bp.c_str(), bp.length()); - TEMP_FAILURE_RETRY(::close(fd)); - if (ret < 0) + } return ret; + } + bufferlist bl; bl.push_back(bp); bufferlist::iterator i = bl.begin(); @@ -1012,20 +994,14 @@ int FileStore::update_version_stamp() int FileStore::version_stamp_is_valid(uint32_t *version) { - char fn[PATH_MAX]; - snprintf(fn, sizeof(fn), "%s/store_version", basedir.c_str()); - int fd = ::open(fn, O_RDONLY, 0644); - if (fd < 0) { - if (errno == ENOENT) - return 0; - else - return -errno; - } bufferptr bp(PATH_MAX); - int ret = safe_read(fd, bp.c_str(), bp.length()); - TEMP_FAILURE_RETRY(::close(fd)); - if (ret < 0) + int ret = safe_read_file(basedir.c_str(), "store_version", + bp.c_str(), bp.length()); + if (ret < 0) { + if (ret == -ENOENT) + return 0; return ret; + } bufferlist bl; bl.push_back(bp); bufferlist::iterator i = bl.begin(); @@ -1038,17 +1014,11 @@ int FileStore::version_stamp_is_valid(uint32_t *version) int FileStore::write_version_stamp() { - char fn[PATH_MAX]; - snprintf(fn, sizeof(fn), "%s/store_version", basedir.c_str()); - int fd = ::open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd < 0) - return -errno; bufferlist bl; ::encode(target_version, bl); - - int ret = safe_write(fd, bl.c_str(), bl.length()); - TEMP_FAILURE_RETRY(::close(fd)); - return ret; + + return safe_write_file(basedir.c_str(), "store_version", + bl.c_str(), bl.length()); } int FileStore::read_op_seq(uint64_t *seq) diff --git a/src/osd/OSD.cc b/src/osd/OSD.cc index 9a2fbb5c576..2f86bc431ef 100644 --- a/src/osd/OSD.cc +++ b/src/osd/OSD.cc @@ -771,85 +771,13 @@ int OSD::dump_journal(CephContext *cct, const std::string &dev, const std::strin int OSD::write_meta(const std::string &base, const std::string &file, const char *val, size_t vallen) { - int ret; - char fn[PATH_MAX]; - char tmp[PATH_MAX]; - int fd; - - // does the file already have correct content? - char oldval[80]; - ret = read_meta(base, file, oldval, sizeof(oldval)); - if (ret == (int)vallen && memcmp(oldval, val, vallen) == 0) - return 0; // yes. - - snprintf(fn, sizeof(fn), "%s/%s", base.c_str(), file.c_str()); - snprintf(tmp, sizeof(tmp), "%s/%s.tmp", base.c_str(), file.c_str()); - fd = ::open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd < 0) { - ret = errno; - derr << "write_meta: error opening '" << tmp << "': " - << cpp_strerror(ret) << dendl; - return -ret; - } - ret = safe_write(fd, val, vallen); - if (ret) { - derr << "write_meta: failed to write to '" << tmp << "': " - << cpp_strerror(ret) << dendl; - TEMP_FAILURE_RETRY(::close(fd)); - return ret; - } - - ret = ::fsync(fd); - TEMP_FAILURE_RETRY(::close(fd)); - if (ret) { - ::unlink(tmp); - derr << "write_meta: failed to fsync to '" << tmp << "': " - << cpp_strerror(ret) << dendl; - return ret; - } - ret = ::rename(tmp, fn); - if (ret) { - ::unlink(tmp); - derr << "write_meta: failed to rename '" << tmp << "' to '" << fn << "': " - << cpp_strerror(ret) << dendl; - return ret; - } - - fd = ::open(base.c_str(), O_RDONLY); - if (fd < 0) { - ret = errno; - derr << "write_meta: failed to open dir '" << base << "': " - << cpp_strerror(ret) << dendl; - return -ret; - } - ::fsync(fd); - TEMP_FAILURE_RETRY(::close(fd)); - - return 0; + return safe_write_file(base.c_str(), file.c_str(), val, vallen); } int OSD::read_meta(const std::string &base, const std::string &file, char *val, size_t vallen) { - char fn[PATH_MAX]; - int fd, len; - - snprintf(fn, sizeof(fn), "%s/%s", base.c_str(), file.c_str()); - fd = ::open(fn, O_RDONLY); - if (fd < 0) { - int err = errno; - return -err; - } - len = safe_read(fd, val, vallen); - if (len < 0) { - TEMP_FAILURE_RETRY(::close(fd)); - return len; - } - // close sometimes returns errors, but only after write() - TEMP_FAILURE_RETRY(::close(fd)); - - val[len] = 0; - return len; + return safe_read_file(base.c_str(), file.c_str(), val, vallen); } int OSD::write_meta(const std::string &base, uuid_d& cluster_fsid, uuid_d& osd_fsid, int whoami) |