diff --git a/src/CryDevice.cpp b/src/CryDevice.cpp index 71a1f831..be592eb5 100644 --- a/src/CryDevice.cpp +++ b/src/CryDevice.cpp @@ -4,6 +4,7 @@ #include "CryDir.h" #include "CryFile.h" +#include "CrySymlink.h" #include "messmer/fspp/fuse/FuseErrnoException.h" #include "messmer/blobstore/implementations/onblocks/BlobStoreOnBlocks.h" @@ -68,6 +69,8 @@ unique_ptr CryDevice::Load(const bf::path &path) { return make_unique(this, std::move(parent), entry.key); } else if (entry.type == fspp::Dir::EntryType::FILE) { return make_unique(this, std::move(parent), entry.key); + } else if (entry.type == fspp::Dir::EntryType::SYMLINK) { + return make_unique(this, std::move(parent), entry.key); } else { throw FuseErrnoException(EIO); } diff --git a/src/CryDir.cpp b/src/CryDir.cpp index fbd5d2b2..fc99f3cc 100644 --- a/src/CryDir.cpp +++ b/src/CryDir.cpp @@ -9,6 +9,7 @@ #include "CryDevice.h" #include "CryFile.h" #include "CryOpenFile.h" +#include "impl/SymlinkBlob.h" //TODO Get rid of this in favor of exception hierarchy using fspp::fuse::CHECK_RETVAL; @@ -67,8 +68,12 @@ fspp::Dir::EntryType CryDir::getType() const { return fspp::Dir::EntryType::DIR; } -void CryDir::createSymlink(const string &name, const bf::path &target) { - //TODO +void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) { + auto blob = LoadBlob(); + auto child = device()->CreateBlob(); + Key childkey = child->key(); + blob->AddChildSymlink(name, childkey, uid, gid); + SymlinkBlob::InitializeSymlink(std::move(child), target); } } diff --git a/src/CryDir.h b/src/CryDir.h index b932dec4..d141becf 100644 --- a/src/CryDir.h +++ b/src/CryDir.h @@ -16,7 +16,7 @@ public: //TODO return type variance to CryFile/CryDir? std::unique_ptr createAndOpenFile(const std::string &name, mode_t mode, uid_t uid, gid_t gid) override; void createDir(const std::string &name, mode_t mode, uid_t uid, gid_t gid) override; - void createSymlink(const std::string &name, const boost::filesystem::path &target) override; + void createSymlink(const std::string &name, const boost::filesystem::path &target, uid_t uid, gid_t gid) override; //TODO Make Entry a public class instead of hidden in DirBlob (which is not publicly visible) std::unique_ptr> children() const override; diff --git a/src/CrySymlink.cpp b/src/CrySymlink.cpp new file mode 100644 index 00000000..b74c7f78 --- /dev/null +++ b/src/CrySymlink.cpp @@ -0,0 +1,42 @@ +#include "CrySymlink.h" + +#include "messmer/fspp/fuse/FuseErrnoException.h" +#include "CryDevice.h" +#include "CrySymlink.h" +#include "impl/SymlinkBlob.h" + +//TODO Get rid of this in favor of exception hierarchy +using fspp::fuse::CHECK_RETVAL; +using fspp::fuse::FuseErrnoException; + +namespace bf = boost::filesystem; + +using std::unique_ptr; +using std::make_unique; +using std::string; +using std::vector; + +using blockstore::Key; + +namespace cryfs { + +CrySymlink::CrySymlink(CryDevice *device, unique_ptr parent, const Key &key) +: CryNode(device, std::move(parent), key) { +} + +CrySymlink::~CrySymlink() { +} + +unique_ptr CrySymlink::LoadBlob() const { + return make_unique(CryNode::LoadBlob()); +} + +fspp::Dir::EntryType CrySymlink::getType() const { + return fspp::Dir::EntryType::SYMLINK; +} + +bf::path CrySymlink::target() const { + return LoadBlob()->target(); +} + +} diff --git a/src/CrySymlink.h b/src/CrySymlink.h new file mode 100644 index 00000000..995ee873 --- /dev/null +++ b/src/CrySymlink.h @@ -0,0 +1,29 @@ +#pragma once +#ifndef CRYFS_LIB_CRYSYMLINK_H_ +#define CRYFS_LIB_CRYSYMLINK_H_ + +#include +#include "CryNode.h" +#include "impl/SymlinkBlob.h" +#include "impl/DirBlob.h" + +namespace cryfs { + +class CrySymlink: public fspp::Symlink, CryNode { +public: + CrySymlink(CryDevice *device, std::unique_ptr parent, const blockstore::Key &key); + virtual ~CrySymlink(); + + boost::filesystem::path target() const override; + + fspp::Dir::EntryType getType() const override; + +private: + std::unique_ptr LoadBlob() const; + + DISALLOW_COPY_AND_ASSIGN(CrySymlink); +}; + +} + +#endif diff --git a/src/impl/DirBlob.cpp b/src/impl/DirBlob.cpp index cb3e3e46..e66cc44d 100644 --- a/src/impl/DirBlob.cpp +++ b/src/impl/DirBlob.cpp @@ -9,6 +9,7 @@ #include "MagicNumbers.h" #include "../CryDevice.h" #include "FileBlob.h" +#include "SymlinkBlob.h" using std::unique_ptr; using std::make_unique; @@ -133,6 +134,10 @@ void DirBlob::AddChildFile(const std::string &name, const Key &blobKey, mode_t m AddChild(name, blobKey, fspp::Dir::EntryType::FILE, mode, uid, gid); } +void DirBlob::AddChildSymlink(const std::string &name, const blockstore::Key &blobKey, uid_t uid, gid_t gid) { + AddChild(name, blobKey, fspp::Dir::EntryType::SYMLINK, S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH, uid, gid); +} + void DirBlob::AddChild(const std::string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid) { if (hasChild(name)) { @@ -189,7 +194,7 @@ void DirBlob::AppendChildrenTo(vector *result) const { void DirBlob::statChild(const Key &key, struct ::stat *result) const { auto child = GetChild(key); - //TODO Loading the blob for only getting the size is not very performant. + //TODO Loading the blob for only getting the size of the file/symlink is not very performant. // Furthermore, this is the only reason why DirBlob needs a pointer to CryDevice, which is ugly result->st_mode = child.mode; result->st_uid = child.uid; @@ -201,8 +206,14 @@ void DirBlob::statChild(const Key &key, struct ::stat *result) const { result->st_mtim = result->st_ctim = result->st_atim; if (child.type == fspp::Dir::EntryType::FILE) { result->st_size = FileBlob(_device->LoadBlob(key)).size(); - } else { + } else if (child.type == fspp::Dir::EntryType::DIR) { + //TODO Why do dirs have 4096 bytes in size? Does that make sense? result->st_size = 4096; + } else if (child.type == fspp::Dir::EntryType::SYMLINK) { + //TODO Necessary with fuse or does fuse set this on symlinks anyhow? + result->st_size = SymlinkBlob(_device->LoadBlob(key)).target().native().size(); + } else { + assert(false); } //TODO Move ceilDivision to general utils which can be used by cryfs as well result->st_blocks = blobstore::onblocks::utils::ceilDivision(result->st_size, 512); @@ -211,7 +222,7 @@ void DirBlob::statChild(const Key &key, struct ::stat *result) const { void DirBlob::chmodChild(const Key &key, mode_t mode) { auto found = _findChild(key); - assert ((S_ISREG(mode) && S_ISREG(found->mode)) || (S_ISDIR(mode) && S_ISDIR(found->mode))); + assert ((S_ISREG(mode) && S_ISREG(found->mode)) || (S_ISDIR(mode) && S_ISDIR(found->mode)) || (S_ISLNK(mode))); found->mode = mode; _changed = true; } diff --git a/src/impl/DirBlob.h b/src/impl/DirBlob.h index 42e7fd57..2d847634 100644 --- a/src/impl/DirBlob.h +++ b/src/impl/DirBlob.h @@ -25,7 +25,7 @@ public: mode |= S_IFDIR; break; } - assert((S_ISREG(mode) && type == fspp::Dir::EntryType::FILE) || (S_ISDIR(mode) && type == fspp::Dir::EntryType::DIR)); + assert((S_ISREG(mode) && type == fspp::Dir::EntryType::FILE) || (S_ISDIR(mode) && type == fspp::Dir::EntryType::DIR) || (type == fspp::Dir::EntryType::SYMLINK)); } fspp::Dir::EntryType type; @@ -46,6 +46,7 @@ public: const Entry &GetChild(const blockstore::Key &key) const; void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid); void AddChildFile(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid); + void AddChildSymlink(const std::string &name, const blockstore::Key &blobKey, uid_t uid, gid_t gid); void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type, mode_t mode, uid_t uid, gid_t gid); void RemoveChild(const blockstore::Key &key); void flush(); diff --git a/src/impl/MagicNumbers.h b/src/impl/MagicNumbers.h index 6a5b5e5f..968c3297 100644 --- a/src/impl/MagicNumbers.h +++ b/src/impl/MagicNumbers.h @@ -6,7 +6,8 @@ namespace cryfs { enum MagicNumbers { DIR = 0x00, - FILE = 0x01 + FILE = 0x01, + SYMLINK = 0x02 }; } diff --git a/src/impl/SymlinkBlob.cpp b/src/impl/SymlinkBlob.cpp new file mode 100644 index 00000000..1dcb7e73 --- /dev/null +++ b/src/impl/SymlinkBlob.cpp @@ -0,0 +1,55 @@ +#include "SymlinkBlob.h" + +#include "MagicNumbers.h" +#include +#include + +using std::unique_ptr; +using std::make_unique; +using std::string; +using blobstore::Blob; + +namespace bf = boost::filesystem; + +namespace cryfs { + +SymlinkBlob::SymlinkBlob(unique_ptr blob) +: _target(_readTargetFromBlob(*blob)) { +} + +SymlinkBlob::SymlinkBlob(const bf::path &target) :_target(target) { +} + +SymlinkBlob::~SymlinkBlob() { +} + +unique_ptr SymlinkBlob::InitializeSymlink(unique_ptr blob, const bf::path &target) { + assert(blob.get() != nullptr); + string targetStr = target.native(); + blob->resize(1 + targetStr.size()); + unsigned char magicNumber = MagicNumbers::SYMLINK; + blob->write(&magicNumber, 0, 1); + blob->write(targetStr.c_str(), 1, targetStr.size()); + return make_unique(target); +} + +void SymlinkBlob::_checkMagicNumber(const Blob &blob) { + unsigned char value; + blob.read(&value, 0, 1); + assert(value == MagicNumbers::SYMLINK); +} + +bf::path SymlinkBlob::_readTargetFromBlob(const blobstore::Blob &blob) { + _checkMagicNumber(blob); + size_t targetStrSize = blob.size() - 1; // -1 because of the magic number + char targetStr[targetStrSize + 1]; // +1 because of the nullbyte + blob.read(targetStr, 1, targetStrSize); + targetStr[targetStrSize] = '\0'; + return targetStr; +} + +const bf::path &SymlinkBlob::target() const { + return _target; +} + +} diff --git a/src/impl/SymlinkBlob.h b/src/impl/SymlinkBlob.h new file mode 100644 index 00000000..41b29de5 --- /dev/null +++ b/src/impl/SymlinkBlob.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef CRYFS_LIB_IMPL_SYMLINKBLOB_H_ +#define CRYFS_LIB_IMPL_SYMLINKBLOB_H_ + +#include +#include +#include + +namespace cryfs { + +class SymlinkBlob { +public: + static std::unique_ptr InitializeSymlink(std::unique_ptr blob, const boost::filesystem::path &target); + + SymlinkBlob(std::unique_ptr blob); + SymlinkBlob(const boost::filesystem::path &target); + virtual ~SymlinkBlob(); + + const boost::filesystem::path &target() const; + +private: + boost::filesystem::path _target; + + static void _checkMagicNumber(const blobstore::Blob &blob); + static boost::filesystem::path _readTargetFromBlob(const blobstore::Blob &blob); +}; + +} + +#endif