Commit a47dd48f authored by Marc Vef's avatar Marc Vef
Browse files

First cut on dentry support and permissions. First proof-of-concept...

First cut on dentry support and permissions. First proof-of-concept implementation for opendir, readdir, lookup, and releasedir. Implement next after paper is done.
dentry support is only partial and has to be restructured as the implementation of highlevel fuse is not transferable to lowlevel fuse. Readdir is significantly more complex but also much more lightweight which will help performance.
parent 756af21d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -21,11 +21,11 @@ find_package(Boost 1.56.0 COMPONENTS system filesystem serialization)
include_directories(${FUSE3_INCLUDE_DIR} include/)
set(SOURCE_FILES src/main.cpp src/main.h src/fuse_ops.h src/configure.h
        src/classes/metadata.h src/classes/metadata.cpp src/classes/fs_data.h src/classes/fs_data.cpp
        src/adafs_ops/metadata_ops.h #src/adafs_ops/dentry_ops.h
        src/adafs_ops/metadata_ops.h src/adafs_ops/dentry_ops.h src/adafs_ops/access.h

        src/util.cpp
        src/fuse_ops/file.cpp src/fuse_ops/directory.cpp
        src/adafs_ops/metadata_ops.cpp #src/adafs_ops/dentry_ops.cpp
        src/adafs_ops/metadata_ops.cpp src/adafs_ops/dentry_ops.cpp src/adafs_ops/access.cpp

        )
add_executable(adafs ${SOURCE_FILES} src/main.cpp)
+53 −51
Original line number Diff line number Diff line
@@ -8,33 +8,34 @@
/**
 * Checks access for mask (can be R_OK, W_OK, or X_OK (or combined) AFAIK and not verified) against metadata's mode.
 * First the mask is checked agains the 3 bits for the user, then for the 3 bits of the group, and lastly other.
 * If all three checks have failed, return -EACCESS (no access)
 * If all three checks have failed, return EACCESS (no access)
 * @param req
 * @param md
 * @param mask
 * @return
 */
int chk_access(const Metadata& md, int mask) {
    ADAFS_DATA->logger->debug("chk_access() enter: metadata_uid {} fusecontext_uid {}", md.uid(),
                              fuse_get_context()->uid);
int chk_access(const fuse_req_t& req, const Metadata& md, int mask) {
    ADAFS_DATA->spdlogger()->debug("chk_access() enter: metadata_uid {} fusecontext_uid {}", md.uid(),
                                   fuse_req_ctx(req)->uid);
    // root user is a god
    if (fuse_get_context()->uid == 0)
    if (fuse_req_ctx(req)->uid == 0)
        return 0;

    //check user leftmost 3 bits for rwx in md->mode
    if (md.uid() == fuse_get_context()->uid) {
    if (md.uid() == fuse_req_ctx(req)->uid) {
        // Because mode comes only with the first 3 bits used, the user bits have to be shifted to the right to compare
        if ((mask & md.mode() >> 6) == (unsigned int) mask)
            return 0;
        else
            return -EACCES;
            return EACCES;
    }

    //check group middle 3 bits for rwx in md->mode
    if (md.gid() == fuse_get_context()->gid) {
    if (md.gid() == fuse_req_ctx(req)->gid) {
        if ((mask & md.mode() >> 3) == (unsigned int) mask)
            return 0;
        else
            return -EACCES;
            return EACCES;
    }

    //check other rightmost 3 bits for rwx in md->mode.
@@ -43,26 +44,27 @@ int chk_access(const Metadata& md, int mask) {
        return 0;
    }

    return -EACCES;
    return EACCES;
}

/**
 * Check if uid from fuse context (i.e., the caller) equals the uid from the object
 * @param req
 * @param md
 * @return
 */
int chk_uid(const Metadata& md) {
int chk_uid(const fuse_req_t& req, const Metadata& md) {

    // root user is a god
    if (fuse_get_context()->uid == 0)
    if (fuse_req_ctx(req)->uid == 0)
        return 0;

    // if user is the user of md, he/she has access
    if (fuse_get_context()->uid == md.uid())
    if (fuse_req_ctx(req)->uid == md.uid())
        return 0;

    // else no permission
    return -EPERM;
    return EPERM;
}

/**
@@ -74,17 +76,17 @@ int chk_uid(const Metadata& md) {
// XXX error handling
int change_access(Metadata& md, mode_t mode, const bfs::path& path) {

    auto path_hash = ADAFS_DATA->hashf(path.string());
    md.mode((uint32_t) mode);

    write_metadata_field(md.mode(), path_hash, md_field_map.at(Md_fields::mode));

#ifdef ACMtime
    md.update_ACM_time(true, true, true);
    write_metadata_field(md.atime(), path_hash, md_field_map.at(Md_fields::atime));
    write_metadata_field(md.ctime(), path_hash, md_field_map.at(Md_fields::ctime));
    write_metadata_field(md.mtime(), path_hash, md_field_map.at(Md_fields::mtime));
#endif
//    auto path_hash = ADAFS_DATA->hashf(path.string());
//    md.mode((uint32_t) mode);
//
//    write_metadata_field(md.mode(), path_hash, md_field_map.at(Md_fields::mode));
//
//#ifdef ACMtime
//    md.update_ACM_time(true, true, true);
//    write_metadata_field(md.atime(), path_hash, md_field_map.at(Md_fields::atime));
//    write_metadata_field(md.ctime(), path_hash, md_field_map.at(Md_fields::ctime));
//    write_metadata_field(md.mtime(), path_hash, md_field_map.at(Md_fields::mtime));
//#endif

    return 0;
}
@@ -100,33 +102,33 @@ int change_access(Metadata& md, mode_t mode, const bfs::path& path) {
 * @return
 */
int change_permissions(Metadata& md, uid_t uid, gid_t gid, const bfs::path& path) {
    auto path_hash = ADAFS_DATA->hashf(path.string());

    // XXX Users should be able to change the group to whatever groups they're belonging to. For now group can only
    // XXX be changed to the active group they're belonging to.
    if (fuse_get_context()->gid != gid)
        return -EPERM;
    // if nothing changed, nothing to do
    if (md.uid() == uid && md.gid() == gid)
        return 0;

    // root can do anything
    if (fuse_get_context()->uid == 0) {
        md.uid(uid);
        md.gid(gid);
        write_metadata_field(md.gid(), path_hash, md_field_map.at(Md_fields::gid));
        write_metadata_field(md.uid(), path_hash, md_field_map.at(Md_fields::uid));

#ifdef ACMtime
        md.update_ACM_time(true, true, true);
        write_metadata_field(md.atime(), path_hash, md_field_map.at(Md_fields::atime));
        write_metadata_field(md.ctime(), path_hash, md_field_map.at(Md_fields::ctime));
        write_metadata_field(md.mtime(), path_hash, md_field_map.at(Md_fields::mtime));
#endif
        return 0;
    }
//    auto path_hash = ADAFS_DATA->hashf(path.string());

//    // XXX Users should be able to change the group to whatever groups they're belonging to. For now group can only
//    // XXX be changed to the active group they're belonging to.
//    if (fuse_get_context()->gid != gid)
//        return -EPERM;
//    // if nothing changed, nothing to do
//    if (md.uid() == uid && md.gid() == gid)
//        return 0;
//
//    // root can do anything
//    if (fuse_get_context()->uid == 0) {
//        md.uid(uid);
//        md.gid(gid);
//        write_metadata_field(md.gid(), path_hash, md_field_map.at(Md_fields::gid));
//        write_metadata_field(md.uid(), path_hash, md_field_map.at(Md_fields::uid));
//
//#ifdef ACMtime
//        md.update_ACM_time(true, true, true);
//        write_metadata_field(md.atime(), path_hash, md_field_map.at(Md_fields::atime));
//        write_metadata_field(md.ctime(), path_hash, md_field_map.at(Md_fields::ctime));
//        write_metadata_field(md.mtime(), path_hash, md_field_map.at(Md_fields::mtime));
//#endif
//        return 0;
//    }
    // if we get here, users what to change uid or gid to something else which is not permitted
    return -EPERM;
    return EPERM;
}

+2 −2
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@
#include "../classes/metadata.h"


int chk_access(const Metadata& md, int mask);
int chk_access(const fuse_req_t& req, const Metadata& md, int mask);

int chk_uid(const Metadata& md);
int chk_uid(const fuse_req_t& req, const Metadata& md);

int change_access(Metadata& md, mode_t mode, const bfs::path& path);

+75 −74
Original line number Diff line number Diff line
@@ -8,25 +8,25 @@ using namespace std;

/**
 * Initializes the dentry directory to hold future dentries
 * @param hash
 * @param inode
 * @return
 */
bool init_dentry_dir(const unsigned long& hash) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path);
    d_path /= to_string(hash);
bool init_dentry_dir(const uint64_t inode) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path());
    d_path /= to_string(inode);
    bfs::create_directories(d_path);

    // XXX This might not be needed as it is another access to the underlying file system
    return bfs::exists(d_path);
}

/**
 * Destroys the dentry directory
 * @param hash
 * @param inode
 * @return true if successfully deleted
 */
bool destroy_dentry_dir(const unsigned long& hash) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path);
    d_path /= to_string(hash);
bool destroy_dentry_dir(const uint64_t inode) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path());
    d_path /= to_string(inode);

    // remove dentry dir
    bfs::remove_all(d_path);
@@ -40,16 +40,23 @@ bool destroy_dentry_dir(const unsigned long& hash) {
 * @param fname
 * @return
 */
bool verify_dentry(const bfs::path& path) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path);
    if (path.has_parent_path()) { // non-root
        d_path /= to_string(ADAFS_DATA->hashf(path.parent_path().string()));
        d_path /= path.filename(); // root
    } else {
        d_path /= to_string(ADAFS_DATA->hashf(path.string()));
    }
    // if file path exists leaf name is a valid dentry of parent_dir
    return bfs::exists(d_path);
bool verify_dentry(const uint64_t inode) {
    // XXX do I need this?
    return false;
//    auto d_path = bfs::path(ADAFS_DATA->dentry_path());
//    if (inode != ADAFS_ROOT_INODE) { // non-root
//        d_path /=
//    }
//
//
//    if (inode.has_parent_path()) { // non-root
//        d_path /= to_string(ADAFS_DATA->hashf(inode.parent_path().string()));
//        d_path /= inode.filename(); // root
//    } else {
//        d_path /= to_string(ADAFS_DATA->hashf(inode.string()));
//    }
//    // if file path exists leaf name is a valid dentry of parent_dir
//    return bfs::exists(d_path);
}


@@ -57,84 +64,78 @@ bool verify_dentry(const bfs::path& path) {
 * Reads all directory entries in a directory with a given @hash. Returns 0 if successful.
 * @dir is assumed to be empty
 */
int read_dentries(vector<string>& dir, const unsigned long hash) {
    auto path = bfs::path(ADAFS_DATA->dentry_path);
    path /= to_string(hash);
    if (!bfs::exists(path)) return 1;
    // shortcut if path is empty = no files in directory
    if (bfs::is_empty(path)) return 0;

    // Below can be simplified with a C++11 range based loop? But how? :( XXX
    bfs::directory_iterator end_dir_it;
    for (bfs::directory_iterator dir_it(path); dir_it != end_dir_it; ++dir_it) {
        const bfs::path cp = (*dir_it);
        dir.push_back(cp.filename().string());
    }
int read_dentries(const uint64_t p_inode, const unsigned long inode) {
//    auto path = bfs::path(ADAFS_DATA->dentry_path());
//    path /= to_string(inode);
//    if (!bfs::exists(path)) return 1;
//    // shortcut if path is empty = no files in directory
//    if (bfs::is_empty(path)) return 0;
//
//    // Below can be simplified with a C++11 range based loop? But how? :( XXX
//    bfs::directory_iterator end_dir_it;
//    for (bfs::directory_iterator dir_it(path); dir_it != end_dir_it; ++dir_it) {
//        const bfs::path cp = (*dir_it);
//        p_inode.push_back(cp.filename().string());
//    }
    return 0;
}

/**
 * Creates an empty file in the dentry folder of the parent directory, acting as a dentry for lookup
 * @param parent_dir
 * @param fname
 * @param inode
 * @return
 */
int create_dentry(const unsigned long parent_dir_hash, const string& fname) {
    ADAFS_DATA->logger->debug("create_dentry() enter with fname: {}", fname);
    // XXX Errorhandling
    auto f_path = bfs::path(ADAFS_DATA->dentry_path);
    f_path /= to_string(parent_dir_hash);
    if (!bfs::exists(f_path)) return -ENOENT;

    f_path /= fname;

    bfs::ofstream ofs{f_path};

    // XXX make sure the file has been created
int create_dentry(const unsigned long p_inode, const uint64_t inode) {
//    ADAFS_DATA->logger->debug("create_dentry() enter with fname: {}", inode);
//    // XXX Errorhandling
//    auto f_path = bfs::path(ADAFS_DATA->dentry_path);
//    f_path /= to_string(p_inode);
//    if (!bfs::exists(f_path)) return -ENOENT;
//
//    f_path /= inode;
//
//    bfs::ofstream ofs{f_path};
//
//    // XXX make sure the file has been created

    return 0;
}

/**
 * Removes a dentry from the parent directory
 * @param parent_dir_hash
 * @param fname
 * @param p_inode
 * @param inode
 * @return
 */
// XXX errorhandling
int remove_dentry(const unsigned long parent_dir_hash, const string& fname) {
    auto f_path = bfs::path(ADAFS_DATA->dentry_path);
    f_path /= to_string(parent_dir_hash);
    if (!bfs::exists(f_path)) {
        ADAFS_DATA->logger->error("remove_dentry() dentry_path '{}' not found", f_path.string());
        return -ENOENT;
    }

    f_path /= fname;
    // remove dentry here
    bfs::remove(f_path);

    // XXX make sure dentry has been deleted
int remove_dentry(const unsigned long p_inode, const uint64_t inode) {
//    auto f_path = bfs::path(ADAFS_DATA->dentry_path());
//    f_path /= to_string(p_inode);
//    if (!bfs::exists(f_path)) {
//        ADAFS_DATA->logger->error("remove_dentry() dentry_path '{}' not found", f_path.string());
//        return -ENOENT;
//    }
//
//    f_path /= inode;
//    // remove dentry here
//    bfs::remove(f_path);
//
//    // XXX make sure dentry has been deleted

    return 0;
}

/**
 * Checks if a directory has no dentries, i.e., is empty.
 * @param adafs_path
 * @param inode
 * @return bool
 */
bool is_dir_empty(const bfs::path& adafs_path) {
    auto d_path = bfs::path(ADAFS_DATA->dentry_path);
    // use hash function to path and append it to d_path
    d_path /= to_string(ADAFS_DATA->hashf(adafs_path.string()));

    return bfs::is_empty(d_path);
}

/**
 * wraps is_dir_empty(bfs::path)
 */
bool is_dir_empty(const string& adafs_path) {
    return is_dir_empty(bfs::path(adafs_path));
bool is_dir_empty(const uint64_t inode) {
//    auto d_path = bfs::path(ADAFS_DATA->dentry_path());
//    // use hash function to path and append it to d_path
//    d_path /= to_string(ADAFS_DATA->hashf(inode.string()));
//
//    return bfs::is_empty(d_path);
    return false;
}
+7 −9
Original line number Diff line number Diff line
@@ -7,20 +7,18 @@

#include "../main.h"

bool init_dentry_dir(const unsigned long& hash);
bool init_dentry_dir(const uint64_t inode);

bool destroy_dentry_dir(const unsigned long& hash);
bool destroy_dentry_dir(const uint64_t inode);

bool verify_dentry(const bfs::path& path);
bool verify_dentry(const uint64_t inode);

int read_dentries(std::vector<std::string>& dir, const unsigned long hash);
int read_dentries(const uint64_t p_inode, const unsigned long inode);

int create_dentry(const unsigned long parent_dir_hash, const std::string& fname);
int create_dentry(const unsigned long p_inode, const uint64_t inode);

int remove_dentry(const unsigned long parent_dir_hash, const std::string& fname);
int remove_dentry(const unsigned long p_inode, const uint64_t inode);

bool is_dir_empty(const bfs::path& adafs_path);

bool is_dir_empty(const std::string& adafs_path);
bool is_dir_empty(const uint64_t inode);

#endif //FS_DENTRY_OPS_H
Loading