Compare commits

...
984 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 3e085beb0e node.cpp 2025-10-27 22:50:26 -05:00
Antonio SJ Musumeci 58abe58ce2 node.cpp 2025-10-27 22:32:28 -05:00
Antonio SJ Musumeci 564bb8c364 node.cpp 2025-10-27 22:32:24 -05:00
Antonio SJ Musumeci 2e3307bac8 node.cpp 2025-10-27 22:32:02 -05:00
Antonio SJ Musumeci d88605de72 node.cpp 2025-10-27 22:31:33 -05:00
Antonio SJ Musumeci 8b5382dde9 node.cpp 2025-10-27 22:30:29 -05:00
Antonio SJ Musumeci 72b9397f67 node.hpp 2025-10-27 22:26:12 -05:00
Antonio SJ Musumeci cda8472120 mergerfs.cpp 2025-10-27 22:25:48 -05:00
Antonio SJ Musumeci 705eb7a5ea mergerfs.cpp 2025-10-27 22:23:08 -05:00
Antonio SJ Musumeci aee1e2d42b fs_open.hpp 2025-10-27 22:17:17 -05:00
Antonio SJ Musumeci 9ac073a1b4 node.cpp 2025-10-27 22:12:51 -05:00
Antonio SJ Musumeci 3394ff525a node.cpp 2025-10-27 22:11:05 -05:00
Antonio SJ Musumeci 05d04b50a1 node.cpp 2025-10-27 22:10:26 -05:00
Antonio SJ Musumeci 42100d985b node.cpp 2025-10-27 22:07:08 -05:00
Antonio SJ Musumeci 7bf3d7617e node.cpp 2025-10-27 21:53:19 -05:00
Antonio SJ Musumeci bd5735dfc9 node.cpp 2025-10-27 21:53:07 -05:00
Antonio SJ Musumeci 09135b0851 node.cpp 2025-10-27 21:52:52 -05:00
Antonio SJ Musumeci 055f6f3335 node.cpp 2025-10-27 21:50:28 -05:00
Antonio SJ Musumeci ddee03452c node.cpp 2025-10-27 21:50:10 -05:00
Antonio SJ Musumeci a7856ae21b node.cpp 2025-10-27 21:49:09 -05:00
Antonio SJ Musumeci ae621d929e node.cpp 2025-10-27 21:49:02 -05:00
Antonio SJ Musumeci eb30c4ac26 node.cpp 2025-10-27 21:48:56 -05:00
Antonio SJ Musumeci db47cdf706 node.cpp 2025-10-27 21:48:26 -05:00
Antonio SJ Musumeci 7d7d50f99c node.cpp 2025-10-27 21:48:22 -05:00
Antonio SJ Musumeci 0f16a14f7f node.cpp 2025-10-27 21:46:09 -05:00
Antonio SJ Musumeci e7f43178c0 node.cpp 2025-10-27 21:46:04 -05:00
Antonio SJ Musumeci 75d6234e0c node.cpp 2025-10-27 21:45:54 -05:00
Antonio SJ Musumeci 93c9400cee node.cpp 2025-10-27 21:44:17 -05:00
Antonio SJ Musumeci efb550dac6 node.cpp 2025-10-27 21:43:59 -05:00
Antonio SJ Musumeci ea11421ef3 node.cpp 2025-10-27 21:43:46 -05:00
Antonio SJ Musumeci 5f38796425 node.cpp 2025-10-27 21:43:29 -05:00
Antonio SJ Musumeci 71355aeede node.cpp 2025-10-27 21:42:44 -05:00
Antonio SJ Musumeci 36aad5ab59 node.cpp 2025-10-27 21:41:56 -05:00
Antonio SJ Musumeci 18ac5a9b55 fs_open.hpp 2025-10-27 21:40:53 -05:00
Antonio SJ Musumeci 33b587ffac node.cpp 2025-10-27 21:40:22 -05:00
Antonio SJ Musumeci 66b38d5e38 node.cpp 2025-10-27 21:40:18 -05:00
Antonio SJ Musumeci 79f18ad09e node.cpp 2025-10-27 21:39:29 -05:00
Antonio SJ Musumeci 3204d74d25 node.cpp 2025-10-27 21:38:20 -05:00
Antonio SJ Musumeci 1e45fa4d31 node.cpp 2025-10-27 21:38:13 -05:00
Antonio SJ Musumeci e55a5aea49 node.cpp 2025-10-27 21:38:06 -05:00
Antonio SJ Musumeci f3997fc8a8 node.cpp 2025-10-27 21:37:59 -05:00
Antonio SJ Musumeci 1745d7c10a node.cpp 2025-10-27 21:00:38 -05:00
Antonio SJ Musumeci 38c8e09913 node.cpp 2025-10-27 21:00:32 -05:00
Antonio SJ Musumeci dd8ecdd792 node.cpp 2025-10-27 21:00:21 -05:00
Antonio SJ Musumeci 04b61c8b29 node.cpp 2025-10-27 20:58:06 -05:00
Antonio SJ Musumeci e9bfa96f26 node.cpp 2025-10-27 08:56:42 -05:00
Antonio SJ Musumeci 95e62e6a98 node.cpp 2025-10-27 08:56:42 -05:00
Antonio SJ Musumeci c33ef49cdf Remove pipe_key from libfuse 2025-10-27 08:56:42 -05:00
trapexit cfaad60275 Misc updates to arg parsing (#1554)
Ignore "defaults" and add a number of more modern VFS mount switches
2025-10-26 21:05:05 -05:00
trapexit 077484323c Fix variable names of fuse-msg-size and posix-acl (#1553) 2025-10-25 17:00:16 -05:00
trapexit 700fcfccf5 Update fuse-msg-size docs (#1552) 2025-10-24 23:34:28 -05:00
trapexit 4b4801add6 Config options docs updates (#1551) 2025-10-24 22:31:04 -05:00
trapexit 741128cb23 Fix lack of include for fmt (#1550) 2025-10-24 21:00:15 -05:00
trapexit 3bee0f0b57 Misc docs updates (#1549) 2025-10-24 20:47:33 -05:00
trapexit 727c522fb8 Rework config, centralize fuse config (#1547) 2025-10-24 08:20:29 -05:00
trapexit 00e5467e99 Misc docs updates (#1544) 2025-10-13 11:44:53 -05:00
trapexit 0371f03def Rework how fuse request context is handled (#1543) 2025-10-12 23:21:36 -05:00
trapexit d18c273293 Allow setting of passthrough max-stack-depth (#1542) 2025-10-07 21:52:13 -05:00
trapexit 0f24bb29ac Make allow-idmap optional (default: false) (#1541)
This is just to correct default behavior for now. More investigation
is necessary to properly support idmapping.
2025-10-06 14:46:58 -05:00
trapexit 3cb35a3a65 Fix rename path generation (#1540) 2025-10-01 17:47:13 -05:00
trapexit 25b0bc72df Fix dirent64::namelen calculation (#1538)
Worked fine on my machine where padding seemed to be all zeroed out
but that doesn't appear to be the case elsewhere.

Could try
https://codeberg.org/jbruchon/libjodycode/src/branch/master/dir.c#L92-L110
but needs to be tested.
2025-09-30 11:25:19 -05:00
trapexit e6fcebe4f3 Misc build fixes (#1537) 2025-09-29 14:05:07 -05:00
trapexit 46f85c0f6d Misc fixes, mostly for FreeBSD (#1536) 2025-09-28 23:17:51 -05:00
trapexit 7a9c3b7eb2 Remove need for and use of ioprio header (#1535) 2025-09-28 00:01:26 -05:00
trapexit 569a151da2 Add ability to proxy ioprio of client apps (#1534) 2025-09-26 11:36:56 -05:00
trapexit 2f3e807e9d Add getdents based readdir functions for Linux (#1533) 2025-09-23 23:51:21 -05:00
trapexit 95ab37fdd2 Fix typo in visualization and add new vid to docs (#1532) 2025-09-22 16:23:50 -05:00
trapexit 1707dc27e3 Misc doc updates (#1531) 2025-09-21 14:25:24 -05:00
trapexit c4126a0796 Add script to generate docs from scratch and push to github (#1530) 2025-09-21 13:04:55 -05:00
pjv 6acef3a638 Fix typo in moveonenospc.md (#1527)
typo
2025-09-21 11:42:15 -05:00
trapexit b3656a0243 Fix broken doc links (#1525) 2025-09-19 19:25:31 -05:00
trapexit 41b40b94fa Misc docs updates (#1524) 2025-09-19 19:20:28 -05:00
trapexit ca59ae53a5 idmap mount support (#1523)
* Add idmap mount support

* Further replacement of std::filesystem::path to fs::path

* Update docs regarding idmap support
2025-09-18 00:07:28 -05:00
trapexit 8c649d33ee Start using std::filesystem::path for fusepath (#1522) 2025-09-17 22:51:16 -05:00
trapexit f64ff9ca1e Revert removal of readdir init setup, simplify cfg usage (#1520) 2025-09-06 11:03:51 -05:00
trapexit 99c28ad3bc Add Ubuntu 25.04 builds (#1519) 2025-09-04 19:29:47 -05:00
trapexit 5becb334cd Misc updates of docs (#1518) 2025-09-01 20:58:58 -05:00
trapexit 071af2e516 More build process updates (#1516) 2025-08-29 07:51:05 -05:00
trapexit 90b3585347 Update readme to match index of mkdocs (#1515) 2025-08-22 20:53:22 -05:00
trapexit d7549f8d53 Fix string manipulation functions after refactor (#1514) 2025-08-22 19:42:31 -05:00
trapexit 6ac7f389c5 Misc changes (#1513)
* Start moving to string_view where possible
* Rework readdir object to use atomic load/store
2025-08-21 22:42:58 -05:00
trapexit 2101ef5bcc Updates to docs, add nonraid, more intro to fs details (#1512) 2025-08-21 15:09:04 -05:00
Max.-F. HelmandMax-F-Helm dd1bea52cd Allow empty lines in config ini files (#1509)
* Allow empty lines in config files

* Add example comment to ini example in docs/quickstart.md

---------

Co-authored-by: Max-F-Helm <m7t24zklr@relay.firefox.com>
2025-08-13 20:00:05 -05:00
trapexit df19f99976 Fix constructing string from attrval setxattr config (#1508) 2025-08-11 19:50:12 -05:00
trapexit 24e778b95a Rework calculation of branch for release make targets (#1507) 2025-08-10 18:39:18 -05:00
trapexit a8a7b34cf3 Add Debian 13 build targets (#1506) 2025-08-10 17:50:06 -05:00
trapexit 6ae9641bcc Misc documentation updates (#1505) 2025-08-10 17:23:55 -05:00
trapexit 4439c2a030 Add setting of oom_score_adj (#1504)
To limit risk of killing by OOM Killer.
2025-08-07 23:58:06 -05:00
trapexit 9872710720 More makefile tweaks, build log, smaller git clones (#1503) 2025-08-06 12:24:25 -05:00
trapexit b18aba86b6 Build improvements (#1501)
* Remove python dependency
* Remove ecfd
* Improve makefiles
* Make preload.so more 32bit compatible
2025-08-04 23:39:52 -05:00
trapexit 33fc26f81f Cleanup copy functions (#1500) 2025-07-27 16:23:45 -05:00
trapexit e8cd74cab0 Move everything to negative errno return types (#1499) 2025-07-25 17:53:48 -05:00
trapexit 2b500f194a Rework of runtime interface (#1498)
Also have special error handling for when branches are invalid and
ENOENT would be returned for getattr and readdir so users understand
what is going on and the runtime interface can still be used to fix
the problem.
2025-07-21 18:18:59 -05:00
trapexit e87867cd2a Clear O_NOFOLLOW when opening a relative fd (#1494) 2025-07-08 18:25:00 -05:00
trapexit fa851eda3a Ensure passthrough and keep_cache are mutually exclusive (#1493) 2025-07-06 18:31:47 -05:00
trapexit f852474073 Update the secondary group cache (#1492)
* Use concurrent_flat_map to build a global cache rather than per
thread.
* Create global maintanence thread manager for clearing unused cache
entries.
2025-07-05 17:05:53 -05:00
trapexit 9e626228be Add mergerfs.collect-info (#1491)
A tool to collect info for support requests.
2025-07-03 20:18:05 -05:00
trapexit 71ddae8713 Misc documentation changes (#1489) 2025-06-29 19:08:57 -05:00
trapexit e216a81a27 Rework movefile and cow break to use copyfile (#1488) 2025-06-28 17:27:40 -05:00
trapexit 264c5600f8 Misc cleanup (#1486)
* Skip non-directories when loading branches
* Warn if branch doesn't exist
* Add braces to globbing
2025-06-25 22:43:49 -05:00
trapexit be49dbe1bd Remove linux only getdents in favor of readdir for FreeBSD sake (#1485) 2025-06-24 19:31:40 -05:00
trapexit eb124316c9 Set default read thread count to 0 (#1484) 2025-06-23 22:39:42 -05:00
trapexit 77ef88b04c Add fsck.mergerfs tool (#1483) 2025-06-23 21:57:55 -05:00
trapexit 34f07e57f5 Add RHEL/CentOS 10 build support (#1479) 2025-06-09 14:33:55 -05:00
trapexit 095fcf82a3 Misc doc updates 2025-06-08 23:59:17 -05:00
trapexit 1b167d34d8 Spawn "mount" on branches when waiting for mounts enabled 2025-06-08 15:09:47 -05:00
trapexit 23824eaeb4 Misc cleanup 2025-06-06 19:10:19 -05:00
trapexit e310d24615 Add support for FUSE passthrough 2025-06-05 23:47:42 -05:00
trapexit 92312c8507 Add debugging of mutexes (#1470)
When DEBUG defined it will used timed locks which will log when held
too long along with where.
2025-05-26 00:44:39 -05:00
trapexit 4b751c2d37 Fix incorrect dyn cast (#1467) 2025-05-19 22:46:55 -05:00
trapexit 86b50d2d4e Rework policies to return branches rather than path strings 2025-05-17 16:35:18 -05:00
trapexit 7e084ea7db Update README.md to fix link (#1464) 2025-05-12 09:48:19 -05:00
trapexit 6eca711f58 Update mkdocs build action (#1458) 2025-05-10 12:56:10 -05:00
trapexit 923d9608e8 Add details to docs about FreeBSD limitations 2025-05-09 21:18:20 -05:00
trapexit 3a034712f6 Add versioning to docs using 'mike' 2025-05-08 19:53:54 -05:00
Zack Guo 5db04b4b5f Update installation.md for SUSE 2025-05-08 07:44:57 -05:00
trapexit fa3266ca7f Add podman release build tooling + misc build fixes (#1455) 2025-05-08 00:00:53 -05:00
trapexit 344014aa81 Rework thread pool for increased stability + config and doc updates (#1453)
Also updates fmt to 11.2.0 and replaced syslog usage with a wrapper
leveraging fmt.
2025-05-05 18:28:49 -05:00
SolipsistmonkeyandJesse Harwin 455a81290b Fix build instructions typo
the location of install-build-pkgs was in buildtools and not tools
---------

Co-authored-by: Jesse Harwin <jesse@jharwin.com>
2025-04-28 14:00:20 -05:00
trapexit deb54c58d8 Merge pull request #1448 from trapexit/defaultperms
Add config option to control default_permissions
2025-04-25 17:34:16 -05:00
Antonio SJ Musumeci 2b04a791b7 Add config option to control default_permissions
Defaults true but there might be some situations where it is useful to
disable.
2025-04-25 16:34:39 -05:00
trapexit 8ee998e8aa Merge pull request #1447 from trapexit/branchcfg
Improve mount waiting + misc doc improvements
2025-04-23 16:40:26 -05:00
Antonio SJ Musumeci de2a82800a Improve mount waiting + misc doc improvements
Adds user.mergerfs.branch and user.mergerfs.branch_mounts_here
Adds .mergerfs.branch and .mergerfs.branch_mounts_here files.
2025-04-23 15:58:36 -05:00
trapexit 212681ddc5 Merge pull request #1445 from trapexit/docs
Misc doc updates + add logo
2025-04-19 22:45:27 -05:00
Antonio SJ Musumeci 9dac54f223 Misc doc updates + add logo 2025-04-19 22:44:08 -05:00
trapexit 78fa34098e Merge pull request #1444 from trapexit/docs
Add FAQ entry regarding OS support
2025-04-15 23:41:45 -05:00
Antonio SJ Musumeci 54681e885a Add FAQ entry regarding OS support 2025-04-15 23:40:42 -05:00
trapexit de5a871326 Merge pull request #1443 from trapexit/inode
Change the "devino" inode calculation
2025-04-15 19:29:43 -05:00
Antonio SJ Musumeci 77bf16e9ae Change the "devino" inode calculation
Use hashing of branch path rather than use st_dev.
2025-04-15 18:54:51 -05:00
trapexit ad54c8be19 Merge pull request #1441 from trapexit/docs
Add doc pages for minfreespace and moveonenospc + other misc updates
2025-04-10 23:00:43 -05:00
Antonio SJ Musumeci c769e959e8 Add doc pages for minfreespace and moveonenospc + other misc updates 2025-04-10 22:59:49 -05:00
trapexit 1f3ebcf418 Merge pull request #1439 from trapexit/statx
Add statx support
2025-04-04 22:25:30 -05:00
Antonio SJ Musumeci 80085c9844 Add statx support 2025-04-04 22:23:16 -05:00
trapexit b85cc8c370 Merge pull request #1438 from trapexit/docs
Update docs regarding error handling
2025-04-04 18:42:37 -05:00
Antonio SJ Musumeci 95732222d9 Update docs regarding error handling 2025-04-04 18:41:58 -05:00
trapexit 17a551d0a4 Merge pull request #1435 from trapexit/rapidhash
Replace usage of wyhash with rapidhash
2025-03-30 18:03:35 -05:00
Antonio SJ Musumeci f74dc36e92 Replace usage of wyhash with rapidhash
https://github.com/Nicoshev/rapidhash
2025-03-30 17:57:20 -05:00
trapexit 764bd8f7be Merge pull request #1434 from trapexit/change-defaults
Change misc defaults
2025-03-30 17:25:18 -05:00
Antonio SJ Musumeci b7f7f7fa97 Change misc defaults
* category.create=pfrd
* moveonenospc=pfrd
* cache.files=off
* parallel-direct-writes=true

These changes were made to match the recommended settings for most
users to reduce confusion.
2025-03-30 17:22:38 -05:00
trapexit 7f410073d4 Merge pull request #1433 from trapexit/maxpages
Support Linux v6.13 FUSE max_page_limit
2025-03-30 00:09:03 -05:00
Antonio SJ Musumeci 92ae63e842 Support Linux v6.13 FUSE max_page_limit 2025-03-30 00:07:08 -05:00
trapexit d31e97e64c Merge pull request #1431 from trapexit/docs
Add links to options docs page to individual pages
2025-03-27 19:55:19 -05:00
Antonio SJ Musumeci 579690f411 Add links to options docs page to individual pages 2025-03-27 19:47:37 -05:00
trapexit ca449a73be Merge pull request #1429 from trapexit/docs
Update docs on default create policy
2025-03-26 14:34:28 -05:00
Antonio SJ Musumeci 2d86ed946a Update docs on default create policy 2025-03-26 14:33:11 -05:00
trapexit 3e454f528c Merge pull request #1426 from trapexit/docs
Misc doc updates
2025-03-12 18:30:21 -05:00
Antonio SJ Musumeci a4fdbae1ca Misc doc updates 2025-03-12 18:29:38 -05:00
trapexit aff66812c0 Merge pull request #1425 from trapexit/docs-tips
Misc doc updates
2025-03-08 22:03:14 -06:00
Antonio SJ Musumeci 6472ac51e9 Misc doc updates 2025-03-08 22:02:34 -06:00
trapexit d3770802b1 Merge pull request #1423 from trapexit/docs-inotify
Add FAQ entry regarding inotify
2025-03-05 16:59:45 -06:00
Antonio SJ Musumeci bcbff5c82e Add FAQ entry regarding inotify 2025-03-05 16:58:27 -06:00
trapexit 09743ee187 Merge pull request #1416 from trapexit/docs
Add FAQ section on common perceived problems
2025-02-22 22:30:28 -06:00
Antonio SJ Musumeci 3a33a6db2b Add FAQ section on common perceived problems 2025-02-22 22:25:39 -06:00
trapexit c0d5f4795a Merge pull request #1415 from trapexit/docs
Update docs on out-of-band usage
2025-02-19 14:13:32 -06:00
Antonio SJ Musumeci ef9074ab4a Update docs on out-of-band usage 2025-02-19 14:13:06 -06:00
trapexit 2d03b89c2d Merge pull request #1414 from theHenMan/patch-2
Update terminology.md
2025-02-19 00:09:55 -06:00
trapexit 64cc63b2bc Merge pull request #1413 from theHenMan/patch-1
Update quickstart.md
2025-02-18 23:04:59 -06:00
Hennie Botha 514c478a39 Update terminology.md
Fixed typo
2025-02-19 07:04:11 +02:00
Hennie Botha 8179f91c48 Update quickstart.md
Fixed a typo
2025-02-19 07:02:22 +02:00
trapexit 371764d800 Merge pull request #1412 from trapexit/docs
Rework questions on what settings and policies to use
2025-02-11 19:47:18 -06:00
Antonio SJ Musumeci 1c44ef7967 Rework questions on what settings and policies to use 2025-02-11 19:46:33 -06:00
trapexit aa97bac533 Merge pull request #1409 from trapexit/docs
Add details about logging to syslog
2025-01-25 20:58:57 -06:00
Antonio SJ Musumeci 12d6302457 Add details about logging to syslog 2025-01-25 20:56:28 -06:00
trapexit 740fc3f84a Merge pull request #1408 from trapexit/docs
Add details about user namespacing and root squashing
2025-01-25 13:54:43 -06:00
Antonio SJ Musumeci f18719ced2 Add details about user namespacing and root squashing 2025-01-25 13:54:06 -06:00
trapexit 366f5fcc4c Merge pull request #1407 from trapexit/docs
Misc doc updates
2025-01-25 13:02:36 -06:00
Antonio SJ Musumeci 1729835b8c Misc doc updates 2025-01-25 13:00:00 -06:00
trapexit 1180fd6dd3 Merge pull request #1402 from trapexit/docs
Doc and mover script updates
2025-01-20 01:45:51 -06:00
Antonio SJ Musumeci 19509e33d2 Doc and mover script updates 2025-01-20 01:43:58 -06:00
trapexit 8f355d7dd5 Merge pull request #1399 from oregonpillow/master
Readability changes to mkdocs installation
2025-01-18 15:21:20 -06:00
debian 2aa9697c87 re-order OS, shorten Debian based OS instructions 2025-01-18 01:14:50 +01:00
trapexit 978d067921 Merge pull request #1398 from trapexit/freebsd
Improve FreeBSD compatibility
2025-01-15 22:16:13 -06:00
Antonio SJ Musumeci b83bd5fd14 Improve FreeBSD compatibility
First draft. Need to investigate why upstream patches include certain
function that appear to exist in FreeBSD 14.2.
2025-01-15 22:10:02 -06:00
trapexit dd906c8a4c Merge pull request #1395 from oregonpillow/latest-release
fix edit uri
2025-01-11 18:07:15 -06:00
debian 6fb9111c03 fix edit uri 2025-01-12 00:56:39 +01:00
trapexit bc382e0fd2 Merge pull request #1392 from trapexit/usr1
Send invalidate node request outside lock
2025-01-10 00:35:59 -05:00
trapexit 0197984db3 Send invalidate node request outside lock 2025-01-09 23:22:07 -06:00
trapexit 248b1aca1c Merge pull request #1391 from trapexit/cpp
Move fuse.c and fuse_lowlevel.c to C++
2025-01-09 20:10:35 -05:00
Antonio SJ Musumeci 9056898bbf Move fuse.c and fuse_lowlevel.c to C++ 2025-01-09 19:05:32 -06:00
trapexit 5e584f246a Merge pull request #1390 from trapexit/tiered
Add tiered cache details to docs
2025-01-08 00:56:48 -05:00
Antonio SJ Musumeci 7e964283b9 Add tiered cache details to docs 2025-01-07 23:55:13 -06:00
trapexit 03dc17fff2 Merge pull request #1389 from trapexit/misc
Update funding and cirrus builds
2025-01-07 00:18:32 -05:00
Antonio SJ Musumeci 8cb68dce4a Update funding and cirrus builds 2025-01-06 23:07:33 -06:00
trapexit 28260c434a Merge pull request #1388 from trapexit/mkdocs
Update README to point to docs, update project comparisons
2025-01-05 23:40:54 -06:00
Antonio SJ Musumeci fae9b993fe Update README to point to docs, update project comparisons 2025-01-05 23:39:48 -06:00
trapexit 171eb9c7c0 Merge pull request #1387 from trapexit/workflows
Fix pip install in mkdocs workflow
2025-01-03 12:41:47 -06:00
Antonio SJ Musumeci 174e703d2e Fix pip install in mkdocs workflow 2025-01-03 12:40:50 -06:00
trapexit efd0dfaa2f Merge pull request #1386 from trapexit/docs
Rework mkdocs based documentation
2025-01-03 12:33:54 -06:00
Antonio SJ Musumeci 75d7a93724 Rework mkdocs based documentation
Lots of restructoring and extra information.
2025-01-03 12:17:26 -06:00
Timothy Pillow 50fa7ac694 add mkdocs
Initial commit of a mkdocs based docs setup
2024-12-21 09:36:07 -06:00
trapexit 7d64a47b88 Merge pull request #1377 from trapexit/movecopy-readme
Add FAQ entry on 'move' and 'copy'
2024-11-30 00:23:39 -06:00
Antonio SJ Musumeci 5fba42f076 Add FAQ entry on 'move' and 'copy' 2024-11-30 00:12:44 -06:00
trapexit b0bb7ef38c Merge pull request #1346 from grunthos503/master
Add missing --relative flag to rsync in percent-full mover script
2024-07-18 15:28:42 -05:00
Alan Millar 85ce7d6178 Add missing --relative flag to rsync in percent-full mover script 2024-07-18 12:14:02 -07:00
trapexit 40359df453 Merge pull request #1342 from techie2000/patch-2
Update README.md
2024-07-18 13:50:29 -05:00
techie2000 9b2a5a75ee Update README.md
Correcting a couple of typos.
Enhancing readability.
2024-06-28 10:15:02 +01:00
trapexit b21172c609 Merge pull request #1321 from trapexit/allowmmap
Add support for 'direct-io-allow-mmap' if supported by kernel
2024-03-21 21:54:04 -04:00
Antonio SJ Musumeci f0444a1ca9 Add support for 'direct-io-allow-mmap' if supported by kernel 2024-03-21 20:48:42 -05:00
trapexit 977d04229f Merge pull request #1320 from trapexit/readme
Suggest cache.files=auto-full rather than partial
2024-03-21 21:36:27 -04:00
Antonio SJ Musumeci 757ae66327 Suggest cache.files=auto-full rather than partial 2024-03-17 00:47:33 -05:00
trapexit 42d0b5734c Merge pull request #1312 from trapexit/preload-tmpfile
Special case O_TMPFILE in preload
2024-02-29 15:41:22 -05:00
Antonio SJ Musumeci 78050ed6b0 Special case O_TMPFILE in preload 2024-02-29 14:33:27 -06:00
trapexit 19ee44afe7 Merge pull request #1309 from trapexit/root-nodeid
Root nodeid
2024-02-25 18:29:58 -05:00
Antonio SJ Musumeci a234e9446c Update README to remove unnecessary references to export-support for NFS 2024-02-25 17:27:08 -06:00
Antonio SJ Musumeci 951eb9540d Ensure lookups of FUSE_ROOT_ID always return generation=0 2024-02-25 17:14:15 -06:00
trapexit 85a936c95e Merge pull request #1308 from trapexit/readme
Change mount type from fuse.mergerfs to mergerfs in README
2024-02-24 02:09:40 -05:00
Antonio SJ Musumeci 8210e63a66 Change mount type from fuse.mergerfs to mergerfs in README 2024-02-24 01:08:30 -06:00
trapexit facdb66746 Merge pull request #1307 from trapexit/readme
Add NFS options to remote filesystems section of readme
2024-02-19 19:20:41 -06:00
Antonio SJ Musumeci 1916e54a24 Add NFS options to remote filesystems section of readme 2024-02-19 19:19:00 -06:00
trapexit 7fabe515f4 Merge pull request #1306 from trapexit/export-support
Add export-support option as possible workaround for NFS EIO issues
2024-02-19 19:00:54 -06:00
Antonio SJ Musumeci 0ca5d6aad0 Add export-support option as possible workaround for NFS EIO issues 2024-02-19 18:58:50 -06:00
trapexit 0c1aeb411d Merge pull request #1305 from trapexit/lib-updates
Library updates
2024-02-18 17:56:39 -06:00
Antonio SJ Musumeci 17fdbf7f42 Update fuse_kernel.h 2024-02-18 17:50:34 -06:00
Antonio SJ Musumeci 27349e41ef Update fmt 2024-02-18 17:24:18 -06:00
Antonio SJ Musumeci b3ce95c422 Update nonstd::optional 2024-02-18 17:24:18 -06:00
Antonio SJ Musumeci cedc9f0f25 Update ghc::filesystem 2024-02-18 17:24:18 -06:00
trapexit d68ad9ac01 Expand the preload docs (#1299) 2024-01-30 00:03:39 -06:00
trapexit ae6c4f7c25 Rework mergerfs vs X section of readme (#1298) 2024-01-28 18:12:29 -06:00
trapexit 87475098a1 Add credit to nohajc for preload idea (#1295) 2024-01-22 12:52:51 -06:00
trapexit d4dc0701d6 Create ld_preload to reopen files on underlying filesystem (#1294) 2024-01-22 12:42:55 -06:00
0x6d61726b c1c2f075ee Update README.md (#1283)
* Update README.md

Information added that mergerfs v2.35.0 and newer sets `allow_other` (but not 2.33.5, that is used by Debian 12.2)
2023-12-01 15:45:13 -06:00
trapexit 96d34f1eab Merge pull request #1280 from trapexit/getpid
Fix user.mergerfs.pid
2023-11-11 19:48:28 -06:00
Antonio SJ Musumeci f66fb7e2d4 Fix user.mergerfs.pid 2023-11-11 19:03:32 -06:00
trapexit edb3368078 Merge pull request #1277 from trapexit/flushdefault
Set flush-on-close default to opened-for-write
2023-10-31 23:26:08 -05:00
Antonio SJ Musumeci bd70928268 Set flush-on-close default to opened-for-write 2023-10-31 09:23:52 -05:00
trapexit 337aade02d Merge pull request #1276 from trapexit/allowother
Only set allow_other when running as root
2023-10-29 02:54:51 -05:00
Antonio SJ Musumeci d6a2f06797 Only set allow_other when running as root 2023-10-29 01:44:18 -05:00
trapexit 7890a4996c Merge pull request #1275 from trapexit/noflush
Add flushonclose feature
2023-10-28 19:49:17 -05:00
Antonio SJ Musumeci 6aa6452d3e Add flushonclose feature 2023-10-28 19:47:23 -05:00
trapexit 7d6c8e9333 Merge pull request #1271 from trapexit/warn-root
Warn if not running as root
2023-10-22 00:45:41 -05:00
Antonio SJ Musumeci a7069153bd Warn if not running as root 2023-10-22 00:43:19 -05:00
trapexit 5c71a75fdd Merge pull request #1269 from trapexit/wyhash
Update wyhash to 4.2
2023-10-21 18:04:14 -05:00
Antonio SJ Musumeci fd33df04a3 Update wyhash to 4.2 2023-10-21 18:03:05 -05:00
trapexit a9ae36a3e4 Merge pull request #1263 from trapexit/pthread_getname_np
Remove use of pthread_getname_np to work with older musl versions
2023-10-14 01:20:00 -05:00
Antonio SJ Musumeci 569537df9e Remove use of pthread_getname_np to work with older musl versions 2023-10-14 01:15:29 -05:00
trapexit 8534ee7ceb Merge pull request #1262 from trapexit/readdir-errs
Rework thread pool queue depth impl
2023-10-14 01:13:48 -05:00
Antonio SJ Musumeci 620cab2948 Rework thread pool queue depth impl 2023-10-14 00:58:10 -05:00
trapexit 8635456818 Merge pull request #1254 from trapexit/typo
Fix readdir doc typos
2023-09-23 14:54:25 -05:00
Antonio SJ Musumeci 0888f372aa Fix readdir doc typos 2023-09-23 14:53:41 -05:00
trapexit e09b10c55b Merge pull request #1249 from trapexit/tpfix
Fix thread pool destruction where threads don't explicitly exit themselves
2023-09-09 17:07:51 -05:00
Antonio SJ Musumeci 766b923116 Fix thread pool destruction where threads don't explicitly exit themselves 2023-09-09 16:48:47 -05:00
trapexit dd7e9e2aa7 Merge pull request #1244 from trapexit/docs
Add some new questions to FAQ
2023-09-05 22:56:50 -05:00
Antonio SJ Musumeci 3fc0aa3809 Add some new questions to FAQ 2023-09-05 22:56:08 -05:00
trapexit b1dcc94e98 Merge pull request #1242 from trapexit/debug-removal
Remove unneeded debug logging from thread pool
2023-09-04 18:40:38 -05:00
Antonio SJ Musumeci 6d2c99e487 Remove unneeded debug logging from thread pool 2023-09-04 18:39:43 -05:00
trapexit 9d9eacfe7b Merge pull request #1241 from trapexit/version
Add website and license to version option
2023-09-04 16:02:24 -05:00
Antonio SJ Musumeci b91cc1c191 Add website and license to version option 2023-09-04 15:57:47 -05:00
trapexit 6d2f26c60e Merge pull request #1240 from trapexit/dynamic-threadpool
Rework thread pool, add ability to add/remove threads at runtime
2023-09-03 23:37:29 -05:00
Antonio SJ Musumeci 0a94bd7cfc Rework thread pool, add ability to add/remove threads at runtime 2023-09-03 23:36:03 -05:00
trapexit 211e6c7115 Merge pull request #1239 from trapexit/validation
Add proper input validation for readdir policy
2023-09-02 15:48:43 -05:00
Antonio SJ Musumeci 335ca55a6e Add proper input validation for readdir policy 2023-09-02 15:47:49 -05:00
trapexit 69de4de53f Merge pull request #1238 from trapexit/boundedqueue
Change threadpool to use concurrentqueue
2023-09-02 00:47:26 -05:00
Antonio SJ Musumeci 76c8d48dbd Change threadpool to use concurrentqueue 2023-09-02 00:45:47 -05:00
trapexit a927a15e9c Merge pull request #1237 from trapexit/threadfix
Initialize readdir threadpool after daemonizing
2023-08-28 22:48:59 -05:00
Antonio SJ Musumeci 0c555e71a0 Initialize readdir threadpool after daemonizing 2023-08-28 22:47:04 -05:00
trapexit 82781b6ff8 Merge pull request #1235 from DavidFair/readme_direct_parallel
Docs: clarify parallel-direct-writes and cache.files
2023-08-26 18:52:57 -05:00
David Fairbrother b545352712 Nit: cache.files=per-process grammar
Change which 'comm' name -> whose 'comm' name.
Whose implies possession, and the process possess the name we're
matching
2023-08-26 21:27:16 +01:00
David Fairbrother 3d05999db0 State cache.files per-process behaviour w/out match
This makes it clear, along with the previous commit, that per-process will allow
us to use parallel direct writes as they will mount with the equivalent
of cache.files=off (i.e. direct_io)
2023-08-26 21:24:34 +01:00
David Fairbrother f4a8105a42 Clarify parallel direct writes conditions
Clarify that paralell direct can only be enabled where the kernel
version supports it, which is 6.2+.

State the two cache.files modes which can support parallel direct writes
in the description.
2023-08-26 21:22:37 +01:00
trapexit 004b616115 Merge pull request #1234 from trapexit/fixes
Misc fixes for older platforms
2023-08-22 08:13:56 -05:00
Antonio SJ Musumeci 8d7017273f Misc fixes for older platforms 2023-08-22 08:10:55 -05:00
trapexit 51b65cd126 Merge pull request #1233 from trapexit/atomic
Use relaxed memory order for atomic counters
2023-08-21 17:19:09 -05:00
Antonio SJ Musumeci 0ed03a1535 Use relaxed memory order for atomic counters 2023-08-21 15:43:03 -05:00
trapexit b6d3c34d3f Merge pull request #1232 from trapexit/wyhash
Update wyhash to final4
2023-08-21 00:20:04 -05:00
Antonio SJ Musumeci fdecabfaab Update wyhash to final4 2023-08-21 00:18:41 -05:00
trapexit 3a1d8e03a1 Merge pull request #1231 from trapexit/readdir
Fix setting of ugids for concurrent readdir
2023-08-20 23:12:56 -05:00
Antonio SJ Musumeci e586d2fd1e Fix setting of ugids for concurrent readdir 2023-08-20 23:06:05 -05:00
trapexit e0087cd8a7 Merge pull request #1228 from trapexit/threading
Add thread names for easier debugging
2023-08-14 17:52:31 -05:00
Antonio SJ Musumeci 2377cb05b6 Add thread names for easier debugging 2023-08-14 17:51:41 -05:00
trapexit a8ffbc84f6 Merge pull request #1227 from trapexit/gidcache
Add ability to invalidate gid cache on demand
2023-08-13 23:32:33 -05:00
Antonio SJ Musumeci 0aafdefc18 Add ability to invalidate gid cache on demand
SIGUSR2 or ioctl
2023-08-13 23:30:52 -05:00
trapexit 7221fded71 Merge pull request #1226 from feltcat/feltcat-patch-1
Fix a typo in README.md
2023-08-13 23:13:30 -05:00
feltcat 7a09f560f7 Fix a typo in README.md 2023-08-14 03:45:01 +00:00
trapexit 8b769dff7f Merge pull request #1225 from trapexit/new-funcs
Add callbacks for newer functions
2023-08-13 17:46:09 -05:00
Antonio SJ Musumeci 6dcf6111af Add callbacks for newer functions
* setupmapping
* removemapping
* syncfs
* tmpfile
2023-08-13 17:38:13 -05:00
trapexit 9849bcd3cf Merge pull request #1224 from trapexit/readdir
Add readdir policies
2023-08-11 00:05:28 -05:00
Antonio SJ Musumeci c92a10021e Add readdir policies 2023-08-11 00:05:14 -05:00
trapexit d817fa4e97 Merge pull request #1220 from trapexit/fix-logging
Fix logging pin-threads details
2023-07-30 23:14:16 -05:00
Antonio SJ Musumeci eb141c4c4e Fix logging pin-threads details 2023-07-30 23:13:14 -05:00
trapexit 282ce08ddb Merge pull request #1219 from trapexit/fuse-config
Move fuse thread args out of fuse session object
2023-07-30 22:56:27 -05:00
Antonio SJ Musumeci 51d97bb444 Move fuse thread args out of fuse session object 2023-07-30 22:52:43 -05:00
trapexit 539f2221a3 Merge pull request #1213 from trapexit/lib-updates
Lib updates
2023-07-15 18:51:43 -04:00
Antonio SJ Musumeci 2b1804d364 Add a DEPENDENCIES file to list dependencies
Mostly to remind myself which libs to update on occasion
2023-07-15 17:49:57 -05:00
Antonio SJ Musumeci 556c6522c0 Update fmt to v10.0.0 2023-07-15 17:47:36 -05:00
Antonio SJ Musumeci 543603bdfc Update ghc::filesystem to v1.5.14 2023-07-15 14:47:44 -05:00
trapexit 7a86ed6508 Merge pull request #1212 from trapexit/erofs
Create functions can set branches RO on EROFS
2023-07-13 22:34:29 -04:00
Antonio SJ Musumeci 707d298d7c Create functions can set branches RO on EROFS 2023-07-13 21:19:13 -05:00
trapexit 36a4b7a23a Merge pull request #1209 from trapexit/o_direct
Align msg buffer memory to allow O_DIRECT to work
2023-07-09 19:39:52 -04:00
Antonio SJ Musumeci 22833bdfe2 Align msg buffer memory to allow O_DIRECT to work 2023-07-09 15:32:11 -05:00
trapexit 775c3cad90 Merge pull request #1208 from trapexit/syslog
Simplify syslog wrapper a bit
2023-07-06 00:15:57 -05:00
Antonio SJ Musumeci fa537961c5 Simplify syslog wrapper a bit 2023-07-06 00:13:42 -05:00
trapexit ee13810f3b Merge pull request #1207 from trapexit/atomic
Fix build on older distros due to std::atomic definitions
2023-07-02 23:57:45 -05:00
Antonio SJ Musumeci 5d6c9cc56a Fix build on older distros due to std::atomic definitions
std::atomic_uint64_t missing in some older distros
2023-07-02 23:46:46 -05:00
trapexit 832903378e Merge pull request #1206 from trapexit/write-errs
Fix error handling for cached writes
2023-06-29 22:51:34 -05:00
Antonio SJ Musumeci 2722f4f64d Fix error handling for cached writes 2023-06-29 22:50:24 -05:00
trapexit 7cdd12bda4 Merge pull request #1204 from trapexit/gc
Add manual GC triggering + configurable process queue depth
2023-06-26 23:26:47 -05:00
Antonio SJ Musumeci 5ab0fbcaee Add manual GC triggering + configurable process queue depth
Yes, these are unrelated changes but somehow ended up being
prototyped together and I'm too lazy to separate them.
2023-06-26 22:44:23 -05:00
trapexit 42836e0961 Merge pull request #1203 from trapexit/append-move
Fix move when in append mode + fix read/write direct_io vs cached behavior
2023-06-24 23:13:57 -05:00
Antonio SJ Musumeci 6a14a10e6c Fix read/write behavior and return value depending on direct_io
Also add parallel direct write option for 6.2+ kernels.
2023-06-24 23:06:14 -05:00
Antonio SJ Musumeci 82430e1079 Fix moveonenospc when file opened with append mode 2023-06-12 23:40:08 -05:00
trapexit 701f88de43 Merge pull request #1171 from trapexit/srcmounts
Make srcmounts readonly
2023-04-05 22:31:29 -04:00
Antonio SJ Musumeci c72997f298 Make srcmounts readonly
All tools only read from srcmounts and the current setup had a bug
which caused branch mode and minfreespace to be stripped when using
config file.
2023-04-05 22:24:24 -04:00
trapexit c681d6c2fe Merge pull request #1170 from trapexit/fuse_destroy
Fix crash when exiting with open deleted files
2023-04-05 00:35:04 -04:00
Antonio SJ Musumeci 6bc3d77992 Fix crash when exiting with open deleted files 2023-04-05 00:32:30 -04:00
trapexit c266bae96d Merge pull request #1166 from trapexit/docs
Remove duplicate entries in docs
2023-04-01 14:41:33 -04:00
Antonio SJ Musumeci cdcfef2ceb Remove duplicate entries in docs 2023-04-01 14:40:49 -04:00
trapexit 7a62a629b9 Merge pull request #1165 from trapexit/thread-defaults
Change default read-thread-count from -1 to 0 as documented
2023-03-31 13:38:08 -04:00
Antonio SJ Musumeci 0a64d74b4b Change default read-thread-count from -1 to 0 as documented 2023-03-31 12:54:57 -04:00
trapexit 26c8b5c642 Merge pull request #1161 from trapexit/fix
Workaround older gcc bug with namespacing std::hash
2023-03-28 21:57:51 -04:00
Antonio SJ Musumeci 26fd11cb4d Workaround older gcc bug with namespacing std::hash 2023-03-28 21:53:38 -04:00
trapexit cdceade1ae Merge pull request #1159 from trapexit/wait
More actively report to syslog when waiting for branches to mount
2023-03-28 19:03:50 -04:00
Antonio SJ Musumeci eebd6e9eb4 Actively report to syslog when waiting for mounts 2023-03-28 19:01:21 -04:00
trapexit 9744eab819 Merge pull request #1156 from trapexit/options
Fix loading readonly / write once options from file
2023-03-19 23:41:15 -04:00
Antonio SJ Musumeci 1c5b0f2f5f Fix loading readonly / write once options from file 2023-03-19 23:40:32 -04:00
trapexit d9033b7713 Merge pull request #1155 from trapexit/mover-scripts
Fix mover script link typo
2023-03-19 22:08:31 -04:00
Antonio SJ Musumeci b1d11779f0 Fix mover script link typo 2023-03-19 22:07:03 -04:00
trapexit be19b9ce71 Merge pull request #1154 from trapexit/mover-scripts
Move tiered cache mover scripts to files in tools dir
2023-03-19 22:03:31 -04:00
Antonio SJ Musumeci 9fcaf6a674 Move tiered cache mover scripts to files in tools dir 2023-03-19 01:23:19 -04:00
trapexit 2dd4b788ce Merge pull request #1153 from trapexit/readme
Misc README updates
2023-03-18 21:48:16 -04:00
Antonio SJ Musumeci 5152c63480 Misc README updates 2023-03-18 21:47:37 -04:00
trapexit bd02bfd54c Merge pull request #1151 from kephir4eg/master
Fix config file parsing
2023-03-11 00:45:24 -05:00
Ilya Taranov d04e04e89a Fix config file parsing 2023-03-10 20:55:20 -08:00
trapexit 10a9969a64 Merge pull request #1149 from trapexit/readahead
Add ability to set readahead of mergerfs and branches
2023-03-09 00:13:49 -05:00
Antonio SJ Musumeci 486c5d3734 Add ability to set readahead of mergerfs and branches 2023-03-09 00:12:57 -05:00
trapexit 55bd0ca25a Merge pull request #1147 from trapexit/readme
Update README regarding why epmfs is default
2023-03-06 23:26:00 -05:00
Antonio SJ Musumeci ee2855bb38 Update README regarding why epmfs is default 2023-03-06 23:25:42 -05:00
trapexit 65c004a309 Merge pull request #1146 from jonaswinkler/master
Fix typo in basic setup options
2023-03-06 09:26:21 -05:00
Jonas Winkler a598f63235 Update README.md 2023-03-06 15:17:00 +01:00
trapexit 6e845b337a Merge pull request #1145 from trapexit/readme
Lots of misc README updates
2023-03-05 01:43:08 -05:00
Antonio SJ Musumeci e25fa1954e Lots of misc README updates 2023-03-05 01:42:42 -05:00
trapexit b48d368130 Merge pull request #1143 from trapexit/rework-read
Rework fuse read to use same buffers as messages
2023-02-26 23:37:14 -05:00
Antonio SJ Musumeci 373d331f39 Rework fuse read to use same buffers as messages 2023-02-26 23:31:44 -05:00
trapexit 4a647beec1 Merge pull request #1142 from trapexit/remove-splice
Remove splicing features
2023-02-26 01:45:29 -05:00
Antonio SJ Musumeci 84592a9f13 Remove splicing features
After numerous tests it was found the splice features were at best
the same performance as standard IO and at worse actually slower.
To simplify the code all splice features are removed.
2023-02-26 01:42:58 -05:00
trapexit 09fbb80f8c Merge pull request #1140 from trapexit/pinthreads
Add ability to pin read and processing threads
2023-02-20 22:45:54 -05:00
Antonio SJ Musumeci 34310170ac Add ability to pin read and processing threads 2023-02-19 17:52:29 -05:00
trapexit e39ace0eba Merge pull request #1139 from trapexit/per-process-page-cache
Add "per-process" file caching feature
2023-02-18 01:34:37 -05:00
Antonio SJ Musumeci 9effeb6869 Add "per-process" file caching feature
Allows chosing page caching based on process name.
2023-02-18 01:16:36 -05:00
trapexit b51a5970b2 Merge pull request #1137 from trapexit/btrfs-ioctl
Return NOTTY for all btrfs ioctl calls
2023-02-11 10:25:09 -05:00
Antonio SJ Musumeci 2d9c915641 Return NOTTY for all btrfs ioctl calls
mergerfs tries to pass through ioctl calls but due to the complexity
of btrfs calls their is risk of segfaults. In the future it may be
required to whitelist all supported ioctl calls.
2023-02-11 10:07:55 -05:00
trapexit 737e59d232 Merge pull request #1133 from trapexit/lazy-umount
Add option to lazy umount target mount point
2023-02-02 22:12:17 -05:00
Antonio SJ Musumeci 9adddfc66d Add option to lazy umount target mount point 2023-02-02 21:20:23 -05:00
trapexit 9a6b7835f4 Merge pull request #1131 from trapexit/docs
Add more apps to section on sqlite3 mmap problems
2023-01-29 23:57:46 -05:00
Antonio SJ Musumeci ac4e097511 Add more apps to section on sqlite3 mmap problems 2023-01-29 23:56:50 -05:00
trapexit 3d73c6a596 Merge pull request #1130 from trapexit/docs
Update README regarding requesting support and policy descriptions
2023-01-29 23:24:33 -05:00
Antonio SJ Musumeci 9670fb9704 Update README regarding requesting support and policy descriptions 2023-01-29 23:24:09 -05:00
trapexit e2ece936e6 Merge pull request #1129 from trapexit/fuse-version
Remove FUSE_USE_VERSION, no longer used
2023-01-29 18:27:26 -05:00
Antonio SJ Musumeci a4491169b0 Remove FUSE_USE_VERSION, no longer used 2023-01-29 17:04:08 -05:00
trapexit 0d566befba Merge pull request #1128 from trapexit/checkbranches
Forbid adding mountpoint to branch list
2023-01-29 15:50:25 -05:00
Antonio SJ Musumeci 3e313cd605 Forbid adding mountpoint to branch list 2023-01-29 11:51:56 -05:00
trapexit 5bf2a2be59 Merge pull request #1127 from trapexit/bugfixes
Backport bug fixes from libfuse3
2023-01-28 01:17:50 -05:00
Antonio SJ Musumeci 7ed91c1668 Backport bug fixes from libfuse3 2023-01-28 01:17:07 -05:00
trapexit 041e3f50d5 Merge pull request #1126 from trapexit/removeallowother
Hard code setting of allow_other, ignore when set
2023-01-26 23:40:12 -05:00
Antonio SJ Musumeci 92ccfd3c89 Hard code setting of allow_other, ignore when set 2023-01-26 23:37:37 -05:00
trapexit 24f2d3c94d Merge pull request #1125 from trapexit/scheduling-priority
Allow setting of scheduling priority
2023-01-25 23:42:20 -05:00
Antonio SJ Musumeci a7113bb163 Allow setting of scheduling priority 2023-01-25 23:38:31 -05:00
trapexit ec5ac0c517 Merge pull request #1124 from trapexit/builds
Update CirrusCI setup
2023-01-25 20:24:52 -05:00
Antonio SJ Musumeci fdec3355a1 Update CirrusCI setup 2023-01-25 20:12:53 -05:00
trapexit 16e9ba2f19 Merge pull request #1123 from trapexit/wait
Add option to wait for branches to become new mounts
2023-01-25 19:51:58 -05:00
Antonio SJ Musumeci 939eb3996a Add option to wait for branches to become new mounts
branches-mount-timeout=UINT64 in seconds (default: 0)
2023-01-25 11:59:14 -05:00
trapexit 2d8d0f7ad6 Merge pull request #1120 from trapexit/nonempty
Remove 'nonempty' argument
2023-01-16 23:41:51 -05:00
Antonio SJ Musumeci 3ee93d4c56 Remove 'nonempty' argument
This makes it like all other filesystems and brings it into alignment
with libfuse3's behavior.
2023-01-16 23:33:36 -05:00
trapexit 0b2afda30e Merge pull request #1119 from trapexit/useino
Remove use_ino option and make behavior same as if set
2023-01-16 16:53:30 -05:00
Antonio SJ Musumeci d11807ef7c Remove use_ino option and make behavior same as if set 2023-01-16 16:21:44 -05:00
trapexit 3e725a66e3 Merge pull request #1118 from trapexit/msghandling
Add async message processing
2023-01-16 13:41:00 -05:00
Antonio SJ Musumeci 24423b8d2a Add async message processing 2023-01-16 13:15:53 -05:00
trapexit c89fbde48d Merge pull request #1116 from davidkaufman/patch-1
small grammar correction to README file
2023-01-13 13:47:07 -05:00
David Kaufman b65e52d56b small grammar correction to README file
sorry, i'm a grammar nerd with ocd :-)
2023-01-03 00:58:08 -05:00
trapexit 6c2db76b92 Merge pull request #1109 from trapexit/fixes
Fix regression testing for implemented functions
2022-12-11 14:44:54 -05:00
Antonio SJ Musumeci 094ad862e7 Fix regression testing for implemented functions 2022-12-11 14:44:19 -05:00
trapexit e81b1a6b9d Merge pull request #1107 from trapexit/32bitfixes
Fix printf for 32bit systems
2022-12-08 22:06:15 -05:00
Antonio SJ Musumeci a6ca96fa08 Fix printf for 32bit systems 2022-12-08 22:04:15 -05:00
trapexit a449829d5f Merge pull request #1106 from trapexit/xattrfix
Fix reading of setxattr name
2022-12-08 18:40:57 -05:00
Antonio SJ Musumeci d14427ad9e Fix reading of setxattr name 2022-12-08 18:40:08 -05:00
trapexit ac1f4b4d52 Merge pull request #1105 from trapexit/ll
Misc cleanup
2022-11-27 23:29:53 -05:00
Antonio SJ Musumeci ddf5e53aa4 Misc cleanup 2022-11-27 22:23:38 -06:00
trapexit a2a12afb34 Merge pull request #1104 from trapexit/ll
Remove libfuse abstraction in prep for adding request data
2022-11-27 00:39:05 -05:00
Antonio SJ Musumeci 7606430188 Remove libfuse abstraction in prep for adding request data 2022-11-26 23:30:27 -06:00
trapexit 629806e896 Merge pull request #1093 from lgtm-migrator/codeql
Add CodeQL workflow for GitHub code scanning
2022-11-11 09:53:27 -05:00
LGTM Migrator 50fbf14e1e Add CodeQL workflow for GitHub code scanning 2022-11-11 07:59:54 +00:00
trapexit 4a67bbdc71 Merge pull request #1092 from trapexit/cleanup
Remove write_buf, simplify FUSE msg dispatching
2022-11-10 23:24:25 -05:00
Antonio SJ Musumeci dba9c4451a Remove write_buf, simplify FUSE msg dispatching 2022-11-10 22:43:02 -05:00
trapexit fea499e220 Merge pull request #1091 from trapexit/cleanup
Remove unnecessary libfuse abstractions
2022-11-07 23:35:18 -05:00
Antonio SJ Musumeci 9d056d609f Remove unnecessary libfuse abstractions 2022-11-07 23:09:25 -05:00
trapexit 9a839dd8ba Merge pull request #1089 from trapexit/32bit
Tweaks for 32bit systems
2022-11-05 23:37:19 -04:00
Antonio SJ Musumeci 32e2c9c48e Tweaks for 32bit systems 2022-11-05 23:28:10 -04:00
trapexit 83d7886a0a Merge pull request #1087 from trapexit/kernelh
Update fuse_kernel.h
2022-11-03 23:29:22 -04:00
Antonio SJ Musumeci 316abba0ac Update fuse_kernel.h 2022-11-03 23:28:44 -04:00
trapexit 8001c58c36 Merge pull request #1085 from trapexit/symlinkify
Fix setting of stat vars for symlinkify
2022-10-31 19:47:36 -04:00
Antonio SJ Musumeci 3b0d7fb26e Fix setting of stat vars for symlinkify 2022-10-30 22:27:33 -04:00
trapexit 5d3c5002e0 Merge pull request #1058 from trapexit/docs
Support doc update
2022-08-08 14:09:22 -04:00
Antonio SJ Musumeci 9082285cc8 Support doc update 2022-08-08 13:40:43 -04:00
trapexit d1762b2bac Merge pull request #1057 from trapexit/cfgerrs
Error when given invalid policy names
2022-08-06 23:08:10 -04:00
Antonio SJ Musumeci 46ce28b149 Error when given invalid policy names 2022-08-06 23:01:23 -04:00
trapexit faadeb75e0 Merge pull request #1045 from Gelma/master
Fix typos
2022-06-22 14:27:13 -04:00
Andrea Gelmini 338e559e53 Fix typos 2022-06-22 11:51:23 +02:00
trapexit 3db673410f Merge pull request #1039 from trapexit/docs
Add details about usage of FUSE to docs
2022-05-23 12:49:06 -04:00
Antonio SJ Musumeci 77f098614f Add details about usage of FUSE to docs 2022-05-23 12:40:00 -04:00
trapexit 9589846e9a Merge pull request #1032 from trapexit/support
Rework support section in readme
2022-04-25 17:44:16 -04:00
Antonio SJ Musumeci e2dda3c1b1 Rework support section in readme 2022-04-25 17:42:31 -04:00
trapexit d9ab18282f Merge pull request #1031 from trapexit/root-parent
Return ENOENT when dotdot for root node requested
2022-04-12 23:18:57 -04:00
Antonio SJ Musumeci 14c2ff9ab8 Return ENOENT when dotdot for root node requested 2022-04-12 23:17:39 -04:00
trapexit 77e6f400b5 Merge pull request #1026 from trapexit/loglock
Lock less often during logging
2022-03-20 22:45:07 -04:00
Antonio SJ Musumeci b95ff8ba3a Lock less often during logging 2022-03-20 22:34:55 -04:00
trapexit 4bf8738e5c Merge pull request #1025 from trapexit/estale
Change ENOENT to ESTALE for looking up paths to handle rename race conditions
2022-03-20 12:19:00 -04:00
Antonio SJ Musumeci 3f060f4512 Change ENOENT to ESTALE for looking up paths to handle rename race conditions
As done in https://github.com/libfuse/libfuse/pull/636
2022-03-20 12:09:22 -04:00
trapexit c4c35a9ad0 Merge pull request #1023 from trapexit/follow-symlink-fix
Fix query of attr during symlink
2022-03-19 17:01:11 -04:00
Antonio SJ Musumeci c9a935835f Fix query of attr during symlink
Using getattr when follow-symlink is enabled causes invalid type to the kernel
if symlink pointed to non-symlink.
2022-03-19 16:18:46 -04:00
trapexit 015002ebd9 Merge pull request #1020 from trapexit/slab-maintenance
Rework node slab garbage collection to limit blocking work threads
2022-03-19 15:54:29 -04:00
Antonio SJ Musumeci 9ca10b2413 Rework node slab garbage collection to limit blocking work threads
Also remove debug mode from forcing foreground mode
2022-03-19 13:33:14 -04:00
trapexit 2d5c2c2b6e Merge pull request #1007 from trapexit/trapexit-patch-1
Update bug_report.md
2022-01-18 21:16:22 -05:00
trapexit e3ab739a56 Update bug_report.md 2022-01-18 21:15:59 -05:00
trapexit c784d06029 Merge pull request #1005 from trapexit/readme-readahead
Update README.md regarding readahead
2022-01-15 17:39:31 -05:00
trapexit 58803f4845 Update README.md 2022-01-15 17:33:07 -05:00
trapexit f159fe61f9 Merge pull request #997 from trapexit/link-getattr
Call FUSE getattr rather than syscall for extra logic
2021-12-18 17:21:19 -05:00
Antonio SJ Musumeci c43b464d6c Call FUSE getattr rather than syscall for extra logic
While more efficient calling regular getattr (stat) leads to incorrect
information being returned and would not work correctly when using
symlinkify.
2021-12-18 17:07:23 -05:00
trapexit 34c684a4f5 Merge pull request #991 from trapexit/fix/symlink
Get attrs for link, not target
2021-12-16 19:35:18 -05:00
Antonio SJ Musumeci e256c88e4c Get attrs for link, not target 2021-12-16 19:11:29 -05:00
trapexit 518b4a6b85 Merge pull request #984 from trapexit/disable-locks
Ensure server handling of locks is disabled
2021-11-27 00:02:55 -05:00
Antonio SJ Musumeci ee8532c70d Ensure server handling of locks is disabled 2021-11-27 00:01:12 -05:00
trapexit c58da1ab9c Merge pull request #979 from trapexit/node-size
Remove embedded name in node struct
2021-11-01 22:46:09 -04:00
Antonio SJ Musumeci 68b3026264 Remove embedded name in node struct
The 32 byte array is below the average size so those 32 bytes are wasted a
large portion of the time.
2021-11-01 21:08:58 -04:00
trapexit 810c5a40f7 Merge pull request #978 from trapexit/debug2
Add new debug printing routines, fix ioctl cmd type
2021-10-27 22:31:58 -04:00
Antonio SJ Musumeci 18dead4d86 Add new debug printing routines 2021-10-27 22:12:10 -04:00
trapexit 2082357d3a Merge pull request #977 from trapexit/readme
Readme
2021-10-25 21:58:01 -04:00
Antonio SJ Musumeci 68719da828 Remove ioctl runtime section, use getfattr instead of xattr 2021-10-25 21:57:30 -04:00
trapexit 31fd1ec82e Merge pull request #975 from trapexit/sponsorship
Remove subscribestar from sponsor platforms
2021-10-15 23:30:33 -04:00
trapexit c3845097ee Remove subscribestar from sponsor platforms 2021-10-15 23:30:22 -04:00
trapexit d76934ee9d Merge pull request #972 from trapexit/log-node-info
Add option to log node memory usage metrics
2021-10-11 12:33:11 -04:00
Antonio SJ Musumeci 5f737cb7bf Add option to log node memory usage metrics 2021-10-11 11:57:16 -04:00
trapexit 50ede1ba97 Merge pull request #969 from trapexit/node-size
Reduce struct node size
2021-10-03 22:44:03 -04:00
Antonio SJ Musumeci 930dad31de Reduce struct node size
* Replace the stat values with a crc32b of them instead (for auto-cache)
* Replace char flag with bitfield
* Remove node generation. nodeid is an unsigned 64bit int. Would take 500K+ years
  to rollover at 1M nodes per second.
2021-10-03 22:15:34 -04:00
trapexit 88b91161e0 Merge pull request #968 from trapexit/fix
Fix regression from remember_node refactor
2021-09-30 22:14:51 -04:00
Antonio SJ Musumeci 8150957a01 Fix regression from remember_node refactor 2021-09-30 22:13:29 -04:00
trapexit fee97efafb Merge pull request #967 from trapexit/malloc-trim
Add malloc_trim configure test
2021-09-30 20:25:42 -04:00
trapexit 80856cf7ac Merge pull request #966 from trapexit/libupdates
Update ghc::filesystem and nonstd::optional
2021-09-30 20:05:40 -04:00
Antonio SJ Musumeci c929781d08 Add malloc_trim configure test 2021-09-30 20:00:40 -04:00
Antonio SJ Musumeci 313aa2a1d3 Update ghc::filesystem and nonstd::optional 2021-09-30 19:43:05 -04:00
trapexit 021ce3d97c Merge pull request #964 from trapexit/fmp
Rework of allocation in libfuse
2021-09-19 17:31:39 -04:00
Antonio SJ Musumeci 4ea0de3ef2 Rework dirents buffer management 2021-09-19 16:48:03 -04:00
Antonio SJ Musumeci 6b5c484fbf Major rework of memory allocation using fixed mem pools 2021-09-19 16:48:03 -04:00
trapexit b165332486 Merge pull request #959 from trapexit/readme
Add details on dropping caches before benchmarks
2021-09-03 07:22:21 -04:00
Antonio SJ Musumeci f598d3bc38 Add details on dropping caches before benchmarks 2021-09-03 07:21:16 -04:00
trapexit 4daa9e4eb9 Merge pull request #957 from trapexit/cirrus-ci
Add new cirrus-ci targets
2021-08-29 14:25:17 -04:00
Antonio SJ Musumeci 122f9c1d5d Add new cirrus-ci targets 2021-08-29 13:56:56 -04:00
trapexit 2da5cda4b3 Merge pull request #956 from trapexit/mount-bsd
Remove usage printing regression in mount_bsd
2021-08-29 13:20:07 -04:00
Antonio SJ Musumeci 5263a65dcf Remove usage printing regression in mount_bsd 2021-08-29 13:09:38 -04:00
trapexit 1255d3d1b2 Merge pull request #955 from trapexit/mfs
Fix infinite loop in mfs action policy
2021-08-29 13:07:40 -04:00
Antonio SJ Musumeci 8def16a811 Fix infinite loop in mfs action policy 2021-08-29 12:33:52 -04:00
trapexit 7d1683b607 Merge pull request #954 from trapexit/libfuse-cleanup
Major cleanup of libfuse to remove unneeded features
2021-08-29 12:15:05 -04:00
Antonio SJ Musumeci 43a6d66e3c Major cleanup of libfuse to remove unneeded features
* Remove request interrupt code. Required tracking of all requests unnecesssarily.
* Remove all debugging printing. Have plans to do full replacement.
* Remove deprecated functions.
* Remove unneeded error checking.
* Remove "userdata" which was unused.
* Remove allow_root feature.
2021-08-29 00:05:30 -04:00
trapexit 8b33f9a766 Merge pull request #953 from trapexit/readme-chia
Update docs with Chia wallet address
2021-08-26 12:57:16 -04:00
Antonio SJ Musumeci df721eb253 Update docs with Chia wallet address 2021-08-26 12:56:33 -04:00
trapexit 0547517a99 Merge pull request #951 from trapexit/readme
Add more clarity in what to provide when submitting bug reports
2021-08-22 13:12:03 -04:00
Antonio SJ Musumeci 54e85007a2 Add more clarity in what to provide when submitting bug reports or questions 2021-08-22 13:10:54 -04:00
trapexit 6e53f3b698 Merge pull request #945 from trapexit/readme
Update 'features' section of readme
2021-08-10 21:49:32 -04:00
Antonio SJ Musumeci 1aaf742c13 Update 'features' section of readme 2021-08-10 21:48:51 -04:00
trapexit 3754e20049 Merge pull request #944 from trapexit/trapexit-patch-1
tweak bug report template language
2021-08-09 21:10:16 -04:00
trapexit b4c031d1f0 tweak bug report template language 2021-08-09 21:09:54 -04:00
trapexit 3b8d495c43 Merge pull request #940 from trapexit/ioctl-runtime-removal
Remove config ioctl calls
2021-08-03 17:02:01 -04:00
Antonio SJ Musumeci 61c2187c14 Remove config ioctl calls
While they were going to be the new way of interacting with the runtime config
I've decided to use toml based config for mergerfs v3. In that case all updates
will happen through the config file and ioctl will only be used to load/reload
the config rather than a key:value like API.
2021-08-02 16:56:20 -04:00
trapexit 192e4a4af8 Merge pull request #939 from trapexit/update-nonstd-optional
Update nonstd::optional to v3.4.0
2021-08-02 16:29:40 -04:00
trapexit 749f0290e9 Merge pull request #938 from trapexit/update-ghc-filesystem
Update ghc::filesystem to v1.5.8
2021-08-02 16:24:22 -04:00
Antonio SJ Musumeci 7013ff9aeb Update nonstd::optional to v3.4.0 2021-08-02 16:10:56 -04:00
Antonio SJ Musumeci 3ce0dc56e4 Update ghc::filesystem to v1.5.8 2021-08-02 16:06:43 -04:00
trapexit 8e47bf4946 Merge pull request #926 from trapexit/modzero
pfrd: fix mod by zero error when all branches are filtered
2021-06-25 09:45:01 -04:00
Antonio SJ Musumeci 098f353bf1 pfrd: fix mod by zero error when all branches are filtered 2021-06-25 09:17:16 -04:00
trapexit 859b4da3dc Merge pull request #924 from trapexit/varinit-master
properly initialize fuse_buf in worker loop
2021-06-20 17:40:12 -04:00
Antonio SJ Musumeci 80f30999fa properly initialize fuse_buf in worker loop 2021-06-20 17:38:17 -04:00
trapexit 3fdd322a5c Merge pull request #914 from trapexit/readme
mention bind-propagation and add link to hotio
2021-05-29 13:27:19 -04:00
trapexit c40f42e57e mention bind-propagation and add link to hotio 2021-05-29 13:26:54 -04:00
trapexit 18e87980a4 Merge pull request #913 from trapexit/readme
Update README.md
2021-05-29 13:07:54 -04:00
trapexit 44efbaabe6 Update README.md
Add FAQ entry regarding container runtimes
2021-05-29 13:07:09 -04:00
trapexit a02f3a6856 Merge pull request #891 from trapexit/trapexit-patch-1
Update bug_report.md
2021-03-15 07:58:22 -04:00
trapexit 9573a053d6 Update bug_report.md
update the strace commands and make clear to use a recent release
2021-03-15 07:58:02 -04:00
trapexit 9a5d691619 Merge pull request #889 from trapexit/readme
README: update strace args and add question on snapraid
2021-03-11 15:37:37 -05:00
Antonio SJ Musumeci 495afeffb1 README: update strace args and add question on snapraid 2021-03-11 15:36:50 -05:00
trapexit 4acc1defe2 Merge pull request #888 from Gelma/typo
Fix typo
2021-03-06 17:49:40 -05:00
Andrea Gelmini 58b4eb9089 Fix typo 2021-03-06 23:38:33 +01:00
trapexit ca4b4c6d35 Merge pull request #884 from trapexit/travis
remove travis-ci
2021-02-25 15:40:32 -05:00
Antonio SJ Musumeci 9a90609428 remove travis-ci 2021-02-25 15:40:00 -05:00
trapexit 3e691149f5 Merge pull request #883 from trapexit/link-symlink
new features: follow-symlinks, rename-exdev, link-exdev
2021-02-25 15:33:26 -05:00
Antonio SJ Musumeci 8adebc9489 new features: follow-symlinks, rename-exdev, link-exdev
* follow-symlinks: allows mergerfs to transparently follow symlinks
* link-exdev: in the event a link returns EXDEV create a symlink instead
* rename-exdev: in the event a rename returns EXDEV move the oldpath and
  create a symlink for the newpath
2021-02-11 23:24:08 -05:00
trapexit 50daf84ec0 Merge pull request #880 from trapexit/branch-parse-fix
fix parsing of relative branch paths
2021-02-11 19:37:38 -05:00
Antonio SJ Musumeci d337574665 fix parsing of relative branch paths 2021-02-11 19:22:17 -05:00
trapexit 39d98f1eac Merge pull request #878 from trapexit/readdir_rewind_fix
reset dentry buffer when rewind'ed
2021-02-09 20:21:59 -05:00
trapexit cbea2ba91b Merge branch 'master' into readdir_rewind_fix 2021-02-09 18:13:08 -05:00
Antonio SJ Musumeci f6e37672b8 reset dentry buffer when rewind'ed 2021-02-09 18:09:28 -05:00
trapexit 55bd9a06b1 Merge pull request #876 from trapexit/readme
readme: update support section
2021-02-08 18:15:45 -05:00
Antonio SJ Musumeci 6a9f7e6afc readme: update support section 2021-02-08 17:55:49 -05:00
trapexit f2158d3e66 Merge pull request #871 from trapexit/support
README: update support section
2021-01-23 18:54:42 -05:00
Antonio SJ Musumeci 3bc189cfc8 README: update support section 2021-01-23 18:54:06 -05:00
trapexit d0c5ff7f87 Merge pull request #870 from trapexit/readme
README: add warning/clearity about caching script and hardlinks and cow
2021-01-23 17:19:59 -05:00
Antonio SJ Musumeci dfb544fed8 README: add warning/clearity about caching script and hardlinks and cow 2021-01-23 17:18:07 -05:00
trapexit ae893ca051 Merge pull request #865 from trapexit/config
config: rework global config, remove rwlock, make branches RCU like
2021-01-14 22:10:20 -05:00
Antonio SJ Musumeci 538467b86d config: rework global config, remove rwlock, make branches RCU like
Also added unit tests. Should have done separately but found a number of bugs.
2021-01-14 21:02:10 -05:00
trapexit 46de43d4ad Merge pull request #860 from trapexit/funding
Update FUNDING.yml
2021-01-01 15:52:41 -05:00
trapexit 3808e2683e Update FUNDING.yml 2021-01-01 15:52:15 -05:00
trapexit 52342a76c1 Merge pull request #858 from trapexit/docs
README: misc updates and tweaks
2020-12-29 19:36:30 -05:00
Antonio SJ Musumeci e9e17baf5e README: misc updates and tweaks 2020-12-29 19:34:13 -05:00
trapexit 2e4c6c5fd1 Merge pull request #856 from trapexit/dirname
dirname should not return an empty string
2020-12-18 17:03:43 -05:00
Antonio SJ Musumeci f50812a9ce dirname should not return an empty string 2020-12-18 16:04:17 -05:00
trapexit 861edff49a Merge pull request #855 from trapexit/builds
update cirrus-ci builds
2020-12-17 20:18:16 -05:00
Antonio SJ Musumeci 5e08357f0e update cirrus-ci builds 2020-12-17 20:17:16 -05:00
trapexit 411ee36c90 Merge pull request #854 from trapexit/mspfix
msp policies stopped before root path
2020-12-17 20:14:54 -05:00
Antonio SJ Musumeci 44a98910ba msp policies stopped before root path 2020-12-17 19:55:49 -05:00
trapexit 8a335d8a6e Merge pull request #851 from trapexit/fix
fix segv: zero out data structures
2020-12-13 16:56:55 -05:00
Antonio SJ Musumeci 3900543970 fix segv: zero out data structures 2020-12-13 13:40:04 -05:00
trapexit 2e34c7598a Merge pull request #845 from trapexit/configfile
config: fix invalid error check when parsing config file
2020-11-28 21:49:51 -05:00
Antonio SJ Musumeci 7edd3c6cf3 config: fix invalid error check when parsing config file 2020-11-28 20:26:18 -05:00
trapexit ddf0075938 Merge pull request #844 from trapexit/cleanup
general cleanup, slight memory reduction
2020-11-19 23:30:48 -05:00
Antonio SJ Musumeci 1b26f4908e general cleanup, slight memory reduction 2020-11-19 22:58:52 -05:00
trapexit 2d8fab534a Merge pull request #843 from trapexit/policyfixes
msp policies: used wrong path to check existance
2020-11-19 20:34:51 -05:00
Antonio SJ Musumeci c3fffefbc1 msp policies: used wrong path to check existance 2020-11-19 18:34:28 -05:00
trapexit eaa2593a4b Merge pull request #837 from renkoh/rsync-script-readme
Update percentage full expiring script
2020-10-03 01:05:54 -04:00
Ren Koh 7e583e3f48 use relative option for rsync 2020-10-03 14:45:51 +10:00
trapexit 2ad4891617 Merge pull request #835 from trapexit/readme
README.md: add details on per branch minfreespace
2020-09-29 18:51:30 -04:00
Antonio SJ Musumeci 8990e24c0f README.md: add details on per branch minfreespace 2020-09-29 18:50:25 -04:00
trapexit 88b80234c7 Merge pull request #834 from trapexit/readme
README.md: add details about error handling
2020-09-27 00:30:05 -04:00
Antonio SJ Musumeci f6e37c57fe README.md: add details about error handling 2020-09-27 00:28:00 -04:00
trapexit 562295be90 Merge pull request #833 from trapexit/wyhash-fix
wyhash: use safety mode 1
2020-09-24 09:03:10 -04:00
Antonio SJ Musumeci 04844420dc wyhash: use safety mode 1
mode 0 is not intended for unaligned buffers
2020-09-24 08:45:34 -04:00
trapexit 9c352ac555 Merge pull request #832 from trapexit/rework-errors
rework some function error handling
2020-09-23 00:26:03 -04:00
Antonio SJ Musumeci 5a3184359d rework some function error handling
chmod, chown, removexattr, setxattr, truncate, utimens:
* if no errors: return 0
* if no successes: return first error
* if file acted on was the same as related search function: return its value
* return 0

rmdir, unlink:
* if no errors: return 0
* return first error
2020-09-23 00:01:25 -04:00
trapexit 24f03b04eb Merge pull request #831 from trapexit/minfreespace-fix
properly return const ref from tofrom string wrapper
2020-09-20 11:18:24 -04:00
Antonio SJ Musumeci 610c75fa90 properly return const ref from tofrom string wrapper 2020-09-19 18:57:13 -04:00
trapexit dbc4774e8e Merge pull request #828 from trapexit/remove-old-issue-template
Delete issue_template.md
2020-09-13 10:30:02 -04:00
trapexit 73e8ed72cd Delete issue_template.md 2020-09-13 10:29:45 -04:00
trapexit e694585561 Merge pull request #827 from trapexit/bug_report
Update bug_report.md
2020-09-13 10:28:34 -04:00
trapexit 0b1af2b8ef Update bug_report.md 2020-09-13 10:28:20 -04:00
trapexit 668ac4000a Merge pull request #825 from trapexit/rv
option_parser: return 0 when requesting help or version
2020-09-10 22:16:03 -04:00
Antonio SJ Musumeci 767039c492 option_parser: return 0 when requesting help or version 2020-09-10 21:58:23 -04:00
trapexit b1c6d4864f Merge pull request #821 from trapexit/minfreespace
branches: add per branch minfreespace w/ original value as default
2020-09-04 21:26:38 -04:00
Antonio SJ Musumeci fc3453932a branches: add per branch minfreespace w/ original value as default
example: /mnt/disk0=RW,1G:/mnt/disk1=RW,2G
2020-09-04 21:04:45 -04:00
trapexit 98c49b8001 Merge pull request #819 from trapexit/readme
README.md: fix typos
2020-08-30 22:24:39 -04:00
Antonio SJ Musumeci 27bd39f5ac README.md: fix typos 2020-08-30 22:23:16 -04:00
trapexit 9f84f29e12 Merge pull request #818 from trapexit/lchmod
optionally use lchmod depending on if on Linux or not (BSD)
2020-08-26 15:42:42 -04:00
Antonio SJ Musumeci 6311df774d optionally use lchmod depending on if on Linux or not (BSD) 2020-08-26 12:44:32 -04:00
trapexit 9424784fad Merge pull request #817 from trapexit/readme
README.md: add note indicating only tagged releases are supported
2020-08-26 11:03:50 -04:00
Antonio SJ Musumeci 15fb751f65 README.md: add note indicating only tagged releases are supported 2020-08-26 11:02:56 -04:00
trapexit 3534fb1128 Merge pull request #815 from trapexit/policy-pfrd
add {,ep,msp}pfrd policies
2020-08-25 22:58:44 -04:00
Antonio SJ Musumeci 046844083f add {,ep,msp}pfrd policies
Percentage Free Random Distribution

Chooses a random branch based on the available space percentage free.

IE: if branch A has 1G free and branch B has 2G then B should be chosen
twice as often.
2020-08-25 22:19:01 -04:00
trapexit 7ff995631e Merge pull request #814 from trapexit/cleanup
general cleanup
2020-08-18 19:42:49 -04:00
Antonio SJ Musumeci 8675fce04a add fedora builds to cirrus-ci 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 7fd629ebf3 add #warning to make it more obvious what versions of functions are used 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 2fe20b888e clean up and separate out fs_* files 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 2696079601 break fs.hpp up into separate files 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci ec15872a1f cleanup function signatures and definitions 2020-08-18 19:28:36 -04:00
Antonio SJ Musumeci 6cc6524997 change category to enum class 2020-08-18 19:28:36 -04:00
trapexit 4c711159df Merge pull request #810 from trapexit/libfuse-cleanup
libfuse cleanup: add more header include guards
2020-08-08 19:16:51 -04:00
Antonio SJ Musumeci 7e173108fd libfuse cleanup: add more header include guards 2020-08-08 19:10:36 -04:00
trapexit 17925c4c93 Merge pull request #809 from trapexit/libfuse-cleanup
Libfuse cleanup
2020-08-08 19:00:43 -04:00
trapexit 90081f4e61 Merge pull request #808 from slashbeast/Makefiles-support-ar-cflags-cxxflags-ldflags-from-env
Makefiles fixes: support environmental passed $AR, $CFLAGS, $CXXFLAGS and $LDFLAGS
2020-08-08 16:30:09 -04:00
KARBOWSKI Piotr f488debf96 libfuse/Makefile: support CXXFLAGS and LDFLAGS passed via environment 2020-08-08 22:04:14 +02:00
KARBOWSKI Piotr 678626ef82 Makefile: support CXXFLAGS and LDFLAGS passed via environment 2020-08-08 22:03:50 +02:00
KARBOWSKI Piotr 0bc6711a95 libfuse/Makefile: support $AR 2020-08-08 22:03:28 +02:00
Antonio SJ Musumeci a925fbe59c libfuse cleanup: extern cplusplus cleanup 2020-08-08 14:25:44 -04:00
Antonio SJ Musumeci dc1b698847 libfuse cleanup: remove single threaded 2020-08-07 16:38:24 -04:00
Antonio SJ Musumeci 3c761b708b libfuse cleanup: remove libfuse API compatibility 2020-08-07 16:38:24 -04:00
Antonio SJ Musumeci 3bfdd78434 libfuse cleanup: remove cuse 2020-08-07 16:38:24 -04:00
Antonio SJ Musumeci 5f12fb6a5f libfuse cleanup: remove unnecessary files 2020-08-07 16:38:24 -04:00
Antonio SJ Musumeci f9b831eb1a libfuse cleanup: reindent 2020-08-07 16:38:24 -04:00
trapexit da07bc9637 Merge pull request #807 from trapexit/readme
README.md: add XMR address
2020-08-07 16:28:40 -04:00
Antonio SJ Musumeci badf7e1c60 README.md: add XMR address 2020-08-07 16:28:08 -04:00
trapexit 1f35b65725 Merge pull request #806 from trapexit/readme
README.md: update support section with Open Collective and LBC
2020-08-07 10:53:12 -04:00
Antonio SJ Musumeci 3b382624e5 README.md: update support section with Open Collective and LBC 2020-08-07 10:49:28 -04:00
trapexit 5f3a4cb0f1 Merge pull request #805 from trapexit/wyhash
change from fasthash64 to wyhash
2020-08-06 20:23:15 -04:00
Antonio SJ Musumeci 0371b047f9 change from fasthash64 to wyhash
wyhash has very good performance for small keys (our usecase) and
works on platforms with alignment concerns.

A user had an issue where fasthash64 lead to misaligned reads and
signal exceptions. wyhash does not have such issues and is faster.
2020-08-06 19:00:05 -04:00
trapexit 3f593d9c3b Merge pull request #800 from robertbaker/patch-1
fix: add fakeroot needed for building
2020-08-05 21:07:35 -04:00
Robert Baker 1fe32e9cd5 fix: add fakeroot needed for building 2020-08-05 03:14:47 -07:00
trapexit 58a8f8326d Merge pull request #798 from trapexit/inodecalc-32bit
inodecalc: add 32bit versions of hashs
2020-08-03 19:57:48 -04:00
Antonio SJ Musumeci 30d13b7f77 inodecalc: add 32bit versions of hashs 2020-08-03 19:42:06 -04:00
trapexit 124299e185 Merge pull request #797 from trapexit/getdents64
readdir: use getdents64 for compatibility with ARM64
2020-08-02 19:12:45 -04:00
Antonio SJ Musumeci 5989d41a86 readdir: use getdents64 for compatibility with ARM64 2020-08-02 18:43:09 -04:00
trapexit c97102bc9d Merge pull request #796 from trapexit/readme
README.md: move kernel bugs to wiki
2020-08-02 16:57:28 -04:00
Antonio SJ Musumeci 1d2a1ba31a README.md: move kernel bugs to wiki 2020-08-02 16:56:10 -04:00
trapexit 159c2366c7 Merge pull request #795 from trapexit/readme
README.md: update faq regarding Plex and page caching
2020-08-02 16:19:20 -04:00
Antonio SJ Musumeci e639f3d375 README.md: update faq regarding Plex and page caching 2020-08-02 16:18:47 -04:00
trapexit 23f870d8f2 Merge pull request #794 from trapexit/github-create-new-style-issue-templates
Update issue templates
2020-08-01 00:08:15 -04:00
trapexit 173193df74 Update issue templates 2020-08-01 00:07:50 -04:00
trapexit d1d21ead1f Merge pull request #792 from trapexit/options
add missing options to usage
2020-07-30 17:13:15 -04:00
Antonio SJ Musumeci 0709b2e447 add missing options to usage 2020-07-30 16:55:18 -04:00
trapexit 50d967221e Merge pull request #791 from trapexit/configcleanup
cleanup: move some config data structures to separate files
2020-07-29 21:56:29 -04:00
Antonio SJ Musumeci 15a0aedd25 cleanup: move some config data structures to separate files 2020-07-29 21:42:43 -04:00
trapexit 8052039c6f Merge pull request #790 from trapexit/readme
README: change back to listing fuse.mergerfs for fstab fstype
2020-07-29 14:40:22 -04:00
Antonio SJ Musumeci 8afe72d371 README: change back to listing fuse.mergerfs for fstab fstype 2020-07-29 14:39:53 -04:00
trapexit 0682ebf103 Merge pull request #789 from trapexit/freebsd
freebsd: misc cleanups to get freebsd compiling
2020-07-29 14:04:04 -04:00
Antonio SJ Musumeci a93bd9f7fa freebsd: misc cleanups to get freebsd compiling 2020-07-29 13:08:38 -04:00
trapexit 6f76080c6b Merge pull request #787 from trapexit/nfshack
nfsopenhack: remove empty file check
2020-07-26 18:33:44 -04:00
Antonio SJ Musumeci 139e61efaa nfsopenhack: remove empty file check 2020-07-26 17:11:06 -04:00
trapexit b50a128777 Merge pull request #786 from trapexit/gitnfs
NFS open/creat hack
2020-07-24 21:16:06 -04:00
Antonio SJ Musumeci 93218a343a NFS open/creat hack
A hack to work around non-POSIX NFS <-> FUSE behavior where it turns
an open(O_EXCL|O_CREAT,0444) into multiple calls that results in
EACCES.
2020-07-23 19:36:10 -04:00
trapexit 5a0568495b Merge pull request #784 from trapexit/policy-mspmfs
policy: add "most shared path" policies
2020-07-21 18:40:27 -04:00
Antonio SJ Musumeci 3ec137c4ad policy: add "most shared path" policies
Like path preserving but walks back the path till a match is found. Should
cover the usecase where someone wants a "less strict" form of path
preservation.
2020-07-21 16:31:08 -04:00
trapexit d4eecf3b56 Merge pull request #783 from Gelma/typo
Fix typos
2020-07-20 07:35:08 -04:00
Andrea Gelmini 3a6738475a Fix typos 2020-07-20 13:01:33 +02:00
Andrea Gelmini 468d42088a Removed duplicated include 2020-07-20 12:51:44 +02:00
trapexit 4a4db4be84 Merge pull request #782 from trapexit/moveonenospc-policy
moveonenospc: enhance the feature to allow using a policy
2020-07-15 21:24:17 -04:00
Antonio SJ Musumeci 08d267fc61 moveonenospc: enhance the feature to allow using a policy
Just like functions you can now set a policy for moveonfreenospc. This
allows for more flexibility.

For backwards compatibility moveonfreenospc=true is converted to
moveonfreenospc=mfs.

minfreespace does apply which is slightly different from original behavior.
2020-07-15 19:33:05 -04:00
trapexit f2d4fbfdb2 Merge pull request #781 from trapexit/readdir-offset-fix
readdir: add dirent index array
2020-07-12 13:13:49 -04:00
Antonio SJ Musumeci c4a85f5fad readdir: add dirent index array
This removes the risk of corrupted data being sent back to the kernel
should it use the offset from one opendir-readdir in another. In this case
it would at most skip dirents.

According to the standards the offset is only valid when used within
the opendir -> releasedir but NFS uses offsets across independent
calls.
2020-07-12 12:43:33 -04:00
trapexit c3f4d2914f Merge pull request #780 from trapexit/nopath
remove unnecessary libfuse flags nopath, nullpath_ok, and utime_omit_ok
2020-07-08 19:46:14 -04:00
Antonio SJ Musumeci c0990644e8 remove unnecessary libfuse flags nopath, nullpath_ok, and utime_omit_ok 2020-07-08 19:25:23 -04:00
trapexit b1f30b703f Merge pull request #779 from trapexit/nfs-readdir
temporary fix for short readdirs on NFS
2020-07-08 14:25:37 -04:00
Antonio SJ Musumeci 62899565e6 temporary fix for short readdirs on NFS 2020-07-08 14:10:53 -04:00
trapexit ddb435a44b Merge pull request #777 from trapexit/dpkgfix
fix rpm spec and chmod in deb
2020-06-30 11:39:23 -04:00
Antonio SJ Musumeci d699a97928 fix rpm spec and chmod in deb 2020-06-30 11:07:39 -04:00
trapexit 06660ca25e Merge pull request #776 from trapexit/dpkgfix
debian: fix installing of fresh deb
2020-06-30 09:31:06 -04:00
Antonio SJ Musumeci 8ed3a1f36a debian: fix installing of fresh deb 2020-06-30 09:30:31 -04:00
trapexit 8948fc2935 Merge pull request #775 from trapexit/inode-calc
add 'inodecalc' option to allow selection of inode calculation algo
2020-06-28 20:52:36 -04:00
Antonio SJ Musumeci b4397f7f2e add 'inodecalc' option to allow selection of inode calculation algo 2020-06-28 20:02:07 -04:00
trapexit cb74573fc2 Merge pull request #774 from trapexit/readdir
additional readdir refactor cleanup
2020-06-27 23:33:00 -04:00
Antonio SJ Musumeci dbdd3e22fc additional readdir refactor cleanup
Differences between readdir and getdents is minimal at best. Leaving code
for now to allow for possible expansion later.
2020-06-27 23:17:21 -04:00
trapexit 4160a8e2f8 Merge pull request #767 from trapexit/cirrus-ci
add Cirrus-CI
2020-06-18 22:47:54 -04:00
Antonio SJ Musumeci afb07b170d add Cirrus-CI 2020-06-18 22:37:05 -04:00
trapexit ebcf98ac2d Merge pull request #766 from trapexit/ini
rework config management
2020-06-18 19:53:34 -04:00
Antonio SJ Musumeci 54c41c4994 rework config management
* Add standard way to do str2val and val2str conversion
* Add support for a config file
* Add support for reading and writing settings via ioctl
2020-06-18 18:54:25 -04:00
trapexit 8d989777f8 Merge pull request #763 from trapexit/install-test
Travis-CI: add installing of deb package
2020-06-11 12:53:21 -04:00
Antonio SJ Musumeci a0c1c1a997 Travis-CI: add installing of deb package 2020-06-11 12:45:20 -04:00
trapexit bed493e8dd Merge pull request #758 from trapexit/readme
README: add terminology section and tweak some things
2020-05-25 11:57:32 -04:00
Antonio SJ Musumeci add588fdea README: add terminology section and tweak some things 2020-05-25 11:56:38 -04:00
trapexit a61d420224 Merge pull request #755 from trapexit/support
README: update support section
2020-05-18 23:26:56 -04:00
Antonio SJ Musumeci eb78c512ee README: update support section 2020-05-18 23:26:09 -04:00
trapexit ee5851bb1c Merge pull request #754 from trapexit/deb-build
fix install location and setuid setting in deb pkgs
2020-05-13 19:52:55 -04:00
Antonio SJ Musumeci c6bce81747 fix install location and setuid setting in deb pkgs 2020-05-13 19:37:17 -04:00
trapexit e0bb4e407a Merge pull request #749 from trapexit/docs
README.md: add human readable versions of some errno references
2020-05-06 18:48:41 -04:00
Antonio SJ Musumeci aad2257387 README.md: add human readable versions of some errno references
Also some additional "why use mergerfs over X?"
2020-05-06 18:46:39 -04:00
trapexit 831dba3ef6 Merge pull request #741 from trapexit/docs
README.md: request users update to latest version before they submit …
2020-04-20 09:17:25 -04:00
Antonio SJ Musumeci eff15c72a5 README.md: request users update to latest version before they submit report 2020-04-20 09:16:43 -04:00
trapexit edb50e5411 Merge pull request #737 from trapexit/docs
add basic setup section
2020-04-05 12:46:04 -04:00
Antonio SJ Musumeci 7c37a69d25 add basic setup section 2020-04-05 12:29:20 -04:00
trapexit be3499ee56 Merge pull request #730 from trapexit/readdir
fix getdent name length calculation
2020-03-13 14:13:10 -04:00
Antonio SJ Musumeci 698c4147fc fix getdent name length calculation 2020-03-12 23:45:20 -04:00
trapexit b5fae4b356 Merge pull request #729 from trapexit/readme
README: update a number of sections and fix typos
2020-03-11 12:19:08 -04:00
Antonio SJ Musumeci 0888ee12a5 README: update a number of sections and fix typos 2020-03-11 12:16:26 -04:00
trapexit 7e8635b24b Merge pull request #726 from trapexit/readme
README: add note regarding space calculations
2020-03-10 11:31:25 -04:00
Antonio SJ Musumeci 8ba1abafec README: add note regarding space calculations 2020-03-10 11:21:40 -04:00
trapexit 14d26ba434 Merge pull request #723 from trapexit/getdents
fix filename hashing error
2020-03-07 12:58:22 -05:00
Antonio SJ Musumeci 3a46ec9fab fix filename hashing error 2020-03-07 12:09:40 -05:00
trapexit ada90eab57 Merge pull request #722 from trapexit/alpine
fix name length calculation for musc
2020-02-29 18:05:42 -05:00
Antonio SJ Musumeci 9b2634a1e5 fix name length calculation for musc 2020-02-29 17:15:29 -05:00
trapexit 4cade5685c Merge pull request #720 from trapexit/readdir_plus
Readdir plus
2020-02-28 20:44:54 -05:00
Antonio SJ Musumeci 62873d2d3b use getdents64 on linux 2020-02-28 20:34:12 -05:00
Antonio SJ Musumeci d119807adb restructure readdir, add readdir_plus
Does not enable READDIR_AUTO. Might add in the future.
2020-02-22 19:45:52 -05:00
trapexit 10e8bd9fa3 Merge pull request #716 from trapexit/mount
rework makefiles + install mount tools
2020-02-22 17:00:55 -05:00
Antonio SJ Musumeci 5ce428ca59 rework makefiles + install mount tools 2020-02-21 12:33:00 -05:00
trapexit 2f769e75c4 Merge pull request #711 from trapexit/inode
change inode conversion algo to reduce collision
2020-02-12 21:51:41 -05:00
Antonio SJ Musumeci a646fe016d change inode conversion algo to reduce collision 2020-02-12 21:05:52 -05:00
trapexit f45a52d621 Merge pull request #710 from trapexit/writeback-xattr
add cache.writeback to xattrs
2020-02-12 19:52:16 -05:00
Antonio SJ Musumeci 576ff3694e add cache.writeback to xattrs 2020-02-12 19:48:37 -05:00
trapexit 4cb866cee9 Merge pull request #707 from trapexit/writeback-cache
add writeback caching
2020-02-10 19:22:44 -05:00
trapexit 43ddff6ca9 Merge pull request #706 from trapexit/ctime
ctime
2020-02-10 19:13:35 -05:00
Antonio SJ Musumeci bf4b39065b update fuse_kernel.h 2020-02-10 18:54:07 -05:00
Antonio SJ Musumeci 4b9f3de814 add ctime support 2020-02-10 18:39:15 -05:00
Antonio SJ Musumeci 903d39f968 add writeback caching 2020-02-09 21:08:53 -05:00
trapexit e6c8fa3fad Merge pull request #705 from trapexit/zfs
add links to ZFS comparison
2020-02-04 11:08:18 -05:00
Antonio SJ Musumeci 9952c58a4a add links to ZFS comparison 2020-02-04 11:06:51 -05:00
trapexit be8bb5d2a7 Merge pull request #701 from trapexit/faq
add faq entry on files ending up on 1 branch
2020-01-24 20:13:07 -05:00
Antonio SJ Musumeci 66f0d9e649 add faq entry on files ending up on 1 branch 2020-01-24 20:12:24 -05:00
trapexit 7b355b279d Merge pull request #700 from trapexit/changelog
python 2.6 fix
2020-01-22 16:49:07 -05:00
Antonio SJ Musumeci 0f2f78c5b8 python 2.6 fix 2020-01-22 16:48:46 -05:00
trapexit eb57425315 Merge pull request #699 from trapexit/git2debcl
make git2debcl work on Python 2 & 3
2020-01-22 14:30:01 -05:00
Antonio SJ Musumeci 489ab23e89 make git2debcl work on Python 2 & 3 2020-01-22 11:56:40 -05:00
trapexit 4c55eb0cc0 Merge pull request #697 from trapexit/readme
add README segment on benchmarking
2020-01-20 22:27:29 -05:00
Antonio SJ Musumeci 389785265b add README segment on benchmarking 2020-01-20 22:26:20 -05:00
trapexit a6717990a8 Merge pull request #694 from trapexit/readme
more FAQs
2020-01-19 22:27:29 -05:00
Antonio SJ Musumeci f4b8efce20 more FAQs 2020-01-19 22:26:20 -05:00
trapexit 8cdb7174c4 Merge pull request #689 from trapexit/readme
fix typos and update FAQ regarding policy preference
2020-01-13 14:27:18 -05:00
Antonio SJ Musumeci 4625a3cf43 fix typos and update FAQ regarding policy preference 2020-01-13 14:26:45 -05:00
trapexit 55245e290a Merge pull request #685 from trapexit/update-docs
update docs: openvz kernel bug and new support links
2019-12-10 23:22:34 -05:00
Antonio SJ Musumeci 322fa5778a update docs: openvz kernel bug and new support links 2019-12-10 23:21:45 -05:00
trapexit 5eb3c8cbef add github sponsor and subscribestar to funding 2019-12-08 01:09:31 -05:00
trapexit 5d827566f6 Create FUNDING.yml 2019-12-02 07:19:49 -05:00
trapexit 6058ac0716 Merge pull request #677 from trapexit/moveonenospc
fix short writes on >2GB files when cloning file
2019-10-14 23:34:36 -04:00
Antonio SJ Musumeci 4d82ed9324 fix short writes on >2GB files when cloning file 2019-10-14 23:32:51 -04:00
trapexit 026dc5f750 Merge pull request #675 from trapexit/fuse_ino
improve nodeid generation
2019-09-30 23:34:53 -04:00
Antonio SJ Musumeci 08e1bef5a9 improve nodeid generation
* Change fuse_ino_t to uint64_t
* Change id generation to 64bit
* Randomize generation value (should help with NFS)
2019-09-30 23:28:45 -04:00
trapexit b5f6cd70de Merge pull request #669 from serkef/patch-1
Fix typo
2019-09-20 16:41:10 -04:00
Sergio Kef 58ab7f7fa8 Fix typo 2019-09-20 22:32:09 +02:00
trapexit c46c41c740 Merge pull request #668 from trapexit/travis
update travis config to include bionic
2019-09-16 08:41:50 -04:00
Antonio SJ Musumeci 7a84555db8 update travis config to include bionic 2019-09-16 08:29:07 -04:00
trapexit 0b0f0a1b9e Merge pull request #663 from trapexit/random
only return 1 branch for rand/eprand policies
2019-09-07 17:27:56 -04:00
Antonio SJ Musumeci 0fffabfbe3 only return 1 branch for rand/eprand policies 2019-09-07 17:25:06 -04:00
trapexit 6b3f703ba9 Merge pull request #662 from trapexit/unlink
initialize mutex to fix lockup
2019-09-07 15:14:10 -04:00
Antonio SJ Musumeci 50ad648ce4 initialize mutex to fix lockup 2019-09-07 14:10:36 -04:00
trapexit 2dbcd2b07a Merge pull request #660 from trapexit/docs
add UPGRADE section
2019-09-03 21:43:47 -04:00
Antonio SJ Musumeci 752a15953a add UPGRADE section 2019-09-03 21:43:18 -04:00
trapexit 23d365f67b Merge pull request #659 from zofrex/relink-mount-mergerfs
Force symlink creation (don't error if it already exists)
2019-08-31 13:46:13 -04:00
James Sanderson f77d1d9929 Force symlink creation (don't error if it already exists) 2019-08-31 18:41:54 +01:00
trapexit 4204c807bc Merge pull request #657 from trapexit/docs
update how it works
2019-08-20 20:16:03 -04:00
Antonio SJ Musumeci e6a6ab967b update how it works 2019-08-20 20:13:25 -04:00
trapexit 52e4d60e7c Merge pull request #655 from tnilanon/master
Update README.md
2019-08-15 06:22:39 -04:00
Tanachat Nilanon 38919add61 Update README.md
Fixed typo: changed ' to `
2019-08-14 23:57:56 -07:00
trapexit a7244184bd Merge pull request #653 from xlotlu/patch-1
fix some grammar and typos
2019-08-01 08:06:52 -04:00
Ionuț Ciocîrlan 86bda61aa7 fix some grammar and typos 2019-08-01 14:35:00 +03:00
trapexit 10e5829b8e Merge pull request #643 from trapexit/ioctl_setuid
set uid & gid when calling ioctl
2019-06-17 17:09:16 -04:00
Antonio SJ Musumeci 4c4c27a93e set uid & gid when calling ioctl
Turns out certain ioctl calls check the effective user id (FS_IOC_SETFLAGS).
2019-06-17 13:37:15 -04:00
trapexit 72ac1fc0d1 Merge pull request #639 from trapexit/args
accept old arguments for backwards compatibility
2019-06-10 20:24:36 -04:00
Antonio SJ Musumeci 5ca928e436 accept old arguments for backwards compatibility 2019-06-10 20:20:41 -04:00
trapexit 5d013f4a18 Merge pull request #636 from trapexit/max_pages
allow setting of 'max_pages' (via 'fuse_msg_size')
2019-06-03 21:16:53 -04:00
Antonio SJ Musumeci 7cbd88ac81 allow setting of 'max_pages' (via 'fuse_msg_size')
Linux 4.20 and above allow setting the number of pages per FUSE message
upto 256 (4K * 256 = 1MiB). This can greatly increase read and write
speeds depending on the workload.
2019-06-03 21:01:19 -04:00
trapexit 24af276542 Merge pull request #635 from trapexit/copy_file_range
add copy_file_range support
2019-06-03 08:31:22 -04:00
Antonio SJ Musumeci 8cb7195c3e add copy_file_range support 2019-06-03 07:12:05 -04:00
trapexit c8e178bf71 Merge pull request #633 from trapexit/cache.files
add file caching across opens and runtime control
2019-05-30 00:28:28 -04:00
Antonio SJ Musumeci 529a953e30 add file caching across opens and runtime control 2019-05-30 00:10:08 -04:00
trapexit 02b657edff Merge pull request #632 from trapexit/async-read
jmake async_read optional again
2019-05-28 17:10:17 -04:00
Antonio SJ Musumeci ddf6a2f105 make async_read optional again 2019-05-28 15:57:50 -04:00
trapexit 40a4762cac Merge pull request #630 from trapexit/cache-dir
add readdir caching
2019-05-23 22:26:19 -04:00
Antonio SJ Musumeci 2323c16316 add readdir caching 2019-05-23 22:11:33 -04:00
trapexit bac4b33ef8 Merge pull request #629 from trapexit/cache-symlink
add symlink caching
2019-05-22 19:50:49 -04:00
Antonio SJ Musumeci 1baa706d37 add symlink caching 2019-05-22 19:25:38 -04:00
trapexit 9e3331dab4 Merge pull request #628 from trapexit/hide-cleanup
fix for unlink race condition
2019-05-21 23:09:13 -04:00
Antonio SJ Musumeci 61cded5b34 fix for unlink race condition 2019-05-21 22:57:02 -04:00
trapexit 060cae6270 Merge pull request #627 from trapexit/symlink
fix mount.mergerfs generation
2019-05-20 08:18:21 -04:00
Antonio SJ Musumeci 229d8517c0 fix mount.mergerfs generation 2019-05-20 08:17:06 -04:00
trapexit cc9381f578 Merge pull request #624 from trapexit/parallel_dirops
ensure parallel dirops is enabled if capable
2019-05-20 00:37:56 -04:00
Antonio SJ Musumeci 5f2221155c ensure parallel dirops is enabled if capable 2019-05-20 00:36:42 -04:00
trapexit 7da8dd6b7a Merge pull request #623 from trapexit/async_dio
ensure async_aio is enabled if capable
2019-05-20 00:22:06 -04:00
Antonio SJ Musumeci 2b019b84b0 ensure async_aio is enabled if capable 2019-05-20 00:18:56 -04:00
trapexit 741c559df1 Merge pull request #622 from trapexit/rename
ensure marking open files renamed over as hidden
2019-05-19 23:43:24 -04:00
Antonio SJ Musumeci 3a66a68edb ensure marking open files renamed over as hidden 2019-05-19 23:25:46 -04:00
trapexit af1c0d78e8 Merge pull request #621 from trapexit/posix-acl
add support for POSIX ACLs
2019-05-19 18:26:49 -04:00
Antonio SJ Musumeci 80d56ac94e add support for POSIX ACLs 2019-05-19 18:17:55 -04:00
trapexit 41881f9d05 Merge pull request #620 from trapexit/flock
remove 'remote' flock support
2019-05-19 17:40:03 -04:00
Antonio SJ Musumeci 1ca70521a4 remove 'remote' flock support 2019-05-19 17:34:56 -04:00
trapexit ee0d87d712 Merge pull request #619 from trapexit/hiddenfiles
fix renaming over open unlinked file
2019-05-16 14:49:38 -04:00
Antonio SJ Musumeci c21aa34221 fix renaming over open unlinked file 2019-05-16 14:40:30 -04:00
trapexit 1ad79067e4 Merge pull request #617 from trapexit/docs
update support section of README
2019-05-12 23:58:28 -04:00
Antonio SJ Musumeci 810871d2df update support section of README 2019-05-12 23:57:15 -04:00
trapexit 6404939156 Merge pull request #615 from trapexit/fsname
fix setting of fsname
2019-05-12 14:30:37 -04:00
Antonio SJ Musumeci e052446713 fix setting of fsname 2019-05-12 14:07:21 -04:00
trapexit 5e16851c79 Merge pull request #611 from trapexit/man
tweak docs
2019-05-10 12:03:22 -04:00
Antonio SJ Musumeci 5883020784 tweak docs 2019-05-10 11:54:41 -04:00
trapexit 726c1c850c Merge pull request #610 from trapexit/tests
create integration tests
2019-05-05 17:50:08 -04:00
Antonio SJ Musumeci 374580a85d create integration tests 2019-05-04 17:16:52 -04:00
trapexit 4293cb7253 Merge pull request #609 from trapexit/make
cleanup and rework build system
2019-05-03 18:49:45 -04:00
Antonio SJ Musumeci 825fcf7f35 cleanup and rework build system 2019-05-03 09:42:15 -04:00
trapexit 85e2a3e71b Merge pull request #605 from trapexit/man
add reference to 32bit mmap kernel bug
2019-04-23 20:02:01 -04:00
Antonio SJ Musumeci 3288d834a9 add reference to 32bit mmap kernel bug 2019-04-23 20:01:36 -04:00
trapexit 209dc2252a Merge pull request #603 from trapexit/backcompat
fix outarg size to accomidate newer fuse_kernel.h on older platforms
2019-04-16 22:23:03 -04:00
Antonio SJ Musumeci df0d0552b2 fix outarg size calculation to accomidate newer fuse_kernel.h on older platforms 2019-04-16 22:13:32 -04:00
trapexit 05f33c84e4 Merge pull request #601 from trapexit/hidden
remove .fuse_hidden file creation
2019-04-14 19:52:19 -04:00
Antonio SJ Musumeci 6ecc618d83 remove .fuse_hidden file creation
addresses #521

mergerfs is written using libfuse's high level API. The HLAPI forces a certain
behavior with regard to unlinking or rename overwriting of open files. It
renames the file to .fuse_hiddenXXXXXX and when incoming requests refer
to the unlinked file it will use path based calls and use that path.

This behavior leads to rmdirs failing or some software noticing
the .fuse_hidden files.

This patch changes the HLAPI and removes entirely the .fuse_hidden behavior.

See the README for more.
2019-04-13 21:41:51 -04:00
trapexit 46be946672 Merge pull request #599 from trapexit/clonefile
fall back to other file copy methods in clonefile
2019-04-13 15:15:37 -04:00
Antonio SJ Musumeci 2a075ead92 fall back to other file copy methods in clonefile 2019-04-13 15:08:13 -04:00
trapexit 8bd09628ef Merge pull request #593 from trapexit/outbuf
ioctl: don't set outbufsz when not needed
2019-03-20 10:30:56 -04:00
Antonio SJ Musumeci b69819e4b2 ioctl: don't set outbufsz when not needed 2019-03-20 10:18:55 -04:00
trapexit 2ca0cb0fbf Merge pull request #592 from trapexit/tarball
makefile: remove superclean dependency from tarball
2019-03-12 23:24:41 -04:00
Antonio SJ Musumeci 61d764d0fd makefile: remove superclean dependency from tarball 2019-03-12 23:24:17 -04:00
trapexit 8b007ada61 Merge pull request #591 from trapexit/parallel-deb
parallel deb building
2019-03-12 23:14:06 -04:00
Antonio SJ Musumeci b0b265b8e5 parallel deb building 2019-03-12 23:11:27 -04:00
trapexit cab7033d5b Merge pull request #589 from trapexit/make
rework makefiles for better manage parallel builds
2019-03-10 19:39:08 -04:00
Antonio SJ Musumeci 1be9900733 rework makefiles for better manage parallel builds 2019-03-10 19:29:35 -04:00
trapexit 3c2a9cd4f8 Merge pull request #586 from trapexit/man
replace {attr,entry,negative}_timeout references
2019-02-23 01:40:08 -05:00
Antonio SJ Musumeci 38037feab7 replace {attr,entry,negative}_timeout references 2019-02-23 01:39:28 -05:00
trapexit 99d44cfc5d Merge pull request #585 from trapexit/debfix
fix building of deb pkg
2019-02-23 01:35:59 -05:00
Antonio SJ Musumeci 2af0dcd273 fix building of deb pkg 2019-02-23 01:35:22 -05:00
trapexit d9f430fda8 Merge pull request #584 from trapexit/timeouts
make attr, entry, negative_entry cache timeouts runtime configurable
2019-02-23 01:24:45 -05:00
Antonio SJ Musumeci 0918dfd117 make attr, entry, negative_entry cache timeouts runtime configurable 2019-02-23 01:03:20 -05:00
trapexit a64c7c97ad Merge pull request #583 from trapexit/libfuse
replace libfuse's autoconf with makefile and ecfd
2019-02-18 21:46:10 -05:00
Antonio SJ Musumeci 95c0cc741d replace libfuse's autoconf with makefile 2019-02-18 21:36:32 -05:00
trapexit 8b0342067c Merge pull request #582 from trapexit/discord
add discord server to docs
2019-02-17 14:55:56 -05:00
Antonio SJ Musumeci 1d48dba0b7 add discord server to docs 2019-02-17 14:46:50 -05:00
trapexit e8622fdd64 Merge pull request #581 from trapexit/ioctl
fix FS_IOC_{GET,SET}{FLAGS,VERSION} ioctl calls
2019-02-17 13:11:57 -05:00
Antonio SJ Musumeci 7bf607bb6b fix FS_IOC_{GET,SET}{FLAGS,VERSION} ioctl calls 2019-02-17 11:48:19 -05:00
trapexit a80f9a0b29 Merge pull request #580 from trapexit/freebsd
misc fixes for FreeBSD
2019-02-06 23:43:16 -05:00
Antonio SJ Musumeci 940c323251 misc fixes for FreeBSD 2019-02-06 23:20:28 -05:00
trapexit babe8bcf47 Merge pull request #579 from trapexit/freebsd
remove O_LARGEFILE
2019-02-06 21:14:50 -05:00
Antonio SJ Musumeci 5ab7d2dd72 remove O_LARGEFILE 2019-02-06 21:06:11 -05:00
trapexit 401502579b Merge pull request #577 from trapexit/modules
remove libfuse modules
2019-02-03 19:56:45 -05:00
Antonio SJ Musumeci 65d3bfe84d remove libfuse modules 2019-02-03 19:49:11 -05:00
trapexit cfd5d31338 Merge pull request #575 from trapexit/cleanup
general code cleanup
2019-01-31 23:50:40 -05:00
Antonio SJ Musumeci 9d9ee7b71d general code cleanup 2019-01-31 22:47:09 -05:00
trapexit 5a1a60742e Merge pull request #574 from trapexit/opts
remove `defaults`, hard code atomic_o_trunc, big_writes, and default_…
2019-01-30 22:53:35 -05:00
Antonio SJ Musumeci c5b2415daf remove defaults, hard code atomic_o_trunc, big_writes, and default_permissions
`defaults` is a value used by all filesystems and isn't passed through to
mergerfs when mounting via the fstab or the mount command. This led
to inconsistent application of options. atomic_o_trunc, big_writes, and
default_permissions should be enabled all the time anyway and splice_*
can lead to issues so they are not always enabled.
2019-01-30 22:46:21 -05:00
trapexit a0bb2f8480 Merge pull request #568 from trapexit/docs
fix <> escaping in argument list
2019-01-24 11:16:55 -05:00
Antonio SJ Musumeci f4a06cae18 fix <> escaping in argument list 2019-01-24 11:15:55 -05:00
trapexit 21d690a210 Merge pull request #567 from trapexit/statfs_cache
add statfs cache
2019-01-23 23:11:17 -05:00
Antonio SJ Musumeci 5be7e007ce add statfs cache 2019-01-23 23:06:07 -05:00
trapexit f42bd8666e Merge pull request #565 from trapexit/open_cache
add policy cache for 'open'
2019-01-22 22:50:25 -05:00
Antonio SJ Musumeci 7a057daa0c add policy cache for 'open'
A fusepath -> basepath cache for `open` to limit the overhead of FUSE in 'open, read/write, close' patterns (such as Transmission).
2019-01-22 09:21:15 -05:00
trapexit 8e0aa1d9a6 Merge pull request #564 from trapexit/nfs
add mention of noforget argument for NFS exports
2019-01-21 18:42:19 -05:00
Antonio SJ Musumeci a13b822543 add mention of noforget argument for NFS exports 2019-01-21 18:41:19 -05:00
trapexit 40319081e0 Merge pull request #563 from trapexit/ioctl
make ioctl on directories use open policy
2019-01-18 22:58:13 -05:00
Antonio SJ Musumeci 9fd3b968fc make ioctl on directories use open policy 2019-01-18 22:41:52 -05:00
trapexit 24a7973955 Merge pull request #559 from trapexit/docs
clarify descriptions regarding funcitons and those without policies
2019-01-15 10:26:46 -05:00
Antonio SJ Musumeci a57c680cc8 clarify descriptions regarding funcitons and those without policies 2019-01-15 10:18:57 -05:00
trapexit 11be067895 Merge pull request #558 from trapexit/docs
add more info on different caching techniques
2019-01-15 09:51:09 -05:00
Antonio SJ Musumeci 3a1213435f add more info on different caching techniques 2019-01-15 09:39:39 -05:00
trapexit 56f7df028b Merge pull request #556 from trapexit/direct
set direct_io per open/create, now runtime configurable
2019-01-11 15:36:39 -05:00
Antonio SJ Musumeci 87b2795f2b set direct_io per open/create, now runtime configurable 2019-01-11 15:23:14 -05:00
trapexit c36bc5320d Merge pull request #555 from trapexit/docs
misc doc updates
2019-01-09 11:31:40 -05:00
Antonio SJ Musumeci 7eefb58ff7 misc doc updates 2019-01-09 11:25:06 -05:00
trapexit 6a247501e1 Merge pull request #552 from trapexit/enospc
update README regarding valid ENOSPC errors
2019-01-07 09:26:35 -05:00
Antonio SJ Musumeci 0b5a0d1228 update README regarding valid ENOSPC errors 2019-01-07 09:20:34 -05:00
trapexit 8686eadcf4 Merge pull request #550 from trapexit/mv_warning
mention mv quirk in known issues
2019-01-06 13:24:51 -05:00
Antonio SJ Musumeci aa56e12040 mention mv quirk in known issues 2019-01-06 13:24:12 -05:00
trapexit adcbf0eef0 Merge pull request #549 from trapexit/cow
optimize link_cow eligibility check
2019-01-04 10:53:19 -05:00
Antonio SJ Musumeci 10f7f8bdea optimize link_cow eligibility check 2019-01-04 10:21:28 -05:00
trapexit 90e422c113 Merge pull request #544 from trapexit/newest
check minfreespace on newest policy create
2018-12-12 15:56:58 -05:00
Antonio SJ Musumeci dfa1c1ad6f check minfreespace on newest policy create 2018-12-12 15:53:53 -05:00
trapexit bd792757e0 Merge pull request #543 from trapexit/docs
misc README updates
2018-12-12 15:52:18 -05:00
Antonio SJ Musumeci 40969402e8 misc README updates 2018-12-12 15:48:53 -05:00
trapexit fee9a449ba Merge pull request #539 from trapexit/makefile_cleanup
general cleanup of makefile, add static & lto building
2018-12-02 15:34:14 -05:00
Antonio SJ Musumeci 6de8e4471c general cleanup of makefile, add static & lto building 2018-12-02 15:30:15 -05:00
trapexit f0f64f8ed1 Merge pull request #537 from trapexit/alpine
fix building on alpine w/ musl
2018-11-28 19:40:53 -05:00
Antonio SJ Musumeci 6ae68465cf fix building on alpine w/ musl 2018-11-28 17:08:40 -05:00
trapexit 3c1f19e85a Merge pull request #534 from trapexit/xattr
change xattr setting notsup to nosys
2018-11-19 23:32:07 -05:00
trapexit 2a8a6b8fe0 Merge pull request #533 from trapexit/truncate
fix indexing of truncate targets
2018-11-19 23:31:12 -05:00
Antonio SJ Musumeci 7d9458fdd1 change xattr setting notsup to nosys 2018-11-19 23:30:39 -05:00
Antonio SJ Musumeci 43b676a6e2 fix indexing of truncate targets 2018-11-19 23:25:16 -05:00
trapexit 606d9c4d3f Merge pull request #531 from trapexit/o_path
fix building on platforms without O_PATH
2018-11-06 08:15:02 -05:00
Antonio SJ Musumeci c46134c03a fix building on platforms without O_PATH 2018-11-06 07:59:28 -05:00
trapexit 0abb2a960c Merge pull request #530 from trapexit/dedup
optimize readdir file dedup
2018-11-06 00:12:49 -05:00
Antonio SJ Musumeci 3631ab7df2 optimize readdir file dedup
Use fasthash64 to hash filenames to uint64_t and store in khash
set. Significantly reduces malloc/free'ing and memory usage.
2018-11-06 00:05:30 -05:00
trapexit b8b3ab06a7 Merge pull request #529 from trapexit/statvfs
add ability to change statfs behavior
2018-11-05 21:38:41 -05:00
Antonio SJ Musumeci 680f8194f9 add ability to change statfs behavior 2018-11-05 21:33:27 -05:00
trapexit 3d33428a4e Merge pull request #528 from trapexit/nc
rename NW (no write) to NC (no create)
2018-11-01 23:38:14 -04:00
Antonio SJ Musumeci 7524e57262 rename NW (no write) to NC (no create) 2018-11-01 23:37:45 -04:00
trapexit 1195e66a8a Merge pull request #527 from trapexit/branch_tagging
add tagging branches RW/RO/NW
2018-11-01 23:22:59 -04:00
Antonio SJ Musumeci b55ebba4ed add tagging branches RW/RO/NW
This allows users to tag a branch as readonly or not for writing regardless
of how the filesystem is mounted. Should simplify deployments and offer
more flexibility.
2018-11-01 23:13:49 -04:00
trapexit 8dd0dc11ee Merge pull request #525 from trapexit/docs
misc updates to docs
2018-10-31 10:28:01 -04:00
Antonio SJ Musumeci 8eacb008b3 misc updates to docs 2018-10-31 10:22:40 -04:00
trapexit 9c16ab6999 Merge pull request #520 from trapexit/cleanups
misc cleanups
2018-10-24 22:47:28 -04:00
Antonio SJ Musumeci 9e0ab1f518 misc cleanups 2018-10-24 22:38:36 -04:00
trapexit 47bf58eae9 Merge pull request #517 from trapexit/cleanup
policy return cleanup
2018-10-15 19:19:37 -04:00
Antonio SJ Musumeci 8a48b74cb4 policy return cleanup 2018-10-15 19:03:02 -04:00
trapexit 570c93ec2c Merge pull request #516 from trapexit/clone
add FICLONE and copy_file_range to clonefile
2018-10-12 10:42:26 -04:00
Antonio SJ Musumeci 85026d5780 add FICLONE and copy_file_range to clonefile
If available FICLONE and copy_file_range will be tried in addition to sendfile
when copying data between two files. The fallback is a tradition read/write
loop. On systems that support these it should improve performance.
2018-10-12 10:18:49 -04:00
trapexit bb2e6eb605 Merge pull request #515 from trapexit/xattr.h
remove libattr dependency
2018-10-09 14:00:50 -04:00
Antonio SJ Musumeci 1885a82cce remove libattr dependency 2018-10-09 13:50:45 -04:00
trapexit 602a37b231 Merge pull request #514 from trapexit/runtime_xattr
add ability to turn on/off xattr support at runtime
2018-10-09 08:52:55 -04:00
Antonio SJ Musumeci 65f482e483 add ability to turn on/off xattr support at runtime 2018-10-09 08:41:31 -04:00
trapexit 6ee44e451c Merge pull request #513 from trapexit/errno
fix building on certain platforms
2018-10-09 08:25:05 -04:00
Antonio SJ Musumeci 8d1a1564bf fix building on certain platforms 2018-10-09 08:23:37 -04:00
trapexit 4af9248eba Merge pull request #509 from trapexit/cow
add link_cow feature
2018-10-06 22:48:01 -04:00
Antonio SJ Musumeci 93f7d7d927 add link_cow feature
When enabled if a regular file is opened which has a link count > 1 it will
copy the file to a temporary file and rename over the original. Effectively
breaking the link. This behavior is similar to cow-shell and other LD_PRELOAD
based "CoW" solutions.
2018-10-06 22:05:05 -04:00
trapexit 19a7d43b31 Merge pull request #506 from trapexit/literal_glob
keep literal when glob fails
2018-10-01 17:12:40 -04:00
Antonio SJ Musumeci 9afefef032 keep literal when glob fails 2018-10-01 16:56:40 -04:00
trapexit 8c3462caf2 Merge pull request #505 from trapexit/depends
add fuse to debian pkg dependency
2018-09-30 23:46:18 -04:00
Antonio SJ Musumeci 340f3c85c9 add fuse to debian pkg dependency 2018-09-30 23:45:46 -04:00
trapexit ff04098319 Merge pull request #504 from trapexit/disable_security.capabilities
add security_capability option
2018-09-30 23:34:07 -04:00
Antonio SJ Musumeci f8563369bc add security_capability option 2018-09-30 23:30:53 -04:00
trapexit 4152769182 Merge pull request #499 from trapexit/docs
add comment regarding tar error
2018-09-06 08:28:40 -04:00
Antonio SJ Musumeci 057eafef7f add comment regarding tar error 2018-09-06 08:28:21 -04:00
trapexit c7e93720ff Merge pull request #496 from trapexit/doc-updates
tweak docs, add FAQ regarding vendoring of libfuse
2018-08-20 13:40:41 -04:00
Antonio SJ Musumeci 587ab62583 tweak docs, add FAQ regarding vendoring of libfuse 2018-08-20 13:40:01 -04:00
trapexit a894360402 Merge pull request #495 from ahippo/external-fuse
Option for building with system libfuse
2018-08-20 12:05:37 -04:00
Andrey Mazo 756d4ff0ff Makefile: option for building with system libfuse
Add INTERNAL_FUSE variable to control linking against
bundled libfuse vs external/system libfuse.
If external libfuse is requested,
`pkg-config` is used to obtain CFLAGS/LIBS/LDFLAGS and
to check that the provided external libfuse is >= 2.9.7.

This might hopefully get mergerfs closer to inclusion into distros.

Link: https://github.com/trapexit/mergerfs/issues/431
Link: https://bugs.gentoo.org/587956#c6
2018-08-20 00:05:58 +03:00
Andrey Mazo ec6adaa376 options: move "-o threads=" help text to libfuse/
Parsing of and acting upon "-o threads" option is done in the bundled `libfuse/`,
so move the corresponding help text there too.
This make help text consistent with behavior
when building against system libfuse,
which doesn't support this option.
2018-08-19 21:04:35 +03:00
trapexit dee2c3e23b Merge pull request #494 from trapexit/hardlinks
add FAQ entry on hard links
2018-08-18 22:10:54 -04:00
Antonio SJ Musumeci fc52f89ddc add FAQ entry on hard links 2018-08-18 22:10:23 -04:00
trapexit fdbafa63ba Merge pull request #493 from ahippo/ldflags
Makefile: don't touch LDFLAGS
2018-08-13 07:18:58 -04:00
Andrey Mazo e987ff46c2 Makefile: don't touch LDFLAGS
These are more like LIBS rather than LDFLAGS.
They are used just in one place,
so simply put them inline.

Also, use LDFLAGS provided by the user during actual link.

This helps users pass their own LDFLAGS.
For example, this is important on Gentoo,
where LDFLAGS='-Wl,-O1 -Wl,--as-needed' is passed by default.
2018-08-13 06:02:29 +03:00
trapexit b1ecc58114 Merge pull request #491 from trapexit/inode_docs
add details regarding use_ino
2018-07-25 17:29:06 -04:00
Antonio SJ Musumeci 39d5ab8e83 add details regarding use_ino 2018-07-25 17:28:36 -04:00
trapexit 4b1f9afe1e Merge pull request #482 from trapexit/docs
change examples to use /mnt rather than /tmp
2018-05-04 08:59:27 -04:00
Antonio SJ Musumeci c005463111 change examples to use /mnt rather than /tmp 2018-05-04 09:00:27 -04:00
trapexit 50384f4e86 Merge pull request #475 from trapexit/version
fix version.hpp creation, again
2018-03-24 06:53:35 -07:00
Antonio SJ Musumeci 73e8867400 fix version.hpp creation, again 2018-03-24 09:51:25 -04:00
trapexit 0545da67ff Merge pull request #474 from trapexit/version
fix versioning with tarball building
2018-03-16 23:27:04 -04:00
Antonio SJ Musumeci 24e690b995 fix versioning with tarball building 2018-03-16 23:26:29 -04:00
trapexit 89200a0ab8 Merge pull request #473 from trapexit/version
version bumps and calculation fixes
2018-03-09 21:14:44 -05:00
Antonio SJ Musumeci e521833cec bump change date on readme and rebuild man 2018-03-09 21:12:52 -05:00
Antonio SJ Musumeci 737d941941 bump builtin libfuse version 2018-03-09 21:10:09 -05:00
Antonio SJ Musumeci 9830e29945 fix version generation 2018-03-09 21:06:00 -05:00
trapexit 6418b00ac3 Merge pull request #472 from trapexit/clonepathperms
ignore clonepath metadata errors. fixes #470
2018-03-08 16:56:33 -05:00
Antonio SJ Musumeci cfe9c28a10 ignore clonepath metadata errors. fixes #470 2018-03-08 16:55:35 -05:00
trapexit aa6e4c1865 Merge pull request #471 from trapexit/clonepath
stop clonepath at base directory. fixes #467
2018-03-08 16:48:53 -05:00
Antonio SJ Musumeci f48c16d162 stop clonepath at base directory. fixes #467 2018-03-07 18:46:32 -05:00
trapexit 62ae871d51 Merge pull request #469 from trapexit/hard_remove
add hard_remove details and comment on unionfs
2018-03-03 13:25:15 -05:00
trapexit 906702de77 add hard_remove details and comment on unionfs 2018-03-03 13:24:49 -05:00
trapexit db27a91a58 Merge pull request #461 from agsimmons/master
Remove duplicate word in README
2018-01-18 15:41:57 -05:00
Andrew Simmons b4a003c5a8 Remove duplicate word in README 2018-01-18 15:38:16 -05:00
trapexit b48054fa2c add XRP to support list 2017-12-21 09:47:53 -05:00
trapexit d8f05ac8ae add BCH, ETH, & LTC donation addresses 2017-12-20 22:11:26 -05:00
trapexit 8843ace757 Merge pull request #454 from trapexit/faq
add explination in FAQ about what mergerfs is / is not
2017-11-15 16:40:24 -05:00
Antonio SJ Musumeci 7d93d5917e add explination in FAQ about what mergerfs is / is not 2017-11-15 16:39:25 -05:00
trapexit 8846a135bc Merge pull request #452 from trapexit/faq
faq answer on epmfs
2017-11-07 14:49:38 -05:00
Antonio SJ Musumeci 7207e7fdd6 faq answer on epmfs 2017-11-07 14:48:38 -05:00
trapexit a8e6128034 Merge pull request #451 from trapexit/faq
update FAQ and misc tweaks
2017-11-07 09:00:29 -05:00
Antonio SJ Musumeci f72bb1e1ed update FAQ and misc tweaks 2017-11-07 08:59:49 -05:00
trapexit cf5d28ea4a Merge pull request #450 from trapexit/support
add support info and how mergerfs works section
2017-11-05 13:44:40 -05:00
Antonio SJ Musumeci bd14f33c31 add support info and how mergerfs works section 2017-11-05 13:43:33 -05:00
trapexit 73561c1861 Merge pull request #449 from trapexit/issue-template
Create issue_template.md
2017-11-05 12:10:15 -05:00
trapexit 43d50ca471 Create issue_template.md 2017-11-05 12:09:53 -05:00
trapexit 8ea0b395b2 Merge pull request #445 from trapexit/setreuid
call 32bit versions of set/geteuid on 32bit platforms
2017-10-17 22:23:27 -04:00
Antonio SJ Musumeci 1d6d22727c call 32bit versions of set/geteuid on 32bit platforms 2017-10-17 22:21:29 -04:00
Antonio SJ Musumeci b3c10f5bdf Merge pull request #435 from trapexit/man
rebuild manpage
2017-08-06 14:43:03 -04:00
Antonio SJ Musumeci 821d1b6964 rebuild manpage 2017-08-06 14:42:35 -04:00
Antonio SJ Musumeci 6067478e00 Merge pull request #434 from trapexit/makefile
fixup makefile
2017-08-06 14:41:11 -04:00
Antonio SJ Musumeci ebe737e995 fixup makefile 2017-08-06 14:39:56 -04:00
Antonio SJ Musumeci e2a9509896 Merge pull request #433 from trapexit/doc-update
misc updates to docs
2017-08-06 14:34:14 -04:00
Antonio SJ Musumeci 9104f25c09 misc updates to docs 2017-08-06 14:33:59 -04:00
Antonio SJ Musumeci 713c911264 Merge pull request #429 from trapexit/support
expand support section of readme
2017-07-30 16:53:26 -04:00
Antonio SJ Musumeci c1d85ef825 expand support section of readme 2017-07-30 16:52:29 -04:00
Antonio SJ Musumeci 43eaf29df5 Merge pull request #428 from trapexit/cacheinfo
add info on creating a caching behavior
2017-07-23 19:59:26 -04:00
Antonio SJ Musumeci cc1f07ef39 add info on creating a caching behavior 2017-07-23 19:59:11 -04:00
Antonio SJ Musumeci c35a4dadee Merge pull request #424 from trapexit/threading
Threading
2017-07-03 00:02:35 -04:00
Antonio SJ Musumeci 75ed37a11a add setting of thread pool size 2017-07-02 23:50:39 -04:00
Antonio SJ Musumeci 8043829815 fixed threads 2017-07-01 23:29:47 -04:00
Antonio SJ Musumeci deb70c7a4f Merge pull request #423 from trapexit/khash
improve khash performance
2017-07-01 11:54:39 -04:00
Antonio SJ Musumeci 0708110ec1 improve khash performance 2017-07-01 11:53:35 -04:00
Antonio SJ Musumeci dae637925b Merge pull request #422 from trapexit/pragma
use pragma once in headers
2017-06-30 11:53:31 -04:00
Antonio SJ Musumeci a7b126cb48 use pragma once in headers 2017-06-30 11:38:34 -04:00
Antonio SJ Musumeci d66774d3f3 Merge pull request #421 from trapexit/ignorepponrename
add 'ignore path preserving on rename' feature
2017-06-30 00:02:08 -04:00
Antonio SJ Musumeci a2bddec8c7 add 'ignore path preserving on rename' feature 2017-06-30 00:01:00 -04:00
Antonio SJ Musumeci b95fbd76ec Merge pull request #420 from trapexit/mkstemp-movefile
use temp files (then rename) when moving files for moveonenospc
2017-06-28 21:18:59 -04:00
Antonio SJ Musumeci aea2b40dda use temp files (then rename) when moving files for moveonenospc 2017-06-28 21:17:47 -04:00
Antonio SJ Musumeci fa3ff40218 Merge pull request #418 from trapexit/fix
use fusepath from fileinfo. closes #417
2017-06-24 17:04:37 -04:00
Antonio SJ Musumeci bfd410def2 use fusepath from fileinfo. closes #417 2017-06-24 17:03:41 -04:00
Antonio SJ Musumeci 721bd4d337 Merge pull request #416 from trapexit/time
add time.h to fix compiling on some platforms
2017-06-15 19:36:31 -04:00
Antonio SJ Musumeci 7296d3d90d add time.h to fix compiling on some platforms 2017-06-15 19:35:28 -04:00
Antonio SJ Musumeci a7cddefcc9 Merge pull request #415 from trapexit/static
add libfuse 2.9.7 to repo and build against libfuse.a
2017-06-02 16:14:54 -04:00
Antonio SJ Musumeci de0985c135 add libfuse 2.9.7 to repo and build against libfuse.a 2017-06-02 11:25:25 -04:00
Antonio SJ Musumeci 986f230ba7 Merge pull request #412 from trapexit/benchmark
add nullrw feature to facilitate benchmarking
2017-05-26 19:51:28 -04:00
Antonio SJ Musumeci 87c2f2f9dc add nullrw feature to facilitate benchmarking 2017-05-26 18:11:18 -04:00
Antonio SJ Musumeci 63f84b255f Merge pull request #409 from trapexit/ro-symlinks
symlinkify: file -> symlink-to-original-file after timeout
2017-05-05 00:47:24 -04:00
Antonio SJ Musumeci 6a7675f2bf symlinkify: file -> symlink-to-original-file after timeout 2017-05-05 00:14:04 -04:00
Antonio SJ Musumeci 25ce9938c6 Merge pull request #405 from trapexit/runtime-drop
make dropcacheonclose runtime configurable
2017-04-21 17:00:12 -04:00
Antonio SJ Musumeci 8ba3a08f40 make dropcacheonclose runtime configurable 2017-04-21 16:58:39 -04:00
Antonio SJ Musumeci 23dbe48324 Merge pull request #403 from trapexit/copyfile
better handle incomplete reads/writes in copying files
2017-04-13 13:21:34 -04:00
Antonio SJ Musumeci ccaa458739 better handle incomplete reads/writes in copying files 2017-04-13 12:55:34 -04:00
Antonio SJ Musumeci f95b07f83b Merge pull request #402 from trapexit/nopath
enable nopath and nullpath_ok
2017-04-12 14:39:44 -04:00
Antonio SJ Musumeci 162b99e6b8 enable nopath and nullpath_ok 2017-04-12 13:52:06 -04:00
Antonio SJ Musumeci 3c8afc1bb0 Merge pull request #400 from trapexit/movefile-tweak
tweak movefile behavior
2017-04-11 15:25:07 -04:00
Antonio SJ Musumeci f15437c713 tweak movefile behavior 2017-04-11 15:24:09 -04:00
Antonio SJ Musumeci d9be1c4841 Merge pull request #399 from trapexit/travis
add more travis build targets
2017-04-11 10:07:35 -04:00
Antonio SJ Musumeci 5f3aa6eee5 add more travis build targets 2017-04-11 10:00:37 -04:00
Antonio SJ Musumeci 1089af9077 Merge pull request #398 from trapexit/fadvise
hide fs::fadvise as it's not used directly
2017-04-11 09:59:00 -04:00
Antonio SJ Musumeci 2fbeb67dd9 hide fs::fadvise as it's not used directly 2017-04-11 09:42:33 -04:00
Antonio SJ Musumeci c4ad239171 Merge pull request #397 from trapexit/fadvise
support older libfuse without utime_omit_ok flag
2017-04-11 09:32:36 -04:00
Antonio SJ Musumeci 8b976ab3c1 support older libfuse without utime_omit_ok flag 2017-04-11 09:23:40 -04:00
Antonio SJ Musumeci 1499ccf3fb Merge pull request #396 from trapexit/fadvise
fadvise cleanup
2017-04-11 09:03:12 -04:00
Antonio SJ Musumeci 1a1fa06aa3 fadvise cleanup 2017-04-11 09:02:05 -04:00
Antonio SJ Musumeci e3e764ecae Merge pull request #395 from trapexit/utime-omit
enable utime_omit_ok flag
2017-04-10 21:38:54 -04:00
Antonio SJ Musumeci 617195d71e enable utime_omit_ok flag 2017-04-10 21:36:56 -04:00
Antonio SJ Musumeci 8cfb1cbee4 Merge pull request #394 from trapexit/getgrouplist
work around getgrouplist signature difference on osx
2017-04-08 10:24:26 -04:00
Antonio SJ Musumeci be3eb7e3ed work around getgrouplist signature difference on osx 2017-04-07 16:23:18 -04:00
Antonio SJ Musumeci 1c3b72de09 Merge pull request #393 from trapexit/inode
handle 32bit and 64bit inode recalculation
2017-04-07 10:47:49 -04:00
Antonio SJ Musumeci 0600734942 handle 32bit and 64bit inode recalculation 2017-04-07 09:47:19 -04:00
Antonio SJ Musumeci e6dc8c2cb3 Merge pull request #392 from trapexit/fadvise
restructure fadvise
2017-04-07 00:22:12 -04:00
Antonio SJ Musumeci 9d0798dbfb restructure fadvise 2017-04-07 00:12:57 -04:00
Antonio SJ Musumeci 581356a0c3 Merge pull request #391 from trapexit/fallocate
restructure fallocate abstraction
2017-04-06 23:40:41 -04:00
Antonio SJ Musumeci e2acffe55b restructure fallocate abstraction 2017-04-06 23:35:31 -04:00
Antonio SJ Musumeci 998966e6e6 Merge pull request #390 from trapexit/futimesat
abstract futimesat
2017-04-06 23:21:41 -04:00
Antonio SJ Musumeci 42d454ac27 abstract futimesat 2017-04-06 23:13:02 -04:00
Antonio SJ Musumeci ad6062b9a8 Merge pull request #389 from trapexit/stattime
abstract access to highres atime/mtime
2017-04-06 22:22:48 -04:00
Antonio SJ Musumeci 0b2bf17cd7 abstract access to highres atime/mtime 2017-04-06 22:16:17 -04:00
Antonio SJ Musumeci 292b850e07 Merge pull request #388 from trapexit/types
use correct integer types
2017-04-06 21:35:52 -04:00
Antonio SJ Musumeci e20d566f96 use correct integer types 2017-04-06 21:29:39 -04:00
Antonio SJ Musumeci 776ff3e3f1 Merge pull request #387 from trapexit/cppfind
use compiler's preprocessor rather than cpp explicitly
2017-04-06 21:15:02 -04:00
Antonio SJ Musumeci 1539aca3d8 use compiler's preprocessor rather than cpp explicitly 2017-04-06 21:08:55 -04:00
Antonio SJ Musumeci 3244314310 Merge pull request #386 from trapexit/travis-ci
setup travis-ci
2017-04-04 08:41:32 -04:00
Antonio SJ Musumeci 9d799ffc9f setup travis-ci 2017-04-04 08:32:36 -04:00
Antonio SJ Musumeci 9486bc9b21 Merge pull request #385 from trapexit/clone-errors
make fs::attr return ENOTSUP on EINVAL #381
2017-04-04 06:57:42 -04:00
Antonio SJ Musumeci c043ef95a3 make fs::attr return ENOTSUP on EINVAL #381 2017-04-03 21:11:20 -04:00
Antonio SJ Musumeci 3728e39655 Merge pull request #383 from trapexit/docs
explicitly define path preservation, better explain move issues
2017-04-03 14:36:07 -04:00
Antonio SJ Musumeci 215f129a2a explicitly define path preservation, better explain move issues 2017-04-03 14:35:08 -04:00
Antonio SJ Musumeci af9a71f84c Merge pull request #382 from trapexit/docs
fix incorrect section header syntax
2017-04-03 12:06:26 -04:00
Antonio SJ Musumeci bb4ec913c6 fix incorrect section header syntax 2017-04-03 12:05:51 -04:00
Antonio SJ Musumeci 38e5815ba7 Merge pull request #379 from trapexit/docs
add info on inodes running out to faq on filled drives
2017-03-13 13:43:22 -04:00
Antonio SJ Musumeci cf2cb54e2a add info on inodes running out to faq on filled drives 2017-03-13 13:42:46 -04:00
Antonio SJ Musumeci b7269e9963 Merge pull request #378 from trapexit/docs
try to clarify how path preserving policies work and other tweaks to docs
2017-03-11 15:08:46 -05:00
Antonio SJ Musumeci 94ebccc6ed try to clarify how path preserving policies work and other tweaks to docs 2017-03-11 15:07:48 -05:00
Antonio SJ Musumeci 09dabc7b4c Merge pull request #376 from trapexit/docs
update docs to include dropcacheonclose and warn about directory mtime
2017-02-18 20:06:54 -05:00
Antonio SJ Musumeci 4e7e74d6f0 update docs to include dropcacheonclose and warn about directory mtime 2017-02-18 20:05:06 -05:00
Antonio SJ Musumeci dca6cb4826 Merge pull request #374 from trapexit/flush-cache-on-close
add option to drop file caches before closing files
2017-02-18 16:59:49 -05:00
Antonio SJ Musumeci 6aa62d03ff add option to drop file caches before closing files 2017-02-18 16:06:29 -05:00
Antonio SJ Musumeci 3a89491507 Merge pull request #372 from trapexit/apply-but-check
check metadata on chown/chmod errors when cloning
2017-02-15 23:32:38 -05:00
Antonio SJ Musumeci 492d895632 check metadata on chown/chmod errors when cloning 2017-02-15 09:08:27 -05:00
Antonio SJ Musumeci 899670bc93 Merge pull request #365 from trapexit/docs
misc document updates
2017-01-31 19:24:48 -05:00
Antonio SJ Musumeci 9cc9bb9de2 misc document updates 2017-01-31 19:10:54 -05:00
Antonio SJ Musumeci c25974de68 Merge pull request #362 from trapexit/docs
update documentation, focus on explaining double caching & direct_io
2017-01-19 12:01:10 -05:00
Antonio SJ Musumeci 16e7c72e99 update documentation, focus on explaining double caching & direct_io 2017-01-19 11:49:52 -05:00
Antonio SJ Musumeci 011febcb91 Merge pull request #361 from trapexit/cleanup
Cleanup
2017-01-18 23:30:30 -05:00
Antonio SJ Musumeci a60d815e92 add ifndefs to all headers 2017-01-17 11:45:44 -05:00
Antonio SJ Musumeci e93c946198 limit need to explicitly call .c_str() 2017-01-17 11:45:44 -05:00
Antonio SJ Musumeci 7b4e1ea36d remove clone command 2017-01-17 11:45:44 -05:00
Antonio SJ Musumeci 726b88e039 restructure error calculation 2017-01-17 11:45:43 -05:00
Antonio SJ Musumeci 078e4bab49 Merge pull request #360 from trapexit/posix-acl
check for system.posix_acl_default before setting umask
2017-01-16 23:21:51 -05:00
Antonio SJ Musumeci d67d5dec77 check for system.posix_acl_default before setting umask 2017-01-16 12:19:58 -05:00
Antonio SJ Musumeci 10a9918d20 Merge pull request #352 from trapexit/directio
use different read and write functions when using direct_io
2016-12-20 17:08:20 -05:00
Antonio SJ Musumeci 1aa76a54c2 use different read and write functions when using direct_io 2016-12-20 17:06:34 -05:00
1090 changed files with 138097 additions and 11562 deletions
+150
View File
@@ -0,0 +1,150 @@
linux_task:
name: "alpine:latest"
container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine-arm:latest"
arm_container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "fedora:41"
container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora-arm:41"
arm_container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "ubuntu:22.04"
container:
image: ubuntu:22.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:24.04"
container:
image: ubuntu:24.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:latest"
container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu-arm:latest"
arm_container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:12"
container:
image: debian:12
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:latest"
container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian-arm:latest"
arm_container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
+2
View File
@@ -0,0 +1,2 @@
github: [trapexit]
custom: ['https://github.com/trapexit/support']
+49
View File
@@ -0,0 +1,49 @@
---
name: Bug report
about: For the reporting of behavior not as described
title: ''
labels: bug, investigating
assignees: ''
---
**Describe the bug**
A clear and concise description of the unexpected behavior.
**Please be sure to use latest release of mergerfs to ensure the issue still exists. Not your distro's latest but the latest official release.**
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
**To Reproduce**
Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
Please simplify the reproduction as much as possible.
- Unless it is dependenat on multiple branches, use a single branch
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
**Expected behavior**
A clear and concise description of the expected behavior.
**System information:**
- OS, kernel version: `uname -a`
- mergerfs version: `mergerfs -V`
- mergerfs settings
- List of drives, filesystems, & sizes:
- `df -h`
- `lsblk -f`
- A strace of the application having a problem:
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
- strace of mergerfs while app tried to do it's thing:
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature, investigating
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+10
View File
@@ -0,0 +1,10 @@
---
name: Question
about: When you're looking for information or general support
title: ''
labels: investigating, question
assignees: ''
---
For general questions and support please use [GitHub Discussions](https://github.com/trapexit/mergerfs/discussions) or [Discord](https://discord.gg/MpAr69V).
+42
View File
@@ -0,0 +1,42 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "7 3 * * 6"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
if: ${{ matrix.language == 'python' || matrix.language == 'cpp' }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
+49
View File
@@ -0,0 +1,49 @@
name: "mkdocs"
on:
push:
branches:
- master
tags:
- '*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Configure Git Credentials
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Install dependencies
working-directory: ./mkdocs
run: |
pip install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike
- name: Deploy docs
working-directory: ./mkdocs
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$REF_TYPE" = "tag" ]; then
mike deploy "$REF_NAME" "latest" --allow-empty --ignore-remote-status --update-aliases
mike set-default "latest"
#git checkout gh-pages
#git push -f origin gh-pages
elif [ "$REF_TYPE" = "branch" ] && [ "$REF_NAME" = "master" ]; then
mike deploy "master" --ignore-remote-status --allow-empty
#git checkout gh-pages
#git push -f origin gh-pages
fi
+9 -1
View File
@@ -14,12 +14,20 @@
# build artifacts
mergerfs
obj/
build/
src/version.hpp
VERSION
# Debian files
debian/files
debian/changelog
debian/debhelper-build-stamp
debian/mergerfs.debhelper.log
debian/mergerfs.substvars
# RPM files
rpmbuild/
# mkdocs files
mkdocs/venv
__pycache__/
+16
View File
@@ -0,0 +1,16 @@
## Libraries
### included in repo
* libfuse: https://github.com/libfuse/libfuse (heavily modified fork of v2.x)
* rapidhash: https://github.com/Nicoshev/rapidhash
* fmt: https://github.com/fmtlib/fmt
* concurrentqueue: https://github.com/cameron314/concurrentqueue
* scope_guard: https://github.com/Neargye/scope_guard
* subprocess: https://github.com/arun11299/cpp-subprocess
* CLI11: https://github.com/CLIUtils/CLI11
* boost
* download boost
* ./bootstrap.sh
* ./b2 tools/bcp
* ./dist/bin/bcp unordered/concurrent_flat_map.hpp <target_dir>
+1 -1
View File
@@ -1,7 +1,7 @@
/*
ISC License
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
+277 -135
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
# Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -12,159 +12,246 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
PKGCONFIG = $(shell which pkg-config)
GIT = $(shell which git)
TAR = $(shell which tar)
MKDIR = $(shell which mkdir)
TOUCH = $(shell which touch)
CP = $(shell which cp)
RM = $(shell which rm)
LN = $(shell which ln)
FIND = $(shell which find)
INSTALL = $(shell which install)
MKTEMP = $(shell which mktemp)
STRIP = $(shell which strip)
PANDOC = $(shell which pandoc)
SED = $(shell which sed)
GZIP = $(shell which gzip)
RPMBUILD = $(shell which rpmbuild)
GIT2DEBCL = ./tools/git2debcl
CPPFIND = ./tools/cppfind
ifeq ($(PKGCONFIG),"")
$(error "pkg-config not installed")
ifeq ($(shell id -u),0)
FAKEROOT ?=
endif
ifeq ($(PANDOC),"")
$(warning "pandoc does not appear available: manpage won't be buildable")
CP ?= cp
FAKEROOT ?= fakeroot
FIND ?= find
GIT ?= git
GIT2DEBCL ?= buildtools/git2debcl
INSTALL ?= install
LN ?= ln
MKDIR ?= mkdir
MKTEMP ?= mktemp
MV ?= mv
RM ?= rm
SED ?= sed
STRIP ?= strip
TAR ?= tar
TOUCH ?= touch
BUILDDIR := build
GITHUB_REPO ?= "https://github.com/trapexit/mergerfs"
ifndef GIT_REPO
ifneq ($(shell $(GIT) --version 2> /dev/null),)
ifeq ($(shell test -e .git; echo $$?),0)
GIT_REPO = 1
endif
endif
endif
XATTR_AVAILABLE = $(shell test ! -e /usr/include/attr/xattr.h; echo $$?)
USE_XATTR ?= 1
UGID_USE_RWLOCK ?= 0
FUSE_AVAILABLE = $(shell ! pkg-config --exists fuse; echo $$?)
ifeq ($(FUSE_AVAILABLE),0)
FUSE_AVAILABLE = $(shell test ! -e /usr/include/fuse.h; echo $$?)
ifdef NDEBUG
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -fno-omit-frame-pointer -DDEBUG
endif
ifeq ($(FUSE_AVAILABLE),0)
$(error "FUSE development package doesn't appear available")
ifdef STATIC
STATIC_FLAGS := -static
else
STATIC_FLAGS :=
endif
FLAG_NOPATH = $(shell $(CPPFIND) "flag_nopath")
FALLOCATE = $(shell $(CPPFIND) "fuse_fs_fallocate")
FLOCK = $(shell $(CPPFIND) "fuse_fs_flock")
READ_BUF = $(shell $(CPPFIND) "fuse_fs_read_buf")
WRITE_BUF = $(shell $(CPPFIND) "fuse_fs_write_buf")
UGID_USE_RWLOCK = 0
OPTS = -O2
SRC = $(wildcard src/*.cpp)
OBJ = $(SRC:src/%.cpp=obj/%.o)
DEPS = $(OBJ:obj/%.o=obj/%.d)
TARGET = mergerfs
MANPAGE = $(TARGET).1
FUSE_CFLAGS = $(shell $(PKGCONFIG) --cflags fuse)
CFLAGS = -g -Wall \
$(OPTS) \
-Wno-unused-result \
$(FUSE_CFLAGS) \
-DFUSE_USE_VERSION=29 \
-MMD \
-DFLAG_NOPATH=$(FLAG_NOPATH) \
-DFALLOCATE=$(FALLOCATE) \
-DFLOCK=$(FLOCK) \
-DREAD_BUF=$(READ_BUF) \
-DWRITE_BUF=$(WRITE_BUF) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
LDFLAGS = $(shell $(PKGCONFIG) fuse --libs)
PREFIX = /usr/local
EXEC_PREFIX = $(PREFIX)
DATAROOTDIR = $(PREFIX)/share
DATADIR = $(DATAROOTDIR)
BINDIR = $(EXEC_PREFIX)/bin
SBINDIR = $(EXEC_PREFIX)/sbin
MANDIR = $(DATAROOTDIR)/man
MAN1DIR = $(MANDIR)/man1
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
ifeq ($(XATTR_AVAILABLE),0)
$(warning "xattr not available: disabling")
CFLAGS += -DWITHOUT_XATTR
ifdef LTO
LTO_FLAGS := -flto
else
LTO_FLAGS :=
endif
all: $(TARGET)
SRC := $(wildcard src/*.cpp)
OBJS := $(SRC:src/%.cpp=build/.objs/%.cpp.o)
DEPS := $(SRC:src/%.cpp=build/.objs/%.cpp.d)
TESTS := $(wildcard tests/*.cpp)
TESTS_OBJS := $(filter-out build/.objs/mergerfs.cpp.o,$(OBJS))
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.o)
TESTS_DEPS := $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.d)
TESTS_DEPS += $(DEPS)
MANPAGE := mergerfs.1
CPPFLAGS ?=
override CPPFLAGS += \
-D_FILE_OFFSET_BITS=64
CFLAGS ?= \
$(OPT_FLAGS) \
-Wall \
-Wno-unused-result \
-pipe
override CFLAGS += \
-MMD \
-MP
CXXFLAGS ?= \
$(OPT_FLAGS) \
$(STATIC_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-Wpessimizing-move \
-Werror=pessimizing-move \
-Wredundant-move \
-Werror=redundant-move \
-Wno-unused-result \
-pipe
override CXXFLAGS += \
-std=c++17 \
-MMD \
-MP
override INC_FLAGS := \
-Isrc \
-Ilibfuse/include
override MFS_FLAGS := \
-DUSE_XATTR=$(USE_XATTR) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
override TESTS_FLAGS := \
-Isrc \
-DTESTS
LIBFUSE := libfuse/$(BUILDDIR)/libfuse.a
LDFLAGS ?=
LDLIBS := \
-lrt \
-pthread
override LDLIBS += \
$(LIBFUSE)
ifeq ($(CXX),g++)
GCC_VERSION := $(shell $(CXX) -dumpversion | cut -f1 -d.)
ifeq ($(shell test $(GCC_VERSION) -lt 9; echo $$?),0)
override LDLIBS += -lstdc++fs
endif
endif
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR ?=
PREFIX ?= /usr/local
EXEC_PREFIX ?= $(PREFIX)
DATAROOTDIR ?= $(PREFIX)/share
DATADIR ?= $(DATAROOTDIR)
BINDIR ?= $(EXEC_PREFIX)/bin
SBINDIR ?= $(EXEC_PREFIX)/sbin
LIBDIR ?= $(EXEC_PREFIX)/lib
MANDIR ?= $(DATAROOTDIR)/man
MAN1DIR ?= $(MANDIR)/man1
INSTALLBINDIR ?= $(DESTDIR)$(BINDIR)
INSTALLSBINDIR ?= $(DESTDIR)$(SBINDIR)
INSTALLLIBDIR ?= $(DESTDIR)$(LIBDIR)/mergerfs
INSTALLMAN1DIR ?= $(DESTDIR)$(MAN1DIR)
.PHONY: all
all: libfuse $(BUILDDIR)/mergerfs $(BUILDDIR)/fsck.mergerfs $(BUILDDIR)/mergerfs.collect-info
.PHONY: help
help:
@echo "usage: make"
@echo "make XATTR_AVAILABLE=0 - to build program without xattrs functionality (auto discovered otherwise)"
@echo "usage: make ARG\n"
@echo "USE_XATTR=0 - build program without xattrs functionality"
@echo "STATIC=1 - build static binary"
@echo "LTO=1 - build with link time optimization"
@echo "CLEANUP=1 - cleanup images between release build process"
@echo "PKGDIR=/dir/ - location for release build pkgs"
@echo "GITREF=gitref - gitref to use for release builds"
$(TARGET): src/version.hpp obj/obj-stamp $(OBJ)
$(CXX) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
mount.mergerfs: $(TARGET)
$(LN) -fs "$<" "$@"
$(BUILDDIR)/mergerfs: $(LIBFUSE) src/version.hpp $(OBJS)
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
$(BUILDDIR)/fsck.mergerfs:
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/mergerfs.collect-info:
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/tests: $(BUILDDIR)/mergerfs $(TESTS_OBJS)
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
.PHONY: libfuse
$(LIBFUSE):
libfuse:
$(MAKE) NDEBUG=$(NDEBUG) -C libfuse
tests: $(BUILDDIR)/tests
.PHONY:
changelog:
$(GIT2DEBCL) --name $(TARGET) > ChangeLog
ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > ChangeLog
else
@echo "WARNING: need git repo to generate ChangeLog"
endif
authors:
$(GIT) log --format='%aN <%aE>' | sort -f | uniq > AUTHORS
.PHONY: version
version: src/version.hpp
src/version.hpp:
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
@echo "#ifndef _VERSION_HPP" > src/version.hpp
@echo "#define _VERSION_HPP" >> src/version.hpp
@echo "static const char MERGERFS_VERSION[] = \"$(VERSION)\";" >> src/version.hpp
@echo "#endif" >> src/version.hpp
./buildtools/update-version
obj/obj-stamp:
$(MKDIR) -p obj
$(BUILDDIR)/stamp:
$(MKDIR) -p $(BUILDDIR)/.objs
$(MKDIR) -p $(BUILDDIR)/.test_objs
$(TOUCH) $@
obj/%.o: src/%.cpp
$(CXX) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/.objs/%.cpp.o: src/%.cpp $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/.test_objs/%.cpp.o: tests/%.cpp
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/preload.so: $(BUILDDIR)/stamp tools/preload.c
$(CC) -shared -fPIC $(CFLAGS) -o $@ tools/preload.c
preload: $(BUILDDIR)/preload.so
.PHONY: clean
clean: rpm-clean
ifneq ($(GIT),)
ifeq ($(shell test -e .git; echo $$?),0)
$(RM) -f src/version.hpp
endif
endif
$(RM) -rf obj
$(RM) -f "$(TARGET)" mount.mergerfs
$(RM) -rf $(BUILDDIR)
$(FIND) . -name "*~" -delete
$(MAKE) -C libfuse clean
.PHONY: distclean
distclean: clean
$(GIT) clean -fd
ifdef GIT_REPO
$(GIT) clean -xfd
endif
install: install-base install-mount.mergerfs install-man
.PHONY: install
install: install-base install-mount-tools install-preload install-man
install-base: $(TARGET)
.PHONY: install-base
install-base: all
$(MKDIR) -p "$(INSTALLBINDIR)"
$(INSTALL) -v -m 0755 "$(TARGET)" "$(INSTALLBINDIR)/$(TARGET)"
$(INSTALL) -v -m 0755 "$(BUILDDIR)/mergerfs" "$(INSTALLBINDIR)/mergerfs"
$(LN) -fs "mergerfs" "${INSTALLBINDIR}/fsck.mergerfs"
$(LN) -fs "mergerfs" "${INSTALLBINDIR}/mergerfs.collect-info"
install-mount.mergerfs: mount.mergerfs
.PHONY: install-mount-tools
install-mount-tools: install-base
$(MKDIR) -p "$(INSTALLBINDIR)"
$(CP) -a "$<" "$(INSTALLBINDIR)/$<"
$(MAKE) -C libfuse install
install-man: $(MANPAGE)
.PHONY: install-man
install-man: man/$(MANPAGE)
$(MKDIR) -p "$(INSTALLMAN1DIR)"
$(INSTALL) -v -m 0644 "man/$(MANPAGE)" "$(INSTALLMAN1DIR)/$(MANPAGE)"
install-strip: install-base
$(STRIP) "$(INSTALLBINDIR)/$(TARGET)"
.PHONY: install-preload
install-preload: preload
$(MKDIR) -p "$(INSTALLLIBDIR)"
$(INSTALL) -v -m 444 "$(BUILDDIR)/preload.so" "$(INSTALLLIBDIR)/preload.so"
.PHONY: install-strip
install-strip: install-base
$(STRIP) "$(INSTALLBINDIR)/mergerfs"
.PHONY: uninstall
uninstall: uninstall-base uninstall-mount.mergerfs uninstall-man
uninstall-base:
$(RM) -f "$(INSTALLBINDIR)/$(TARGET)"
$(RM) -f "$(INSTALLBINDIR)/mergerfs"
uninstall-mount.mergerfs:
$(RM) -f "$(INSTALLBINDIR)/mount.mergerfs"
@@ -172,45 +259,100 @@ uninstall-mount.mergerfs:
uninstall-man:
$(RM) -f "$(INSTALLMAN1DIR)/$(MANPAGE)"
$(MANPAGE): README.md
ifneq (,$(PANDOC))
$(PANDOC) -s -t man -o "man/$(MANPAGE)" README.md
endif
man: $(MANPAGE)
tarball: clean man changelog authors src/version.hpp
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
.PHONY: tarball
tarball: changelog version
$(eval VERSION := $(shell cat VERSION))
$(eval VERSION := $(subst -,_,$(VERSION)))
$(eval FILENAME := $(TARGET)-$(VERSION))
$(eval FILENAME := mergerfs-$(VERSION))
$(eval TMPDIR := $(shell $(MKTEMP) --tmpdir -d .$(FILENAME).XXXXXXXX))
$(MKDIR) $(TMPDIR)/$(FILENAME)
$(CP) -ar . $(TMPDIR)/$(FILENAME)
$(TAR) --exclude=.git -cz -C $(TMPDIR) -f $(FILENAME).tar.gz $(FILENAME)
$(RM) -rf $(TMPDIR)
.PHONY: debian-changelog
debian-changelog:
$(GIT2DEBCL) --name $(TARGET) > debian/changelog
ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > debian/changelog
else
$(CP) -v ChangeLog debian/changelog
endif
signed-deb: debian-changelog
dpkg-buildpackage
.PHONY: signed-deb
signed-deb:
$(MAKE) distclean
$(MAKE) debian-changelog
$(FAKEROOT) dpkg-buildpackage -nc
deb: debian-changelog
dpkg-buildpackage -uc -us
.PHONY: deb
deb:
$(MAKE) distclean
$(MAKE) debian-changelog
$(MAKE) version
$(FAKEROOT) dpkg-buildpackage -nc -uc -us
$(MKDIR) -p ./$(BUILDDIR)/pkgs/
$(MV) -v ../mergerfs*deb ./$(BUILDDIR)/pkgs/
.PHONY: rpm-clean
rpm-clean:
$(RM) -rf rpmbuild
.PHONY: rpm
rpm: tarball
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
$(eval VERSION := $(shell cat VERSION))
$(eval VERSION := $(subst -,_,$(VERSION)))
$(MKDIR) -p rpmbuild/BUILD rpmbuild/RPMS rpmbuild/SOURCES
$(SED) 's/__VERSION__/$(VERSION)/g' $(TARGET).spec > \
rpmbuild/SOURCES/$(TARGET).spec
cp -ar $(TARGET)-$(VERSION).tar.gz rpmbuild/SOURCES
$(RPMBUILD) -ba rpmbuild/SOURCES/$(TARGET).spec \
$(SED) 's/__VERSION__/$(VERSION)/g' mergerfs.spec > \
rpmbuild/SOURCES/mergerfs.spec
cp -ar mergerfs-$(VERSION).tar.gz rpmbuild/SOURCES
rpmbuild -ba rpmbuild/SOURCES/mergerfs.spec \
--define "_topdir $(CURDIR)/rpmbuild"
.PHONY: all clean install help
.PHONY: install-build-pkgs
install-build-pkgs:
./buildtools/install-build-pkgs
include $(wildcard obj/*.d)
.PHONY: install-build-tools
install-build-tools:
./bulidtools/install-build-tools
define build_release
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
./buildtools/build-release \
--target=$(1) \
$(if $(CLEANUP),--cleanup) \
$(if $(PKGDIR),--pkgdirpath=$(PKGDIR)) \
--git-repo=$(if $(REMOTE_REPO),$(GITHUB_REPO),".") \
--branch=$(GITREF)
endef
.PHONY: release
.PHONY: release-amd64 release-arm64 release-armhf release-riscv64
.PHONY: release-sample release-static release-tarball
release:
$(call build_release,"all")
release-sample:
$(call build_release,"debian:13.amd64")
release-amd64:
$(call build_release,"amd64")
release-arm64:
$(call build_release,"arm64")
release-armhf:
$(call build_release,"armhf")
release-riscv64:
$(call build_release,"risc64")
release-static:
$(call build_release,"static")
release-tarball:
$(call build_release,"tarball")
.PHONY: tags
tags:
rm -fv TAGS
find . -name "*.c" -print | etags --append -
find . -name "*.h" -print | etags --append -
find . -name "*.cpp" -print | etags --append -
find . -name "*.hpp" -print | etags --append -
-include $(DEPS)
+94 -432
View File
@@ -1,463 +1,125 @@
% mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link>
% 2016-12-14
# mergerfs - a featureful union filesystem
# NAME
**mergerfs** is a
[FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace) based
[union filesystem](https://en.wikipedia.org/wiki/Union_mount) geared
towards simplifying storage and management of files across numerous
commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
**aufs**, **DrivePool**,
etc.](https://trapexit.github.io/mergerfs/latest/project_comparisons/)
mergerfs - another (FUSE based) union filesystem
# SYNOPSIS
## Features
mergerfs -o&lt;options&gt; &lt;srcmounts&gt; &lt;mountpoint&gt;
# DESCRIPTION
**mergerfs** is a union filesystem geared towards simplifying storage and management of files across numerous commodity storage devices. It is similar to **mhddfs**, **unionfs**, and **aufs**.
# FEATURES
* Runs in userspace (FUSE)
* Configurable behaviors
* Logically combine numerous filesystems/paths into a single
mount point (JBOFS: Just a Bunch of FileSystems)
* Combine paths of the same or different filesystems
* Ability to add or remove filesystems/paths without impacting the
rest of the data
* Unaffected by individual filesystem failure
* Configurable file selection and creation placement
* File IO
[passthrough](https://trapexit.github.io/mergerfs/latest/config/passthrough/)
for near native IO performance (where supported)
* Works with filesystems of any size
* Works with filesystems of [almost any
type](https://trapexit.github.io/mergerfs/latest/faq/compatibility_and_integration/#what-filesystems-can-be-used-as-branches)
* Ignore read-only filesystems when creating files
* [Hard links](https://trapexit.github.io/mergerfs/preview/faq/technical_behavior_and_limitations/#do-hard-links-work)
* Hard link [copy-on-write /
CoW](https://trapexit.github.io/mergerfs/latest/config/link_cow/)
* [Runtime configurable](https://trapexit.github.io/mergerfs/latest/runtime_interface/)
* Support for extended attributes (xattrs)
* Support for file attributes (chattr)
* Runtime configurable (via xattrs)
* Safe to run as root
* Opportunistic credential caching
* Works with heterogeneous filesystem types
* Handling of writes to full drives (transparently move file to drive with capacity)
* Handles pool of readonly and read/write drives
* Support for POSIX ACLs
# OPTIONS
###options###
## Non-features
* **defaults**: a shortcut for FUSE's **atomic_o_trunc**, **auto_cache**, **big_writes**, **default_permissions**, **splice_move**, **splice_read**, and **splice_write**. These options seem to provide the best performance.
* **direct_io**: causes FUSE to bypass an addition caching step which can increase write speeds at the detriment of read speed.
* **minfreespace**: the minimum space value used for creation policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
* **moveonenospc**: when enabled (set to **true**) if a **write** fails with **ENOSPC** or **EDQUOT** a scan of all drives will be done looking for the drive with most free space which is at least the size of the file plus the amount which failed to write. An attempt to move the file to that drive will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false)
* **func.&lt;func&gt;=&lt;policy&gt;**: sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
* **category.&lt;category&gt;=&lt;policy&gt;**: Sets policy of all FUSE functions in the provided category. Example: **category.create=mfs**
* **fsname**: sets the name of the filesystem as seen in **mount**, **df**, etc. Defaults to a list of the source paths concatenated together with the longest common prefix removed.
* **use_ino**: causes mergerfs to supply file/directory inodes rather than libfuse. While not a default it is generally recommended it be enabled so that hard linked files share the same inode value.
* Read/write overlay on top of read-only filesystem like OverlayFS
* File whiteout
* RAID like parity calculation (see [SnapRAID](https://www.snapraid.it))
* Redundancy
* Splitting of files across branches
**NOTE:** Options are evaluated in the order listed so if the options are **func.rmdir=rand,category.action=ff** the **action** category setting will override the **rmdir** setting.
###srcmounts###
## How it works
The srcmounts (source mounts) argument is a colon (':') delimited list of paths to be included in the pool. It does not matter if the paths are on the same or different drives nor does it matter the filesystem. Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
mergerfs logically merges multiple filesystem paths together. Not
block devices, not filesystem mounts, just paths. It acts as a proxy
to the underlying filesystem paths. Combining the behaviors of some
functions and being a selector for others.
To make it easier to include multiple source mounts mergerfs supports [globbing](http://linux.die.net/man/7/glob). **The globbing tokens MUST be escaped when using via the shell else the shell itself will expand it.**
When the contents of a directory are requested mergerfs combines the
list of files from each directory, deduplicating entries, and returns
that list.
When a file or directory is created a policy is first run to determine
which branch will be selected for the creation.
For functions which change attributes or remove the file the behavior
may be applied to all instances found.
The way in which mergerfs behaves is controlled by the
[config/options/settings](https://trapexit.github.io/mergerfs/latest/config/options). More
specifically by
[policies](https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies/).
### Visualization
```
$ mergerfs -o defaults,allow_other /mnt/disk\*:/mnt/cdrom /media/drives
A + B = C
/disk1 /disk2 /merged
| | |
+-- /dir1 +-- /dir1 +-- /dir1
| | | | | |
| +-- file1 | | | +-- file1
| | +-- file2 | +-- file2
| | +-- file3 | +-- file3
| | |
+-- /dir2 | +-- /dir2
| | | | |
| +-- file4 | | +-- file4
| | |
| +-- /dir3 +-- /dir3
| | | | |
| | +-- file5 | +-- file5
| | |
+-- file6 | +-- file6
+-- file7 +-- file7 +-- file7
```
The above line will use all mount points in /mnt prefixed with **disk** and the **cdrom**.
To have the pool mounted at boot or otherwise accessable from related tools use **/etc/fstab**.
## QuickStart
```
# <file system> <mount point> <type> <options> <dump> <pass>
/mnt/disk*:/mnt/cdrom /media/drives fuse.mergerfs defaults,allow_other 0 0
```
https://trapexit.github.io/mergerfs/latest/quickstart/
**NOTE:** the globbing is done at mount or xattr update time (see below). If a new directory is added matching the glob after the fact it will not be automatically included.
**NOTE:** for mounting via **fstab** to work you must have **mount.fuse** installed. For Ubuntu/Debian it is included in the **fuse** package.
## Documentation
# FUNCTIONS / POLICIES / CATEGORIES
https://trapexit.github.io/mergerfs
The POSIX filesystem API has a number of functions. **creat**, **stat**, **chown**, etc. In mergerfs these functions are grouped into 3 categories: **action**, **create**, and **search**. Functions and categories can be assigned a policy which dictates how **mergerfs** behaves. Any policy can be assigned to a function or category though some are not very practical. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` (though only if there were more than one copy of the file).
Policies, when called to create, will ignore drives which are readonly or have less than **minfreespace**. This allows for read/write and readonly drives to be mixed together and keep drives which may remount as readonly on error from further affecting the pool.
## Support
#### Function / Category classifications ####
https://trapexit.github.io/mergerfs/latest/support/
| Category | FUSE Functions |
|----------|----------------|
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
| create | create, mkdir, mknod, symlink |
| search | access, getattr, getxattr, ioctl, listxattr, open, readlink |
| N/A | fallocate, fgetattr, fsync, ftruncate, ioctl, read, readdir, release, statfs, write |
Due to FUSE limitations **ioctl** behaves differently if its acting on a directory. It'll use the **getattr** policy to find and open the directory before issuing the **ioctl**. In other cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will also be used.
## Sponsorship and Donations
#### Policy descriptions ####
[https://github.com/trapexit/support](https://github.com/trapexit/support)
| Policy | Description |
|--------------|-------------|
| all | Search category: acts like **ff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **ff**. It will exclude readonly drives and those with free space less than **minfreespace**. |
| epall (existing path, all) | Search category: acts like **epff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all existing paths found. **create** works like **epff**. It will exclude readonly drives and those with free space less than **minfreespace**. |
| epff | Given the order of the drives, as defined at mount time or when configured via the xattr interface, act on the first one found where the path already exists. For **create** category it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). Falls back to **ff**. |
| eplfs (existing path, least free space) | If the path exists on multiple drives use the one with the least free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **lfs**. |
| eplus (existing path, least used space) | If the path exists on multiple drives use the one with the least used space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **lus**. |
| epmfs (existing path, most free space) | If the path exists on multiple drives use the one with the most free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| eprand (existing path, random) | Calls **epall** and then randomizes. |
| erofs | Exclusively return **-1** with **errno** set to **EROFS**. By setting **create** functions to this you can in effect turn the filesystem readonly. |
| ff (first found) | Given the order of the drives, as defined at mount time or when configured via xattr interface, act on the first one found. For **create** category it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). |
| lfs (least free space) | Pick the drive with the least available free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| lus (least used space) | Pick the drive with the least used space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| mfs (most free space) | Pick the drive with the most available free space. For **create** category it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **ff**. |
| newest (newest file) | Pick the file / directory with the largest mtime. For **create** category it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). |
| rand (random) | Calls **all** and then randomizes. |
Development and support of a project like mergerfs requires a
significant amount of time and effort. The software is released under
the very liberal [ISC](https://opensource.org/license/isc-license-txt)
license and is therefore free to use for personal or commercial uses.
**epff**, **eplfs**, **eplus**, and **epmf** are path preserving policies. As the descriptions above explain they will only consider drives where the path being accessed exists. Non-path preserving policies will clone paths as necessary.
If you are a non-commercial user and find mergerfs and its support valuable
and would like to support the project financially it would be very
much appreciated.
#### Defaults ####
| Category | Policy |
|----------|--------|
| action | all |
| create | epmfs |
| search | ff |
#### rename & link ####
[rename](http://man7.org/linux/man-pages/man2/rename.2.html) is a tricky function in a merged system. Normally if a rename can't be done atomically due to the source and destination paths existing on different mount points it will return **-1** with **errno = EXDEV**. The atomic rename is most critical for replacing files in place atomically (such as securing writing to a temp file and then replacing a target). The problem is that by merging multiple paths you can have N instances of the source and destinations on different drives. This can lead to several undesirable situtations with or without errors and it's not entirely obvious what to do when an error occurs.
Originally mergerfs would return EXDEV whenever a rename was requested which was cross directory in any way. This made the code simple and was technically complient with POSIX requirements. However, many applications fail to handle EXDEV at all and treat it as a normal error or they only partially support EXDEV (don't respond the same as `mv` would). Such apps include: gvfsd-fuse v1.20.3 and prior, Finder / CIFS/SMB client in Apple OSX 10.9+, NZBGet, Samba's recycling bin feature.
* If using a **create** policy which tries to preserve directory paths (epff,eplfs,eplus,epmfs)
* Using the **rename** policy get the list of files to rename
* For each file attempt rename:
* If failure with ENOENT run **create** policy
* If create policy returns the same drive as currently evaluating then clone the path
* Re-attempt rename
* If **any** of the renames succeed the higher level rename is considered a success
* If **no** renames succeed the first error encountered will be returned
* On success:
* Remove the target from all drives with no source file
* Remove the source from all drives which failed to rename
* If using a **create** policy which does **not** try to preserve directory paths
* Using the **rename** policy get the list of files to rename
* Using the **getattr** policy get the target path
* For each file attempt rename:
* If the source drive != target drive:
* Clone target path from target drive to source drive
* Rename
* If **any** of the renames succeed the higher level rename is considered a success
* If **no** renames succeed the first error encountered will be returned
* On success:
* Remove the target from all drives with no source file
* Remove the source from all drives which failed to rename
The the removals are subject to normal entitlement checks.
The above behavior will help minimize the likelihood of EXDEV being returned but it will still be possible. To remove the possibility all together mergerfs would need to perform the as **mv** does when it receives EXDEV normally.
**link** uses the same basic strategy.
#### readdir ####
[readdir](http://linux.die.net/man/3/readdir) is different from all other filesystem functions. While it could have it's own set of policies to tweak its behavior at this time it provides a simple union of files and directories found. Remember that any action or information queried about these files and directories come from the respective function. For instance: an **ls** is a **readdir** and for each file/directory returned **getattr** is called. Meaning the policy of **getattr** is responsible for choosing the file/directory which is the source of the metadata you see in an **ls**.
#### statvfs ####
[statvfs](http://linux.die.net/man/2/statvfs) normalizes the source drives based on the fragment size and sums the number of adjusted blocks and inodes. This means you will see the combined space of all sources. Total, used, and free. The sources however are dedupped based on the drive so multiple sources on the same drive will not result in double counting it's space.
# BUILDING
**NOTE:** Prebuilt packages can be found at: https://github.com/trapexit/mergerfs/releases
First get the code from [github](http://github.com/trapexit/mergerfs).
```
$ git clone https://github.com/trapexit/mergerfs.git
$ # or
$ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ver>.tar.gz
```
#### Debian / Ubuntu
```
$ sudo apt-get install g++ pkg-config git git-buildpackage pandoc debhelper libfuse-dev libattr1-dev python
$ cd mergerfs
$ make deb
$ sudo dpkg -i ../mergerfs_version_arch.deb
```
#### Fedora
```
$ su -
# dnf install rpm-build fuse-devel libattr-devel pandoc gcc-c++ git make which python
# cd mergerfs
# make rpm
# rpm -i rpmbuild/RPMS/<arch>/mergerfs-<verion>.<arch>.rpm
```
#### Generically
Have git, python, pkg-config, pandoc, libfuse, libattr1 installed.
```
$ cd mergerfs
$ make
$ make man
$ sudo make install
```
# RUNTIME
#### .mergerfs pseudo file ####
```
<mountpoint>/.mergerfs
```
There is a pseudo file available at the mount point which allows for the runtime modification of certain **mergerfs** options. The file will not show up in **readdir** but can be **stat**'ed and manipulated via [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls.
Even if xattrs are disabled for mergerfs the [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls against this pseudo file will still work.
Any changes made at runtime are **not** persisted. If you wish for values to persist they must be included as options wherever you configure the mounting of mergerfs (fstab).
##### Keys #####
Use `xattr -l /mount/point/.mergerfs` to see all supported keys. Some are informational and therefore readonly.
###### user.mergerfs.srcmounts ######
Used to query or modify the list of source mounts. When modifying there are several shortcuts to easy manipulation of the list.
| Value | Description |
|--------------|-------------|
| [list] | set |
| +<[list] | prepend |
| +>[list] | append |
| -[list] | remove all values provided |
| -< | remove first in list |
| -> | remove last in list |
###### minfreespace ######
Input: interger with an optional multiplier suffix. **K**, **M**, or **G**.
Output: value in bytes
###### moveonenospc ######
Input: **true** and **false**
Ouput: **true** or **false**
###### categories / funcs ######
Input: short policy string as described elsewhere in this document
Output: the policy string except for categories where its funcs have multiple types. In that case it will be a comma separated list
##### Example #####
```
[trapexit:/tmp/mount] $ xattr -l .mergerfs
user.mergerfs.srcmounts: /tmp/a:/tmp/b
user.mergerfs.minfreespace: 4294967295
user.mergerfs.moveonenospc: false
...
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.category.search .mergerfs
ff
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.category.search newest .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.category.search .mergerfs
newest
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts +/tmp/c .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs
/tmp/a:/tmp/b:/tmp/c
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts =/tmp/c .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs
/tmp/c
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts '+</tmp/a:/tmp/b' .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs
/tmp/a:/tmp/b:/tmp/c
```
#### file / directory xattrs ####
While they won't show up when using [listxattr](http://linux.die.net/man/2/listxattr) **mergerfs** offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following:
* **user.mergerfs.basepath:** the base mount point for the file given the current getattr policy
* **user.mergerfs.relpath:** the relative path of the file from the perspective of the mount point
* **user.mergerfs.fullpath:** the full path of the original file given the getattr policy
* **user.mergerfs.allpaths:** a NUL ('\0') separated list of full paths to all files found
```
[trapexit:/tmp/mount] $ ls
A B C
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.fullpath A
/mnt/a/full/path/to/A
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.basepath A
/mnt/a
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.relpath A
/full/path/to/A
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.allpaths A | tr '\0' '\n'
/mnt/a/full/path/to/A
/mnt/b/full/path/to/A
```
# TOOLING
* https://github.com/trapexit/mergerfs-tools
* mergerfs.ctl: A tool to make it easier to query and configure mergerfs at runtime
* mergerfs.fsck: Provides permissions and ownership auditing and the ability to fix them
* mergerfs.dedup: Will help identify and optionally remove duplicate files
* mergerfs.mktrash: Creates FreeDesktop.org Trash specification compatible directories on a mergerfs mount
* https://github.com/trapexit/scorch
* scorch: A tool to help discover silent corruption of files
# TIPS / NOTES
* Run mergerfs as `root` unless you're merging paths which are owned by the same user otherwise strange permission issues may arise.
* https://github.com/trapexit/backup-and-recovery-howtos : A set of guides / howtos on creating a data storage system, backing it up, maintaining it, and recovering from failure.
* If you don't see some directories / files you expect in a merged point be sure the user has permission to all the underlying directories. If `/drive0/a` has is owned by `root:root` with ACLs set to `0700` and `/drive1/a` is `root:root` and `0755` you'll see only `/drive1/a`. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
* Do *not* use `direct_io` if you expect applications (such as rtorrent) to [mmap](http://linux.die.net/man/2/mmap) files. It is not currently supported in FUSE w/ `direct_io` enabled.
* Since POSIX gives you only error or success on calls its difficult to determine the proper behavior when applying the behavior to multiple targets. **mergerfs** will return an error only if all attempts of an action fail. Any success will lead to a success returned.
* The recommended options are **defaults,allow_other**. The **allow_other** is to allow users who are not the one which executed mergerfs access to the mountpoint. **defaults** is described above and should offer the best performance. It's possible that if you're running on an older platform the **splice** features aren't available and could error. In that case simply use the other options manually.
* If write performance is valued more than read it may be useful to enable **direct_io**. Best to benchmark with and without and choose appropriately.
* Remember: some policies mixed with some functions may result in strange behaviors. Not that some of these behaviors and race conditions couldn't happen outside **mergerfs** but that they are far more likely to occur on account of attempt to merge together multiple sources of data which could be out of sync due to the different policies.
* An example: [Kodi](http://kodi.tv) and [Plex](http://plex.tv) can use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether to scan for new content rather than simply performing a full scan. If using the current default **getattr** policy of **ff** its possible **Kodi** will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
* Due to previously mentioned issues its generally best to set **category** wide policies rather than individual **func**'s. This will help limit the confusion of tools such as [rsync](http://linux.die.net/man/1/rsync).
# KNOWN ISSUES / BUGS
#### rtorrent fails with ENODEV (No such device)
Be sure to turn off `direct_io`. rtorrent and some other applications use [mmap](http://linux.die.net/man/2/mmap) to read and write to files and offer no failback to traditional methods. FUSE does not currently support mmap while using `direct_io`. There will be a performance penalty on writes with `direct_io` off but it's the only way to get such applications to work. If the performance loss is too high for other apps you can mount mergerfs twice. Once with `direct_io` enabled and one without it.
#### mmap performance is really bad
There [is a bug](https://lkml.org/lkml/2016/3/16/260) in caching which affects overall performance of mmap through FUSE in Linux 4.x kernels. It is fixed in [4.4.10 and 4.5.4](https://lkml.org/lkml/2016/5/11/59).
#### Trashing files occasionally fails
This is the same issue as with Samba. `rename` returns `EXDEV` (in our case that will really only happen with path preserving policies like `epmfs`) and the software doesn't handle the situtation well. This is unfortunately a common failure of software which moves files around. The standard indicates that an implementation `MAY` choose to support non-user home directory trashing of files (which is a `MUST`). The implementation `MAY` also support "top directory trashes" which many probably do.
To create a `$topdir/.Trash` directory as defined in the standard use the [mergerfs-tools](https://github.com/trapexit/mergerfs-tools) tool `mergerfs.mktrash`.
#### Samba: Moving files / directories fails
Workaround: Copy the file/directory and then remove the original rather than move.
This isn't an issue with Samba but some SMB clients. GVFS-fuse v1.20.3 and prior (found in Ubuntu 14.04 among others) failed to handle certain error codes correctly. Particularly **STATUS_NOT_SAME_DEVICE** which comes from the **EXDEV** which is returned by **rename** when the call is crossing mount points. When a program gets an **EXDEV** it needs to explicitly take an alternate action to accomplish it's goal. In the case of **mv** or similar it tries **rename** and on **EXDEV** falls back to a manual copying of data between the two locations and unlinking the source. In these older versions of GVFS-fuse if it received **EXDEV** it would translate that into **EIO**. This would cause **mv** or most any application attempting to move files around on that SMB share to fail with a IO error.
[GVFS-fuse v1.22.0](https://bugzilla.gnome.org/show_bug.cgi?id=734568) and above fixed this issue but a large number of systems use the older release. On Ubuntu the version can be checked by issuing `apt-cache showpkg gvfs-fuse`. Most distros released in 2015 seem to have the updated release and will work fine but older systems may not. Upgrading gvfs-fuse or the distro in general will address the problem.
In Apple's MacOSX 10.9 they replaced Samba (client and server) with their own product. It appears their new client does not handle **EXDEV** either and responds similar to older release of gvfs on Linux.
#### Supplemental user groups
Due to the overhead of [getgroups/setgroups](http://linux.die.net/man/2/setgroups) mergerfs utilizes a cache. This cache is opportunistic and per thread. Each thread will query the supplemental groups for a user when that particular thread needs to change credentials and will keep that data for the lifetime of the thread. This means that if a user is added to a group it may not be picked up without the restart of mergerfs. However, since the high level FUSE API's (at least the standard version) thread pool dynamically grows and shrinks it's possible that over time a thread will be killed and later a new thread with no cache will start and query the new data.
The gid cache uses fixed storage to simplify the design and be compatible with older systems which may not have C++11 compilers. There is enough storage for 256 users' supplemental groups. Each user is allowed upto 32 supplemental groups. Linux >= 2.6.3 allows upto 65535 groups per user but most other *nixs allow far less. NFS allowing only 16. The system does handle overflow gracefully. If the user has more than 32 supplemental groups only the first 32 will be used. If more than 256 users are using the system when an uncached user is found it will evict an existing user's cache at random. So long as there aren't more than 256 active users this should be fine. If either value is too low for your needs you will have to modify `gidcache.hpp` to increase the values. Note that doing so will increase the memory needed by each thread.
#### mergerfs or libfuse crashing
If suddenly the mergerfs mount point disappears and `Transport endpoint is not connected` is returned when attempting to perform actions within the mount directory **and** the version of libfuse (use `mergerfs -v` to find the version) is older than `2.9.4` its likely due to a bug in libfuse. Affected versions of libfuse can be found in Debian Wheezy, Ubuntu Precise and others.
In order to fix this please install newer versions of libfuse. If using a Debian based distro (Debian,Ubuntu,Mint) you can likely just install newer versions of [libfuse](https://packages.debian.org/unstable/libfuse2) and [fuse](https://packages.debian.org/unstable/fuse) from the repo of a newer release.
#### mergerfs under heavy load and memory preasure leads to kernel panic
https://lkml.org/lkml/2016/9/14/527
```
[25192.515454] kernel BUG at /build/linux-a2WvEb/linux-4.4.0/mm/workingset.c:346!
[25192.517521] invalid opcode: 0000 [#1] SMP
[25192.519602] Modules linked in: netconsole ip6t_REJECT nf_reject_ipv6 ipt_REJECT nf_reject_ipv4 configfs binfmt_misc veth bridge stp llc nf_conntrack_ipv6 nf_defrag_ipv6 xt_conntrack ip6table_filter ip6_tables xt_multiport iptable_filter ipt_MASQUERADE nf_nat_masquerade_ipv4 xt_comment xt_nat iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack xt_CHECKSUM xt_tcpudp iptable_mangle ip_tables x_tables intel_rapl x86_pkg_temp_thermal intel_powerclamp eeepc_wmi asus_wmi coretemp sparse_keymap kvm_intel ppdev kvm irqbypass mei_me 8250_fintek input_leds serio_raw parport_pc tpm_infineon mei shpchp mac_hid parport lpc_ich autofs4 drbg ansi_cprng dm_crypt algif_skcipher af_alg btrfs raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid0 multipath linear raid10 raid1 i915 crct10dif_pclmul crc32_pclmul aesni_intel i2c_algo_bit aes_x86_64 drm_kms_helper lrw gf128mul glue_helper ablk_helper syscopyarea cryptd sysfillrect sysimgblt fb_sys_fops drm ahci r8169 libahci mii wmi fjes video [last unloaded: netconsole]
[25192.540910] CPU: 2 PID: 63 Comm: kswapd0 Not tainted 4.4.0-36-generic #55-Ubuntu
[25192.543411] Hardware name: System manufacturer System Product Name/P8H67-M PRO, BIOS 3904 04/27/2013
[25192.545840] task: ffff88040cae6040 ti: ffff880407488000 task.ti: ffff880407488000
[25192.548277] RIP: 0010:[<ffffffff811ba501>] [<ffffffff811ba501>] shadow_lru_isolate+0x181/0x190
[25192.550706] RSP: 0018:ffff88040748bbe0 EFLAGS: 00010002
[25192.553127] RAX: 0000000000001c81 RBX: ffff8802f91ee928 RCX: ffff8802f91eeb38
[25192.555544] RDX: ffff8802f91ee938 RSI: ffff8802f91ee928 RDI: ffff8804099ba2c0
[25192.557914] RBP: ffff88040748bc08 R08: 000000000001a7b6 R09: 000000000000003f
[25192.560237] R10: 000000000001a750 R11: 0000000000000000 R12: ffff8804099ba2c0
[25192.562512] R13: ffff8803157e9680 R14: ffff8803157e9668 R15: ffff8804099ba2c8
[25192.564724] FS: 0000000000000000(0000) GS:ffff88041f280000(0000) knlGS:0000000000000000
[25192.566990] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[25192.569201] CR2: 00007ffabb690000 CR3: 0000000001e0a000 CR4: 00000000000406e0
[25192.571419] Stack:
[25192.573550] ffff8804099ba2c0 ffff88039e4f86f0 ffff8802f91ee928 ffff8804099ba2c8
[25192.575695] ffff88040748bd08 ffff88040748bc58 ffffffff811b99bf 0000000000000052
[25192.577814] 0000000000000000 ffffffff811ba380 000000000000008a 0000000000000080
[25192.579947] Call Trace:
[25192.582022] [<ffffffff811b99bf>] __list_lru_walk_one.isra.3+0x8f/0x130
[25192.584137] [<ffffffff811ba380>] ? memcg_drain_all_list_lrus+0x190/0x190
[25192.586165] [<ffffffff811b9a83>] list_lru_walk_one+0x23/0x30
[25192.588145] [<ffffffff811ba544>] scan_shadow_nodes+0x34/0x50
[25192.590074] [<ffffffff811a0e9d>] shrink_slab.part.40+0x1ed/0x3d0
[25192.591985] [<ffffffff811a53da>] shrink_zone+0x2ca/0x2e0
[25192.593863] [<ffffffff811a64ce>] kswapd+0x51e/0x990
[25192.595737] [<ffffffff811a5fb0>] ? mem_cgroup_shrink_node_zone+0x1c0/0x1c0
[25192.597613] [<ffffffff810a0808>] kthread+0xd8/0xf0
[25192.599495] [<ffffffff810a0730>] ? kthread_create_on_node+0x1e0/0x1e0
[25192.601335] [<ffffffff8182e34f>] ret_from_fork+0x3f/0x70
[25192.603193] [<ffffffff810a0730>] ? kthread_create_on_node+0x1e0/0x1e0
```
There is a bug in the kernel. A work around appears to be turning off `splice`. Add `no_splice_write,no_splice_move,no_splice_read` to mergerfs' options. Should be placed after `defaults` if it is used since it will turn them on.
# FAQ
#### Why use mergerfs over mhddfs?
mhddfs is no longer maintained and has some known stability and security issues (see below). MergerFS provides a superset of mhddfs' features and should offer the same or maybe better performance.
#### Why use mergerfs over aufs?
While aufs can offer better peak performance mergerfs offers more configurability and is generally easier to use. mergerfs however doesn't offer the same overlay features (which tends to result in whiteout files being left around the underlying filesystems.)
#### Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation / striping?
With simple JBOD / drive concatenation / stripping / RAID0 a single drive failure will lead to full pool failure. mergerfs performs a similar behavior without the catastrophic failure and general lack of recovery. Drives can fail and all other data will continue to be accessable.
When combined with something like [SnapRaid](http://www.snapraid.it) and/or an offsite full backup solution you can have the flexibilty of JBOD without the single point of failure.
#### Can drives be written to directly? Outside of mergerfs while pooled?
Yes. It will be represented immediately in the pool as the policies would describe.
#### Why do I get an "out of space" error even though the system says there's lots of space left?
Please reread the sections above about policies, path preserving, and the **moveonenospc** option. If the policy is path preserving and a drive is almost full and the drive the policy would pick then the writing of the file may fill the drive and receive ENOSPC errors. That is expected with those settings. If you don't want that: enable **moveonenospc** and don't use a path preserving policy.
#### How are inodes calculated?
mergerfs-inode = (original-inode | (device-id << 32))
While `ino_t` is 64 bits few filesystems use more than 32. Similarly, while `dev_t` is also 64 bits it was traditionally 16 bits. Bitwise or'ing them together should work most of the time. Should it cause a problem in the future the values could be hashed instead.
#### It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
[mhddfs](https://github.com/trapexit/mhddfs) tries to handle being run as **root** by calling [getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319) and if it returns **0** then it will [chown](http://linux.die.net/man/1/chown) the file. Not only is that a race condition but it doesn't handle many other situations. Rather than attempting to simulate POSIX ACL behaviors the proper behavior is to use [seteuid](http://linux.die.net/man/2/seteuid) and [setegid](http://linux.die.net/man/2/setegid), become the user making the original call and perform the action as them. This is how [mergerfs](https://github.com/trapexit/mergerfs) handles things.
If you are familiar with POSIX standards you'll know that this behavior poses a problem. **seteuid** and **setegid** affect the whole process and **libfuse** is multithreaded by default. We'd need to lock access to **seteuid** and **setegid** with a mutex so that the several threads aren't stepping on one anofther and files end up with weird permissions and ownership. This however wouldn't scale well. With lots of calls the contention on that mutex would be extremely high. Thankfully on Linux and OSX we have a better solution.
OSX has a [non-portable pthread extension](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/pthread_setugid_np.2.html) for per-thread user and group impersonation.
Linux does not support [pthread_setugid_np](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/pthread_setugid_np.2.html) but user and group IDs are a per-thread attribute though documentation on that fact or how to manipulate them is not well distributed. From the **4.00** release of the Linux man-pages project for [setuid](http://man7.org/linux/man-pages/man2/setuid.2.html).
> At the kernel level, user IDs and group IDs are a per-thread attribute. However, POSIX requires that all threads in a process share the same credentials. The NPTL threading implementation handles the POSIX requirements by providing wrapper functions for the various system calls that change process UIDs and GIDs. These wrapper functions (including the one for setuid()) employ a signal-based technique to ensure that when one thread changes credentials, all of the other threads in the process also change their credentials. For details, see nptl(7).
Turns out the setreuid syscalls apply only to the thread. GLIBC hides this away using RT signals to inform all threads to change credentials. Taking after **Samba** mergerfs uses **syscall(SYS_setreuid,...)** to set the callers credentials for that thread only. Jumping back to **root** as necessary should escalated privileges be needed (for instance: to clone paths).
For non-Linux systems mergerfs uses a read-write lock and changes credentials only when necessary. If multiple threads are to be user X then only the first one will need to change the processes credentials. So long as the other threads need to be user X they will take a readlock allow multiple threads to share the credentials. Once a request comes in to run as user Y that thread will attempt a write lock and change to Y's credentials when it can. If the ability to give writers priority is supported then that flag will be used so threads trying to change credentials don't starve. This isn't the best solution but should work reasonably well. As new platforms are supported if they offer per thread credentials those APIs will be adopted.
# SUPPORT
#### Issues with the software
* github.com: https://github.com/trapexit/mergerfs/issues
* email: trapexit@spawn.link
#### Support development
* Gratipay: https://gratipay.com/~trapexit
* BitCoin: 12CdMhEPQVmjz3SSynkAEuD5q9JmhTDCZA
# LINKS
* http://github.com/trapexit/mergerfs
* http://github.com/trapexit/mergerfs-tools
* http://github.com/trapexit/scorch
* http://github.com/trapexit/backup-and-recovery-howtos
If you are using mergerfs commercially please consider sponsoring the
project to ensure it continues to be maintained and receive
updates. If custom features are needed feel free to [contact me
directly](mailto:support@spawn.link).
+55
View File
@@ -0,0 +1,55 @@
#!/bin/sh
set -e
if [ $# -ne 2 ]; then
echo "usage: $0 <branch> <git_repo>"
echo "argv: $@"
echo "argc: $#"
exit 1
fi
GIT_REPO="${1}"
BRANCH="${2}"
BUILDDIR="/tmp/build"
echo "Clone source from ${GIT_REPO} to ${BUILDDIR}"
git \
clone \
--single-branch \
--branch="${BRANCH}" \
"${GIT_REPO}" \
"${BUILDDIR}"
cd "${BUILDDIR}"
git log HEAD^1..
mkdir "/build"
if [ -e /usr/bin/apt-get ]; then
make deb
elif [ -e /usr/bin/dnf ]; then
make rpm
elif [ -e /usr/bin/yum ]; then
. /opt/rh/devtoolset-9/enable
make rpm
elif [ -e /sbin/apk ]; then
echo "NOT YET SUPPORTED"
exit 1
elif [ -e /usr/sbin/pkg ]; then
echo "NOT YET SUPPORTED"
exit 1
else
echo "NOT YET SUPPORTED"
exit 1
fi
echo "Copy packages to host..."
find "${BUILDDIR}" \
-type f \
\( -name "*.deb" -or -name "*.rpm" \) \
-not -name "*dbgsym*" \
-not -name "*sym*" \
-not -name "*src.rpm" \
-exec cp -v {} /build/ \;
exit 0
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
import subprocess
import sys
import os
import argparse
import time
def build(git_repo,
branch,
containerfile,
pkgdirpath):
timestamp = time.time_ns()
args = ['podman',
'build',
'--pull=always',
'--force-rm']
if os.path.exists(git_repo):
git_repo = os.path.realpath(git_repo)
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro']
args += ['--build-arg=GIT_REPO=/tmp/mergerfs.git']
else:
args += [f'--build-arg=GIT_REPO={git_repo}']
args += ['-o',pkgdirpath,
'-f',containerfile,
f'--build-arg=BRANCH={branch}',
f'--build-arg=BUILD_TIMESTAMP={timestamp}',
'buildtools/']
# TODO: Capture output and write to log
print(args)
rv = subprocess.run(args)
os.makedirs(pkgdirpath,exist_ok=True)
report_filepath = os.path.join(pkgdirpath,"build-report.txt")
with open(report_filepath,"a+") as f:
build = os.path.basename(containerfile)
f.write(build + ": ")
f.write(f"branch={branch}; ")
f.write(f"timestamp={timestamp}; ")
f.write("rv=")
if rv.returncode == 0:
f.write("success;")
else:
f.write("fail;")
f.write("\n")
def setup():
args = ['sudo',
'apt-get',
'install',
'-fy',
'qemu-user-static',
'qemu-user-binfmt']
print(args)
subprocess.run(args)
def podman_cleanup():
args = ['podman',
'system',
'prune',
'-af']
print(args)
subprocess.run(args)
def parse_args():
p = argparse.ArgumentParser()
p.add_argument('--target',
default='debian:13.amd64')
p.add_argument('--pkgdirpath',
default='build/pkgs/')
p.add_argument('--branch',
default='master')
p.add_argument('--setup',
required=False,
action='store_true')
p.add_argument('--cleanup',
required=False,
action='store_true')
p.add_argument('--git-repo',
required=True,
type=str)
return p.parse_args()
def should_skip(filename):
if filename.endswith('~'):
return True
return False
def main():
args = parse_args()
print(args)
if args.setup:
setup()
sys.exit(0)
containerfiles = []
basepath = 'buildtools/containerfiles'
if os.path.exists(f'{basepath}/{args.target}'):
containerfiles.append(f'{basepath}/{args.target}')
elif args.target == 'all':
for root,dirnames,filenames in os.walk(basepath):
filenames.sort()
for filename in filenames:
if should_skip(filename):
continue
containerfile = f'buildtools/containerfiles/{filename}'
containerfiles.append(containerfile)
else:
for root,dirnames,filenames in os.walk(basepath):
filenames.sort()
for filename in filenames:
if args.target not in filename:
continue
if should_skip(filename):
continue
containerfile = f'buildtools/containerfiles/{filename}'
containerfiles.append(containerfile)
for containerfile in containerfiles:
if args.cleanup:
podman_cleanup()
build(git_repo=args.git_repo,
branch=args.branch,
containerfile=containerfile,
pkgdirpath=args.pkgdirpath)
sys.exit(0)
if __name__ == '__main__':
main()
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:11 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:12 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=armhf debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=i386 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 debian:13 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:40 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:40 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:41 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:41 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:42 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:42 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 fedora:43 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=arm64 fedora:43 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:8 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:8 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 quay.io/rockylinux/rockylinux:9 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 quay.io/rockylinux/rockylinux:9 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_amd64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/arm64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_arm64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/armhf alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_armhf.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/i386 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_i386.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 ubuntu:latest as build
RUN apt-get update && apt-get install -y makeself
RUN mkdir -p /tmp/mergerfs/
RUN --mount=type=bind,src=pkgs,dst=/pkgs ls -lhd /pkgs
RUN --mount=type=bind,src=pkgs,dst=/pkgs cp -v /pkg/*static*.gz /tmp/mergerfs/
RUN makeself /tmp/mergerfs/ /build/mergerfs-installer.run "mergerfs" echo "foo"
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/riscv64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+13
View File
@@ -0,0 +1,13 @@
ARG BUILD_TIMESTAMP=0
FROM alpine:latest as tarball
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
RUN cd /tmp/mergerfs && make version tarball
RUN mkdir /build
RUN cp -v /tmp/mergerfs/*.tar.gz /build/
FROM scratch
COPY --from=tarball /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:22.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:24.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:25.04 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -x
BASEPATH="/tmp"
for x in $(seq -w 2)
do
FILEPATH="${BASEPATH}/mergerfs-${x}.img"
MOUNTPOINT="${BASEPATH}/mergerfs-${x}"
truncate -s 1G "${FILEPATH}"
mkdir -p "${MOUNTPOINT}"
mkfs.ext4 -m0 -L "mergerfs${x}" "${FILEPATH}"
sudo mount "${FILEPATH}" "${MOUNTPOINT}"
done
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
input_file="${1}"
while IFS= read -r line || [ -n "$line" ]; do
# Skip empty lines
if [ -z "$line" ]; then
continue
fi
image=$(echo "$line" | cut -d' ' -f1)
platform=$(echo "$line" | cut -d' ' -f2-)
echo "${image} ${platform}"
cat <<EOF > "containerfiles/${image##*/}.${platform}"
FROM --platform=${platform} ${image} as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs \$GIT_REPO \$BRANCH
FROM scratch
COPY --from=build /build/ /
EOF
done < "$input_file"
+202
View File
@@ -0,0 +1,202 @@
#!/bin/sh
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
# 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.
# Default values
NAME=""
VERSION="::guess::"
DISTRO="::guess::"
CODENAME="::guess::"
URGENCY="medium"
# Function to display usage
usage() {
cat << EOF
Usage: $0 --name NAME [OPTIONS]
Generated debian/changelog from git log
Options:
--name NAME Name of package (required)
--version VERSION Place in git history to include upto (default: ::guess::)
--distro DISTRO Distribution name (default: ::guess::)
--codename NAME Distribution codename (default: ::guess::)
--urgency URGENCY Urgency (default: medium)
-h, --help Show this help message
EOF
exit 1
}
# Parse command line arguments
while [ $# -gt 0 ]; do
case "$1" in
--name)
NAME="$2"
shift 2
;;
--version)
VERSION="$2"
shift 2
;;
--distro)
DISTRO="$2"
shift 2
;;
--codename)
CODENAME="$2"
shift 2
;;
--urgency)
URGENCY="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
# Check required arguments
if [ -z "$NAME" ]; then
echo "Error: --name is required" >&2
usage
fi
# Function to get git tags sorted in reverse order
git_tags() {
git tag -l | sort -rV
}
# Function to get git log between two tags
git_log() {
local fromtag="$1"
local totag="$2"
git log --no-merges --oneline "${fromtag}...${totag}" 2>/dev/null
}
# Function to get author and time for a tag
git_author_and_time() {
local tag="$1"
git log -1 --format="-- %an <%ae> %cD" "$tag"
}
# Function to get git version
git_version() {
git describe --always --tags --dirty 2>/dev/null || echo "unknown"
}
# Function to guess distribution
guess_distro() {
if command -v lsb_release >/dev/null 2>&1; then
lsb_release -i -s 2>/dev/null | tr '[:upper:]' '[:lower:]'
else
echo "unknown"
fi
}
# Function to guess codename
guess_codename() {
if command -v lsb_release >/dev/null 2>&1; then
lsb_release -c -s 2>/dev/null | tr '[:upper:]' '[:lower:]'
else
echo "unknown"
fi
}
# Handle guessing of values
if [ "$DISTRO" = "::guess::" ]; then
DISTRO=$(guess_distro)
fi
if [ "$CODENAME" = "::guess::" ]; then
CODENAME=$(guess_codename)
fi
if [ "$VERSION" = "::guess::" ]; then
VERSION=$(git_version)
fi
# Version suffix
VERSUFFIX="~${DISTRO}-${CODENAME}"
# Get all tags
TAGS=$(git_tags)
# Create temporary file for tag pairs
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
# Check if version is in tags
if echo "$TAGS" | grep -q "^${VERSION}$"; then
# If version is a tag, start from that tag
echo "$TAGS" | awk -v ver="$VERSION" 'BEGIN{found=0} {if($0==ver)found=1; if(found)print}' > "$TMPFILE"
else
# If version is not a tag, use HEAD as starting point
echo "$VERSION HEAD" > "$TMPFILE"
echo "$TAGS" >> "$TMPFILE"
fi
# Process tags and generate changelog
PREV_TAG=""
PREV_REF=""
FIRST=1
while IFS= read -r line; do
if [ -z "$line" ]; then
continue
fi
# Handle the special case of VERSION HEAD
if [ "$line" = "$VERSION HEAD" ]; then
TAG="$VERSION"
REF="HEAD"
else
TAG="$line"
REF="$line"
fi
if [ -n "$PREV_TAG" ]; then
# Get log entries between current and previous tag
LOG_ENTRIES=$(git_log "$REF" "$PREV_REF")
# Skip if no changes
if [ -z "$LOG_ENTRIES" ] || [ "$LOG_ENTRIES" = "" ]; then
PREV_TAG="$TAG"
PREV_REF="$REF"
continue
fi
# Print package header
printf "%s (%s%s) %s; urgency=%s\n\n" "$NAME" "$PREV_TAG" "$VERSUFFIX" "$CODENAME" "$URGENCY"
# Print each log entry
echo "$LOG_ENTRIES" | while IFS= read -r entry; do
if [ -n "$entry" ]; then
printf " * %s\n" "$entry"
fi
done
# Print author and time
AUTHOR_TIME=$(git_author_and_time "$PREV_REF")
printf "\n %s\n\n" "$AUTHOR_TIME"
fi
PREV_TAG="$TAG"
PREV_REF="$REF"
done < "$TMPFILE"
+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
if [ -e /usr/bin/apt-get ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get -qy update
apt-get -qy --force-yes --no-install-suggests --no-install-recommends \
install \
ca-certificates \
build-essential \
dpkg-dev \
git \
g++ \
debhelper \
lsb-release \
fakeroot
elif [ -e /usr/bin/dnf ]; then
dnf -y update
dnf -y install \
git rpm-build gcc-c++ make which libatomic
elif [ -e /usr/bin/yum ]; then
yum -y update
yum -y install \
git rpm-build gcc-c++ make which
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc\*
elif [ -e /usr/bin/zypper ]; then
zypper update -y
zypper install -y \
git rpm-build gcc-c++ make which
elif [ -e /sbin/apk ]; then
apk add \
abuild git gcc g++ make \
linux-headers
elif [ -e /usr/sbin/pkg ]; then
pkg install \
git gmake gcc
fi
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
if [ -e /usr/bin/apt-get ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get -qy update
apt-get -qy --force-yes --no-install-suggests --no-install-recommends install \
qemu-user-static \
podman
elif [ -e /usr/bin/dnf ]; then
dnf -y update
dnf -y install \
qemu-user-static \
podman
elif [ -e /sbin/apk ]; then
apk add \
python3 \
qemu-user-static \
podman
fi
+39
View File
@@ -0,0 +1,39 @@
debian:11 amd64
debian:11 arm64
debian:11 armhf
debian:11 i386
debian:12 amd64
debian:12 arm64
debian:12 armhf
debian:12 i386
debian:13 amd64
debian:13 arm64
debian:13 armhf
debian:13 i386
debian:13 riscv64
fedora:40 amd64
fedora:40 arm64
fedora:41 amd64
fedora:41 arm64
fedora:42 amd64
fedora:42 arm64
fedora:43 amd64
fedora:43 arm64
quay.io/rockylinux/rockylinux:10 amd64
quay.io/rockylinux/rockylinux:10 arm64
quay.io/rockylinux/rockylinux:8 amd64
quay.io/rockylinux/rockylinux:8 arm64
quay.io/rockylinux/rockylinux:9 amd64
quay.io/rockylinux/rockylinux:9 arm64
ubuntu:22.04 amd64
ubuntu:22.04 arm64
ubuntu:22.04 armhf
ubuntu:22.04 riscv64
ubuntu:24.04 amd64
ubuntu:24.04 arm64
ubuntu:24.04 armhf
ubuntu:24.04 riscv64
ubuntu:25.04 amd64
ubuntu:25.04 arm64
ubuntu:25.04 armhf
ubuntu:25.04 riscv64
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
VERSION=$(git describe --always --tags --dirty)
if [ $? -ne 0 ]; then
VERSION=$(cat VERSION)
fi
if [ "${VERSION}" = "" ]; then
VERSION="unknown"
fi
echo "version = ${VERSION}"
echo -n "${VERSION}" > VERSION
grep -sq \"${VERSION}\" src/version.hpp
RV=$?
if [ $RV -ne 0 ]; then
echo "Writing 'src/version.hpp'"
echo "#pragma once" > src/version.hpp
echo "static const char MERGERFS_VERSION[] = \"${VERSION}\";" >> src/version.hpp
fi
-1
View File
@@ -1 +0,0 @@
9
+5 -8
View File
@@ -2,10 +2,7 @@ Source: mergerfs
Section: utils
Priority: optional
Maintainer: Antonio SJ Musumeci <trapexit@spawn.link>
Build-Depends: debhelper (>= 8.0.0),
libfuse-dev,
libattr1-dev,
pkg-config
Build-Depends: debhelper-compat (= 10)
Standards-Version: 3.9.4
Homepage: http://github.com/trapexit/mergerfs
@@ -13,8 +10,8 @@ Package: mergerfs
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libfuse2
fuse [linux-any] | fuse4bsd [kfreebsd-any]
Description: another FUSE union filesystem
mergerfs is similar to mhddfs, unionfs, and aufs. Like mhddfs in that it too
uses FUSE. Like aufs in that it provides multiple policies for how to handle
behavior.
mergerfs is a union filesystem geared towards simplifying storage and
management of files across numerous commodity storage devices. It is
similar to mhddfs, unionfs, and aufs.
Vendored Executable
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
set -e
case "${1}" in
configure)
chmod 4755 /usr/bin/mergerfs-fusermount
;;
esac
+5 -3
View File
@@ -1,11 +1,13 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
export DH_VERBOSE=1
%:
dh $@
override_dh_auto_install:
$(MAKE) DESTDIR=$(CURDIR)/debian/mergerfs PREFIX=/usr install
$(MAKE) \
DESTDIR=$(CURDIR)/debian/mergerfs \
PREFIX=/usr \
install
+59
View File
@@ -0,0 +1,59 @@
Current Maintainer
------------------
Nikolaus Rath <Nikolaus@rath.org>
Past Maintainers
----------------
Miklos Szeredi <miklos@szeredi.hu> (until 12/2015)
Contributors
------------
CUSE has been written by Tejun Heo <teheo@suse.de>. Furthermore, the
following people have contributed patches (autogenerated list):
Anatol Pomozov <anatol.pomozov@gmail.com>
Antonio SJ Musumeci <trapexit@spawn.link>
Christopher Harrison <ch12@sanger.ac.uk>
Csaba Henk <csaba.henk@creo.hu>
cvs2git <>
Dalvik Khertel <khertel@outlook.com>
Daniel Thau <danthau@bedrocklinux.org>
David McNab <david@rebirthing.co.nz>
David Sheets <sheets@alum.mit.edu>
Emmanuel Dreyfus <manu@netbsd.org>
Enke Chen <enkechen@yahoo.com>
Eric Engestrom <eric@engestrom.ch>
Eric Wong <normalperson@yhbt.net>
Fabrice Bauzac <fbauzac@amadeus.com>
Feng Shuo <steve.shuo.feng@gmail.com>
Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Ikey Doherty <michael.i.doherty@intel.com>
Jan Blumschein <jan@jan-blumschein.de>
Joachim Schiele <joachim.schiele@daimler.com>
Joachim Schiele <js@lastlog.de>
John Muir <john@jmuir.com>
Laszlo Papp <ext-laszlo.papp@nokia.com>
Madan Valluri <mvalluri@cumulus-systems.com>
Mark Glines <mark@glines.org>
Max Krasnyansky <maxk@kernel.org>
Michael Grigoriev <mag@luminal.org>
Miklos Szeredi <miklos@szeredi.hu>
Miklos Szeredi <mszeredi@suse.cz>
mkmm@gmx-topmail.de <mkmm@gmx-topmail.de>
Natanael Copa <ncopa@alpinelinux.org>
Nikolaus Rath <Nikolaus@rath.org>
Olivier Blin <olivier.blin@softathome.com>
Ratna_Bolla@dell.com <Ratna_Bolla@dell.com>
Reuben Hawkins <reubenhwk@gmail.com>
Richard W.M. Jones <rjones@redhat.com>
Riku Voipio <riku.voipio@linaro.org>
Roland Bauerschmidt <rb@debian.org>
Sam Stuewe <halosghost@archlinux.info>
Sebastian Pipping <sebastian@pipping.org>
therealneworld@gmail.com <therealneworld@gmail.com>
Winfried Koehler <w_scan@gmx-topmail.de>
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+502
View File
@@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+155
View File
@@ -0,0 +1,155 @@
# Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
#
# 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.
AR ?= ar
CHMOD ?= chmod
CHOWN ?= chown
INSTALL ?= install
MKDIR ?= mkdir
RM ?= rm
STRIP ?= strip
TOUCH ?= touch
OS := $(shell uname -s)
ifeq ($(OS),Linux)
UTILS := utils
INSTALLUTILS := install-utils
else
UTILS :=
INSTALLUTILS :=
endif
ifeq ($(NDEBUG),1)
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -DDEBUG -fno-omit-frame-pointer
endif
ifeq ($(LTO),1)
LTO_FLAGS := -flto
else
LTO_FLAGS :=
endif
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR ?=
PREFIX ?= /usr/local
EXEC_PREFIX ?= $(PREFIX)
DATAROOTDIR ?= $(PREFIX)/share
DATADIR ?= $(DATAROOTDIR)
BINDIR ?= $(EXEC_PREFIX)/bin
SBINDIR ?= /sbin
MANDIR ?= $(DATAROOTDIR)/man
MAN1DIR ?= $(MANDIR)/man1
INSTALLBINDIR ?= $(DESTDIR)$(BINDIR)
INSTALLSBINDIR ?= $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR ?= $(DESTDIR)$(MAN1DIR)
BUILDDIR := build
SRC_C = \
lib/buffer.c \
lib/crc32b.c \
lib/fuse_opt.c
SRC_CXX = $(wildcard lib/*.cpp)
OBJS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.o)
OBJS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.o)
DEPS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.d)
DEPS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.d)
CPPFLAGS ?=
CFLAGS ?= \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-pipe
override CFLAGS += \
-std=gnu99 \
-MMD \
-MP
CXXFLAGS ?= \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-pipe
override CXXFLAGS += \
-std=c++17 \
-MMD \
-MP
FUSERMOUNT_DIR := $(BINDIR)
FUSE_FLAGS := \
-Iinclude \
-I$(BUILDDIR) \
-D_REENTRANT \
-D_FILE_OFFSET_BITS=64 \
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
LDFLAGS ?=
LDLIBS := \
-lrt \
-latomic \
-pthread
all: $(BUILDDIR)/libfuse.a $(UTILS)
$(BUILDDIR)/stamp:
$(MKDIR) -p $(BUILDDIR)/.objs/
$(TOUCH) $@
$(BUILDDIR)/libfuse.a: $(OBJS)
$(AR) rcs $(BUILDDIR)/libfuse.a $(OBJS)
.PHONY: utils
utils: mergerfs-fusermount mount.mergerfs
$(BUILDDIR)/mergerfs-fusermount: util/fusermount.c lib/mount_util.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -Ilib -o $(BUILDDIR)/mergerfs-fusermount util/fusermount.c lib/mount_util.c
mergerfs-fusermount: $(BUILDDIR)/mergerfs-fusermount
$(BUILDDIR)/mount.mergerfs: $(BUILDDIR)/libfuse.a util/mount.mergerfs.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o $(BUILDDIR)/mount.mergerfs util/mount.mergerfs.c $(BUILDDIR)/libfuse.a $(LDFLAGS)
mount.mergerfs: $(BUILDDIR)/mount.mergerfs
$(BUILDDIR)/.objs/%.c.o: lib/%.c | $(BUILDDIR)/stamp
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
$(BUILDDIR)/.objs/%.cpp.o: lib/%.cpp | $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
.PHONY: clean
clean:
$(RM) -rf $(BUILDDIR)
.PHONY: distclean
distclean: clean
.PHONY: strip
strip:
$(STRIP) --strip-all $(BUILDDIR)/mount.mergerfs
$(STRIP) --strip-all $(BUILDDIR)/mergerfs-fusermount
install-utils: mergerfs-fusermount mount.mergerfs strip
$(INSTALL) -D $(BUILDDIR)/mergerfs-fusermount "$(INSTALLBINDIR)/mergerfs-fusermount"
$(INSTALL) -D $(BUILDDIR)/mount.mergerfs "$(INSTALLSBINDIR)/mount.mergerfs"
$(CHOWN) root:root "$(INSTALLBINDIR)/mergerfs-fusermount"
$(CHMOD) u+s "$(INSTALLBINDIR)/mergerfs-fusermount"
install: $(INSTALLUTILS)
.PHONY: objects strip utils install install-utils
-include $(DEPS)
+303
View File
@@ -0,0 +1,303 @@
What is new in 2.9
- Add "zero copy" support for kernel 2.6.35 or newer
- Make maximum background requests tunable on kernel 2.6.32 or newer
- Require --no-canonicalize in (u)mount (util-linux version 2.18 or
newer) to fix security problems with fusermount
- Use dynamically sized hash tables in high level library
- Memory use of filesystem daemon can shrink more easily
- Add "auto_unmount" option
- Add "remember" option
- Add man pages for fusermount, mount.fuse and ulockmgr_server
- API changes:
o Introduce "store" and "retrieve" for accessing kernel buffers on
kernel 2.6.36 or newer
o Introduce abstract buffer for zero copy operations
o Allow path calculation to be omitted on certain operations
o Allow batching forget requests
o Add "flock" method
o Add support for ioctl on directories
o Add delete notification
o Add fallocate operation (linux kernel 3.5 or newer)
- Bug fixes and small improvements
============================================================================
What is new in 2.8
- More scalable directory tree locking
- Atomic open(O_TRUNC) support
- Support big write requests on kernels 2.6.26 and newer
- Out-of-tree fuse module removed
- Better NFS exporting support
- New ioctl and poll requests
- New CUSE (Character Device in Userspace) interface
- Allow umask processing in userspace
- Added cache invalidation notifications
- Bugfixes and small improvements
============================================================================
What is new in 2.7
- Stacking support for the high level API
- Add filename charset conversion module
- Improved mounting
============================================================================
What is new in 2.6
- Improved read characteristics (asynchronous reads)
- Support for aborting filesystem connection
- POSIX file locking support
- Request interruption support
- Building module for Linux kernels earlier than 2.6.9 not supported
- Allow block device based filesystems to support swap files
- Several bugs fixed, including a rare system hang on SMP
============================================================================
What is new in 2.5
- Merge library part of FreeBSD port
- New atomic create+open, access and ftruncate operations
- On filesystems implementing the new create+open operation, and
running on Linux kernels 2.6.15 or later, the 'cp' operation will
work correctly when copying read-only files.
- New option parsing interface added to the library
- Lots of minor improvements and fixes
============================================================================
What is new in 2.4
- Simplify device opening. Now '/dev/fuse' is a requirement
- Allow module auto-loading if user has access to '/dev/fuse'
- Allow mounting over a regular file for unprivileged users
- Allow mounting of arbitrary FUSE filesystems from /etc/fstab
- New mount options: 'umask=M', 'uid=N', 'gid=N'
- Check for non-empty mountpoint, and refuse mount by default. New
mount option: 'nonempty'
- Low level (inode based) API added
- Allow 'direct_io' and 'keep_cache' options to be set on a
case-by-case basis on open.
- Add 'attr_timeout' and 'entry_timeout' mount options to the
high-level library. Until now these timeouts were fixed at 1 sec.
- Some bugfixes
============================================================================
What is new in 2.3
- Add new directory related operations: opendir(), readdir(),
releasedir() and fsyncdir()
- Add init() and destroy() operations which are called before the
event loop is started and after it has exited
- Update kernel ABI so that on dual architectures (e.g. AMD64) 32bit
binaries work under a 64bit kernel
- Bugfixes
============================================================================
What is new in 2.2
Userspace changes:
- Add fuse_file_info structure to file operations, this allows the
filesystem to return a file handle in open() which is passed to
read(), write(), flush(), fsync() and release().
- Add source compatibility with 2.1 and 1.4 releases
- Binary compatibility with 2.1 release is retained
Kernel changes:
- Make requests interruptible. This prevents the filesystem to go
into an unbreakable deadlock with itself.
- Make readpages() synchronous. Asynchronous requests are deadlock
prone, since they cannot be interrupted (see above)
- Remove shared-writeable mapping support, which could deadlock the
machine
- Remove INVALIDATE userspace initiated request
- Update ABI to be independent of sizeof(long), so dual-size archs
don't cause problems
- Remove /sys/fs/fuse/version. Version checking is now done through
the fuse device
- Replace directory reading method on the kernel interface. Instead
of passing an open file descriptor to the kernel, send data through
the FUSE device, like all other operations.
============================================================================
What is new in 2.1
* Bug fixes
* Improved support for filesystems implementing a custom event-loop
* Add 'pkg-config' support
* Kernel module can be compiled separately
============================================================================
What is new in 1.9
* Lots of bugs fixed
* Minor modifications to the library API
* Improvements to the kernel/userspace interface
* Mounting by non-root made more secure
* Build shared library in addition to the static one
* Consolidated mount options
* Optimized reading under 2.6 kernels
* Direct I/O support
* Support file I/O on deleted files
* Extended attributes support
============================================================================
What is new in 1.3
* Thanks to user bugreports and stress testing with LTP and sfx-linux
a number of bugs were fixed, some quite serious.
* Fix compile problems with recent SuSE kernles
============================================================================
What is new in 1.2
* Fix mount problems on recent 2.6 kernels with SELinux enabled
* Fixed writing files lager than 2GBytes
* Other bugfixes
============================================================================
What is new in 1.1
* Support for the 2.6 kernels
* Support for exporting filesystem over NFS in 2.6 kernels
* Read efficiency improvements: read in 64k blocks instead of 4k
(Michael Grigoriev). Can be turned on with '-l' option of fusermount
* Lazy automatic unmount
* Added 'fsync()' VFS call to the FUSE interface
* Bugfixes
============================================================================
What is new in 1.0
* Cleanups and bugfixes
* Added 'release()' VFS call to the FUSE interface
* 64 bit file offsets (handling of > 4 GByte files)
* libfuse is now under LGPL
* New 'statfs' call (Mark Glines)
* Cleaned up mount procedure (mostly by Mark Glines)
NOTE: Binaries linked with with a previous version of libavfs may
not work with the new version of the fusermount program. In such
case recompile the program after installing the new libavfs library.
* Fix for problems under linux kernel 2.4.19
============================================================================
What is new in 0.95
* Optimized read/write operations. Raw throughput has increased to
about 60Mbyte/s on a Celeron/360
* Python bindings by Jeff Epler
* Perl bindings by Mark Glines
* Improved multithreaded operation
* Simplified library interface
* Bugfixes
============================================================================
What is new in 0.9:
* Everything
+33
View File
@@ -0,0 +1,33 @@
NFS exporting is supported in Linux kernels 2.6.27 or later.
You need to add an fsid=NNN option to /etc/exports to make exporting a
FUSE directory work.
Filesystem support
------------------
NFS exporting works to some extent on all fuse filesystems, but not
perfectly. This is due to the stateless nature of the protocol, the
server has no way of knowing whether the client is keeping a reference
to a file or not, and hence that file may be removed from the server's
cache. In that case there has to be a way to look up that object
using the inode number, otherwise an ESTALE error will be returned.
1) low-level interface
Filesystems need to implement special lookups for the names "." and
"..". The former may be requested on any inode, including
non-directories, while the latter is only requested for directories.
Otherwise these special lookups should behave identically to ordinary
lookups.
2) high-level interface
Because the high-level interface is path based, it is not possible to
delegate looking up by inode to the filesystem.
To work around this, currently a "noforget" option is provided, which
makes the library remember nodes forever. This will make the NFS
server happy, but also results in an ever growing memory footprint for
the filesystem. For this reason if the filesystem is large (or the
memory is small), then this option is not recommended.
+108
View File
@@ -0,0 +1,108 @@
libfuse
=======
Warning: unresolved security issue
----------------------------------
Be aware that FUSE has an unresolved security bug
([bug #15](https://github.com/libfuse/libfuse/issues/15)): the
permission check for accessing a cached directory is only done once
when the directory entry is first loaded into the cache. Subsequent
accesses will re-use the results of the first check, even if the
directory permissions have since changed, and even if the subsequent
access is made by a different user.
This bug needs to be fixed in the Linux kernel and has been known
since 2006 but unfortunately no fix has been applied yet. If you
depend on correct permission handling for FUSE file systems, the only
workaround is to completely disable caching of directory
entries. Alternatively, the severity of the bug can be somewhat
reduced by not using the `allow_other` mount option.
About
-----
FUSE (Filesystem in Userspace) is an interface for userspace programs
to export a filesystem to the Linux kernel. The FUSE project consists
of two components: the *fuse* kernel module (maintained in the regular
kernel repositories) and the *libfuse* userspace library (maintained
in this repository). libfuse provides the reference implementation
for communicating with the FUSE kernel module.
A FUSE file system is typically implemented as a standalone
application that links with libfuse. libfuse provides functions to
mount the file system, unmount it, read requests from the kernel, and
send responses back. libfuse offers two APIs: a "high-level",
synchronous API, and a "low-level" asynchronous API. In both cases,
incoming requests from the kernel are passed to the main program using
callbacks. When using the high-level API, the callbacks may work with
file names and paths instead of inodes, and processing of a request
finishes when the callback function returns. When using the low-level
API, the callbacks must work with inodes and responses must be sent
explicitly using a separate set of API functions.
Installation
------------
./configure
make -j8
make install
You may also need to add `/usr/local/lib` to `/etc/ld.so.conf` and/or
run *ldconfig*. If you're building from the git repository (instead of
using a release tarball), you also need to run `./makeconf.sh` to
create the `configure` script.
You'll also need a fuse kernel module (Linux kernels 2.6.14 or later
contain FUSE support).
For more details see the file `INSTALL`
Security implications
---------------------
If you run `make install`, the *fusermount* program is installed
set-user-id to root. This is done to allow normal users to mount
their own filesystem implementations.
There must however be some limitations, in order to prevent Bad User from
doing nasty things. Currently those limitations are:
- The user can only mount on a mountpoint, for which it has write
permission
- The mountpoint is not a sticky directory which isn't owned by the
user (like /tmp usually is)
- No other user (including root) can access the contents of the
mounted filesystem (though this can be relaxed by allowing the use
of the `allow_other` and `allow_root` mount options in `fuse.conf`)
Building your own filesystem
------------------------------
FUSE comes with several example file systems in the `examples`
directory. For example, the *fusexmp* example mirrors the contents of
the root directory under the mountpoint. Start from there and adapt
the code!
The documentation of the API functions and necessary callbacks is
mostly contained in the files `include/fuse.h` (for the high-level
API) and `include/fuse_lowlevel.h` (for the low-level API). An
autogenerated html version of the API is available in the `doc/html`
directory and at http://libfuse.github.io/doxygen.
Getting Help
------------
If you need help, please ask on the <fuse-devel@lists.sourceforge.net>
mailing list (subscribe at
https://lists.sourceforge.net/lists/listinfo/fuse-devel).
Please report any bugs on the GitHub issue tracker at
https://github.com/libfuse/main/issues.
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#ifdef __cplusplus
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif
+220
View File
@@ -0,0 +1,220 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
* A dynamic list of formatting arguments with storage.
*
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, char_type>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_t = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty())
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.emplace_back(detail::unwrap(arg.value));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0] = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
operator basic_format_args<Context>() const {
return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
!named_info_.empty());
}
/**
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*
* Note that custom types and string types (but not string views) are copied
* into the store dynamically allocating memory if necessary.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
* Adds a reference to the argument into the dynamic store for later passing
* to a formatting function.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* char band[] = "Rolling Stones";
* store.push_back(std::cref(band));
* band[9] = 'c'; // Changing str affects the output.
* std::string result = fmt::vformat("{}", store);
* // result == "Rolling Scones"
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
/**
* Adds named argument into the dynamic store for later passing to a
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/// Erase all elements from the store.
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = {};
}
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+637
View File
@@ -0,0 +1,637 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
faint = 1 << 1,
italic = 1 << 2,
underline = 1 << 3,
blink = 1 << 4,
reverse = 1 << 5,
conceal = 1 << 6,
strikethrough = 1 << 7,
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
constexpr rgb() : r(0), g(0), b(0) {}
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
constexpr rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
constexpr rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace detail {
// A bit-packed variant of an RGB color, a terminal color, or unset color.
// see text_style for the bit-packing scheme.
struct color_type {
constexpr color_type() noexcept = default;
constexpr color_type(color rgb_color) noexcept
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
constexpr color_type(rgb rgb_color) noexcept
: color_type(static_cast<color>(
(static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
constexpr color_type(terminal_color term_color) noexcept
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
constexpr auto is_terminal_color() const noexcept -> bool {
return (value_ & (1 << 25)) != 0;
}
constexpr auto value() const noexcept -> uint32_t {
return value_ & 0xFFFFFF;
}
constexpr color_type(uint32_t value) noexcept : value_(value) {}
uint32_t value_ = 0;
};
} // namespace detail
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
// The information is packed as follows:
// ┌──┐
// │ 0│─┐
// │..│ ├── foreground color value
// │23│─┘
// ├──┤
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
// ├──┤
// │26│──── overflow bit, always zero (see below)
// ├──┤
// │27│─┐
// │..│ │
// │50│ │
// ├──┤ │
// │51│ ├── background color (same format as the foreground color)
// │52│ │
// ├──┤ │
// │53│─┘
// ├──┤
// │54│─┐
// │..│ ├── emphases
// │61│─┘
// ├──┤
// │62│─┬── unused
// │63│─┘
// └──┘
// The overflow bits are there to make operator|= efficient.
// When ORing, we must throw if, for either the foreground or background,
// one style specifies a terminal color and the other specifies any color
// (terminal or RGB); in other words, if one discriminator is 11 and the
// other is 11 or 01.
//
// We do that check by adding the styles. Consider what adding does to each
// possible pair of discriminators:
// 00 + 00 = 000
// 01 + 00 = 001
// 11 + 00 = 011
// 01 + 01 = 010
// 11 + 01 = 100 (!!)
// 11 + 11 = 110 (!!)
// In the last two cases, the ones we want to catch, the third bit——the
// overflow bit——is set. Bingo.
//
// We must take into account the possible carry bit from the bits
// before the discriminator. The only potentially problematic case is
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
// bit is impossible in that case, because 00 (unset color) means the
// 24 bits that precede the discriminator are all zero.
//
// This test can be applied to both colors simultaneously.
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: style_(static_cast<uint64_t>(em) << 54) {}
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
report_error("can't OR a terminal color");
style_ |= rhs.style_;
return *this;
}
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
-> text_style {
return lhs |= rhs;
}
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
return style_ == rhs.style_;
}
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
return !(*this == rhs);
}
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return (style_ & (1 << 24)) != 0;
}
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return (style_ & (1ULL << 51)) != 0;
}
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return (style_ >> 54) != 0;
}
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return style_ & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return (style_ >> 27) & 0x3FFFFFF;
}
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return static_cast<emphasis>(style_ >> 54);
}
private:
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
uint64_t style_ = 0;
};
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return foreground.value_;
}
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return static_cast<uint64_t>(background.value_) << 27;
}
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs;
}
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(color_type text_color,
const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code
// sequence.
if (text_color.is_terminal_color()) {
bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value();
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value());
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) {
if (ts.has_emphasis()) {
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, fmt, args);
if (ts != text_style()) reset_color<Char>(buf);
}
} // namespace detail
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
* Formats a string and prints it to the specified file stream using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* Formats a string and prints it to stdout using ANSI escape sequences to
* specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
/**
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename... T>
inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
}
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
-> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
}
/**
* Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
}
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = detail::copy<Char>(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(arg.value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
* Returns an argument that will be formatted using ANSI escape sequences,
* to be used in a formatting function.
*
* **Example**:
*
* fmt::print("Elapsed time: {0:.2f} seconds",
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
* fmt::bg(fmt::color::blue)));
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
+539
View File
@@ -0,0 +1,539 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
// A compile-time string which is compiled into fast formatting code.
FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
* `constexpr if` compiler support.
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return detail::get<N - 1>(rest...);
}
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (is_static_named_arg<T>()) {
if (name == T::name) return N;
}
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter.
return -1;
}
# endif
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
# endif
(void)name;
return -1;
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type =
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
*out++ = value;
return out;
}
};
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
return write<Char>(out, arg);
}
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
}
};
template <typename Char> struct parse_arg_id_result {
arg_id_kind kind;
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
if constexpr (result.end >= str.size() || str[result.end] != '}') {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
}
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail
FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
remove_cvref_t<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
return buf.count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_
+5
View File
@@ -0,0 +1,5 @@
// This file is only provided for compatibility and may be removed in future
// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
// otherwise.
#include "format.h"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+427
View File
@@ -0,0 +1,427 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
# endif
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else
# define FMT_USE_FCNTL 0
# endif
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
/**
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
}
FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
format_args args);
/**
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... T>
auto windows_error(int error_code, string_view message, const T&... args)
-> std::system_error {
return vwindows_error(error_code, message, vargs<T...>{{args...}});
}
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
template <typename... T>
inline void print(string_view fmt, const T&... args) {
fmt::vargs<T...> vargs = {{args...}};
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class FMT_API file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
};
// Constructs a file object which doesn't represent any file.
inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
~file() noexcept;
// Returns the file descriptor.
inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
auto getpagesize() -> long;
namespace detail {
struct buffer_size {
constexpr buffer_size() = default;
size_t value = 0;
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
oflag = new_oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
// Intel has a bug that results in failure to deduce a constructor
// for empty parameter packs.
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
ostream_params(int new_oflag) : oflag(new_oflag) {}
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
# endif
};
} // namespace detail
FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
/// A fast buffered output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream : private detail::buffer<char> {
private:
file file_;
ostream(cstring_view path, const detail::ostream_params& params);
static void grow(buffer<char>& buf, size_t);
public:
ostream(ostream&& other) noexcept;
~ostream();
operator writer() {
detail::buffer<char>& buf = *this;
return buf;
}
inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
inline void close() {
flush();
file_.close();
}
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
}
};
/**
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_OS_H_
+167
View File
@@ -0,0 +1,167 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
#ifdef _MSVC_STL_UPDATE
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
# define FMT_MSVC_STL_UPDATE 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = make_unsigned_t<std::streamsize>;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete;
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
auto&& output = std::basic_ostream<Char>(&formatbuf);
output.imbue(std::locale::classic()); // The default is always unlocalized.
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
};
using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
/**
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
inline void vprint(std::ostream& os, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
FILE* f = nullptr;
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
}
}
#endif
detail::ignore_unused(f);
detail::write_buffer(os, buffer);
}
/**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::vargs<T...> vargs = {{args...}};
if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt.str, vargs);
detail::write_buffer(os, buffer);
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, FMT_STRING("{}\n"),
fmt::format(fmt, std::forward<T>(args)...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_
+633
View File
@@ -0,0 +1,633 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
};
namespace detail {
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
template <>
inline auto find<false, char>(const char* first, const char* last, char value,
const char*& out) -> bool {
out =
static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
return out != nullptr;
}
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
inline static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int {
report_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
if (is_signed)
arg_ = static_cast<int>(static_cast<target_type>(value));
else
arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
} else {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
if (is_signed)
arg_ = static_cast<long long>(value);
else
arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = static_cast<typename Context::char_type>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
auto operator()(const Char* s) -> const Char* { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class printf_width_handler {
private:
format_specs& specs_;
public:
inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.set_align(align::left);
width = 0 - width;
}
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned {
report_error("width is not integer");
return 0;
}
};
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
template <typename T> void write(T value) {
detail::write<Char>(this->out, value, this->specs, this->locale);
}
public:
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { write(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (!std::is_same<T, Char>::value) {
write(value);
return;
}
format_specs s = this->specs;
if (s.type() != presentation_type::none &&
s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.set_sign(sign::none);
s.clear_alt();
s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align() == align::none || s.align() == align::numeric)
s.set_align(align::right);
detail::write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
write(value);
}
void operator()(const char* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(const wchar_t* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { write(value); }
void operator()(const void* value) {
if (value)
write(value);
else
write_null_pointer();
}
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
template <typename Char>
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-': specs.set_align(align::left); break;
case '+': specs.set_sign(sign::plus); break;
case '0': specs.set_fill('0'); break;
case ' ':
if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break;
case '#': specs.set_alt(); break;
default: return;
}
}
}
template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
int value = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.set_fill('0');
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd': return in(t, integral_set) ? pt::dec : pt::none;
case 'o': return in(t, integral_set) ? pt::oct : pt::none;
case 'X': upper = true; FMT_FALLTHROUGH;
case 'x': return in(t, integral_set) ? pt::hex : pt::none;
case 'E': upper = true; FMT_FALLTHROUGH;
case 'e': return in(t, float_set) ? pt::exp : pt::none;
case 'F': upper = true; FMT_FALLTHROUGH;
case 'f': return in(t, float_set) ? pt::fixed : pt::none;
case 'G': upper = true; FMT_FALLTHROUGH;
case 'g': return in(t, float_set) ? pt::general : pt::none;
case 'A': upper = true; FMT_FALLTHROUGH;
case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c': return in(t, integral_set) ? pt::chr : pt::none;
case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default: return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!find<false, Char>(it, end, '%', it)) {
it = end; // find leaves it == nullptr if it doesn't find '%'.
break;
}
Char c = *it++;
if (it != end && *it == c) {
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs();
specs.set_align(align::right);
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
}
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
specs.set_fill(' ');
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = sv;
}
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
if (specs.fill_unit<Char>() == '0') {
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
specs.set_align(align::numeric);
} else {
// Ignore '0' flag for non-numeric types or if '-' flag is also present.
specs.set_fill(' ');
}
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j': convert_arg<intmax_t>(arg, t); break;
case 'z': convert_arg<size_t>(arg, t); break;
case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default: --it; convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (is_integral_type(arg.type())) {
// Normalize type.
switch (type) {
case 'i':
case 'u': type = 'd'; break;
case 'c':
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
bool upper = false;
specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
if (upper) specs.set_upper();
start = it;
// Format argument.
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
} // namespace detail
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename Char>
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return {buf.data(), buf.size()};
}
/**
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
}
/**
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_
+850
View File
@@ -0,0 +1,850 @@
// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
// ADL overloads. Only participate in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(*detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U, typename V = typename std::remove_cv<U>::type>
static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto all_true(...) -> std::false_type;
template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
template <typename Tuple, typename F, size_t... Is>
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get;
// Using a free function get<Is>(Tuple) now.
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
ignore_unused(unused);
}
template <typename Tuple, typename F>
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
std::forward<Tuple>(t), std::forward<F>(f));
}
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
}
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range>
using range_reference_type =
decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Formatter>
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
-> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
}
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11.
template <typename Char> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
parse_context<Char>& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx));
++i;
}
int i;
FormatContext& ctx;
basic_string_view<char_type> separator;
};
} // namespace detail
template <typename T> struct is_tuple_like {
static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<Tuple, Char>::value>> {
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{};
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && detail::to_ascii(*it) == 'n') {
++it;
set_brackets({}, {});
set_separator({});
}
if (it != end && *it != '}') report_error("invalid format specifier");
ctx.advance_to(it);
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy<Char>(closing_bracket_, ctx.out());
}
};
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
namespace detail {
template <typename Char, typename Element>
using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
template <typename T, typename Char>
struct range_formatter<
T, Char,
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
is_formattable<T, Char>>::value>> {
private:
detail::range_formatter_type<Char, T> underlying_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public:
FMT_CONSTEXPR range_formatter() {}
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
return underlying_;
}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {});
++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it;
}
ctx.advance_to(it);
return underlying_.parse(ctx);
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it; // Need an lvalue
out = underlying_.format(item, ctx);
++i;
}
out = detail::copy<Char>(closing_bracket_, out);
return out;
}
};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
is_range<T, Char>::value, detail::range_format_kind_<T>,
std::integral_constant<range_format, range_format::disabled>> {};
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
};
// A map formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<
bool_constant<range_format_kind<R, Char>::value == range_format::map>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
-> const Char* {
return ctx.begin();
}
template <size_t N>
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>)
-> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
report_error("incompatible format specs for tuple elements");
}
#endif
return end;
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& v) -> all {
return {v.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(value), ctx);
}
};
FMT_BEGIN_EXPORT
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{1, 2, 3};
* fmt::print("{}", fmt::join(v, ", "));
* // Output: 1, 2, 3
*
* `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
* auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep};
}
/**
* Returns an object that formats `std::initializer_list` with elements
* separated by `sep`.
*
* **Example**:
*
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
+728
View File
@@ -0,0 +1,728 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>) && \
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
Char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return this->write_padded(ctx, writer{bs});
}
};
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
is_formattable<T, Char>::value) &&
is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
if constexpr (!std::is_void<T>::value)
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
template <typename Char> struct formatter<std::monostate, Char> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
bool debug_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
char c = *it;
if (it != end && ((c >= '0' && c <= '9') || c == '{'))
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && *it == 's') {
specs_.set_type(presentation_type::string);
++it;
}
return it;
}
template <typename FormatContext>
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto buf = memory_buffer();
if (specs_.type() == presentation_type::string) {
buf.append(ec.message());
} else {
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
}
auto quoted = memory_buffer();
auto str = string_view(buf.data(), buf.size());
if (debug_) {
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
str = string_view(quoted.data(), quoted.size());
}
return detail::write<char>(ctx.out(), str, specs);
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.set_sign(sign::plus);
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.dynamic()) {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.copy_fill_from(specs);
outer_specs.set_align(specs.align());
specs.width = 0;
specs.set_fill({});
specs.set_align(align::none);
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_
+369
View File
@@ -0,0 +1,369 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include "color.h"
#include "format.h"
#include "ostream.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if FMT_USE_LOCALE
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<
S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#if FMT_USE_LOCALE
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = parse_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
template <typename Char, typename... T> struct basic_fstring {
private:
basic_string_view<Char> str_;
static constexpr int num_static_named_args =
detail::count_static_named_args<T...>();
using checker = detail::format_string_checker<
Char, static_cast<int>(sizeof...(T)), num_static_named_args,
num_static_named_args != detail::count_named_args<T...>()>;
using arg_pack = detail::arg_pack<T...>;
public:
using t = basic_fstring;
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<Char>(s, checker(s, arg_pack()));
}
template <typename S,
FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
std::is_same<typename S::char_type, Char>::value)>
FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
FMT_CONSTEXPR int ignore =
(parse_format_string(sv, checker(sv, arg_pack())), 0);
detail::ignore_unused(ignore);
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
template <typename Char, typename... T>
using basic_format_string = basic_fstring<Char, T...>;
template <typename... T>
using wformat_string = typename basic_format_string<wchar_t, T...>::t;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
}
#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
return {s};
}
} // namespace literals
#endif
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, fmt, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
auto buf = wmemory_buffer();
detail::vformat_to(buf, fmt, args);
buf.push_back(L'\0');
if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);
detail::write_buffer(os, buffer);
}
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>
void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace fs
{
struct dirent64
{
public:
uint64_t ino;
int64_t off;
uint16_t reclen;
uint8_t type;
char name[];
public:
size_t
namelen() const
{
return ::strlen(name);
}
};
}
+422
View File
@@ -0,0 +1,422 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
*/
#ifndef _FUSE_H_
#define _FUSE_H_
#include "extern_c.h"
#include "fuse_common.h"
#include "fuse_kernel.h"
#include "fuse_req_ctx.h"
#include "fuse_conn_info.hpp"
#include <fcntl.h>
#include <time.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>
#include <stdint.h>
EXTERN_C_BEGIN
/* ----------------------------------------------------------- *
* Basic FUSE API *
* ----------------------------------------------------------- */
struct fuse_dirents_t;
typedef struct fuse_dirents_t fuse_dirents_t;
/**
* The file system operations:
*
* Most of these should work very similarly to the well known UNIX
* file system operations. A major exception is that instead of
* returning an error in 'errno', the operation should return the
* negated error value (-errno) directly.
*
* All methods are optional, but some are essential for a useful
* filesystem (e.g. getattr). Open, flush, release, fsync, opendir,
* releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock,
* init and destroy are special purpose methods, without which a full
* featured filesystem can still be implemented.
*
* Almost all operations take a path which can be of any length.
*
* Changed in fuse 2.8.0 (regardless of API version)
* Previously, paths were limited to a length of PATH_MAX.
*
* See http://fuse.sourceforge.net/wiki/ for more information. There
* is also a snapshot of the relevant wiki pages in the doc/ folder.
*/
struct fuse_operations
{
int (*getattr)(const fuse_req_ctx_t *,
const char *,
struct stat *,
fuse_timeouts_t *);
int (*readlink)(const fuse_req_ctx_t *,
const char *,
char *,
size_t);
int (*mknod)(const fuse_req_ctx_t *,
const char *,
mode_t,
dev_t);
int (*mkdir)(const fuse_req_ctx_t *,
const char *,
mode_t);
int (*unlink)(const fuse_req_ctx_t *,
const char *);
int (*rmdir)(const fuse_req_ctx_t *,
const char *);
int (*symlink)(const fuse_req_ctx_t *,
const char *,
const char *,
struct stat *,
fuse_timeouts_t *);
int (*rename)(const fuse_req_ctx_t *,
const char *,
const char *);
int (*link)(const fuse_req_ctx_t *,
const char *,
const char *,
struct stat *,
fuse_timeouts_t *);
int (*chmod)(const fuse_req_ctx_t *,
const char *,
mode_t);
int (*fchmod)(const fuse_req_ctx_t *,
const uint64_t,
const mode_t);
int (*chown)(const fuse_req_ctx_t *,
const char *,
uid_t,
gid_t);
int (*fchown)(const fuse_req_ctx_t *,
const uint64_t,
const uid_t,
const gid_t);
int (*truncate)(const fuse_req_ctx_t *,
const char *,
off_t);
int (*open)(const fuse_req_ctx_t *,
const char *,
fuse_file_info_t *);
int (*statfs)(const fuse_req_ctx_t *,
const char *,
struct statvfs *);
int (*flush)(const fuse_req_ctx_t *,
const fuse_file_info_t *);
int (*release)(const fuse_req_ctx_t *,
const fuse_file_info_t *);
int (*fsync)(const fuse_req_ctx_t *,
const uint64_t,
int);
int (*setxattr)(const fuse_req_ctx_t *,
const char *,
const char *,
const char *,
size_t,
int);
int (*getxattr)(const fuse_req_ctx_t *,
const char *,
const char *,
char *,
size_t);
int (*listxattr)(const fuse_req_ctx_t *,
const char *,
char *,
size_t);
int (*removexattr)(const fuse_req_ctx_t *,
const char *,
const char *);
int (*opendir)(const fuse_req_ctx_t *,
const char *,
fuse_file_info_t *);
int (*readdir)(const fuse_req_ctx_t *,
const fuse_file_info_t *,
fuse_dirents_t *);
int (*readdir_plus)(const fuse_req_ctx_t *,
const fuse_file_info_t *,
fuse_dirents_t *);
int (*releasedir)(const fuse_req_ctx_t *,
const fuse_file_info_t *);
int (*fsyncdir)(const fuse_req_ctx_t *,
const fuse_file_info_t *,
int);
void *(*init)(struct fuse_conn_info_t *conn);
void (*destroy)(void);
int (*access)(const fuse_req_ctx_t *,
const char *,
int);
int (*create)(const fuse_req_ctx_t *,
const char *,
mode_t,
fuse_file_info_t *);
int (*ftruncate)(const fuse_req_ctx_t *,
const uint64_t,
off_t);
int (*fgetattr)(const fuse_req_ctx_t *,
const uint64_t,
struct stat *,
fuse_timeouts_t *);
int (*lock)(const fuse_req_ctx_t *,
const fuse_file_info_t *,
int cmd,
struct flock *);
int (*utimens)(const fuse_req_ctx_t *,
const char *,
const struct timespec tv[2]);
int (*futimens)(const fuse_req_ctx_t *,
const uint64_t fh,
const struct timespec tv[2]);
int (*bmap)(const fuse_req_ctx_t *,
const char *,
size_t blocksize,
uint64_t *idx);
int (*ioctl)(const fuse_req_ctx_t *ctx,
const fuse_file_info_t *ffi,
unsigned long cmd,
void *arg,
unsigned int flags,
void *data,
uint32_t *out_bufsz);
int (*write)(const fuse_req_ctx_t *,
const fuse_file_info_t *ffi,
const char *data,
size_t size,
off_t off);
int (*read)(const fuse_req_ctx_t *,
const fuse_file_info_t *ffi,
char *buf,
size_t size,
off_t off);
int (*flock)(const fuse_req_ctx_t *,
const fuse_file_info_t *,
int op);
int (*fallocate)(const fuse_req_ctx_t *,
const uint64_t,
int,
off_t,
off_t);
ssize_t (*copy_file_range)(const fuse_req_ctx_t *,
const fuse_file_info_t *src_fi,
off_t src_off,
const fuse_file_info_t *dst_fi,
off_t dst_off,
const size_t size,
const unsigned int flags);
ssize_t (*setupmapping)(const fuse_req_ctx_t *,
uint64_t *fh_,
uint64_t foffset_,
uint64_t len_,
uint64_t flags_,
uint64_t moffset_);
int (*removemapping)(const fuse_req_ctx_t *);
int (*syncfs)(const fuse_req_ctx_t *);
int (*tmpfile)(const fuse_req_ctx_t *,
const char *,
mode_t,
fuse_file_info_t *);
int (*statx)(const fuse_req_ctx_t *,
const char *fusepath,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
int (*statx_fh)(const fuse_req_ctx_t *,
const uint64_t fh,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
};
/**
* Main function of FUSE.
*
* This is for the lazy. This is all that has to be called from the
* main() function.
*
* This function does the following:
* - parses command line options (-d -s and -h)
* - passes relevant mount options to the fuse_mount()
* - installs signal handlers for INT, HUP, TERM and PIPE
* - registers an exit handler to unmount the filesystem on program exit
* - creates a fuse handle
* - registers the operations
* - calls either the single-threaded or the multi-threaded event loop
*
* Note: this is currently implemented as a macro.
*
* @param argc the argument counter passed to the main() function
* @param argv the argument vector passed to the main() function
* @param op the file system operation
* @return 0 on success, nonzero on failure
*/
/* ----------------------------------------------------------- *
* More detailed API *
* ----------------------------------------------------------- */
/**
* Create a new FUSE filesystem.
*
* @param ch the communication channel
* @param args argument vector
* @param op the filesystem operations
* @param op_size the size of the fuse_operations structure
* @return the created FUSE handle
*/
struct fuse *fuse_new(struct fuse_chan *ch,
struct fuse_args *args,
const struct fuse_operations *op);
/**
* Destroy the FUSE handle.
*
* The communication channel attached to the handle is also destroyed.
*
* NOTE: This function does not unmount the filesystem. If this is
* needed, call fuse_unmount() before calling this function.
*
* @param f the FUSE handle
*/
void fuse_destroy();
/**
* Exit from event loop
*
* @param f the FUSE handle
*/
void fuse_exit(struct fuse *f);
int fuse_config_read_thread_count(const struct fuse *f);
int fuse_config_process_thread_count(const struct fuse *f);
int fuse_config_process_thread_queue_depth(const struct fuse *f);
const char* fuse_config_pin_threads(const struct fuse *f);
/**
* FUSE event loop with multiple threads
*
* Requests from the kernel are processed, and the appropriate
* operations are called. Request are processed in parallel by
* distributing them between multiple threads.
*
* Calling this function requires the pthreads library to be linked to
* the application.
*
* @param f the FUSE handle
* @return 0 if no error occurred, -1 otherwise
*/
int fuse_loop_mt(struct fuse *f);
/**
* Get the current context
*
* The context is only valid for the duration of a filesystem
* operation, and thus must not be stored and used later.
*
* @return the context
*/
struct fuse_context *fuse_get_context(void);
/**
* Check if the current request has already been interrupted
*
* @return 1 if the request has been interrupted, 0 otherwise
*/
int fuse_interrupted(void);
/**
* Obsolete, doesn't do anything
*
* @return -EINVAL
*/
int fuse_invalidate(struct fuse *f, const char *path);
/* Deprecated, don't use */
int fuse_is_lib_option(const char *opt);
/**
* The real main function
*
* Do not call this directly, use fuse_main()
*/
int fuse_main(int argc,
char *argv[],
const struct fuse_operations *op);
void fuse_populate_maintenance_thread(struct fuse *fuse);
int fuse_log_metrics_get(void);
void fuse_log_metrics_set(int enabled);
/**
* Iterate over cache removing stale entries
* use in conjunction with "-oremember"
*
* NOTE: This is already done for the standard sessions
*
* @param fuse struct fuse pointer for fuse instance
* @return the number of seconds until the next cleanup
*/
int fuse_clean_cache(struct fuse *fuse);
/*
* Stacking API
*/
/*
* These functions call the relevant filesystem operation, and return
* the result.
*
* If the operation is not defined, they return -ENOSYS, with the
* exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
*/
/* ----------------------------------------------------------- *
* Advanced API for event handling, don't worry about this... *
* ----------------------------------------------------------- */
/* NOTE: the following functions are deprecated, and will be removed
from the 3.0 API. Use the lowlevel session functions instead */
/** This is the part of fuse_main() before the event loop */
struct fuse *fuse_setup(int argc,
char *argv[],
const struct fuse_operations *op,
char **mountpoint);
/** This is the part of fuse_main() after the event loop */
void fuse_teardown(char *mountpoint);
/** Return the exited flag, which indicates if fuse_exit() has been
called */
int fuse_exited();
/** This function is obsolete and implemented as a no-op */
void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
/** Get session from fuse object */
struct fuse_session *fuse_get_session();
void fuse_gc1();
void fuse_gc();
void fuse_invalidate_all_nodes();
int fuse_passthrough_open(const int fd);
int fuse_passthrough_close(const int backing_id);
EXTERN_C_END
#endif /* _FUSE_H_ */
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
typedef struct fuse_attr_s fuse_attr_t;
struct fuse_attr_s
{
uint64_t ino;
uint64_t size;
uint64_t blocks;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t _padding;
};
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "int_types.h"
#include <climits>
#include <string>
#define FUSE_CFG_INVALID_ID -1
#define FUSE_CFG_INVALID_UMASK -1
struct fuse_cfg_t
{
s64 uid = FUSE_CFG_INVALID_ID;
bool valid_uid() const;
s64 gid = FUSE_CFG_INVALID_ID;
bool valid_gid() const;
s64 umask = FUSE_CFG_INVALID_UMASK;
bool valid_umask() const;
s64 remember_nodes = 0;
bool debug = false;
int max_background = 0;
int congestion_threshold = 0;
u32 max_pages = 32;
int passthrough_max_stack_depth = 1;
int read_thread_count = 0;
int process_thread_count = -1;
int process_thread_queue_depth = 2;
std::string pin_threads = "false";
};
extern fuse_cfg_t fuse_cfg;
+398
View File
@@ -0,0 +1,398 @@
#pragma once
#include "extern_c.h"
#include "fuse_opt.h"
#include "fuse_timeouts.h"
#include <stdint.h>
#include <sys/types.h>
/* This interface uses 64 bit off_t */
#if _FILE_OFFSET_BITS != 64
#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
#endif
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
#define FUSE_DEFAULT_MAX_MAX_PAGES 256
EXTERN_C_BEGIN
/**
* Information about open files
*
* Changed in version 2.5
*/
typedef struct fuse_file_info_t fuse_file_info_t;
struct fuse_file_info_t
{
/** Open flags. Available in open() and release() */
int flags;
/** In case of a write operation indicates if this was caused by a
writepage */
uint32_t writepage : 1;
/** Can be filled in by open, to use direct I/O on this file.
Introduced in version 2.4 */
uint32_t direct_io : 1;
/** Can be filled in by open, to indicate, that cached file data
need not be invalidated. Introduced in version 2.4 */
uint32_t keep_cache : 1;
/** Indicates a flush operation. Set in flush operation, also
maybe set in highlevel lock operation and lowlevel release
operation. Introduced in version 2.6 */
uint32_t flush : 1;
/** Can be filled in by open, to indicate that the file is not
seekable. Introduced in version 2.8 */
uint32_t nonseekable : 1;
/* Indicates that flock locks for this file should be
released. If set, lock_owner shall contain a valid value.
May only be set in ->release(). Introduced in version
2.9 */
uint32_t flock_release : 1;
/* Requests the kernel to cache entries returned by readdir */
uint32_t cache_readdir : 1;
uint32_t auto_cache : 1;
uint32_t parallel_direct_writes:1;
uint32_t noflush:1;
uint32_t passthrough:1;
/** File handle. May be filled in by filesystem in open().
Available in all other file operations */
uint64_t fh;
int32_t backing_id;
/** Lock owner id. Available in locking operations and flush */
uint64_t lock_owner;
};
/**
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
*
* FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
* FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
* FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
*/
#define FUSE_CAP_ASYNC_READ (1ULL << 0)
#define FUSE_CAP_POSIX_LOCKS (1ULL << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1ULL << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1ULL << 4)
#define FUSE_CAP_BIG_WRITES (1ULL << 5)
#define FUSE_CAP_DONT_MASK (1ULL << 6)
#define FUSE_CAP_FLOCK_LOCKS (1ULL << 10)
#define FUSE_CAP_IOCTL_DIR (1ULL << 11)
#define FUSE_CAP_READDIR_PLUS (1ULL << 13)
#define FUSE_CAP_READDIR_PLUS_AUTO (1ULL << 14)
#define FUSE_CAP_ASYNC_DIO (1ULL << 15)
#define FUSE_CAP_WRITEBACK_CACHE (1ULL << 16)
#define FUSE_CAP_PARALLEL_DIROPS (1ULL << 18)
#define FUSE_CAP_POSIX_ACL (1ULL << 19)
#define FUSE_CAP_CACHE_SYMLINKS (1ULL << 20)
#define FUSE_CAP_MAX_PAGES (1ULL << 21)
#define FUSE_CAP_SETXATTR_EXT (1ULL << 22)
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1ULL << 23)
#define FUSE_CAP_CREATE_SUPP_GROUP (1ULL << 24)
#define FUSE_CAP_PASSTHROUGH (1ULL << 25)
#define FUSE_CAP_HANDLE_KILLPRIV (1ULL << 26)
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1ULL << 27)
#define FUSE_CAP_ALLOW_IDMAP (1ULL << 28)
/**
* Ioctl flags
*
* FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
* FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
* FUSE_IOCTL_RETRY: retry with new iovecs
* FUSE_IOCTL_DIR: is a directory
*
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
*/
#define FUSE_IOCTL_COMPAT (1 << 0)
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
#define FUSE_IOCTL_RETRY (1 << 2)
#define FUSE_IOCTL_DIR (1 << 4)
#define FUSE_IOCTL_MAX_IOV 256
struct fuse_session;
struct fuse_chan;
/**
* Create a FUSE mountpoint
*
* Returns a control file descriptor suitable for passing to
* fuse_new()
*
* @param mountpoint the mount point path
* @param args argument vector
* @return the communication channel on success, NULL on failure
*/
struct fuse_chan *fuse_mount(const char *mountpoint,
struct fuse_args *args);
/**
* Umount a FUSE mountpoint
*
* @param mountpoint the mount point path
* @param ch the communication channel
*/
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
/**
* Parse common options
*
* The following options are parsed:
*
* '-f' foreground
* '-d' '-odebug' foreground, but keep the debug option
* '-h' '--help' help
* '-ho' help without header
* '-ofsname=..' file system name, if not present, then set to the program
* name
*
* All parameters may be NULL
*
* @param args argument vector
* @param mountpoint the returned mountpoint, should be freed after use
* @param foreground set to 1 if one of the relevant options is present
* @return 0 on success, -1 on failure
*/
int fuse_parse_cmdline(struct fuse_args *args,
char **mountpoint,
int *foreground);
/**
* Go into the background
*
* @param foreground if true, stay in the foreground
* @return 0 on success, -1 on failure
*/
int fuse_daemonize(int foreground);
/**
* Get the version of the library
*
* @return the version
*/
int fuse_version(void);
/* ----------------------------------------------------------- *
* Data buffer *
* ----------------------------------------------------------- */
/**
* Buffer flags
*/
enum fuse_buf_flags {
/**
* Buffer contains a file descriptor
*
* If this flag is set, the .fd field is valid, otherwise the
* .mem fields is valid.
*/
FUSE_BUF_IS_FD = (1 << 1),
/**
* Seek on the file descriptor
*
* If this flag is set then the .pos field is valid and is
* used to seek to the given offset before performing
* operation on file descriptor.
*/
FUSE_BUF_FD_SEEK = (1 << 2),
/**
* Retry operation on file descriptor
*
* If this flag is set then retry operation on file descriptor
* until .size bytes have been copied or an error or EOF is
* detected.
*/
FUSE_BUF_FD_RETRY = (1 << 3),
};
/**
* Buffer copy flags
*/
enum fuse_buf_copy_flags {
/**
* Don't use splice(2)
*
* Always fall back to using read and write instead of
* splice(2) to copy data from one file descriptor to another.
*
* If this flag is not set, then only fall back if splice is
* unavailable.
*/
FUSE_BUF_NO_SPLICE = (1 << 1),
/**
* Force splice
*
* Always use splice(2) to copy data from one file descriptor
* to another. If splice is not available, return -EINVAL.
*/
FUSE_BUF_FORCE_SPLICE = (1 << 2),
/**
* Try to move data with splice.
*
* If splice is used, try to move pages from the source to the
* destination instead of copying. See documentation of
* SPLICE_F_MOVE in splice(2) man page.
*/
FUSE_BUF_SPLICE_MOVE = (1 << 3),
/**
* Don't block on the pipe when copying data with splice
*
* Makes the operations on the pipe non-blocking (if the pipe
* is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
* man page.
*/
FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
};
/**
* Single data buffer
*
* Generic data buffer for I/O, extended attributes, etc... Data may
* be supplied as a memory pointer or as a file descriptor
*/
struct fuse_buf {
/**
* Size of data in bytes
*/
size_t size;
/**
* Buffer flags
*/
enum fuse_buf_flags flags;
/**
* Memory pointer
*
* Used unless FUSE_BUF_IS_FD flag is set.
*/
void *mem;
/**
* File descriptor
*
* Used if FUSE_BUF_IS_FD flag is set.
*/
int fd;
/**
* File position
*
* Used if FUSE_BUF_FD_SEEK flag is set.
*/
off_t pos;
};
/**
* Data buffer vector
*
* An array of data buffers, each containing a memory pointer or a
* file descriptor.
*
* Allocate dynamically to add more than one buffer.
*/
struct fuse_bufvec {
/**
* Number of buffers in the array
*/
size_t count;
/**
* Index of current buffer within the array
*/
size_t idx;
/**
* Current offset within the current buffer
*/
size_t off;
/**
* Array of buffers
*/
struct fuse_buf buf[1];
};
/* Initialize bufvec with a single buffer of given size */
#define FUSE_BUFVEC_INIT(size__) \
((struct fuse_bufvec) { \
/* .count= */ 1, \
/* .idx = */ 0, \
/* .off = */ 0, \
/* .buf = */ { /* [0] = */ { \
/* .size = */ (size__), \
/* .flags = */ (enum fuse_buf_flags) 0, \
/* .mem = */ NULL, \
/* .fd = */ -1, \
/* .pos = */ 0, \
} } \
} )
/**
* Get total size of data in a fuse buffer vector
*
* @param bufv buffer vector
* @return size of data
*/
size_t fuse_buf_size(const struct fuse_bufvec *bufv);
/**
* Copy data from one buffer vector to another
*
* @param dst destination buffer vector
* @param src source buffer vector
* @param flags flags controlling the copy
* @return actual number of bytes copied or -errno on error
*/
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
enum fuse_buf_copy_flags flags);
/* ----------------------------------------------------------- *
* Signal handling *
* ----------------------------------------------------------- */
/**
* Exit session on HUP, TERM and INT signals and ignore PIPE signal
*
* Stores session in a global variable. May only be called once per
* process until fuse_remove_signal_handlers() is called.
*
* @param se the session to exit
* @return 0 on success, -1 on failure
*/
int fuse_set_signal_handlers(struct fuse_session *se);
/**
* Restore default signal handlers
*
* Resets global session. After this fuse_set_signal_handlers() may
* be called again.
*
* @param se the same session as given in fuse_set_signal_handlers()
*/
void fuse_remove_signal_handlers(struct fuse_session *se);
EXTERN_C_END
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "int_types.h"
typedef struct fuse_conn_info_t fuse_conn_info_t;
struct fuse_conn_info_t
{
u32 proto_major;
u32 proto_minor;
u64 capable;
u64 want;
};
+31
View File
@@ -0,0 +1,31 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
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.
*/
#pragma once
#include <stdint.h>
typedef struct fuse_dirent_s fuse_dirent_t;
struct fuse_dirent_s
{
uint64_t ino;
uint64_t off;
uint32_t namelen;
uint32_t type;
char name[];
};

Some files were not shown because too many files have changed in this diff Show More