From 8716cf918e75c540814d75ccb9fa26ff6085cf63 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 18 Feb 2016 21:51:50 -0500 Subject: Create and use RAII file abstraction --- CMakeLists.txt | 1 + src/CMakeLists.txt | 2 +- src/operations.cc | 46 ++++++++++++----------------- src/operations.h | 4 ++- src/posix_extras.cc | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/posix_extras.h | 68 ++++++++++++++++++++++++++++++++++++++++++ src/scoville.cc | 24 +++++++-------- src/utility.cc | 44 --------------------------- src/utility.h | 26 ---------------- 9 files changed, 189 insertions(+), 111 deletions(-) create mode 100644 src/posix_extras.cc create mode 100644 src/posix_extras.h delete mode 100644 src/utility.cc delete mode 100644 src/utility.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8121a56..8ff0ac8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftrapv") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong --param=ssp-buffer-size=4") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything -Wno-c++98-compat") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-padded") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-weak-vtables") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-macros") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b7cf018..3f1fb15 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,8 +36,8 @@ add_executable( scoville encoding.cc operations.cc + posix_extras.cc scoville.cc - utility.cc ) target_include_directories( diff --git a/src/operations.cc b/src/operations.cc index 2eb326a..e709b8a 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -33,13 +33,14 @@ #include #include -#include "utility.h" +#include "posix_extras.h" namespace scoville { namespace { -int root_fd_; +// Pointer to the directory underlying the mount point. +File* root_; struct Directory { DIR* fd; @@ -72,25 +73,14 @@ mode_t DirectoryTypeToFileType(const unsigned char type) { } } -#define RETURN_ON_ERROR(call) \ - if ((call) == -1) { \ - return -errno; \ - } \ - do { \ - } while (false) - void* Initialize(fuse_conn_info*) { LOG(INFO) << "initialize"; return nullptr; } -void Destroy(void*) { - LOG(INFO) << "destroy"; - LOG_IF(ERROR, close(root_fd_) == -1) << "could not close root FD: " - << ErrnoText(); -} +void Destroy(void*) { LOG(INFO) << "destroy"; } -int Getattr(const char* const path, struct stat* output) { +int Getattr(const char* const path, struct stat* output) noexcept { LOG(INFO) << "getattr(" << path << ")"; if (path[0] == '\0') { @@ -98,16 +88,20 @@ int Getattr(const char* const path, struct stat* output) { return -ENOENT; } - if (strcmp(path, "/") == 0) { - // They're asking for information about the mount point. - RETURN_ON_ERROR(fstat(root_fd_, output)); + try { + if (strcmp(path, "/") == 0) { + // They're asking for information about the mount point. + *output = root_->Stat(); + return 0; + } + + // Trim the leading slash so LinkStatAt will treat it relative to root_. + LOG(INFO) << "getattr: trimming leading slash"; + *output = root_->LinkStatAt(path + 1); return 0; + } catch (const IoError& e) { + return -e.number(); } - - // Trim the leading slash so fstatat will treat it relative to root_fd_. - LOG(INFO) << "getattr: trimming leading slash"; - RETURN_ON_ERROR(fstatat(root_fd_, path + 1, output, AT_SYMLINK_NOFOLLOW)); - return 0; } int Opendir(const char* const path, fuse_file_info* const file_info) { @@ -176,12 +170,10 @@ int Releasedir(const char*, fuse_file_info* const file_info) { return 0; } -#undef RETURN_ON_ERROR - } // namespace -fuse_operations FuseOperations(const int root_fd) { - root_fd_ = root_fd; +fuse_operations FuseOperations(File* const root) { + root_ = root; fuse_operations result; diff --git a/src/operations.h b/src/operations.h index 2e55f24..9b0f0f1 100644 --- a/src/operations.h +++ b/src/operations.h @@ -18,9 +18,11 @@ #define FUSE_USE_VERSION 26 #include +#include "posix_extras.h" + namespace scoville { -fuse_operations FuseOperations(int root_fd); +fuse_operations FuseOperations(File* root); } // namespace scoville diff --git a/src/posix_extras.cc b/src/posix_extras.cc new file mode 100644 index 0000000..d06dc41 --- /dev/null +++ b/src/posix_extras.cc @@ -0,0 +1,85 @@ +// Copyright 2016 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#define _POSIX_C_SOURCE 201502L +#undef _GNU_SOURCE + +#include "posix_extras.h" + +#include +#include +#include + +#include +#include +#include // a POSIX header, not a libc one +#include +#include +#include + +namespace scoville { + +std::string IoError::Message(const int number) noexcept { + try { + std::vector text(64); + int strerror_result; + while ((strerror_result = strerror_r(number, text.data(), text.size())) == + ERANGE) { + VLOG(1) << "IoError::Message: doubling message size from " << text.size(); + text.resize(text.size() * 2); + } + + if (strerror_result == 0) { + return std::string(text.begin(), text.end()); + } + } catch (...) { + } + return "(could not generate error message)"; +} + +File::File(const char* const path, const int flags) : path_(path) { + if ((fd_ = open(path, flags)) == -1) { + throw IoError(errno); + } +} + +File::~File() noexcept { + VLOG(1) << "closing file descriptor " << fd_; + if (close(fd_) == -1) { + LOG(ERROR) << "failed to close file descriptor " << fd_ << ": " + << IoError::Message(errno); + } +} + +struct stat File::Stat() const { + struct stat result; + if (fstat(fd_, &result) == -1) { + throw IoError(errno); + } + return result; +} + +struct stat File::LinkStatAt(const char* const path) const { + if (path[0] == '/') { + throw std::invalid_argument("absolute path"); + } + + struct stat result; + if (fstatat(fd_, path, &result, AT_SYMLINK_NOFOLLOW) == -1) { + throw IoError(errno); + } + return result; +} + +} // scoville diff --git a/src/posix_extras.h b/src/posix_extras.h new file mode 100644 index 0000000..b9c0f12 --- /dev/null +++ b/src/posix_extras.h @@ -0,0 +1,68 @@ +// Copyright 2016 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#ifndef POSIX_EXTRAS_H_ +#define POSIX_EXTRAS_H_ + +#include +#include + +#include +#include +#include + +namespace scoville { + +class IoError : public std::runtime_error { + public: + explicit IoError(const int number) + : std::runtime_error(Message(number)), number_(number) {} + + int number() const noexcept { return number_; } + + // Converts an errno into a human-readable message, a la strerror(3). + // Thread-safe. + static std::string Message(int) noexcept; + + private: + int number_; +}; + +// RAII wrapper for Unix file descriptors. +class File { + public: + File(const char* path, int flags); + virtual ~File() noexcept; + + const std::string& path() const noexcept { return path_; } + + // Calls fstat(2) on the file descriptor. + struct stat Stat() const; + + // Calls lstat(2) on the path relative to the file descriptor. The path must + // indeed be relative (i.e., it must not start with '/'). + struct stat LinkStatAt(const char* path) const; + + private: + File(File&) = delete; + + void operator=(File) = delete; + + std::string path_; + int fd_; +}; + +} // scoville + +#endif // POSIX_EXTRAS_H_ diff --git a/src/scoville.cc b/src/scoville.cc index d8d1692..b572b5f 100644 --- a/src/scoville.cc +++ b/src/scoville.cc @@ -15,15 +15,13 @@ // this program. If not, see . #include +#include -#include #include #include -#include -#include #include "operations.h" -#include "utility.h" +#include "posix_extras.h" constexpr char kUsage[] = R"(allow forbidden characters on VFAT file systems @@ -35,15 +33,17 @@ int main(int argc, char* argv[]) { google::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); + // Open an FD to the underlying file system so we can still do operations on + // it while it's overlayed. + std::unique_ptr root; const char* const root_path = argv[argc - 1]; - int root_fd; - if ((root_fd = open(root_path, O_DIRECTORY)) == -1) { - std::cerr << "scoville: bad mount point `" << root_path - << "': " << scoville::ErrnoText(); - std::exit(EXIT_FAILURE); + try { + root.reset(new scoville::File(root_path, O_DIRECTORY)); + } catch (const scoville::IoError& e) { + LOG(FATAL) << "scoville: bad mount point `" << root_path + << "': " << e.what(); } - LOG(INFO) << "overlaying " << root_path; - - const fuse_operations operations = scoville::FuseOperations(root_fd); + LOG(INFO) << "overlaying " << root->path(); + const fuse_operations operations = scoville::FuseOperations(root.get()); return fuse_main(argc, argv, &operations, nullptr); } diff --git a/src/utility.cc b/src/utility.cc deleted file mode 100644 index 5ccb58a..0000000 --- a/src/utility.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 Benjamin Barenblat -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -#define _POSIX_C_SOURCE 201502L -#undef _GNU_SOURCE - -#include "utility.h" - -#include - -#include -#include -#include - -#include - -namespace scoville { - -std::string ErrnoText() { - std::vector text(64); - int strerror_result; - while ((strerror_result = strerror_r(errno, text.data(), text.size())) == - ERANGE) { - VLOG(1) << "ErrnoText doubling message size from " << text.size(); - text.resize(text.size() * 2); - } - if (strerror_result != 0) { - return "(could not generate error message)"; - } - return std::string(text.begin(), text.end()); -} - -} // namespace scoville diff --git a/src/utility.h b/src/utility.h deleted file mode 100644 index 53e3840..0000000 --- a/src/utility.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 Benjamin Barenblat -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -#ifndef UTILITY_H_ -#define UTILITY_H_ - -#include - -namespace scoville { - -std::string ErrnoText(); - -} // namespace scoville - -#endif // UTILITY_H_ -- cgit v1.2.3