Add mergerfs.collect-info

A tool to collect info for support requests.
This commit is contained in:
Antonio SJ Musumeci 2025-06-29 22:22:54 -05:00
parent 71ddae8713
commit 768d3a2aac
13 changed files with 391 additions and 42 deletions

View file

@ -137,6 +137,7 @@ build/tests: build/mergerfs tests-objects
mergerfs: build/mergerfs mergerfs: build/mergerfs
ln -fs "mergerfs" "build/fsck.mergerfs" ln -fs "mergerfs" "build/fsck.mergerfs"
ln -fs "mergerfs" "build/mergerfs.collect-info"
tests: build/tests tests: build/tests
@ -184,6 +185,7 @@ install-base: build/mergerfs
$(MKDIR) -p "$(INSTALLBINDIR)" $(MKDIR) -p "$(INSTALLBINDIR)"
$(INSTALL) -v -m 0755 "build/mergerfs" "$(INSTALLBINDIR)/mergerfs" $(INSTALL) -v -m 0755 "build/mergerfs" "$(INSTALLBINDIR)/mergerfs"
ln -s "mergerfs" "${INSTALLBINDIR}/fsck.mergerfs" ln -s "mergerfs" "${INSTALLBINDIR}/fsck.mergerfs"
ln -s "mergerfs" "${INSTALLBINDIR}/mergerfs.collect-info"
install-mount-tools: install-base install-mount-tools: install-base
$(MKDIR) -p "$(INSTALLBINDIR)" $(MKDIR) -p "$(INSTALLBINDIR)"

View file

@ -35,6 +35,7 @@ make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
/usr/bin/mergerfs /usr/bin/mergerfs
/usr/bin/mergerfs-fusermount /usr/bin/mergerfs-fusermount
/usr/bin/fsck.mergerfs /usr/bin/fsck.mergerfs
/usr/bin/mergerfs.collect-info
/sbin/mount.mergerfs /sbin/mount.mergerfs
/usr/lib/mergerfs/preload.so /usr/lib/mergerfs/preload.so
%doc %{_mandir}/* %doc %{_mandir}/*

7
mkdocs/build-venv Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
set -x
set -e
python3 -m venv venv
. ./venv/bin/activate
pip3 install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike

View file

@ -7,7 +7,7 @@ otherwise it will be difficult or impossible to diagnose. Also please
read the documentation as it provides details on many previously read the documentation as it provides details on many previously
encountered questions/issues. encountered questions/issues.
**Please make sure you are using the [latest **Make sure you are using the [latest
release](https://github.com/trapexit/mergerfs/releases) or have tried release](https://github.com/trapexit/mergerfs/releases) or have tried
it in comparison. Old versions, which are often included in distros it in comparison. Old versions, which are often included in distros
like Debian and Ubuntu, are not ever going to be updated and the issue like Debian and Ubuntu, are not ever going to be updated and the issue
@ -16,27 +16,35 @@ you are encountering may have been addressed already.**
**For commercial support or feature requests please [contact me **For commercial support or feature requests please [contact me
directly.](mailto:support@spawn.link)** directly.](mailto:support@spawn.link)**
### Information to include in bug reports ### Information to include in bug reports
* [Information about the broader problem along with any attempted * [Information about the broader problem along with any attempted
solutions.](https://xyproblem.info) solutions.](https://xyproblem.info)
* Solution already ruled out and why. * Solution already ruled out and why.
* Version of mergerfs: `mergerfs --version` * The details from the output of the `mergerfs.collect-info` tool.
* mergerfs settings / arguments: from fstab, systemd unit, command * Alternatively:
line, OMV plugin, etc. * Version of mergerfs: `mergerfs --version`
* Version of the OS: `uname -a` and `lsb_release -a` * mergerfs settings / arguments: from fstab, systemd unit, command
* List of branches, their filesystem types, sizes (before and after issue): `df -h` line, OMV plugin, etc.
* Version of the OS: `uname -a` and `lsb_release -a`
* List of branches, their filesystem types, sizes (before and after
issue): `df -h`, `lsblk -o NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA`
* **All** information about the relevant paths and files: permissions, ownership, etc. * **All** information about the relevant paths and files: permissions, ownership, etc.
* **All** information about the client app making the requests: version, uid/gid * **All** information about the application making the requests: version, uid/gid
* Runtime environment: * Runtime environment:
* Is mergerfs running within a container? * Is mergerfs running within a container?
* Are the client apps using mergerfs running in a container? * Are the client apps using mergerfs running in a container?
* A `strace` of the app having problems: * A `strace` of the app having problems:
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>` * `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
* A `strace` of mergerfs while the program is trying to do whatever it is failing to do: * A `strace` of mergerfs while the program is trying to do whatever it
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt` is failing to do:
* **Precise** directions on replicating the issue. Do not leave **anything** out. * `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* Try to recreate the problem in the simplest way using standard programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. * **Precise** directions on replicating the issue. Do not leave
**anything** out.
* Try to recreate the problem in the simplest way using standard
programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple
mergerfs setup possible.
### Contact / Issue submission ### Contact / Issue submission

View file

@ -1,6 +0,0 @@
#pragma once
namespace fsck
{
int main(int argc, char **argv);
}

View file

@ -15,7 +15,8 @@
*/ */
#include "mergerfs.hpp" #include "mergerfs.hpp"
#include "fsck_mergerfs.hpp" #include "mergerfs_fsck.hpp"
#include "mergerfs_collect_info.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fs_readahead.hpp" #include "fs_readahead.hpp"
@ -272,8 +273,8 @@ namespace l
} }
int int
main(const int argc_, main(int argc_,
char **argv_) char **argv_)
{ {
int rv; int rv;
Config::Read cfg; Config::Read cfg;
@ -339,7 +340,9 @@ _pick_app_and_run(int argc_,
appname = appname.filename(); appname = appname.filename();
if(appname == "fsck.mergerfs") if(appname == "fsck.mergerfs")
return fsck::main(argc_,argv_); return mergerfs::fsck::main(argc_,argv_);
if(appname == "mergerfs.collect-info")
return mergerfs::collect_info::main(argc_,argv_);
return l::main(argc_,argv_); return l::main(argc_,argv_);
} }

68
src/mergerfs_api.cpp Normal file
View file

@ -0,0 +1,68 @@
#include "mergerfs_api.hpp"
#include "fs_close.hpp"
#include "fs_ioctl.hpp"
#include "fs_open.hpp"
#include "mergerfs_ioctl.hpp"
#include "fs_lgetxattr.hpp"
#include "str.hpp"
#include "scope_guard.hpp"
#include <string.h>
int
mergerfs::api::allpaths(const std::string &input_path_,
std::vector<std::string> &output_paths_)
{
int rv;
IOCTL_BUF buf;
rv = fs::lgetxattr(input_path_,"user.mergerfs.allpaths",buf,sizeof(buf));
if(rv < 0)
return rv;
str::split_on_null(buf,rv,&output_paths_);
return 0;
}
int
_lgetxattr(const std::string &input_path_,
const std::string &key_,
std::string &value_)
{
int rv;
IOCTL_BUF buf;
rv = fs::lgetxattr(input_path_,key_,buf,sizeof(buf));
if(rv < 0)
return rv;
value_ = buf;
return rv;
}
int
mergerfs::api::basepath(const std::string &input_path_,
std::string &basepath_)
{
return ::_lgetxattr(input_path_,"user.mergerfs.basepath",basepath_);
}
int
mergerfs::api::relpath(const std::string &input_path_,
std::string &relpath_)
{
return ::_lgetxattr(input_path_,"user.mergerfs.relpath",relpath_);
}
int
mergerfs::api::fullpath(const std::string &input_path_,
std::string &fullpath_)
{
return ::_lgetxattr(input_path_,"user.mergerfs.fullpath",fullpath_);
}

24
src/mergerfs_api.hpp Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <string>
#include <vector>
namespace mergerfs
{
namespace api
{
int
basepath(const std::string &path,
std::string &basepath);
int
relpath(const std::string &path,
std::string &relpath);
int
fullpath(const std::string &path,
std::string &fullpath);
int
allpaths(const std::string &path,
std::vector<std::string> &paths);
}
}

View file

@ -0,0 +1,227 @@
#include "mergerfs_collect_info.hpp"
#include "mergerfs_api.hpp"
#include "fs_mounts.hpp"
#include "fs_unlink.hpp"
#include "CLI11.hpp"
#include "fmt/core.h"
#include "scope_guard.hpp"
#include "subprocess.hpp"
#include <stdio.h>
static
void
_write_str(const std::string &output_,
const std::string &str_)
{
FILE *f;
f = ::fopen(output_.c_str(),"a");
if(f == NULL)
return;
DEFER{ ::fclose(f); };
::fwrite(str_.c_str(),1,str_.size(),f);
}
static
void
_lsblk(const std::string &output_)
{
auto args =
{
"lsblk",
"--json",
"-o","NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA"
};
subprocess::call(args,
subprocess::output{output_.c_str()});
}
static
void
_mounts(const std::string &output_)
{
auto args =
{
"cat",
"/proc/mounts"
};
subprocess::call(args,
subprocess::output{output_.c_str()});
}
static
void
_mount_point_stats(const std::string &output_)
{
fs::MountVec mounts;
fs::mounts(mounts);
for(const auto &mount : mounts)
{
std::vector<std::string> allpaths;
mergerfs::api::allpaths(mount.dir.string(),allpaths);
for(const auto &path : allpaths)
{
auto args = {"stat",path.c_str()};
subprocess::call(args,
subprocess::output{output_.c_str()});
}
}
}
static
void
_mergerfs_version(const std::string &output_)
{
auto args =
{
"mergerfs",
"--version"
};
try
{
subprocess::call(args,
subprocess::output{output_.c_str()});
}
catch(...)
{
}
}
static
void
_uname(const std::string &output_)
{
auto args =
{
"uname",
"-a"
};
try
{
subprocess::call(args,
subprocess::output{output_.c_str()});
}
catch(...)
{
}
}
static
void
_lsb_release(const std::string &output_)
{
auto args =
{
"lsb_release",
"-a"
};
try
{
subprocess::call(args,
subprocess::output{output_.c_str()});
}
catch(...)
{
}
}
static
void
_df(const std::string &output_)
{
auto args =
{
"df",
"-h"
};
try
{
subprocess::call(args,
subprocess::output{output_.c_str()});
}
catch(...)
{
}
}
static
void
_fstab(const std::string &output_)
{
auto args =
{
"cat",
"/etc/fstab"
};
try
{
subprocess::call(args,
subprocess::output{output_.c_str()});
}
catch(...)
{
}
}
int
mergerfs::collect_info::main(int argc_,
char **argv_)
{
CLI::App app;
const char *output_filepath = "/tmp/mergerfs.info.txt";
app.description("mergerfs.collect-info:"
" Collect info for support requests");
app.name("USAGE: mergerfs.collect-info");
try
{
app.parse(argc_,argv_);
}
catch(const CLI::ParseError &e)
{
return app.exit(e);
}
fmt::print("* Please have mergerfs mounted before running this tool.\n");
fs::unlink(output_filepath);
::_write_str(output_filepath,"::mergerfs --version::\n");
::_mergerfs_version(output_filepath);
::_write_str(output_filepath,"\n::uname -a::\n");
::_uname(output_filepath);
::_write_str(output_filepath,"\n::lsb_release -a::\n");
::_lsb_release(output_filepath);
::_write_str(output_filepath,"\n::df -h::\n");
::_df(output_filepath);
::_write_str(output_filepath,"\n::lsblk::\n");
::_lsblk(output_filepath);
::_write_str(output_filepath,"\n::cat /proc/mounts::\n");
::_mounts(output_filepath);
::_write_str(output_filepath,"\n::mount point stats::\n");
::_mount_point_stats(output_filepath);
::_write_str(output_filepath,"\n::cat /etc/fstab::\n");
::_fstab(output_filepath);
fmt::print("* Upload the following file to your"
" GitHub ticket or put on https://pastebin.com"
" when requesting support.\n* {}\n",output_filepath);
return 0;
}

View file

@ -0,0 +1,11 @@
#pragma once
namespace mergerfs
{
namespace collect_info
{
int
main(int argc,
char **argv);
}
}

View file

@ -1,16 +1,15 @@
#include "fsck_mergerfs.hpp" #include "mergerfs_fsck.hpp"
#include "fs_lchmod.hpp" #include "fs_lchmod.hpp"
#include "fs_lchown.hpp" #include "fs_lchown.hpp"
#include "fs_close.hpp" #include "fs_close.hpp"
#include "fs_ioctl.hpp"
#include "fs_lgetxattr.hpp" #include "fs_lgetxattr.hpp"
#include "fs_lstat.hpp" #include "fs_lstat.hpp"
#include "fs_open.hpp" #include "fs_open.hpp"
#include "int_types.h" #include "int_types.h"
#include "mergerfs_ioctl.hpp"
#include "str.hpp" #include "str.hpp"
#include "fs_copyfile.hpp" #include "fs_copyfile.hpp"
#include "mergerfs_api.hpp"
#include "fmt/core.h" #include "fmt/core.h"
#include "fmt/chrono.h" #include "fmt/chrono.h"
@ -169,22 +168,13 @@ int
_get_allpaths(const std::string &mergerfs_path_, _get_allpaths(const std::string &mergerfs_path_,
PathStatVec &pathstats_) PathStatVec &pathstats_)
{ {
int fd;
int rv; int rv;
IOCTL_BUF buf;
std::vector<std::string> allpaths; std::vector<std::string> allpaths;
strcpy(buf,"allpaths"); rv = mergerfs::api::allpaths(mergerfs_path_,allpaths);
if(rv < 0)
return rv;
fd = fs::open(mergerfs_path_,O_RDONLY|O_NOFOLLOW);
if(fd == -1)
return -errno;
rv = fs::ioctl(fd,IOCTL_FILE_INFO,buf);
fs::close(fd);
str::split_on_null(buf,rv,&allpaths);
for(const auto &path : allpaths) for(const auto &path : allpaths)
pathstats_.emplace_back(path); pathstats_.emplace_back(path);
@ -396,8 +386,8 @@ _fsck(const FS::path &path_,
} }
int int
fsck::main(int argc_, mergerfs::fsck::main(int argc_,
char **argv_) char **argv_)
{ {
CLI::App app; CLI::App app;
FS::path path; FS::path path;

9
src/mergerfs_fsck.hpp Normal file
View file

@ -0,0 +1,9 @@
#pragma once
namespace mergerfs
{
namespace fsck
{
int main(int argc, char **argv);
}
}

View file

@ -6,7 +6,12 @@
#define _IOC_TYPE(X) (((X) >> 8) & 0xFF) #define _IOC_TYPE(X) (((X) >> 8) & 0xFF)
#endif #endif
typedef char IOCTL_BUF[4096]; #ifndef _IOC_SIZEBITS
#define _IOC_SIZEBITS 14
#endif
#define IOCTL_BUF_SIZE ((1 << _IOC_SIZEBITS) - 1)
typedef char IOCTL_BUF[IOCTL_BUF_SIZE];
#define IOCTL_APP_TYPE 0xDF #define IOCTL_APP_TYPE 0xDF
#define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF) #define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF)
#define IOCTL_GC _IO(IOCTL_APP_TYPE,1) #define IOCTL_GC _IO(IOCTL_APP_TYPE,1)