aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Benjamin Barenblat <bbaren@mit.edu>2016-02-18 21:51:50 -0500
committerGravatar Benjamin Barenblat <bbaren@mit.edu>2016-02-18 22:15:45 -0500
commit8716cf918e75c540814d75ccb9fa26ff6085cf63 (patch)
treedc10235e2475590cdd00ff465d7df58a474cb924
parentf92ffae9707904749858eeb5fae3ef750875c8fb (diff)
Create and use RAII file abstraction
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/operations.cc46
-rw-r--r--src/operations.h4
-rw-r--r--src/posix_extras.cc85
-rw-r--r--src/posix_extras.h68
-rw-r--r--src/scoville.cc24
-rw-r--r--src/utility.cc44
-rw-r--r--src/utility.h26
9 files changed, 189 insertions, 111 deletions
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 <sys/types.h>
#include <unistd.h>
-#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 <fuse.h>
+#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 <cerrno>
+#include <stdexcept>
+#include <vector>
+
+#include <fcntl.h>
+#include <glog/logging.h>
+#include <string.h> // a POSIX header, not a libc one
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace scoville {
+
+std::string IoError::Message(const int number) noexcept {
+ try {
+ std::vector<char> 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 <stdexcept>
+#include <string>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+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 <http://www.gnu.org/licenses/>.
#include <iostream>
+#include <memory>
-#include <fcntl.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#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<scoville::File> 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 <string.h>
-
-#include <cerrno>
-#include <string>
-#include <vector>
-
-#include <glog/logging.h>
-
-namespace scoville {
-
-std::string ErrnoText() {
- std::vector<char> 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 <string>
-
-namespace scoville {
-
-std::string ErrnoText();
-
-} // namespace scoville
-
-#endif // UTILITY_H_