mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-25 18:40:18 -04:00
When file unlinked check open_files map
Inadvertantly removed. Didn't realize that the kernel would not always send file handle when unlinked.
This commit is contained in:
parent
7616d8b1ac
commit
9308a3729c
16 changed files with 140 additions and 36 deletions
|
|
@ -21,6 +21,7 @@
|
|||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_copy_file_range.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -57,11 +58,14 @@ FUSE::copy_file_range(const fuse_req_ctx_t *ctx_,
|
|||
const size_t size_,
|
||||
const unsigned int flags_)
|
||||
{
|
||||
FileInfo *src_fi = FileInfo::from_fh(src_ffi_->fh);
|
||||
FileInfo *dst_fi = FileInfo::from_fh(dst_ffi_->fh);
|
||||
FileInfo *src_fi;
|
||||
FileInfo *dst_fi;
|
||||
|
||||
src_fi = state.get_fi(ctx_,src_ffi_->fh);
|
||||
if(not src_fi)
|
||||
return -EBADF;
|
||||
|
||||
dst_fi = state.get_fi(ctx_,dst_ffi_->fh);
|
||||
if(not dst_fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,13 @@ _fallocate(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::fallocate(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
|
|
@ -48,8 +55,9 @@ FUSE::fallocate(const fuse_req_ctx_t *ctx_,
|
|||
off_t offset_,
|
||||
off_t len_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,21 @@ _fchmod(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::fchmod(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,22 @@ _fchown(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::fchown(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,13 @@ _fgetattr(const FileInfo *fi_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::fgetattr(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
|
|
@ -54,13 +60,11 @@ FUSE::fgetattr(const fuse_req_ctx_t *ctx_,
|
|||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
int rv;
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
{
|
||||
timeout_->entry = cfg.cache_negative_entry;
|
||||
return -EBADF;
|
||||
}
|
||||
return -EBADF;
|
||||
|
||||
rv = ::_fgetattr(fi,st_);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,21 @@ _flock(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::flock(const fuse_req_ctx_t *ctx_,
|
||||
const fuse_file_info_t *ffi_,
|
||||
int op_)
|
||||
{
|
||||
FileInfo* fi = FileInfo::from_fh(ffi_->fh);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "fileinfo.hpp"
|
||||
#include "fs_close.hpp"
|
||||
#include "fs_dup.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -39,12 +40,20 @@ _flush(const int fd_)
|
|||
return fs::close(rv);
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::flush(const fuse_req_ctx_t *ctx_,
|
||||
const fuse_file_info_t *ffi_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "fileinfo.hpp"
|
||||
#include "fs_fdatasync.hpp"
|
||||
#include "fs_fsync.hpp"
|
||||
#include "state.hpp"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
|
@ -44,13 +45,21 @@ _fsync(const int fd_,
|
|||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::fsync(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
int isdatasync_)
|
||||
cu64 fh_,
|
||||
int isdatasync_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,21 @@ _ftruncate(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::ftruncate(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
off_t size_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,13 +40,21 @@ _futimens(const int fd_,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::futimens(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_,
|
||||
const struct timespec ts_[2])
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,8 +114,9 @@ _ioctl_file(const fuse_req_ctx_t *ctx_,
|
|||
void *data_,
|
||||
u32 *out_bufsz_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "fileinfo.hpp"
|
||||
#include "fs_pread.hpp"
|
||||
#include "ioprio.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -65,8 +66,9 @@ FUSE::read(const fuse_req_ctx_t *ctx_,
|
|||
off_t offset_)
|
||||
{
|
||||
ioprio::SetFrom iop(ctx_->pid);
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -263,8 +263,9 @@ FUSE::statx_fh(const fuse_req_ctx_t *ctx_,
|
|||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
int rv;
|
||||
FileInfo *fi = FileInfo::from_fh(fh_);
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,fh_);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "fs_pwrite.hpp"
|
||||
#include "fs_pwriten.hpp"
|
||||
#include "ioprio.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
#include "scope_guard/scope_guard.hpp"
|
||||
|
||||
|
|
@ -160,14 +161,15 @@ _write_cached(const char *buf_,
|
|||
|
||||
static
|
||||
int
|
||||
_write(const fuse_file_info_t *ffi_,
|
||||
_write(const fuse_req_ctx_t *ctx_,
|
||||
const fuse_file_info_t *ffi_,
|
||||
const char *buf_,
|
||||
const size_t count_,
|
||||
const off_t offset_)
|
||||
{
|
||||
FileInfo *fi;
|
||||
|
||||
fi = FileInfo::from_fh(ffi_->fh);
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
|
|
@ -180,8 +182,7 @@ _write(const fuse_file_info_t *ffi_,
|
|||
// could change the move file behavior to use a known target file
|
||||
// and have threads use O_EXCL and back off and wait for the
|
||||
// transfer to complete before retrying.
|
||||
mutex_lock(fi->mutex);
|
||||
DEFER { mutex_unlock(fi->mutex); };
|
||||
mutex_lockguard(fi->mutex);
|
||||
|
||||
if(fi->direct_io)
|
||||
return ::_write_direct_io(buf_,count_,offset_,fi);
|
||||
|
|
@ -198,7 +199,7 @@ FUSE::write(const fuse_req_ctx_t *ctx_,
|
|||
{
|
||||
ioprio::SetFrom iop(ctx_->pid);
|
||||
|
||||
return ::_write(ffi_,buf_,count_,offset_);
|
||||
return ::_write(ctx_,ffi_,buf_,count_,offset_);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
|||
|
|
@ -115,6 +115,24 @@ public:
|
|||
FileInfo *fi;
|
||||
};
|
||||
|
||||
public:
|
||||
FileInfo*
|
||||
get_fi(const fuse_req_ctx_t *ctx_,
|
||||
cu64 fh_) const
|
||||
{
|
||||
FileInfo *fi;
|
||||
|
||||
fi = FileInfo::from_fh(fh_);
|
||||
if(not fi)
|
||||
open_files.cvisit(ctx_->nodeid,
|
||||
[&](auto &v_)
|
||||
{
|
||||
fi = v_.second.fi;
|
||||
});
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
public:
|
||||
struct GetSet
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1498,10 +1498,8 @@ fuse_lib_getattr(fuse_req_t *req_,
|
|||
{
|
||||
if(fusepath != NULL)
|
||||
err = f.ops.getattr(&req_->ctx,&fusepath[1],&buf,&timeouts);
|
||||
else if(fh != 0)
|
||||
err = f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts);
|
||||
else
|
||||
err = -ENOENT;
|
||||
err = f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts);
|
||||
|
||||
free_path(hdr_->nodeid,fusepath);
|
||||
}
|
||||
|
|
@ -1538,20 +1536,29 @@ fuse_lib_statx_path(fuse_req_t *req_,
|
|||
struct fuse_statx st{};
|
||||
fuse_timeouts_t timeouts{};
|
||||
|
||||
// TODO: if node unlinked... but FUSE may not send it
|
||||
err = get_path(hdr_->nodeid,&fusepath);
|
||||
if(err == -ESTALE)
|
||||
err = 0;
|
||||
if(err)
|
||||
{
|
||||
fuse_reply_err(req_,err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = f.ops.statx(&req_->ctx,
|
||||
&fusepath[1],
|
||||
inarg_->sx_flags,
|
||||
inarg_->sx_mask,
|
||||
&st,
|
||||
&timeouts);
|
||||
if(fusepath != NULL)
|
||||
err = f.ops.statx(&req_->ctx,
|
||||
&fusepath[1],
|
||||
inarg_->sx_flags,
|
||||
inarg_->sx_mask,
|
||||
&st,
|
||||
&timeouts);
|
||||
else
|
||||
err = f.ops.statx_fh(&req_->ctx,
|
||||
0,
|
||||
inarg_->sx_flags,
|
||||
inarg_->sx_mask,
|
||||
&st,
|
||||
&timeouts);
|
||||
|
||||
free_path(hdr_->nodeid,fusepath);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue