diff --git a/Makefile b/Makefile index d7478680..46b9fdfd 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,7 @@ build/tests: build/mergerfs tests-objects mergerfs: build/mergerfs ln -fs "mergerfs" "build/fsck.mergerfs" + ln -fs "mergerfs" "build/mergerfs.collect-info" tests: build/tests @@ -184,6 +185,7 @@ install-base: build/mergerfs $(MKDIR) -p "$(INSTALLBINDIR)" $(INSTALL) -v -m 0755 "build/mergerfs" "$(INSTALLBINDIR)/mergerfs" ln -s "mergerfs" "${INSTALLBINDIR}/fsck.mergerfs" + ln -s "mergerfs" "${INSTALLBINDIR}/mergerfs.collect-info" install-mount-tools: install-base $(MKDIR) -p "$(INSTALLBINDIR)" diff --git a/mergerfs.spec b/mergerfs.spec index 6fa3654b..8746ee66 100644 --- a/mergerfs.spec +++ b/mergerfs.spec @@ -35,6 +35,7 @@ make install PREFIX=%{_prefix} DESTDIR=%{buildroot} /usr/bin/mergerfs /usr/bin/mergerfs-fusermount /usr/bin/fsck.mergerfs +/usr/bin/mergerfs.collect-info /sbin/mount.mergerfs /usr/lib/mergerfs/preload.so %doc %{_mandir}/* diff --git a/mkdocs/build-venv b/mkdocs/build-venv new file mode 100755 index 00000000..94af9f6d --- /dev/null +++ b/mkdocs/build-venv @@ -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 diff --git a/mkdocs/docs/support.md b/mkdocs/docs/support.md index acb3c742..952e1c51 100644 --- a/mkdocs/docs/support.md +++ b/mkdocs/docs/support.md @@ -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 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 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 @@ -16,27 +16,35 @@ you are encountering may have been addressed already.** **For commercial support or feature requests please [contact me directly.](mailto:support@spawn.link)** + ### Information to include in bug reports * [Information about the broader problem along with any attempted solutions.](https://xyproblem.info) * Solution already ruled out and why. -* Version of mergerfs: `mergerfs --version` -* mergerfs settings / arguments: from fstab, systemd unit, command - 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` +* The details from the output of the `mergerfs.collect-info` tool. +* Alternatively: + * Version of mergerfs: `mergerfs --version` + * mergerfs settings / arguments: from fstab, systemd unit, command + 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 client app making the requests: version, uid/gid +* **All** information about the application making the requests: version, uid/gid * Runtime environment: - * Is mergerfs running within a container? - * Are the client apps using mergerfs running in a container? + * Is mergerfs running within a container? + * Are the client apps using mergerfs running in a container? * A `strace` of the app having problems: - * `strace -fvTtt -s 256 -o /tmp/app.strace.txt ` -* A `strace` of mergerfs while the program is trying to do whatever it is failing to do: - * `strace -fvTtt -s 256 -p -o /tmp/mergerfs.strace.txt` -* **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. + * `strace -fvTtt -s 256 -o /tmp/app.strace.txt ` +* A `strace` of mergerfs while the program is trying to do whatever it + is failing to do: + * `strace -fvTtt -s 256 -p -o /tmp/mergerfs.strace.txt` +* **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 diff --git a/src/fsck_mergerfs.hpp b/src/fsck_mergerfs.hpp deleted file mode 100644 index b9d92f2e..00000000 --- a/src/fsck_mergerfs.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace fsck -{ - int main(int argc, char **argv); -} diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index de713851..ed4ed423 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -15,7 +15,8 @@ */ #include "mergerfs.hpp" -#include "fsck_mergerfs.hpp" +#include "mergerfs_fsck.hpp" +#include "mergerfs_collect_info.hpp" #include "fs_path.hpp" #include "fs_readahead.hpp" @@ -272,8 +273,8 @@ namespace l } int - main(const int argc_, - char **argv_) + main(int argc_, + char **argv_) { int rv; Config::Read cfg; @@ -339,7 +340,9 @@ _pick_app_and_run(int argc_, appname = appname.filename(); 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_); } diff --git a/src/mergerfs_api.cpp b/src/mergerfs_api.cpp new file mode 100644 index 00000000..3971a138 --- /dev/null +++ b/src/mergerfs_api.cpp @@ -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 + + +int +mergerfs::api::allpaths(const std::string &input_path_, + std::vector &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_); +} diff --git a/src/mergerfs_api.hpp b/src/mergerfs_api.hpp new file mode 100644 index 00000000..a1339b24 --- /dev/null +++ b/src/mergerfs_api.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + + +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 &paths); + } +} diff --git a/src/mergerfs_collect_info.cpp b/src/mergerfs_collect_info.cpp new file mode 100644 index 00000000..a3a41843 --- /dev/null +++ b/src/mergerfs_collect_info.cpp @@ -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 + + +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 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; +} diff --git a/src/mergerfs_collect_info.hpp b/src/mergerfs_collect_info.hpp new file mode 100644 index 00000000..80ca41ba --- /dev/null +++ b/src/mergerfs_collect_info.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace mergerfs +{ + namespace collect_info + { + int + main(int argc, + char **argv); + } +} diff --git a/src/fsck_mergerfs.cpp b/src/mergerfs_fsck.cpp similarity index 96% rename from src/fsck_mergerfs.cpp rename to src/mergerfs_fsck.cpp index 9931a246..7e76eee9 100644 --- a/src/fsck_mergerfs.cpp +++ b/src/mergerfs_fsck.cpp @@ -1,16 +1,15 @@ -#include "fsck_mergerfs.hpp" +#include "mergerfs_fsck.hpp" #include "fs_lchmod.hpp" #include "fs_lchown.hpp" #include "fs_close.hpp" -#include "fs_ioctl.hpp" #include "fs_lgetxattr.hpp" #include "fs_lstat.hpp" #include "fs_open.hpp" #include "int_types.h" -#include "mergerfs_ioctl.hpp" #include "str.hpp" #include "fs_copyfile.hpp" +#include "mergerfs_api.hpp" #include "fmt/core.h" #include "fmt/chrono.h" @@ -169,22 +168,13 @@ int _get_allpaths(const std::string &mergerfs_path_, PathStatVec &pathstats_) { - int fd; int rv; - IOCTL_BUF buf; std::vector 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) pathstats_.emplace_back(path); @@ -396,8 +386,8 @@ _fsck(const FS::path &path_, } int -fsck::main(int argc_, - char **argv_) +mergerfs::fsck::main(int argc_, + char **argv_) { CLI::App app; FS::path path; diff --git a/src/mergerfs_fsck.hpp b/src/mergerfs_fsck.hpp new file mode 100644 index 00000000..4aa97aff --- /dev/null +++ b/src/mergerfs_fsck.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace mergerfs +{ + namespace fsck + { + int main(int argc, char **argv); + } +} diff --git a/src/mergerfs_ioctl.hpp b/src/mergerfs_ioctl.hpp index 936c978a..d071a6f3 100644 --- a/src/mergerfs_ioctl.hpp +++ b/src/mergerfs_ioctl.hpp @@ -6,7 +6,12 @@ #define _IOC_TYPE(X) (((X) >> 8) & 0xFF) #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_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF) #define IOCTL_GC _IO(IOCTL_APP_TYPE,1)