#include "func_create_newest.hpp" #include "errno.hpp" #include "fs_clonepath.hpp" #include "fs_exists.hpp" #include "fs_info.hpp" #include "fs_info_t.hpp" #include "fs_path.hpp" #include "fileinfo.hpp" #include "fs_acl.hpp" #include "fs_open_as.hpp" #include "timespec_utils.hpp" #include #define error_and_continue(CUR,ERR) \ do { \ ::_calc_error(CUR,ERR); \ continue; \ } while(0) static inline void _calc_error(int &cur_, const int err_) { switch(cur_) { default: case ENOENT: cur_ = err_; break; case ENOSPC: if(err_ != ENOENT) cur_ = err_; break; case EROFS: if((err_ != ENOENT) && (err_ != ENOSPC)) cur_ = err_; break; } } std::string_view Func2::CreateNewest::name() const { return "newest"; } bool Func2::CreateNewest::path_preserving() const { return false; } int Func2::CreateNewest::operator()(const ugid_t &ugid_, const Branches &branches_, const fs::path &fusepath_, fuse_file_info_t *&ffi_, const mode_t &mode_, const mode_t &umask_) { int rv; int error; struct stat st; timespec newest; fs::info_t info; Branches::Ptr branches; const Branch *chosen; const Branch *clone_src; int fd; fs::path fullpath; mode_t mode; FileInfo *fi; branches = branches_; chosen = nullptr; clone_src = nullptr; error = ENOENT; newest = {0,0}; for(auto &branch : *branches) { if(branch.ro_or_nc()) error_and_continue(error,EROFS); if(!fs::exists(branch.path,fusepath_,&st)) error_and_continue(error,ENOENT); if(chosen && (st.st_mtim < newest)) continue; rv = fs::info(branch.path,&info); if(rv < 0) error_and_continue(error,ENOENT); if(info.readonly) error_and_continue(error,EROFS); if(info.spaceavail < branch.minfreespace()) error_and_continue(error,ENOSPC); newest = st.st_mtim; chosen = &branch; } if(!chosen) return -error; for(auto &branch : *branches) { if(!fs::exists(branch.path,fusepath_.parent_path())) continue; clone_src = &branch; break; } if(!clone_src) return -ENOENT; if(clone_src != chosen) { rv = fs::clonepath(clone_src->path,chosen->path,fusepath_.parent_path()); if(rv < 0) return rv; } fullpath = chosen->path / fusepath_; mode = mode_; if(!fs::acl::dir_has_defaults(fullpath)) mode &= ~umask_; fd = fs::open_as(ugid_,fullpath,ffi_->flags,mode); if(fd < 0) return fd; fi = new FileInfo(fd,chosen,fusepath_,ffi_->direct_io); ffi_->fh = fi->to_fh(); return 0; }