libfuse: harden fuse.cpp against NULL nodes; use lock guards; check allocations

Collapse get_node/get_node_nocheck into a single function that
returns NULL when the node is absent rather than abort()ing.
Callers that previously assumed non-NULL now check the return value
and either reply ESTALE/ENOMEM or fall through gracefully; a few
places where the node is load-bearing (e.g. unhash during remove)
use fatal::abort with a descriptive message.

Switch mutex_lock / mutex_unlock pairs to mutex_lockguard so early
returns on error paths no longer leak the lock, and add NULL-safety
around fuse_dh in readdir/readdir_plus, fuse_dirents_init error
returns, the getattr path when both name and fh are empty, and the
msgbuf allocation in fuse_lib_read.

Pull the new xstrdup/xmalloc wrappers from xalloc.hpp for the root
node name so the common-case OOM path terminates with a diagnostic
instead of a silent corrupted node.

Also guard fuse_session_destroy against a NULL session - called
from error-recovery paths.
This commit is contained in:
Antonio S. Musumeci 2026-04-18 01:00:11 -04:00
parent 3c12ea3a5f
commit 148cc7fa02
3 changed files with 358 additions and 158 deletions

View file

@ -6,7 +6,6 @@
See the file COPYING.LIB
*/
/* For pthread_rwlock_t */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@ -15,9 +14,11 @@
#include "crc32b.h"
#include "debug.hpp"
#include "fatal.hpp"
#include "kvec.h"
#include "mutex.hpp"
#include "node.hpp"
#include "xalloc.hpp"
#include "fuse_cfg.hpp"
#include "fuse_req.hpp"
@ -188,7 +189,7 @@ id_hash(uint64_t ino)
static
node_t*
get_node_nocheck(uint64_t nodeid)
get_node(const uint64_t nodeid)
{
size_t hash = id_hash(nodeid);
node_t *node;
@ -200,22 +201,6 @@ get_node_nocheck(uint64_t nodeid)
return NULL;
}
static
node_t*
get_node(const uint64_t nodeid)
{
node_t *node = get_node_nocheck(nodeid);
if(!node)
{
fprintf(stderr,"fuse internal error: node %llu not found\n",
(unsigned long long)nodeid);
abort();
}
return node;
}
static
void
remove_remembered_node(node_t *node_)
@ -290,10 +275,11 @@ node_table_reduce(struct node_table *t)
return;
newarray = realloc(t->array,sizeof(node_t*) * newsize);
if(newarray != NULL)
t->array = (node_t**)newarray;
if(newarray == NULL)
return;
t->size = newsize;
t->array = (node_t**)newarray;
t->size = newsize;
t->split = t->size / 2;
}
@ -471,33 +457,32 @@ static
void
unhash_name(node_t *node)
{
if(node->name)
if(not (node && node->name && node->parent))
return;
size_t hash = name_hash(node->parent->nodeid,node->name);
node_t **nodep = &f.name_table.array[hash];
for(; *nodep != NULL; nodep = &(*nodep)->name_next)
{
size_t hash = name_hash(node->parent->nodeid,node->name);
node_t **nodep = &f.name_table.array[hash];
if(*nodep != node)
continue;
for(; *nodep != NULL; nodep = &(*nodep)->name_next)
if(*nodep == node)
{
*nodep = node->name_next;
node->name_next = NULL;
unref_node(node->parent);
free(node->name);
node->name = NULL;
node->parent = NULL;
f.name_table.use--;
*nodep = node->name_next;
node->name_next = NULL;
unref_node(node->parent);
free(node->name);
node->name = NULL;
node->parent = NULL;
f.name_table.use--;
if(f.name_table.use < f.name_table.size / 4)
remerge_name();
return;
}
fprintf(stderr,
"fuse internal error: unable to unhash node: %llu\n",
(unsigned long long)node->nodeid);
abort();
if(f.name_table.use < f.name_table.size / 4)
remerge_name();
return;
}
fatal::abort("mergerfs: unable to unhash node: {}",
node->nodeid);
}
static
@ -517,6 +502,12 @@ rehash_name()
for(nodep = &t->array[hash]; *nodep != NULL; nodep = next)
{
node_t *node = *nodep;
if(node->parent == NULL)
{
next = &node->name_next;
continue;
}
size_t newhash = name_hash(node->parent->nodeid,node->name);
if(newhash != hash)
@ -542,8 +533,15 @@ hash_name(node_t *node,
uint64_t parentid,
const char *name)
{
size_t hash = name_hash(parentid,name);
node_t *parent = get_node(parentid);
size_t hash;
node_t *parent;
hash = name_hash(parentid,name);
parent = get_node(parentid);
if(parent == NULL)
return -1;
node->name = strdup(name);
if(node->name == NULL)
return -1;
@ -634,12 +632,18 @@ find_node(uint64_t parent,
const char *name)
{
node_t *node;
mutex_lockguard(f.lock);
mutex_lock(f.lock);
if(!name)
node = get_node(parent);
{
node = get_node(parent);
if(node == NULL)
goto out_err;
}
else
node = lookup_node(parent,name);
{
node = lookup_node(parent,name);
}
if(node == NULL)
{
@ -664,8 +668,8 @@ find_node(uint64_t parent,
remove_remembered_node(node);
}
inc_nlookup(node);
out_err:
mutex_unlock(f.lock);
return node;
}
@ -723,8 +727,11 @@ unlock_path(uint64_t nodeid,
wnode->treelock = 0;
}
for(node = get_node(nodeid);
node != end && node->nodeid != FUSE_ROOT_ID;
node = get_node(nodeid);
if(node == NULL)
return;
for(;node != end && node->nodeid != FUSE_ROOT_ID;
node = node->parent)
{
assert(node->treelock != 0);
@ -794,7 +801,14 @@ try_get_path(uint64_t nodeid,
}
}
for(node = get_node(nodeid); node->nodeid != FUSE_ROOT_ID; node = node->parent)
node = get_node(nodeid);
if(node == NULL)
{
err = -ESTALE;
goto out_unlock;
}
for(; node->nodeid != FUSE_ROOT_ID; node = node->parent)
{
err = -ESTALE;
if(node->name == NULL || node->parent == NULL)
@ -874,7 +888,8 @@ queue_element_wakeup(struct lock_queue_element *qe)
if(!qe->path1)
{
/* Just waiting for it to be unlocked */
if(get_node(qe->nodeid1)->treelock == 0)
node_t *qnode = get_node(qe->nodeid1);
if(qnode && (qnode->treelock == 0))
pthread_cond_signal(&qe->cond);
return;
@ -1102,8 +1117,11 @@ forget_node(const uint64_t nodeid,
if(nodeid == FUSE_ROOT_ID)
return;
mutex_lock(f.lock);
mutex_lockguard(f.lock);
node = get_node(nodeid);
if(node == NULL)
return;
/*
* Node may still be locked due to interrupt idiocy in open,
@ -1142,8 +1160,6 @@ forget_node(const uint64_t nodeid,
kv_push(remembered_node_t,f.remembered_nodes,fn);
}
mutex_unlock(f.lock);
}
static
@ -1352,29 +1368,34 @@ fuse_lib_lookup(fuse_req_t *req_,
if(name[1] == '\0')
{
name = NULL;
mutex_lock(f.lock);
dot = get_node_nocheck(nodeid);
mutex_lockguard(f.lock);
dot = get_node(nodeid);
if(dot == NULL)
{
mutex_unlock(f.lock);
reply_entry(req_,&e,-ESTALE);
return;
}
dot->refctr++;
mutex_unlock(f.lock);
}
else if((name[1] == '.') && (name[2] == '\0'))
{
if(nodeid == 1)
if(nodeid == FUSE_ROOT_ID)
{
reply_entry(req_,&e,-ENOENT);
return;
}
name = NULL;
mutex_lock(f.lock);
nodeid = get_node(nodeid)->parent->nodeid;
mutex_unlock(f.lock);
mutex_lockguard(f.lock);
{
node_t *pnode = get_node(nodeid);
if((pnode == NULL) || (pnode->parent == NULL))
{
reply_entry(req_,&e,-ESTALE);
return;
}
nodeid = pnode->parent->nodeid;
}
}
}
@ -1475,19 +1496,28 @@ fuse_lib_getattr(fuse_req_t *req_,
if(!err)
{
err = ((fusepath != NULL) ?
f.ops.getattr(&req_->ctx,&fusepath[1],&buf,&timeouts) :
f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts));
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;
free_path(hdr_->nodeid,fusepath);
}
if(!err)
{
mutex_lock(f.lock);
node = get_node(hdr_->nodeid);
update_stat(node,&buf);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node = get_node(hdr_->nodeid);
if(node == NULL)
{
fuse_reply_err(req_,ESTALE);
return;
}
update_stat(node,&buf);
}
set_stat(hdr_->nodeid,&buf);
fuse_reply_attr(req_,&buf,timeouts.attr);
}
@ -1653,9 +1683,15 @@ fuse_lib_setattr(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
update_stat(get_node(hdr_->nodeid),&stbuf);
mutex_unlock(f.lock);
{
node_t *node;
mutex_lockguard(f.lock);
node = get_node(hdr_->nodeid);
if(node)
update_stat(node,&stbuf);
}
set_stat(hdr_->nodeid,&stbuf);
fuse_reply_attr(req_,&stbuf,timeouts.attr);
}
@ -1743,19 +1779,18 @@ fuse_lib_mknod(fuse_req_t *req_,
err = -ENOSYS;
if(S_ISREG(arg->mode))
{
fuse_file_info_t fi;
fuse_file_info_t ffi = {};
memset(&fi,0,sizeof(fi));
fi.flags = O_CREAT | O_EXCL | O_WRONLY;
ffi.flags = O_CREAT | O_EXCL | O_WRONLY;
err = f.ops.create(&req_->ctx,
&fusepath[1],
arg->mode,
&fi);
&ffi);
if(!err)
{
err = lookup_path(req_,hdr_->nodeid,name,fusepath,&e,&fi);
err = lookup_path(req_,hdr_->nodeid,name,fusepath,&e,&ffi);
f.ops.release(&req_->ctx,
&fi);
&ffi);
}
}
@ -1996,15 +2031,17 @@ fuse_do_release(fuse_req_ctx_t *req_ctx_,
f.ops.release(req_ctx_,
ffi_);
mutex_lock(f.lock);
mutex_lockguard(f.lock);
{
node_t *node;
node = get_node(ino_);
assert(node->open_count > 0);
node->open_count--;
if(node)
{
assert(node->open_count > 0);
node->open_count--;
}
}
mutex_unlock(f.lock);
}
static
@ -2020,8 +2057,8 @@ fuse_lib_create(fuse_req_t *req_,
struct fuse_entry_param e;
struct fuse_create_in *arg;
arg = (fuse_create_in*)fuse_hdr_arg(hdr_);
name = (const char*)PARAM(arg);
arg = (fuse_create_in*)fuse_hdr_arg(hdr_);
name = (const char*)PARAM(arg);
ffi.flags = arg->flags;
@ -2033,7 +2070,15 @@ fuse_lib_create(fuse_req_t *req_,
// opportunistically allocate a node so we have a new nodeid for
// later in the actual create. Added for use with passthrough.
{
node_t *node = find_node(hdr_->nodeid,name);
node_t *node;
node = find_node(hdr_->nodeid,name);
if(node == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
new_nodeid = node->nodeid;
req_->ctx.nodeid = new_nodeid;
}
@ -2064,7 +2109,9 @@ fuse_lib_create(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(e.ino)->open_count++;
node_t *node = get_node(e.ino);
if(node)
node->open_count++;
mutex_unlock(f.lock);
if(fuse_reply_create(req_,&e,&ffi) == -ENOENT)
@ -2097,6 +2144,12 @@ open_auto_cache(fuse_req_ctx_t *req_ctx_,
mutex_lock(f.lock);
node = get_node(ino);
if(not node)
{
mutex_unlock(f.lock);
return;
}
if(node->is_stat_cache_valid)
{
int err;
@ -2133,7 +2186,7 @@ fuse_lib_open(fuse_req_t *req_,
fuse_file_info_t ffi = {};
struct fuse_open_in *arg;
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
ffi.flags = arg->flags;
@ -2153,7 +2206,9 @@ fuse_lib_open(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(hdr_->nodeid)->open_count++;
node_t *node = get_node(hdr_->nodeid);
if(node)
node->open_count++;
mutex_unlock(f.lock);
/* The open syscall was interrupted,so it must be cancelled */
if(fuse_reply_open(req_,&ffi) == -ENOENT)
@ -2186,6 +2241,11 @@ fuse_lib_read(fuse_req_t *req_,
}
msgbuf = msgbuf_alloc_page_aligned();
if(msgbuf == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
res = f.ops.read(&req_->ctx,
&ffi,
@ -2259,11 +2319,17 @@ fuse_lib_fsync(fuse_req_t *req_,
static
struct fuse_dh*
get_dirhandle(const fuse_file_info_t *llfi,
fuse_file_info_t *fi)
fuse_file_info_t *ffi)
{
struct fuse_dh *dh = (struct fuse_dh *)(uintptr_t)llfi->fh;
memset(fi,0,sizeof(fuse_file_info_t));
fi->fh = dh->fh;
if(dh == NULL)
return NULL;
*ffi = {};
ffi->fh = dh->fh;
return dh;
}
@ -2282,14 +2348,21 @@ fuse_lib_opendir(fuse_req_t *req_,
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
llffi.flags = arg->flags;
dh = (struct fuse_dh *)calloc(1,sizeof(struct fuse_dh));
dh = (fuse_dh*)calloc(1,sizeof(fuse_dh));
if(dh == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
fuse_dirents_init(&dh->d);
err = fuse_dirents_init(&dh->d);
if(err)
{
free(dh);
fuse_reply_err(req_,ENOMEM);
return;
}
mutex_init(dh->lock);
llffi.fh = (uintptr_t)dh;
@ -2312,8 +2385,9 @@ fuse_lib_opendir(fuse_req_t *req_,
{
/* The opendir syscall was interrupted,so it
must be cancelled */
f.ops.releasedir(&req_->ctx,
&ffi);
f.ops.releasedir(&req_->ctx,&ffi);
fuse_dirents_free(&dh->d);
mutex_destroy(dh->lock);
free(dh);
}
@ -2321,6 +2395,7 @@ fuse_lib_opendir(fuse_req_t *req_,
else
{
fuse_reply_err(req_,err);
fuse_dirents_free(&dh->d);
mutex_destroy(dh->lock);
free(dh);
}
@ -2371,9 +2446,15 @@ fuse_lib_readdir(fuse_req_t *req_,
llffi.fh = arg->fh;
dh = get_dirhandle(&llffi,&ffi);
d = &dh->d;
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
mutex_lock(dh->lock);
d = &dh->d;
mutex_lockguard(dh->lock);
rv = 0;
if((arg->offset == 0) || (kv_size(d->data) == 0))
@ -2384,17 +2465,17 @@ fuse_lib_readdir(fuse_req_t *req_,
if(rv)
{
fuse_reply_err(req_,rv);
goto out;
return;
}
size = readdir_buf_size(d,size,arg->offset);
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
out:
mutex_unlock(dh->lock);
if(size == 0)
fuse_reply_buf(req_,NULL,0);
else
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
}
static
@ -2415,9 +2496,15 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
llffi.fh = arg->fh;
dh = get_dirhandle(&llffi,&ffi);
d = &dh->d;
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
mutex_lock(dh->lock);
d = &dh->d;
mutex_lockguard(dh->lock);
rv = 0;
if((arg->offset == 0) || (kv_size(d->data) == 0))
@ -2428,17 +2515,17 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
if(rv)
{
fuse_reply_err(req_,rv);
goto out;
return;
}
size = readdir_buf_size(d,size,arg->offset);
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
out:
mutex_unlock(dh->lock);
if(size == 0)
fuse_reply_buf(req_,NULL,0);
else
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
}
static
@ -2447,7 +2534,7 @@ fuse_lib_releasedir(fuse_req_t *req_,
struct fuse_in_header *hdr_)
{
struct fuse_dh *dh;
fuse_file_info_t ffi;
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
struct fuse_release_in *arg;
@ -2456,6 +2543,11 @@ fuse_lib_releasedir(fuse_req_t *req_,
llffi.flags = arg->flags;
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
f.ops.releasedir(&req_->ctx,
&ffi);
@ -2476,7 +2568,8 @@ fuse_lib_fsyncdir(fuse_req_t *req_,
{
int err;
int is_datasync;
fuse_file_info_t ffi;
fuse_dh *dh;
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
struct fuse_fsync_in *arg;
@ -2484,7 +2577,12 @@ fuse_lib_fsyncdir(fuse_req_t *req_,
is_datasync = !!(arg->fsync_flags & FUSE_FSYNC_FDATASYNC);
llffi.fh = arg->fh;
get_dirhandle(&llffi,&ffi);
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
err = f.ops.fsyncdir(&req_->ctx,
&ffi,
@ -2821,7 +2919,9 @@ fuse_lib_tmpfile(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(e.ino)->open_count++;
node_t *node = get_node(e.ino);
if(node)
node->open_count++;
mutex_unlock(f.lock);
if(fuse_reply_create(req_,&e,&ffi) == -ENOENT)
@ -3007,9 +3107,13 @@ fuse_flush_common(fuse_req_t *req_,
{
flock_to_lock(&lock,&l);
l.owner = ffi_->lock_owner;
mutex_lock(f.lock);
locks_insert(get_node(ino_),&l);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(ino_);
if(n)
locks_insert(n,&l);
}
/* if op.lock() is defined FLUSH is needed regardless
of op.flush() */
@ -3132,11 +3236,15 @@ fuse_lib_getlk(fuse_req_t *req_,
flock_to_lock(&flk,&lk);
lk.owner = ffi.lock_owner;
mutex_lock(f.lock);
conflict = locks_conflict(get_node(hdr_->nodeid),&lk);
if(conflict)
lock_to_flock(conflict,&flk);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(hdr_->nodeid);
conflict = (n ? locks_conflict(n,&lk) : NULL);
if(conflict)
lock_to_flock(conflict,&flk);
}
if(!conflict)
err = fuse_lock_common(req_,hdr_->nodeid,&ffi,&flk,F_GETLK);
else
@ -3156,16 +3264,26 @@ fuse_lib_setlk(fuse_req_t *req_,
struct flock *lock,
int sleep)
{
int err = fuse_lock_common(req_,ino,fi,lock,
sleep ? F_SETLKW : F_SETLK);
int err;
err = fuse_lock_common(req_,
ino,
fi,
lock,
sleep ? F_SETLKW : F_SETLK);
if(!err)
{
lock_t l;
flock_to_lock(lock,&l);
l.owner = fi->lock_owner;
mutex_lock(f.lock);
locks_insert(get_node(ino),&l);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(ino);
if(n)
locks_insert(n,&l);
}
}
fuse_reply_err(req_,err);
@ -3223,20 +3341,20 @@ fuse_lib_ioctl(fuse_req_t *req_,
{
int err;
char *out_buf = NULL;
fuse_file_info_t ffi;
fuse_dh *dh;
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
const void *in_buf;
uint32_t out_size;
const struct fuse_ioctl_in *arg;
arg = (fuse_ioctl_in*)fuse_hdr_arg(hdr_);
if((arg->flags & FUSE_IOCTL_DIR) && !(req_->conn.want & FUSE_CAP_IOCTL_DIR))
{
fuse_reply_err(req_,ENOTTY);
return;
}
if((sizeof(void*) == 4) &&
err = -EPERM;
if(arg->flags & FUSE_IOCTL_UNRESTRICTED)
goto err;
if((sizeof(void*) == 4) &&
(req_->conn.proto_minor >= 16) &&
!(arg->flags & FUSE_IOCTL_32BIT))
{
@ -3247,14 +3365,27 @@ fuse_lib_ioctl(fuse_req_t *req_,
out_size = arg->out_size;
in_buf = (arg->in_size ? PARAM(arg) : NULL);
err = -EPERM;
if(arg->flags & FUSE_IOCTL_UNRESTRICTED)
goto err;
if(arg->flags & FUSE_IOCTL_DIR)
get_dirhandle(&llffi,&ffi);
{
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
err = -EBADF;
goto err;
}
}
else
ffi = llffi;
{
ffi = llffi;
}
if((arg->in_size > 0) &&
(out_size > 0) &&
(arg->in_size != out_size))
{
err = -EINVAL;
goto err;
}
if(out_size)
{
@ -3262,28 +3393,27 @@ fuse_lib_ioctl(fuse_req_t *req_,
out_buf = (char*)malloc(out_size);
if(!out_buf)
goto err;
DEFER { free(out_buf); };
}
assert(!arg->in_size || !out_size || arg->in_size == out_size);
if(out_buf && in_buf)
memcpy(out_buf,in_buf,arg->in_size);
if(in_buf && out_buf)
memcpy(out_buf,in_buf,out_size);
err = f.ops.ioctl(&req_->ctx,
&ffi,
arg->cmd,
(void*)(uintptr_t)arg->arg,
arg->flags,
out_buf ?: (void *)in_buf,
out_buf ? out_buf : (void*)in_buf,
&out_size);
if(err < 0)
goto err;
fuse_reply_ioctl(req_,err,out_buf,out_size);
goto out;
return;
err:
fuse_reply_err(req_,err);
out:
free(out_buf);
}
static
@ -3351,7 +3481,11 @@ remembered_node_cmp(const void *a_,
const remembered_node_t *a = (const remembered_node_t*)a_;
const remembered_node_t *b = (const remembered_node_t*)b_;
return (a->time - b->time);
if(a->time < b->time)
return -1;
if(a->time > b->time)
return 1;
return 0;
}
static
@ -3605,7 +3739,10 @@ metrics_log_nodes_info_to_tmp_dir(struct fuse *f_)
struct stat st;
char const *mode = "a";
sprintf(filepath,"/tmp/mergerfs.%d.info",getpid());
snprintf(filepath,
sizeof(filepath),
"/tmp/mergerfs.%d.info",
getpid());
rv = lstat(filepath,&st);
if((rv == 0) && (st.st_size > INFO_FILE_MAX_SIZE))
@ -3724,7 +3861,7 @@ fuse_new(int fd_,
llop.setlk = NULL;
}
f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop),&f);
f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop));
if(f.se == NULL)
goto out_free_fs;
@ -3749,11 +3886,11 @@ fuse_new(int fd_,
root = node_alloc();
if(root == NULL)
{
fprintf(stderr,"fuse: memory allocation failed\n");
fprintf(stderr,"mergerfs: root node alloc failed\n");
goto out_free_id_table;
}
root->name = strdup("/");
root->name = xstrdup("/");
root->parent = NULL;
root->nodeid = FUSE_ROOT_ID;

View file

@ -50,6 +50,9 @@ fuse_session_new(void *data,
void
fuse_session_destroy(struct fuse_session *se)
{
if(se == nullptr)
return;
if(se->destroy)
se->destroy(se->f);

View file

@ -0,0 +1,60 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#define xstrdup(S) xstrdup_impl((S),__FILE__,__LINE__)
#define xmalloc(S) xmalloc_impl((S),__FILE__,__LINE__)
static
inline
char*
xstrdup_impl(const char *s_,
const char *file_,
const unsigned line_)
{
char *p;
if(s_ == NULL)
{
fprintf(stderr,
"mergerfs: xstrdup called with NULL - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
p = strdup(s_);
if(p == NULL)
{
fprintf(stderr,
"mergerfs: strdup failed - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
return p;
}
static
inline
void*
xmalloc_impl(const size_t size_,
const char *file_,
const unsigned line_)
{
void *p;
p = malloc(size_);
if(p == NULL)
{
fprintf(stderr,
"mergerfs: malloc failed - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
return p;
}