From e662f34ef65a3ae94b4216097acc9f0a25611fac Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 11 Oct 2020 12:04:39 -0400 Subject: Eliminate CMake; flatten directory structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMake is probably more trouble than it’s worth for this project. Replace it with a hand-rolled Ninja file. --- operations.cc | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 operations.cc (limited to 'operations.cc') diff --git a/operations.cc b/operations.cc new file mode 100644 index 0000000..99817a1 --- /dev/null +++ b/operations.cc @@ -0,0 +1,334 @@ +// Copyright (C) 2001-2007 Miklos Szeredi +// Copyright (C) 2011 Sebastian Pipping +// Copyright (C) 2016 Benjamin Barenblat +// +// 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 . + +#include "operations.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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(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_info->fh)->Stat(); + return 0; +} + +template +int OpenResource(const std::string& path, const int flags, + uint64_t* const handle, const mode_t mode = 0) { + try { + std::unique_ptr 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(t.release()); + return 0; + } catch (const std::bad_alloc&) { + return -ENOMEM; + } +} + +template +int ReleaseResource(const uint64_t handle) noexcept { + delete reinterpret_cast(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(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(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_info->fh); + const std::vector read = file->Read(offset, bytes); + std::memcpy(buffer, read.data(), read.size()); + return static_cast(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_info->fh); + const std::vector to_write(buffer, buffer + bytes); + file->Write(offset, to_write); + return static_cast(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_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(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(file_info->fh); + + static_assert(std::is_same(), + "off_t is not convertible with long"); + if (offset != directory->offset()) { + directory->Seek(offset); + } + + for (std::experimental::optional 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(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_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 +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 + +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 -- cgit v1.2.3