2014-05-12 12:41:46 -04:00
|
|
|
/*
|
2026-02-23 13:22:21 -06:00
|
|
|
ISC License
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2026, Antonio SJ Musumeci <trapexit@spawn.link>
|
2016-01-14 16:50:22 -05:00
|
|
|
|
|
|
|
|
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.
|
2014-05-12 12:41:46 -04:00
|
|
|
*/
|
|
|
|
|
|
2026-01-19 15:16:01 -06:00
|
|
|
/*
|
2026-04-02 14:54:46 -05:00
|
|
|
RELEASE OF SHARED AND PER-HANDLE FILEINFO OBJECTS
|
|
|
|
|
=================================================
|
|
|
|
|
|
|
|
|
|
The open_files map stores one canonical FileInfo per nodeid together with a
|
|
|
|
|
ref_count and optional passthrough backing_id. Overlapping opens of the same
|
|
|
|
|
file increment that ref_count but still allocate their own FileInfo objects
|
|
|
|
|
with duplicated file descriptors for the individual FUSE handles.
|
|
|
|
|
|
|
|
|
|
That means release() must handle two ownership cases:
|
|
|
|
|
- the canonical FileInfo stored in open_files
|
|
|
|
|
- a per-handle FileInfo that exists only in fuse_file_info_t.fh
|
|
|
|
|
|
|
|
|
|
On non-final closes, secondary handle FileInfo objects are freed
|
|
|
|
|
immediately after the ref_count decrement while the canonical map entry stays
|
|
|
|
|
alive.
|
|
|
|
|
|
|
|
|
|
On the final close, the map entry is erased atomically and cleanup happens
|
|
|
|
|
after the erase_if() callback returns so close() and delete do not execute
|
|
|
|
|
while the map bucket is locked. If the final handle is different from the
|
|
|
|
|
canonical map-owned FileInfo, both objects must be released.
|
2026-01-19 15:16:01 -06:00
|
|
|
*/
|
|
|
|
|
|
2025-07-25 17:53:48 -05:00
|
|
|
#include "fuse_release.hpp"
|
|
|
|
|
|
2025-06-05 23:47:42 -05:00
|
|
|
#include "state.hpp"
|
|
|
|
|
|
2017-02-18 13:45:43 -05:00
|
|
|
#include "config.hpp"
|
2015-09-15 18:49:18 -04:00
|
|
|
#include "fileinfo.hpp"
|
2020-08-17 16:29:22 -04:00
|
|
|
#include "fs_close.hpp"
|
|
|
|
|
#include "fs_fadvise.hpp"
|
2025-06-05 23:47:42 -05:00
|
|
|
#include "fuse_passthrough.hpp"
|
2015-09-15 18:49:18 -04:00
|
|
|
|
2020-12-19 15:06:08 -05:00
|
|
|
#include "fuse.h"
|
2019-01-06 12:52:55 -05:00
|
|
|
|
2017-02-18 13:45:43 -05:00
|
|
|
|
2025-06-05 23:47:42 -05:00
|
|
|
static
|
|
|
|
|
int
|
2026-01-19 15:16:01 -06:00
|
|
|
_release(cu64 nodeid_,
|
|
|
|
|
FileInfo *fi_,
|
|
|
|
|
const bool dropcacheonclose_)
|
2025-06-05 23:47:42 -05:00
|
|
|
{
|
2026-04-02 14:54:46 -05:00
|
|
|
// The double fadvise is necessary insofar as according to nocache
|
|
|
|
|
// author (https://github.com/Feh/nocache#limitations) the first one
|
|
|
|
|
// doesn't always work due to timing issues.
|
2025-06-05 23:47:42 -05:00
|
|
|
if(dropcacheonclose_)
|
|
|
|
|
{
|
|
|
|
|
fs::fadvise_dontneed(fi_->fd);
|
|
|
|
|
fs::fadvise_dontneed(fi_->fd);
|
|
|
|
|
}
|
2014-05-12 12:41:46 -04:00
|
|
|
|
2026-01-19 15:16:01 -06:00
|
|
|
FUSE::release(nodeid_,fi_);
|
2025-06-05 23:47:42 -05:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 15:16:01 -06:00
|
|
|
void
|
|
|
|
|
FUSE::release(cu64 nodeid_,
|
|
|
|
|
FileInfo * const fi_)
|
2025-06-05 23:47:42 -05:00
|
|
|
{
|
2026-04-02 14:54:46 -05:00
|
|
|
auto &of = state.open_files;
|
|
|
|
|
int backing_id = INVALID_BACKING_ID;
|
|
|
|
|
FileInfo *canonical_fi = nullptr;
|
|
|
|
|
FileInfo *fh_fi_to_free = nullptr;
|
2026-01-19 15:16:01 -06:00
|
|
|
|
2026-05-13 11:30:39 -05:00
|
|
|
// memory_order_acq_rel on the decrement gives us the canonical
|
|
|
|
|
// refcount-publication pattern:
|
|
|
|
|
// - release: prior writes to the OpenFile by this thread are
|
|
|
|
|
// visible to whichever thread observes the final 0.
|
|
|
|
|
// - acquire: the thread that drops the last ref acquires every
|
|
|
|
|
// prior ref holder's writes before tearing down the entry.
|
|
|
|
|
// The concurrent_flat_map bucket lock already serializes concurrent
|
|
|
|
|
// visitors of this key so the acq_rel is good for any observer that
|
|
|
|
|
// might examine ref_count outside a visit().
|
2026-01-19 15:16:01 -06:00
|
|
|
of.erase_if(nodeid_,
|
|
|
|
|
[&](auto &v_)
|
|
|
|
|
{
|
2026-05-13 11:30:39 -05:00
|
|
|
const int prev =
|
|
|
|
|
v_.second.ref_count.fetch_sub(1,std::memory_order_acq_rel);
|
|
|
|
|
if(prev > 1)
|
2026-01-19 15:16:01 -06:00
|
|
|
{
|
|
|
|
|
if(fi_ != v_.second.fi)
|
2026-04-02 14:54:46 -05:00
|
|
|
fh_fi_to_free = fi_;
|
2026-01-19 15:16:01 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 14:54:46 -05:00
|
|
|
canonical_fi = v_.second.fi;
|
2026-01-19 15:16:01 -06:00
|
|
|
backing_id = v_.second.backing_id;
|
2026-04-02 14:54:46 -05:00
|
|
|
if(fi_ != canonical_fi)
|
|
|
|
|
fh_fi_to_free = fi_;
|
2026-01-19 15:16:01 -06:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
FUSE::passthrough_close(backing_id);
|
2026-04-02 14:54:46 -05:00
|
|
|
if(fh_fi_to_free)
|
2026-01-19 15:16:01 -06:00
|
|
|
{
|
2026-04-02 14:54:46 -05:00
|
|
|
fs::close(fh_fi_to_free->fd);
|
|
|
|
|
delete fh_fi_to_free;
|
|
|
|
|
}
|
|
|
|
|
if(canonical_fi)
|
|
|
|
|
{
|
|
|
|
|
fs::close(canonical_fi->fd);
|
|
|
|
|
delete canonical_fi;
|
2026-01-19 15:16:01 -06:00
|
|
|
}
|
2014-05-12 12:41:46 -04:00
|
|
|
}
|
|
|
|
|
|
2025-07-25 17:53:48 -05:00
|
|
|
int
|
2025-10-13 00:21:36 -04:00
|
|
|
FUSE::release(const fuse_req_ctx_t *ctx_,
|
|
|
|
|
const fuse_file_info_t *ffi_)
|
2014-05-12 12:41:46 -04:00
|
|
|
{
|
2026-01-19 15:16:01 -06:00
|
|
|
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
|
|
|
|
|
FUSE: require valid file/dir handle; drop open_files fallback
Several FUSE ops fell back to looking up a FileInfo via
state.open_files when the kernel-provided fh was zero, returning
-ENOENT if the lookup failed. This path papered over handle-tracking
bugs and added cost on every call. Use the handle directly and
return -EBADF when it is invalid - the kernel should always hand us
a valid fh for an open resource.
Also simplify FileInfo::from_fh / DirInfo::from_fh and their to_fh
helpers: callers now handle the null-pointer case explicitly so the
ASSERT_NOT_NULL checks are redundant.
Covers fallocate, fchmod, fchown, fgetattr, flock, ftruncate,
futimens, read, release, releasedir, write, statx, ioctl (file +
dir), readdir (sequential, concurrent-on-read,
concurrent-on-search), and the passthrough fast paths in
create/open.
2026-04-18 00:53:11 -04:00
|
|
|
if(not fi)
|
|
|
|
|
return -EBADF;
|
|
|
|
|
|
2026-01-19 15:16:01 -06:00
|
|
|
return ::_release(ctx_->nodeid,
|
|
|
|
|
fi,
|
|
|
|
|
cfg.dropcacheonclose);
|
2014-05-12 12:41:46 -04:00
|
|
|
}
|