diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 55 | ||||
-rw-r--r-- | src/encoding.cc | 117 | ||||
-rw-r--r-- | src/encoding.h | 39 | ||||
-rw-r--r-- | src/fuse.h | 21 | ||||
-rw-r--r-- | src/operations.cc | 334 | ||||
-rw-r--r-- | src/operations.h | 27 | ||||
-rw-r--r-- | src/posix_extras.cc | 226 | ||||
-rw-r--r-- | src/posix_extras.h | 155 | ||||
-rw-r--r-- | src/scoville.cc | 61 |
9 files changed, 0 insertions, 1035 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 3f1fb15..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,55 +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. - -include(FindPkgConfig) - -pkg_check_modules(FUSE REQUIRED fuse) -pkg_check_modules(GFLAGS REQUIRED libgflags) -pkg_check_modules(GLOG REQUIRED libglog) - -link_directories( - ${FUSE_LIBRARY_DIRS} - ${GFLAGS_LIBRARY_DIRS} - ${GLOG_LIBRARY_DIRS} -) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FUSE_CFLAGS_OTHER}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GFLAGS_CFLAGS_OTHER}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GLOG_CFLAGS_OTHER}") - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FUSE_LDFLAGS_OTHER}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GFLAGS_LDFLAGS_OTHER}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GLOG_LDFLAGS_OTHER}") - -add_executable( - scoville - encoding.cc - operations.cc - posix_extras.cc - scoville.cc -) - -target_include_directories( - scoville - SYSTEM PRIVATE ${FUSE_INCLUDE_DIRS} - SYSTEM PRIVATE ${GFLAGS_INCLUDE_DIRS} - SYSTEM PRIVATE ${GLOG_INCLUDE_DIRS} -) - -target_link_libraries( - scoville - ${FUSE_LIBRARIES} - ${GFLAGS_LIBRARIES} - ${GLOG_LIBRARIES} -) diff --git a/src/encoding.cc b/src/encoding.cc deleted file mode 100644 index 702b2c4..0000000 --- a/src/encoding.cc +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2016, 2018 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. - -#include "encoding.h" - -#include <array> -#include <cstdlib> -#include <functional> -#include <ios> -#include <sstream> -#include <string> - -#include <glog/logging.h> - -namespace scoville { - -namespace { - -void WriteAsciiAsHex(const char c, std::ostringstream* const out) { - if (1 < sizeof(c) && 0x100 <= c) { - // Not ASCII! - throw EncodingFailure("could not encode non-ASCII character '" + - std::string(1, c) + "'"); - } - *out << std::hex << static_cast<int>(c); -} - -char ReadHexAsAscii(std::istringstream* const in) { - std::array<char, 3> hex_str; - in->get(hex_str.data(), hex_str.size()); - char* decoded_end; - const char result = - static_cast<char>(std::strtol(hex_str.data(), &decoded_end, 16)); - if (decoded_end == hex_str.data()) { - throw DecodingFailure("could not decode invalid hex"); - } - return result; -} - -bool IsVfatBadCharacter(const char c) noexcept { - return (0 <= c && c < 0x20) || c == '*' || c == '?' || c == '<' || c == '>' || - c == '|' || c == '"' || c == ':' || c == '\\'; -} - -bool IsVfatBadLastCharacter(const char c) noexcept { - return IsVfatBadCharacter(c) || c == '.' || c == ' '; -} - -void EncodeStream(std::istringstream* const in, std::ostringstream* const out) { - char c; - while (!in->get(c).eof()) { - in->peek(); - const bool processing_last_character = in->eof(); - - if (IsVfatBadCharacter(c) || - (processing_last_character && IsVfatBadLastCharacter(c))) { - *out << '%'; - WriteAsciiAsHex(c, out); - } else if (c == '%') { - *out << "%%"; - } else { - *out << c; - } - } -} - -void DecodeStream(std::istringstream* const in, std::ostringstream* const out) { - char c; - while (!in->get(c).eof()) { - if (c == '%') { - if (in->peek() == '%') { - in->ignore(); - *out << "%"; - } else { - *out << ReadHexAsAscii(in); - } - } else { - *out << c; - } - } -} - -std::string TransformString( - std::function<void(std::istringstream*, std::ostringstream*)> f, - const std::string& in) { - std::istringstream in_stream(in); - std::ostringstream out_stream; - f(&in_stream, &out_stream); - return out_stream.str(); -} - -} // namespace - -std::string Encode(const std::string& in) { - const std::string result = TransformString(EncodeStream, in); - VLOG(1) << "Encode: \"" << in << "\" -> \"" << result << "\""; - return result; -} - -std::string Decode(const std::string& in) { - const std::string result = TransformString(DecodeStream, in); - VLOG(1) << "Decode: \"" << in << "\" -> \"" << result << "\""; - return result; -} - -} // scoville diff --git a/src/encoding.h b/src/encoding.h deleted file mode 100644 index d6e72ea..0000000 --- a/src/encoding.h +++ /dev/null @@ -1,39 +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 ENCODING_H_ -#define ENCODING_H_ - -#include <string> -#include <stdexcept> - -namespace scoville { - -class EncodingFailure : public std::logic_error { - public: - using std::logic_error::logic_error; -}; - -class DecodingFailure : public std::logic_error { - public: - using std::logic_error::logic_error; -}; - -std::string Encode(const std::string&); - -std::string Decode(const std::string&); - -} // scoville - -#endif // ENCODING_H_ diff --git a/src/fuse.h b/src/fuse.h deleted file mode 100644 index f7d012f..0000000 --- a/src/fuse.h +++ /dev/null @@ -1,21 +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 FUSE_H_ -#define FUSE_H_ - -#define FUSE_USE_VERSION 26 -#include <fuse/fuse.h> - -#endif // FUSE_H_ diff --git a/src/operations.cc b/src/operations.cc deleted file mode 100644 index 99817a1..0000000 --- a/src/operations.cc +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> -// Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org> -// Copyright (C) 2016 Benjamin Barenblat <benjamin@barenblat.name> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -// details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see <http://www.gnu.org/licenses/>. - -#include "operations.h" - -#include <cerrno> -#include <cstdint> -#include <cstring> -#include <experimental/optional> -#include <memory> -#include <new> -#include <stdexcept> -#include <string> -#include <system_error> -#include <type_traits> -#include <vector> - -#include <dirent.h> -#include <fcntl.h> -#include <glog/logging.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <time.h> - -#include "encoding.h" -#include "fuse.h" -#include "posix_extras.h" - -namespace scoville { - -namespace { - -// Pointer to the directory underlying the mount point. -File* root_; - -mode_t DirectoryTypeToFileType(const unsigned char type) { - return static_cast<mode_t>(DTTOIF(type)); -} - -std::string MakeRelative(const std::string& path) { - if (path.at(0) != '/') { - throw std::system_error(ENOENT, std::system_category()); - } - return path.substr(1); -} - -void* Initialize(fuse_conn_info*) noexcept { return nullptr; } - -void Destroy(void*) noexcept {} - -int Statfs(const char* const c_path, struct statvfs* const output) { - const std::string path(Encode(c_path)); - if (path == "/") { - *output = root_->StatVFs(); - } else { - *output = - root_->OpenAt(MakeRelative(path).c_str(), O_RDONLY | O_PATH).StatVFs(); - } - return 0; -} - -int Getattr(const char* const c_path, struct stat* output) { - const std::string path(Encode(c_path)); - if (path == "/") { - *output = root_->Stat(); - } else { - *output = root_->LinkStatAt(MakeRelative(path).c_str()); - } - return 0; -} - -int Fgetattr(const char*, struct stat* const output, - struct fuse_file_info* const file_info) { - // This reinterpret_cast violates type aliasing rules, so a compiler may - // invoke undefined behavior if *output is ever dereferenced. However, we - // compile with -fno-strict-aliasing, so this should be safe. - *output = reinterpret_cast<File*>(file_info->fh)->Stat(); - return 0; -} - -template <typename T> -int OpenResource(const std::string& path, const int flags, - uint64_t* const handle, const mode_t mode = 0) { - try { - std::unique_ptr<T> t( - new T(path == "/" ? *root_ : root_->OpenAt(MakeRelative(path).c_str(), - flags, mode))); - static_assert(sizeof(*handle) == sizeof(std::uintptr_t), - "FUSE file handles are a different size than pointers"); - *handle = reinterpret_cast<std::uintptr_t>(t.release()); - return 0; - } catch (const std::bad_alloc&) { - return -ENOMEM; - } -} - -template <typename T> -int ReleaseResource(const uint64_t handle) noexcept { - delete reinterpret_cast<T*>(handle); - return 0; -} - -int Mknod(const char* const c_path, const mode_t mode, const dev_t dev) { - const std::string path(Encode(c_path)); - if (path == "/") { - return -EISDIR; - } else { - root_->MkNod(MakeRelative(path).c_str(), mode, dev); - return 0; - } -} - -int Chmod(const char* const c_path, const mode_t mode) { - const std::string path(Encode(c_path)); - root_->ChModAt(path == "/" ? "." : MakeRelative(path).c_str(), mode); - return 0; -} - -int Rename(const char* const c_old_path, const char* const c_new_path) { - const std::string old_path(Encode(c_old_path)); - const std::string new_path(Encode(c_new_path)); - if (old_path == "/" || new_path == "/") { - return -EINVAL; - } else { - root_->RenameAt(MakeRelative(old_path).c_str(), - MakeRelative(new_path).c_str()); - return 0; - } -} - -int Create(const char* const path, const mode_t mode, - fuse_file_info* const file_info) { - return OpenResource<File>(Encode(path), file_info->flags | O_CREAT, - &file_info->fh, mode); -} - -int Open(const char* const path, fuse_file_info* const file_info) { - return OpenResource<File>(Encode(path), file_info->flags, &file_info->fh); -} - -int Read(const char*, char* const buffer, const size_t bytes, - const off_t offset, fuse_file_info* const file_info) { - // This reinterpret_cast violates type aliasing rules, so a compiler may - // invoke undefined behavior when file is dereferenced on the next line. - // However, we compile with -fno-strict-aliasing, so it should be safe. - auto* const file = reinterpret_cast<File*>(file_info->fh); - const std::vector<std::uint8_t> read = file->Read(offset, bytes); - std::memcpy(buffer, read.data(), read.size()); - return static_cast<int>(read.size()); -} - -int Write(const char*, const char* const buffer, const size_t bytes, - const off_t offset, fuse_file_info* const file_info) { - // See notes in Read about undefined behavior. - auto* const file = reinterpret_cast<File*>(file_info->fh); - const std::vector<std::uint8_t> to_write(buffer, buffer + bytes); - file->Write(offset, to_write); - return static_cast<int>(bytes); -} - -int Utimens(const char* const c_path, const timespec times[2]) { - const std::string path(Encode(c_path)); - root_->UTimeNs(path == "/" ? "." : MakeRelative(path).c_str(), times[0], - times[1]); - return 0; -} - -int Release(const char*, fuse_file_info* const file_info) { - return ReleaseResource<File>(file_info->fh); -} - -int Unlink(const char* c_path) { - const std::string path(Encode(c_path)); - if (path == "/") { - // Removing the root is probably a bad idea. - return -EPERM; - } else { - root_->UnlinkAt(MakeRelative(path).c_str()); - return 0; - } -} - -int Symlink(const char*, const char*) { return -EPERM; } - -int Readlink(const char*, char*, size_t) { return -EINVAL; } - -int Mkdir(const char* const c_path, const mode_t mode) { - const std::string path(Encode(c_path)); - if (path == "/") { - // They're asking to create the mount point. Huh? - return -EEXIST; - } else { - root_->MkDir(MakeRelative(path).c_str(), mode); - return 0; - } -} - -int Opendir(const char* const path, fuse_file_info* const file_info) { - return OpenResource<Directory>(Encode(path), O_DIRECTORY, &file_info->fh); -} - -int Readdir(const char*, void* const buffer, fuse_fill_dir_t filler, - const off_t offset, fuse_file_info* const file_info) { - // See notes in Read about undefined behavior. - auto* const directory = reinterpret_cast<Directory*>(file_info->fh); - - static_assert(std::is_same<off_t, long>(), - "off_t is not convertible with long"); - if (offset != directory->offset()) { - directory->Seek(offset); - } - - for (std::experimental::optional<dirent> entry = directory->ReadOne(); entry; - entry = directory->ReadOne()) { - struct stat stats; - std::memset(&stats, 0, sizeof(stats)); - stats.st_ino = entry->d_ino; - stats.st_mode = DirectoryTypeToFileType(entry->d_type); - const off_t next_offset = directory->offset(); - if (filler(buffer, Decode(entry->d_name).c_str(), &stats, next_offset)) { - break; - } - } - return 0; -} - -int Releasedir(const char*, fuse_file_info* const file_info) { - return ReleaseResource<Directory>(file_info->fh); -} - -int Truncate(const char* const c_path, const off_t size) { - const std::string path(Encode(c_path)); - if (path == "/") { - return -EISDIR; - } else { - root_->OpenAt(MakeRelative(path).c_str(), O_WRONLY).Truncate(size); - return 0; - } -} - -int Ftruncate(const char*, const off_t size, fuse_file_info* const file_info) { - // See notes in Read about undefined behavior. - reinterpret_cast<File*>(file_info->fh)->Truncate(size); - return 0; -} - -int Rmdir(const char* c_path) { - const std::string path(Encode(c_path)); - if (path == "/") { - // Removing the root is probably a bad idea. - return -EPERM; - } else { - root_->RmDirAt(MakeRelative(path).c_str()); - return 0; - } -} - -template <typename Function, Function f, typename... Args> -int CatchAndReturnExceptions(Args... args) noexcept { - try { - return f(args...); - } catch (const std::system_error& e) { - return -e.code().value(); - } catch (...) { - LOG(ERROR) << "caught unexpected value"; - return -ENOTRECOVERABLE; - } -} - -} // namespace - -#define CATCH_AND_RETURN_EXCEPTIONS(f) CatchAndReturnExceptions<decltype(f), f> - -fuse_operations FuseOperations(File* const root) { - root_ = root; - - fuse_operations result; - std::memset(&result, 0, sizeof(result)); - - result.flag_nullpath_ok = true; - result.flag_nopath = true; - result.flag_utime_omit_ok = true; - - result.init = Initialize; - result.destroy = Destroy; - - result.statfs = CATCH_AND_RETURN_EXCEPTIONS(Statfs); - - result.getattr = CATCH_AND_RETURN_EXCEPTIONS(Getattr); - result.fgetattr = CATCH_AND_RETURN_EXCEPTIONS(Fgetattr); - - result.mknod = CATCH_AND_RETURN_EXCEPTIONS(Mknod); - result.chmod = CATCH_AND_RETURN_EXCEPTIONS(Chmod); - result.rename = CATCH_AND_RETURN_EXCEPTIONS(Rename); - result.create = CATCH_AND_RETURN_EXCEPTIONS(Create); - result.open = CATCH_AND_RETURN_EXCEPTIONS(Open); - result.read = CATCH_AND_RETURN_EXCEPTIONS(Read); - result.write = CATCH_AND_RETURN_EXCEPTIONS(Write); - result.utimens = CATCH_AND_RETURN_EXCEPTIONS(Utimens); - result.release = CATCH_AND_RETURN_EXCEPTIONS(Release); - result.truncate = CATCH_AND_RETURN_EXCEPTIONS(Truncate); - result.ftruncate = CATCH_AND_RETURN_EXCEPTIONS(Ftruncate); - result.unlink = CATCH_AND_RETURN_EXCEPTIONS(Unlink); - - result.symlink = CATCH_AND_RETURN_EXCEPTIONS(Symlink); - result.readlink = CATCH_AND_RETURN_EXCEPTIONS(Readlink); - - result.mkdir = CATCH_AND_RETURN_EXCEPTIONS(Mkdir); - result.opendir = CATCH_AND_RETURN_EXCEPTIONS(Opendir); - result.readdir = CATCH_AND_RETURN_EXCEPTIONS(Readdir); - result.releasedir = CATCH_AND_RETURN_EXCEPTIONS(Releasedir); - result.rmdir = CATCH_AND_RETURN_EXCEPTIONS(Rmdir); - - return result; -} - -#undef CATCH_AND_RETURN_EXCEPTIONS - -} // namespace scoville diff --git a/src/operations.h b/src/operations.h deleted file mode 100644 index 33e0767..0000000 --- a/src/operations.h +++ /dev/null @@ -1,27 +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 OPERATIONS_H_ -#define OPERATIONS_H_ - -#include "fuse.h" -#include "posix_extras.h" - -namespace scoville { - -fuse_operations FuseOperations(File* root); - -} // namespace scoville - -#endif // OPERATIONS_H_ diff --git a/src/posix_extras.cc b/src/posix_extras.cc deleted file mode 100644 index 3e916ad..0000000 --- a/src/posix_extras.cc +++ /dev/null @@ -1,226 +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. - -#include "posix_extras.h" - -#include <array> -#include <cerrno> -#include <cstdint> -#include <experimental/optional> -#include <stdexcept> -#include <system_error> -#include <vector> - -#include <dirent.h> -#include <fcntl.h> -#include <glog/logging.h> -#include <stdio.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <unistd.h> - -namespace scoville { - -namespace { - -std::system_error SystemError() { - return std::system_error(errno, std::system_category()); -} - -void ValidatePath(const char* const path) { - if (path[0] == '/') { - throw std::invalid_argument("absolute path"); - } -} - -template <typename T> -T CheckSyscall(const T result) { - if (result == -1) { - throw SystemError(); - } - return result; -} - -} // namespace - -File::File(const char* const path, const int flags, const mode_t mode) - : path_(path) { - fd_ = CheckSyscall(open(path, flags, mode)); - VLOG(1) << "opening file descriptor " << fd_; -} - -File::File(const File& other) : path_(other.path_), fd_(other.Duplicate()) { - VLOG(1) << "opening file descriptor " << fd_; -} - -File::~File() noexcept { - VLOG(1) << "closing file descriptor " << fd_; - try { - CheckSyscall(close(fd_)); - } catch (...) { - LOG(ERROR) << "failed to close file descriptor " << fd_; - } -} - -struct stat File::Stat() const { - struct stat result; - CheckSyscall(fstat(fd_, &result)); - return result; -} - -void File::ChModAt(const char* const path, const mode_t mode) const { - ValidatePath(path); - CheckSyscall(fchmodat(fd_, path, mode, 0)); -} - -struct stat File::LinkStatAt(const char* const path) const { - ValidatePath(path); - struct stat result; - CheckSyscall(fstatat(fd_, path, &result, AT_SYMLINK_NOFOLLOW)); - return result; -} - -void File::MkDir(const char* const path, const mode_t mode) const { - ValidatePath(path); - CheckSyscall(mkdirat(fd_, path, mode | S_IFDIR)); -} - -void File::MkNod(const char* const path, const mode_t mode, - const dev_t dev) const { - ValidatePath(path); - CheckSyscall(mknodat(fd_, path, mode, dev)); -} - -File File::OpenAt(const char* const path, const int flags, - const mode_t mode) const { - ValidatePath(path); - File result; - result.fd_ = CheckSyscall(openat(fd_, path, flags, mode)); - result.path_ = path_ + "/" + path; - return result; -} - -std::vector<std::uint8_t> File::Read(off_t offset, size_t bytes) const { - std::vector<std::uint8_t> result(bytes, 0); - size_t cursor = 0; - ssize_t bytes_read; - while (0 < (bytes_read = CheckSyscall( - pread(fd_, result.data() + cursor, bytes, offset)))) { - cursor += static_cast<size_t>(bytes_read); - offset += bytes_read; - bytes -= static_cast<size_t>(bytes_read); - } - result.resize(cursor); - return result; -} - -std::string File::ReadLinkAt(const char* const path) const { - ValidatePath(path); - std::vector<char> result(64, '\0'); - size_t bytes_read; - - while ((bytes_read = static_cast<size_t>(CheckSyscall(readlinkat( - fd_, path, result.data(), result.size())))) == result.size()) { - // We filled the entire buffer. There may be more data we missed. - result.resize(result.size() * 2); - } - return std::string(result.data(), result.data() + bytes_read); -} - -void File::RenameAt(const char* old_path, const char* new_path) const { - ValidatePath(old_path); - ValidatePath(new_path); - CheckSyscall(renameat(fd_, old_path, fd_, new_path)); -} - -void File::RmDirAt(const char* const path) const { - ValidatePath(path); - CheckSyscall(unlinkat(fd_, path, AT_REMOVEDIR)); -} - -struct statvfs File::StatVFs() const { - struct statvfs result; - CheckSyscall(fstatvfs(fd_, &result)); - return result; -} - -void File::SymLinkAt(const char* const target, const char* const source) const { - ValidatePath(source); - CheckSyscall(symlinkat(target, fd_, source)); -} - -void File::Truncate(const off_t size) { CheckSyscall(ftruncate(fd_, size)); } - -void File::UnlinkAt(const char* const path) const { - ValidatePath(path); - CheckSyscall(unlinkat(fd_, path, 0)); -} - -void File::UTimeNs(const char* const path, const timespec& access, - const timespec& modification) const { - ValidatePath(path); - std::array<const timespec, 2> times{{access, modification}}; - CheckSyscall(utimensat(fd_, path, times.data(), AT_SYMLINK_NOFOLLOW)); -} - -size_t File::Write(const off_t offset, - const std::vector<std::uint8_t>& to_write) { - size_t bytes_written = 0; - while (bytes_written < to_write.size()) { - bytes_written += static_cast<size_t>(CheckSyscall(pwrite( - fd_, to_write.data() + bytes_written, to_write.size() - bytes_written, - offset + static_cast<off_t>(bytes_written)))); - } - return bytes_written; -} - -int File::Duplicate() const { return CheckSyscall(dup(fd_)); } - -Directory::Directory(const File& file) { - // "After a successful call to fdopendir(), fd is used internally by the - // implementation, and should not otherwise be used by the application." We - // therefore need to grab an unmanaged copy of the file descriptor from file. - if (!(stream_ = fdopendir(file.Duplicate()))) { - throw SystemError(); - } - rewinddir(stream_); -} - -Directory::~Directory() noexcept { - try { - CheckSyscall(closedir(stream_)); - } catch (...) { - LOG(ERROR) << "failed to close directory stream"; - } -} - -long Directory::offset() const { return CheckSyscall(telldir(stream_)); } - -void Directory::Seek(const long offset) noexcept { seekdir(stream_, offset); } - -std::experimental::optional<dirent> Directory::ReadOne() { - dirent* result; - errno = 0; - if (!(result = readdir(stream_))) { - if (errno == 0) { - return std::experimental::nullopt; - } else { - throw SystemError(); - } - } - return *result; -} - -} // scoville diff --git a/src/posix_extras.h b/src/posix_extras.h deleted file mode 100644 index 9dec22d..0000000 --- a/src/posix_extras.h +++ /dev/null @@ -1,155 +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 POSIX_EXTRAS_H_ -#define POSIX_EXTRAS_H_ - -#include <cstdint> -#include <experimental/optional> -#include <string> -#include <vector> - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <time.h> - -namespace scoville { - -class File; - -// RAII wrapper for Unix directory streams. -class Directory { - public: - explicit Directory(const File&); - virtual ~Directory() noexcept; - - long offset() const; - - void Seek(long) noexcept; - - std::experimental::optional<dirent> ReadOne(); - - private: - Directory(const Directory&) = delete; - Directory(Directory&&) = delete; - - void operator=(const Directory&) = delete; - void operator=(Directory&&) = delete; - - DIR* stream_; -}; - -// RAII wrapper for Unix file descriptors. -class File { - public: - File(const char* path, int flags) : File(path, flags, 0777) {} - File(const char* path, int flags, mode_t mode); - File(const File&); - File(File&& other) = default; - virtual ~File() noexcept; - - const std::string& path() const noexcept { return path_; } - - // Calls fstat(2) on the file descriptor. - struct stat Stat() const; - - // Changes the file mode of the path relative to the file descriptor. The - // path must indeed be relative (i.e., it must not start with '/'). - void ChModAt(const char* path, mode_t) 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; - - // Creates a directory at the path relative to the file descriptor. The path - // must indeed be relative (i.e., it must not start with '/'). - void MkDir(const char* path, mode_t mode) const; - - // Creates a file at the path relative to the file descriptor. The path must - // indeed be relative (i.e., it must not start with '/'). - void MkNod(const char* path, mode_t mode, dev_t dev) const; - - // Calls openat(2) on the path relative to the file descriptor. The path must - // indeed be relative (i.e., it must not start with '/'). - File OpenAt(const char* const path, const int flags) const { - return OpenAt(path, flags, 0); - } - File OpenAt(const char* path, int flags, mode_t mode) const; - - // Reads exactly the specified number of bytes from the file at the given - // offset, unless doing so would run past the end of the file, in which case - // fewer bytes are returned. - std::vector<std::uint8_t> Read(off_t, size_t) const; - - // Reads the contents of a symbolic link. The path to the symbolic link is - // interpreted relative to the file descriptor and must indeed be relative - // (i.e., it must not start with '/'). - std::string ReadLinkAt(const char* path) const; - - // Renames a file from old_path to new_path. Both paths are interpreted - // relative to the file descriptor, and both must indeed be relative (i.e., - // they must not start with '/'). - void RenameAt(const char* old_path, const char* new_path) const; - - // Removes the directory at the path relative to the file descriptor. The - // path must indeed be relative (i.e., it must not start with '/'). - void RmDirAt(const char* path) const; - - // Retrieves information about the file system containing the referent of the - // file descriptor. - struct statvfs StatVFs() const; - - // Creates a symlink at source pointing to target. target is unvalidated. - // source is interpreted as a path relative to the file descriptor and must - // indeed be relative (i.e., it must not start with '/'). - void SymLinkAt(const char* target, const char* source) const; - - // Truncates the file to the specified size. - void Truncate(off_t); - - // Removes the file at the path relative to the file descriptor. The path - // must indeed be relative (i.e., it must not start with '/'). - void UnlinkAt(const char* path) const; - - // Sets the access and modification times of the file at the path relative to - // the file descriptor. The path must indeed be relative (i.e., it must not - // start with '/'). Does not follow symbolic links. - void UTimeNs(const char* path, const timespec& access, - const timespec& modification) const; - - // Writes the specified byte vector to the file at the given offset. Returns - // the number of bytes written, which will always be the number of bytes given - // as input. - size_t Write(off_t, const std::vector<std::uint8_t>&); - - private: - File() {} - - void operator=(const File&) = delete; - void operator=(File&&) = delete; - - // Duplicates fd_ and returns the raw new file descriptor. - int Duplicate() const; - - std::string path_; - int fd_; - - friend Directory::Directory(const File&); -}; - -} // scoville - -#endif // POSIX_EXTRAS_H_ diff --git a/src/scoville.cc b/src/scoville.cc deleted file mode 100644 index 2eff02e..0000000 --- a/src/scoville.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2007, 2008 Jan Engelhardt <jengelh@medozas.de> -// Copyright (C) 2016 Benjamin Barenblat <benjamin@barenblat.name> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -// details. -// -// You should have received a copy of the GNU General Public License along with -// this program. If not, see <http://www.gnu.org/licenses/>. - -#include <memory> -#include <system_error> -#include <vector> - -#include <fcntl.h> -#include <gflags/gflags.h> -#include <glog/logging.h> - -#include "fuse.h" -#include "operations.h" -#include "posix_extras.h" - -constexpr char kUsage[] = R"(allow forbidden characters on VFAT file systems - -usage: scoville [flags] target_dir [-- fuse_options])"; - -int main(int argc, char* argv[]) { - google::InstallFailureSignalHandler(); - google::SetUsageMessage(kUsage); - google::ParseCommandLineFlags(&argc, &argv, true); - google::InitGoogleLogging(argv[0]); - - // This is an overlay file system, which means once we start FUSE, the - // underlying file system will be inaccessible through normal means. Open a - // file descriptor to the underlying root now 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]; - try { - root.reset(new scoville::File(root_path, O_DIRECTORY)); - } catch (const std::system_error& e) { - LOG(FATAL) << "scoville: bad mount point `" << root_path - << "': " << e.what(); - } - LOG(INFO) << "overlaying " << root->path(); - const fuse_operations operations = scoville::FuseOperations(root.get()); - - // Add -o nonempty to argv so FUSE won't complain about overlaying. - char hyphen_o[] = "-o"; - char nonempty[] = "nonempty"; - std::vector<char*> new_argv(argv, argv + argc); - new_argv.emplace_back(hyphen_o); - new_argv.emplace_back(nonempty); - return fuse_main(new_argv.size(), new_argv.data(), &operations, nullptr); -} |