diff --git a/src/create.cpp b/src/create.cpp index 76586a53..57ede074 100644 --- a/src/create.cpp +++ b/src/create.cpp @@ -60,7 +60,7 @@ _create_core(const string &existingpath, if(rv == -1) return -errno; - fh = reinterpret_cast(new FileInfo(rv)); + fh = reinterpret_cast(new FileInfo(rv,flags)); return 0; } diff --git a/src/fileinfo.hpp b/src/fileinfo.hpp index 1eb0b178..5eaf663f 100644 --- a/src/fileinfo.hpp +++ b/src/fileinfo.hpp @@ -20,13 +20,16 @@ class FileInfo { public: - FileInfo(int _fd) : - fd(_fd) + FileInfo(const int _fd, + const int _flags) : + fd(_fd), + flags(_flags) { } public: int fd; + int flags; }; #endif diff --git a/src/fs_path.hpp b/src/fs_path.hpp index 9a79c3be..836a3c38 100644 --- a/src/fs_path.hpp +++ b/src/fs_path.hpp @@ -38,14 +38,22 @@ namespace fs base += suffix; } + inline + void + make(const string &base, + const char *suffix, + string &output) + { + output = base + suffix; + } + inline void make(const string *base, const char *suffix, string &output) { - output = *base; - output += suffix; + output = *base + suffix; } } }; diff --git a/src/mutex.hpp b/src/mutex.hpp new file mode 100644 index 00000000..23704b78 --- /dev/null +++ b/src/mutex.hpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2016, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef __MUTEX_HPP__ +#define __MUTEX_HPP__ + +#include + +namespace mergerfs +{ + namespace mutex + { + class Guard + { + public: + Guard(pthread_mutex_t &lock) + : _lock(&lock) + { + pthread_mutex_lock(_lock); + } + + ~Guard() + { + pthread_mutex_unlock(_lock); + } + + private: + Guard(); + + private: + pthread_mutex_t *_lock; + }; + } +} + +#endif diff --git a/src/open.cpp b/src/open.cpp index d51a99d1..4c9f8e18 100644 --- a/src/open.cpp +++ b/src/open.cpp @@ -49,7 +49,7 @@ _open_core(const string *basepath, if(fd == -1) return -errno; - fh = reinterpret_cast(new FileInfo(fd)); + fh = reinterpret_cast(new FileInfo(fd,flags)); return 0; } diff --git a/src/read.cpp b/src/read.cpp index e741e777..e0ae36e2 100644 --- a/src/read.cpp +++ b/src/read.cpp @@ -17,12 +17,22 @@ #include #include +#include #include +#include "config.hpp" #include "errno.hpp" #include "fileinfo.hpp" +#include "fs_base_close.hpp" +#include "fs_base_open.hpp" #include "fs_base_read.hpp" +#include "fs_path.hpp" +#include "mutex.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +using namespace mergerfs; static int @@ -38,6 +48,76 @@ _read(const int fd, return ((rv == -1) ? -errno : rv); } +static +bool +_can_failover(const int error, + const int flags) +{ + return (((error == -EIO) || + (error == -ENOTCONN)) && + !(flags & (O_CREAT|O_TRUNC))); +} + +static +int +_failover_read_loop(const std::vector &basepaths, + const char *fusepath, + char *buf, + const size_t count, + const off_t offset, + FileInfo *fi, + const int error) +{ + int fd; + int rv; + std::string fullpath; + + rv = fs::pread(fi->fd,buf,count,offset); + if(rv >= 0) + return rv; + + for(size_t i = 0, ei = basepaths.size(); i != ei; i++) + { + fs::path::make(basepaths[i],fusepath,fullpath); + + fd = fs::open(fullpath,fi->flags); + if(fd == -1) + continue; + + rv = fs::pread(fd,buf,count,offset); + if(rv >= 0) + { + fs::close(fi->fd); + fi->fd = fd; + return rv; + } + + fs::close(fd); + } + + return error; +} + +static +int +_failover_read(const char *fusepath, + char *buf, + const size_t count, + const off_t offset, + FileInfo *fi, + const int error) +{ + static pthread_mutex_t failover_lock = PTHREAD_MUTEX_INITIALIZER; + + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard read_guard(&config.srcmountslock); + const mutex::Guard failover_guard(failover_lock); + + return _failover_read_loop(config.srcmounts,fusepath,buf,count,offset,fi,error); +} + namespace mergerfs { namespace fuse @@ -49,12 +129,14 @@ namespace mergerfs off_t offset, fuse_file_info *ffi) { + int rv; FileInfo *fi = reinterpret_cast(ffi->fh); - return _read(fi->fd, - buf, - count, - offset); + rv = _read(fi->fd,buf,count,offset); + if(_can_failover(rv,fi->flags)) + rv = _failover_read(fusepath,buf,count,offset,fi,rv); + + return rv; } } } diff --git a/src/read_buf.cpp b/src/read_buf.cpp index 58b49a0c..b5df4774 100644 --- a/src/read_buf.cpp +++ b/src/read_buf.cpp @@ -16,13 +16,26 @@ #if READ_BUF +#include + #include #include #include +#include +#include "config.hpp" #include "errno.hpp" #include "fileinfo.hpp" +#include "fs_base_close.hpp" +#include "fs_base_open.hpp" +#include "fs_base_read.hpp" +#include "fs_path.hpp" +#include "mutex.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +using namespace mergerfs; typedef struct fuse_bufvec fuse_bufvec; @@ -33,7 +46,9 @@ _read_buf(const int fd, const size_t size, const off_t offset) { + int rv; fuse_bufvec *src; + void *buf; src = (fuse_bufvec*)malloc(sizeof(fuse_bufvec)); if(src == NULL) @@ -41,15 +56,100 @@ _read_buf(const int fd, *src = FUSE_BUFVEC_INIT(size); - src->buf->flags = (fuse_buf_flags)(FUSE_BUF_IS_FD|FUSE_BUF_FD_SEEK|FUSE_BUF_FD_RETRY); - src->buf->fd = fd; - src->buf->pos = offset; + buf = malloc(size); + if(buf == NULL) + return -ENOMEM; + + rv = fs::pread(fd,buf,size,offset); + if(rv == -1) + return -errno; + + src->buf->mem = buf; + src->buf->size = rv; *bufp = src; return 0; } +static +bool +_can_failover(const int error, + const int flags) +{ + return (((error == -EIO) || + (error == -ENOTCONN)) && + !(flags & (O_CREAT|O_TRUNC))); +} + +static +int +_open_file_and_check(const std::string &fullpath, + const int flags) +{ + int fd; + int rv; + + fd = fs::open(fullpath,flags); + if(fd == -1) + return -1; + + rv = fs::pread(fd,&rv,1,0); + if(rv == -1) + return (fs::close(fd),-1); + + return fd; +} + +static +int +_failover_read_buf_loop(const std::vector &basepaths, + const char *fusepath, + fuse_bufvec **bufp, + const size_t size, + const off_t offset, + FileInfo *fi) +{ + int fd; + std::string fullpath; + + for(size_t i = 0, ei = basepaths.size(); i != ei; i++) + { + fs::path::make(&basepaths[i],fusepath,fullpath); + + fd = _open_file_and_check(fullpath,fi->flags); + if(fd == -1) + continue; + + fs::close(fi->fd); + fi->fd = fd; + + return _read_buf(fd,bufp,size,offset); + } + + return -EIO; +} + +static +int +_failover_read_buf(const char *fusepath, + fuse_bufvec **bufp, + const size_t size, + const off_t offset, + FileInfo *fi) +{ + static pthread_mutex_t failover_lock = PTHREAD_MUTEX_INITIALIZER; + + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard read_guard(&config.srcmountslock); + const mutex::Guard failover_guard(failover_lock); + + return _failover_read_buf_loop(config.srcmounts,fusepath,bufp,size,offset,fi); +} + + namespace mergerfs { namespace fuse @@ -61,12 +161,14 @@ namespace mergerfs off_t offset, fuse_file_info *ffi) { + int rv; FileInfo *fi = reinterpret_cast(ffi->fh); - return _read_buf(fi->fd, - bufp, - size, - offset); + rv = _read_buf(fi->fd,bufp,size,offset); + if(_can_failover(rv,fi->flags)) + rv = _failover_read_buf(fusepath,bufp,size,offset,fi); + + return rv; } } }