Compare commits

...
198 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 61e31eecf6 checkpoint 2022-11-29 17:24:09 -06:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 2d5c2c2b6e Merge pull request #1007 from trapexit/trapexit-patch-1
Update bug_report.md
2022-01-18 21:16:22 -05:00
trapexitandGitHub 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
trapexitandGitHub 58803f4845 Update README.md 2022-01-15 17:33:07 -05:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 31fd1ec82e Merge pull request #975 from trapexit/sponsorship
Remove subscribestar from sponsor platforms
2021-10-15 23:30:33 -04:00
trapexitandGitHub c3845097ee Remove subscribestar from sponsor platforms 2021-10-15 23:30:22 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub fee97efafb Merge pull request #967 from trapexit/malloc-trim
Add malloc_trim configure test
2021-09-30 20:25:42 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 3754e20049 Merge pull request #944 from trapexit/trapexit-patch-1
tweak bug report template language
2021-08-09 21:10:16 -04:00
trapexitandGitHub b4c031d1f0 tweak bug report template language 2021-08-09 21:09:54 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 3fdd322a5c Merge pull request #914 from trapexit/readme
mention bind-propagation and add link to hotio
2021-05-29 13:27:19 -04:00
trapexitandGitHub c40f42e57e mention bind-propagation and add link to hotio 2021-05-29 13:26:54 -04:00
trapexitandGitHub 18e87980a4 Merge pull request #913 from trapexit/readme
Update README.md
2021-05-29 13:07:54 -04:00
trapexitandGitHub 44efbaabe6 Update README.md
Add FAQ entry regarding container runtimes
2021-05-29 13:07:09 -04:00
trapexitandGitHub a02f3a6856 Merge pull request #891 from trapexit/trapexit-patch-1
Update bug_report.md
2021-03-15 07:58:22 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 39d98f1eac Merge pull request #878 from trapexit/readdir_rewind_fix
reset dentry buffer when rewind'ed
2021-02-09 20:21:59 -05:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 46de43d4ad Merge pull request #860 from trapexit/funding
Update FUNDING.yml
2021-01-01 15:52:41 -05:00
trapexitandGitHub 3808e2683e Update FUNDING.yml 2021-01-01 15:52:15 -05:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub eaa2593a4b Merge pull request #837 from renkoh/rsync-script-readme
Update percentage full expiring script
2020-10-03 01:05:54 -04:00
Ren KohandGitHub 7e583e3f48 use relative option for rsync 2020-10-03 14:45:51 +10:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub dbc4774e8e Merge pull request #828 from trapexit/remove-old-issue-template
Delete issue_template.md
2020-09-13 10:30:02 -04:00
trapexitandGitHub 73e8ed72cd Delete issue_template.md 2020-09-13 10:29:45 -04:00
trapexitandGitHub e694585561 Merge pull request #827 from trapexit/bug_report
Update bug_report.md
2020-09-13 10:28:34 -04:00
trapexitandGitHub 0b1af2b8ef Update bug_report.md 2020-09-13 10:28:20 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 17925c4c93 Merge pull request #809 from trapexit/libfuse-cleanup
Libfuse cleanup
2020-08-08 19:00:43 -04:00
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 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
trapexitandGitHub 58a8f8326d Merge pull request #798 from trapexit/inodecalc-32bit
inodecalc: add 32bit versions of hashs
2020-08-03 19:57:48 -04:00
446 changed files with 34022 additions and 23699 deletions
+254 -29
View File
@@ -1,3 +1,13 @@
freebsd_task:
name: "freebsd:11.4"
freebsd_instance:
image_family: freebsd-11-4
env:
ASSUME_ALWAYS_YES: yes
script:
- tools/install-build-pkgs
- gmake -j4
freebsd_task:
name: "freebsd:12.1"
freebsd_instance:
@@ -6,7 +16,35 @@ freebsd_task:
ASSUME_ALWAYS_YES: yes
script:
- tools/install-build-pkgs
- gmake
- gmake -j4
freebsd_task:
name: "freebsd:12.2"
freebsd_instance:
image_family: freebsd-12-2
env:
ASSUME_ALWAYS_YES: yes
script:
- tools/install-build-pkgs
- gmake -j4
freebsd_task:
name: "freebsd:13.0"
freebsd_instance:
image_family: freebsd-13-0
env:
ASSUME_ALWAYS_YES: yes
script:
- tools/install-build-pkgs
- gmake -j4
#macos_task:
# osx_instance:
# image: catalina-base
# script:
# - tools/install-build-pkgs
# - gmake -j4
linux_task:
name: "alpine:3.11"
@@ -20,16 +58,59 @@ linux_task:
- make STATIC=1 LTO=1
linux_task:
name: "centos:6"
name: "alpine:3.12"
container:
image: centos:6
image: alpine:3.12
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
- make STATIC=1 LTO=1
linux_task:
name: "alpine:3.13"
container:
image: alpine:3.13
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine:3.14"
container:
image: alpine:3.14
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine:latest"
container:
image: alpine:latest
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine-arm:latest"
arm_container:
image: alpine:latest
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "centos:7"
@@ -56,39 +137,93 @@ linux_task:
- make rpm
linux_task:
name: "ubuntu:20.04"
name: "fedora:30"
container:
image: ubuntu:20.04
image: fedora:30
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
- make
- make rpm
linux_task:
name: "ubuntu:19.10"
name: "fedora:31"
container:
image: ubuntu:19.10
image: fedora:31
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
- make
- make rpm
linux_task:
name: "ubuntu:18.04"
name: "fedora:32"
container:
image: ubuntu:18.04
image: fedora:32
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora:33"
container:
image: fedora:33
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora:34"
container:
image: fedora:34
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora:latest"
container:
image: fedora:latest
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora-arm:latest"
arm_container:
image: fedora:latest
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- make
- make rpm
linux_task:
name: "ubuntu:14.04"
container:
image: ubuntu:14.04
cpu: 4
memory: 2G
timeout_in: 10m
@@ -116,9 +251,9 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "ubuntu:14.04"
name: "ubuntu:18.04"
container:
image: ubuntu:14.04
image: ubuntu:18.04
cpu: 4
memory: 2G
timeout_in: 10m
@@ -131,9 +266,9 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "debian:10"
name: "ubuntu:20.04"
container:
image: debian:10
image: ubuntu:20.04
cpu: 4
memory: 2G
timeout_in: 10m
@@ -146,9 +281,39 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "debian:9"
name: "ubuntu:latest"
container:
image: debian:9
image: ubuntu:latest
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/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: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:7"
container:
image: debian:8
cpu: 4
memory: 2G
timeout_in: 10m
@@ -176,9 +341,69 @@ linux_task:
- mergerfs -v || true
linux_task:
name: "debian:7"
name: "debian:9"
container:
image: debian:8
image: debian:9
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:10"
container:
image: debian:10
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:11"
container:
image: debian:11
cpu: 4
memory: 2G
timeout_in: 10m
script:
- tools/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: 2G
timeout_in: 10m
script:
- tools/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: 2G
timeout_in: 10m
+3 -3
View File
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: [trapexit] # [user]
github: [trapexit]
patreon: trapexit
open_collective: trapexit
custom: ['https://paypal.me/trapexit','https://buymeacoff.ee/trapexit']
ko_fi: trapexit
custom: ['https://www.subscribestar.com/trapexit','https://paypal.me/trapexit']
open_collective: trapexit
+27 -6
View File
@@ -8,21 +8,42 @@ assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Please do not file bugs for unreleased versions. The master branch is **not** to be considered in a production state.
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.
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 what you expected to happen.
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`
- A strace of the application having a problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
- strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
- 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.
+2 -2
View File
@@ -1,10 +1,10 @@
---
name: Question
about: When you're looking for information
about: When you're looking for information or general support
title: ''
labels: investigating, question
assignees: ''
---
You can also ask questions on Discord for realtime help: https://discord.gg/MpAr69V
For general questions and support please use [GitHub Discussions](https://github.com/trapexit/mergerfs/discussions) or [Discord](https://discord.gg/MpAr69V).
-34
View File
@@ -1,34 +0,0 @@
General description
===================
Expected behavior
=================
Actual behavior
===============
**Precise** steps to reproduce the behavior
===========================================
> Explicitly list **all** steps to reproduce. Preferably create a minimal example of the problem using standard command line tools. The more variables (apps, settings, etc.) that are involved the more difficult it is to debug. Also, **please** be sure to have read all of the README. It contains a lot of information regarding known system and user issues.
System information
==================
Please provide as much of the following information as possible:
* [ ] mergerfs version: `mergerfs -V`
* [ ] mergerfs settings: `cat /etc/fstab` or the command line arguments
* [ ] Linux version: `uname -a`
* [ ] Versions of any additional software being used
* [ ] List of drives, filesystems, & sizes: `df -h`
* [ ] strace of application having problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
* [ ] strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
+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 }}"
-44
View File
@@ -1,44 +0,0 @@
language: cpp
matrix:
include:
- os: linux
dist: precise
compiler: gcc
sudo: required
- os: linux
dist: precise
compiler: clang
sudo: required
- os: linux
dist: trusty
compiler: gcc
sudo: required
- os: linux
dist: trusty
compiler: clang
sudo: required
- os: linux
dist: xenial
compiler: gcc
sudo: required
- os: linux
dist: xenial
compiler: clang
sudo: required
- os: linux
dist: bionic
compiler: gcc
sudo: required
- os: linux
dist: bionic
compiler: clang
sudo: required
script:
- sudo -E apt-get install fakeroot
- sudo -E make install-build-pkgs
- make -j$(($(nproc) + 1))
- make deb
- sudo -E dpkg -i ../*.deb
- mergerfs -v || true
+1 -1
View File
@@ -1,7 +1,7 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2021, 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
+37 -12
View File
@@ -40,7 +40,7 @@ USE_XATTR = 1
UGID_USE_RWLOCK = 0
ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g
OPT_FLAGS := -O0 -g -fsanitize=undefined
else
OPT_FLAGS := -O2
endif
@@ -58,16 +58,24 @@ LTO_FLAGS :=
endif
SRC = $(wildcard src/*.cpp)
OBJS = $(SRC:src/%.cpp=build/%.o)
DEPS = $(SRC:src/%.cpp=build/%.d)
OBJS = $(SRC:src/%.cpp=build/.src/%.o)
DEPS = $(SRC:src/%.cpp=build/.src/%.d)
TESTS = $(wildcard tests/*.cpp)
TESTS_OBJS = $(filter-out build/.src/mergerfs.o,$(OBJS))
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.tests/%.o)
TESTS_DEPS = $(TESTS:tests/%.cpp=build/.tests/%.d)
TESTS_DEPS += $(DEPS)
MANPAGE = mergerfs.1
CXXFLAGS = \
-std=c++0x \
$(OPT_FLAGS) \
CXXFLAGS ?= ${OPT_FLAGS}
CXXFLAGS := \
${CXXFLAGS} \
-std=c++11 \
$(STATIC_FLAGS) \
$(LTO_FLAGS) \
-Wall \
-Wno-unused-result \
-Wno-unused-result \
-MMD
FUSE_FLAGS = \
-Ilibfuse/include \
@@ -76,9 +84,14 @@ FUSE_FLAGS = \
MFS_FLAGS = \
-DUSE_XATTR=$(USE_XATTR) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
LDFLAGS = \
-pthread \
-lrt
TESTS_FLAGS = \
-Isrc \
-DTESTS
LDFLAGS := \
${LDFLAGS} \
-pthread \
-lrt
DESTDIR =
PREFIX = /usr/local
@@ -107,11 +120,19 @@ help:
objects: version build/stamp
$(MAKE) $(OBJS)
tests-objects:
$(MAKE) $(TESTS_OBJS)
build/mergerfs: libfuse objects
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
build/tests: build/mergerfs tests-objects
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
mergerfs: build/mergerfs
tests: build/tests
changelog:
ifeq ($(GIT_REPO),1)
$(GIT2DEBCL) --name mergerfs > ChangeLog
@@ -124,12 +145,16 @@ version:
tools/update-version
build/stamp:
$(MKDIR) -p build
$(MKDIR) -p build/.src build/.tests
$(TOUCH) $@
build/%.o: src/%.cpp
build/.src/%.o: src/%.cpp
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
build/.tests/%.o: tests/%.cpp
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
.PHONY: clean
clean: rpm-clean
$(RM) -rf build
+296 -198
View File
@@ -1,6 +1,6 @@
% mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link>
% 2020-08-03
% 2022-05-23
# NAME
@@ -19,19 +19,19 @@ mergerfs -o&lt;options&gt; &lt;branches&gt; &lt;mountpoint&gt;
# FEATURES
* Runs in userspace (FUSE)
* Configurable behaviors / file placement
* Ability to add or remove filesystems at will
* Resistance to individual filesystem failure
* 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 read-only and read/write drives
* Can turn read-only files into symlinks to underlying file
* Moving of file when filesystem runs out of space while writing
* Ignore read-only filesystems when creating files
* Turn read-only files into symlinks to underlying file
* Hard link copy-on-write / CoW
* supports POSIX ACLs
* Support for POSIX ACLs
* Misc other things
# HOW IT WORKS
@@ -59,7 +59,7 @@ A + B = C
+-- file6
```
mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **not** split data across drives. It is not RAID0 / striping. It is simply a union.
mergerfs does **NOT** support the copy-on-write (CoW) or whiteout behaviors found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **NOT** split data across drives. It is not RAID0 / striping. It is simply a union of other filesystems.
# TERMINOLOGY
@@ -67,9 +67,9 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
* branch: A base path used in the pool.
* pool: The mergerfs mount. The union of the branches.
* relative path: The path in the pool relative to the branch and mount.
* function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
* category: A collection of functions based on basic behavior (action, create, search).
* policy: The algorithm used to select a file when performing a function.
* function: A filesystem call (open, unlink, create, getattr, etc.)
* category: A collection of functions (action, create, search).
* path preservation: Aspect of some policies which includes checking the path for which a file would be created.
@@ -77,31 +77,34 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
If you don't already know that you have a special use case then just start with one of the following option sets.
#### You need `mmap` (used by rtorrent and many sqlite3 base software)
`allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs`
#### You don't need `mmap`
`use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
`allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs`
#### You do need `mmap` (used by rtorrent and some other programs)
`use_ino,cache.files=partial,dropcacheonclose=true,allow_other,category.create=mfs`
See the mergerfs [wiki for real world deployments](https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments) for comparisons / ideas.
# OPTIONS
These options are the same regardless you use them with the `mergerfs` commandline program, used in fstab, or in a config file.
### mount options
* **config**: Path to a config file. Same arguments as below in key=val format.
* **config**: Path to a config file. Same arguments as below in key=val / ini style format.
* **branches**: Colon delimited list of branches.
* **allow_other**: A libfuse option which allows users besides the one which ran mergerfs to see the filesystem. This is required for most use-cases.
* **minfreespace=SIZE**: The minimum space value used for creation policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
* **minfreespace=SIZE**: The minimum space value used for creation policies. Can be overridden by branch specific option. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
* **moveonenospc=BOOL|POLICY**: When enabled if a **write** fails with **ENOSPC** (no space left on device) or **EDQUOT** (disk quota exceeded) the policy selected will run to find a new location for the file. An attempt to move the file to that branch will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false, true = mfs)
* **use_ino**: Causes mergerfs to supply file/directory inodes rather than libfuse. While not a default it is recommended it be enabled so that linked files share the same inode value.
* **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: Selects the inode calculation algorithm. (default: hybrid-hash)
* **dropcacheonclose=BOOL**: When a file is requested to be closed call `posix_fadvise` on it first to instruct the kernel that we no longer need the data and it can drop its cache. Recommended when **cache.files=partial|full|auto-full** to limit double caching. (default: false)
* **symlinkify=BOOL**: When enabled and a file is not writable and its mtime or ctime is older than **symlinkify_timeout** files will be reported as symlinks to the original files. Please read more below before using. (default: false)
* **symlinkify_timeout=INT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
* **symlinkify_timeout=UINT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
* **nullrw=BOOL**: Turns reads and writes into no-ops. The request will succeed but do nothing. Useful for benchmarking mergerfs. (default: false)
* **ignorepponrename=BOOL**: Ignore path preserving on rename. Typically rename and link act differently depending on the policy of `create` (read below). Enabling this will cause rename and link to always use the non-path preserving behavior. This means files, when renamed or linked, will stay on the same drive. (default: false)
* **security_capability=BOOL**: If false return ENOATTR when xattr security.capability is queried. (default: true)
@@ -110,18 +113,23 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
* **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base)
* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none)
* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off)
* **follow-symlinks=never|directory|regular|all**: Turns symlinks into what they point to. (default: never)
* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: When a link fails with EXDEV optionally create a symlink to the file instead.
* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it.
* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false)
* **async_read=BOOL**: Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true)
* **fuse_msg_size=INT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
* **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
* **threads=INT**: Number of threads to use in multithreaded mode. When set to zero it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. If the thread count is set negative it will look up the number of cores then divide by the absolute value. ie. threads=-2 on an 8 core machine will result in 8 / 2 = 4 threads. There will always be at least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: 0)
* **fsname=STR**: 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.
* **func.FUNC=POLICY**: Sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
* **category.CATEGORY=POLICY**: Sets policy of all FUSE functions in the provided category. See POLICIES section for defaults. Example: **category.create=mfs**
* **cache.open=INT**: 'open' policy cache timeout in seconds. (default: 0)
* **cache.statfs=INT**: 'statfs' cache timeout in seconds. (default: 0)
* **cache.attr=INT**: File attribute cache timeout in seconds. (default: 1)
* **cache.entry=INT**: File name lookup cache timeout in seconds. (default: 1)
* **cache.negative_entry=INT**: Negative file name lookup cache timeout in seconds. (default: 0)
* **category.action=POLICY**: Sets policy of all FUSE functions in the action category. (default: epall)
* **category.create=POLICY**: Sets policy of all FUSE functions in the create category. (default: epmfs)
* **category.search=POLICY**: Sets policy of all FUSE functions in the search category. (default: ff)
* **cache.open=UINT**: 'open' policy cache timeout in seconds. (default: 0)
* **cache.statfs=UINT**: 'statfs' cache timeout in seconds. (default: 0)
* **cache.attr=UINT**: File attribute cache timeout in seconds. (default: 1)
* **cache.entry=UINT**: File name lookup cache timeout in seconds. (default: 1)
* **cache.negative_entry=UINT**: Negative file name lookup cache timeout in seconds. (default: 0)
* **cache.files=libfuse|off|partial|full|auto-full**: File page caching mode (default: libfuse)
* **cache.writeback=BOOL**: Enable kernel writeback caching (default: false)
* **cache.symlinks=BOOL**: Cache symlinks (if supported by kernel) (default: false)
@@ -139,21 +147,38 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
#### Value Types
* BOOL = 'true' | 'false'
* INT = [0,MAX_INT]
* INT = [MIN_INT,MAX_INT]
* UINT = [0,MAX_INT]
* SIZE = 'NNM'; NN = INT, M = 'K' | 'M' | 'G' | 'T'
* STR = string
* FUNC = FUSE function
* CATEGORY = FUSE function category
* FUNC = filesystem function
* CATEGORY = function category
* POLICY = mergerfs function policy
### branches
The 'branches' (formerly 'srcmounts') argument is a colon (':') delimited list of paths to be pooled together. 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.
The 'branches' argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem (within reason). 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.
Branches currently have two options which can be set. A type which impacts whether or not the branch is included in a policy calculation and a individual minfreespace value. The values are set by prepending an `=` at the end of a branch designation and using commas as delimiters. Example: /mnt/drive=RW,1234
#### branch type
* RW: (read/write) - Default behavior. Will be eligible in all policy categories.
* RO: (read-only) - Will be excluded from `create` and `action` policies. Same as a read-only mounted filesystem would be (though faster to process).
* NC: (no-create) - Will be excluded from `create` policies. You can't create on that branch but you can change or delete.
#### minfreespace
Same purpose as the global option but specific to the branch. If not set the global value is used.
#### globbing
To make it easier to include multiple branches 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 apply the glob itself.**
Each branch can have a suffix of `=RW` (read / write), `=RO` (read-only), or `=NC` (no create). These suffixes work with globs as well and will apply to each path found. `RW` is the default behavior and those paths will be eligible for all policy categories. `RO` will exclude those paths from `create` and `action` policies (just as a filesystem being mounted `ro` would). `NC` will exclude those paths from `create` policies (you can't create but you can change / delete).
```
# mergerfs -o allow_other,use_ino /mnt/disk\*:/mnt/cdrom /media/drives
@@ -168,7 +193,7 @@ To have the pool mounted at boot or otherwise accessible from related tools use
/mnt/disk*:/mnt/cdrom /mnt/pool fuse.mergerfs allow_other,use_ino 0 0
```
**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:** the globbing is done at mount or when updated using the runtime API. 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.
@@ -218,6 +243,54 @@ In Linux 4.20 a new feature was added allowing the negotiation of the max messag
Since there should be no downsides to increasing `fuse_msg_size` / `max_pages`, outside a minor bump in RAM usage due to larger message buffers, mergerfs defaults the value to 256. On kernels before 4.20 the value has no effect. The reason the value is configurable is to enable experimentation and benchmarking. See the BENCHMARKING section for examples.
### follow-symlinks
This feature, when enabled, will cause symlinks to be interpreted by mergerfs as their target (depending on the mode).
When there is a getattr/stat request for a file mergerfs will check if the file is a symlink and depending on the `follow-symlinks` setting will replace the information about the symlink with that of that which it points to.
When unlink'ing or rmdir'ing the followed symlink it will remove the symlink itself and not that which it points to.
* never: Behave as normal. Symlinks are treated as such.
* directory: Resolve symlinks only which point to directories.
* regular: Resolve symlinks only which point to regular files.
* all: Resolve all symlinks to that which they point to.
Symlinks which do not point to anything are left as is.
WARNING: This feature works but there might be edge cases yet found. If you find any odd behaviors please file a ticket on [github](https://github.com/trapexit/mergerfs/issues).
### link-exdev
If using path preservation and a `link` fails with EXDEV make a call to `symlink` where the `target` is the `oldlink` and the `linkpath` is the `newpath`. The `target` value is determined by the value of `link-exdev`.
* passthrough: Return EXDEV as normal.
* rel-symlink: A relative path from the `newpath`.
* abs-base-symlink: A absolute value using the underlying branch.
* abs-pool-symlink: A absolute value using the mergerfs mount point.
NOTE: It is possible that some applications check the file they link. In those cases it is possible it will error or complain.
### rename-exdev
If using path preservation and a `rename` fails with EXDEV:
1. Move file from **/branch/a/b/c** to **/branch/.mergerfs_rename_exdev/a/b/c**.
2. symlink the rename's `newpath` to the moved file.
The `target` value is determined by the value of `rename-exdev`.
* passthrough: Return EXDEV as normal.
* rel-symlink: A relative path from the `newpath`.
* abs-symlink: A absolute value using the mergerfs mount point.
NOTE: It is possible that some applications check the file they rename. In those cases it is possible it will error or complain.
NOTE: The reason `abs-symlink` is not split into two like `link-exdev` is due to the complexities in managing absolute base symlinks when multiple `oldpaths` exist.
### symlinkify
Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degradation. This feature will turn non-directories which are not writable into symlinks to the original file found by the `readlink` policy after the mtime and ctime are older than the timeout.
@@ -251,28 +324,50 @@ NFS is not fully POSIX compliant and historically certain behaviors, such as ope
This hack addresses the issue where the creation of a file with a read-only mode but with a read/write or write only flag. Normally this is perfectly valid but NFS chops the one open call into multiple calls. Exactly how it is translated depends on the configuration and versions of the NFS server and clients but it results in a permission error because a normal user is not allowed to open a read-only file as writable.
Even though it's a more niche stituation this hack breaks normal security and behavior and as such is `off` by default. If set to `git` it will only perform the hack when the path in question includes `/.git/`. `all` will result it it applying anytime a readonly file which is empty is opened for writing.
Even though it's a more niche situation this hack breaks normal security and behavior and as such is `off` by default. If set to `git` it will only perform the hack when the path in question includes `/.git/`. `all` will result it applying anytime a readonly file which is empty is opened for writing.
# FUNCTIONS / POLICIES / CATEGORIES
# FUNCTIONS, CATEGORIES and POLICIES
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of the core functions are grouped into 3 categories: **action**, **create**, and **search**. These functions and categories can be assigned a policy which dictates which underlying branch/file/directory is chosen when performing that behavior. Any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of the core functions are grouped into 3 categories: **action**, **create**, and **search**. These functions and categories can be assigned a policy which dictates which branch is chosen when performing that function.
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. All functions which work on file handles use the handle which was acquired by `open` or `create`. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions.
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. These functions work with file handles, rather than file paths, which were created by `open` or `create`. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options.
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that sub mounts won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that mounts in the branch won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
NOTE: While any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
#### Functions and their Category classifications
### Functions and their Category classifications
| Category | FUSE Functions |
|----------|-------------------------------------------------------------------------------------|
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
| create | create, mkdir, mknod, symlink |
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range |
In cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will be used.
In cases where something may be searched for (such as a path to clone) **getattr** will usually be used.
### Policies
A policy is the algorithm used to choose a branch or branches for a function to work on. Think of them as ways to filter and sort branches.
Any function in the `create` category will clone the relative path if needed. Some other functions (`rename`,`link`,`ioctl`) have special requirements or behaviors which you can read more about below.
#### Filtering
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting the branches. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). These filters are applied across all policies unless otherwise noted.
* No **search** function policies filter.
* All **action** function policies filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
* All **create** function policies filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
Policies may have their own additional filtering such as those that require existing paths to be present.
If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch. **ENOENT** will be returned if no eligible branch is found.
#### Path Preservation
@@ -288,40 +383,32 @@ When using non-path preserving policies paths will be cloned to target drives as
With the `msp` or `most shared path` policies they are defined as `path preserving` for the purpose of controlling `link` and `rename`'s behaviors since `ignorepponrename` is available to disable that behavior. In mergerfs v3.0 the path preserving behavior of rename and link will likely be separated from the policy all together.
#### Filters
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). The policy defines the sorting but filtering is mostly uniform as described below.
* No **search** policies filter.
* All **action** policies will filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
* All **create** policies will filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch.
#### Policy descriptions
Because of the nature of the behavior the policies act diffierently depending on the function it is used with (based on the category).
A policy's behavior differs, as mentioned above, based on the function it is used with. Sometimes it really might not make sense to even offer certain policies because they are literally the same as others but it makes things a bit more uniform. In mergerfs 3.0 this might change.
| Policy | Description |
|------------------|------------------------------------------------------------|
| all | Search: same as **epall**. Action: same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| epall (existing path, all) | Search: same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
| all | Search: Same as **epall**. Action: Same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| epall (existing path, all) | Search: Same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
| epff (existing path, first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found where the relative path exists. |
| eplfs (existing path, least free space) | Of all the branches on which the relative path exists choose the drive with the least free space. |
| eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the drive with the least used space. |
| epmfs (existing path, most free space) | Of all the branches on which the relative path exists choose the drive with the most free space. |
| eppfrd (existing path, percentage free random distribution) | Like **pfrd** but limited to existing paths. |
| eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. |
| erofs | Exclusively return **-1** with **errno** set to **EROFS** (read-only filesystem). |
| ff (first found) | Search: same as **epff**. Action: same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: Pick the drive with the least available free space. |
| lus (least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: Pick the drive with the least used space. |
| mfs (most free space) | Search: same as **epmfs**. Action: same as **epmfs**. Create: Pick the drive with the most available free space. |
| msplfs (most shared path, least free space) | Search: same as **eplfs**. Action: same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. |
| msplus (most shared path, least used space) | Search: same as **eplus**. Action: same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. |
| mspmfs (most shared path, most free space) | Search: same as **epmfss**. Action: same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. |
| ff (first found) | Search: Same as **epff**. Action: Same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: Pick the drive with the least available free space. |
| lus (least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: Pick the drive with the least used space. |
| mfs (most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: Pick the drive with the most available free space. |
| msplfs (most shared path, least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. |
| msplus (most shared path, least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. |
| mspmfs (most shared path, most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: like **epmfs** but walk back the path if it fails to find a branch at that level. |
| msppfrd (most shared path, percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Like **eppfrd** but will walk back the path if it fails to find a branch at that level. |
| newest | Pick the file / directory with the largest mtime. |
| pfrd (percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Chooses a branch at random with the likelihood of selection based on a branch's available space relative to the total. |
| rand (random) | Calls **all** and then randomizes. Returns 1. |
**NOTE:** If you are using an underlying filesystem that reserves blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the reservation by using `f_bavail` (number of free blocks for unprivileged users) rather than `f_bfree` (number of free blocks) in policy calculations. **df** does NOT use `f_bavail`, it uses `f_bfree`, so direct comparisons between **df** output and mergerfs' policies is not appropriate.
@@ -341,19 +428,6 @@ Because of the nature of the behavior the policies act diffierently depending on
When `ioctl` is used with an open file then it will use the file handle which was created at the original `open` call. However, when using `ioctl` with a directory mergerfs will use the `open` policy to find the directory to act on.
#### unlink
In FUSE there is an opaque "file handle" which is created by `open`, `create`, or `opendir`, passed to the kernel, and then is passed back to the FUSE userland application by the kernel. Unfortunately, the FUSE kernel driver does not always send the file handle when it theoretically could/should. This complicates certain behaviors / workflows particularly in the high level API. As a result mergerfs is currently doing a few hacky things.
libfuse2 and libfuse3, when using the high level API, will rename names to `.fuse_hiddenXXXXXX` if the file is open when unlinked or renamed over. It does this so the file is still available when a request referencing the now missing file is made. This file however keeps a `rmdir` from succeeding and can be picked up by software reading directories.
The change mergerfs has done is that if a file is open when an unlink or rename happens it will open the file and keep it open till closed by all those who opened it prior. When a request comes in referencing that file and it doesn't include a file handle it will instead use the file handle created at unlink/rename time.
This won't result in technically proper behavior but close enough for many usecases.
The plan is to rewrite mergerfs to use the low level API so these invasive libfuse changes are no longer necessary.
#### rename & link ####
**NOTE:** If you're receiving errors from software when files are moved / renamed / linked then you should consider changing the create policy to one which is **not** path preserving, enabling `ignorepponrename`, or contacting the author of the offending software and requesting that `EXDEV` (cross device / improper link) be properly handled.
@@ -407,9 +481,39 @@ The above behavior will help minimize the likelihood of EXDEV being returned but
The options `statfs` and `statfs_ignore` can be used to modify `statfs` behavior.
# ERROR HANDLING
POSIX filesystem functions offer a single return code meaning that there is some complication regarding the handling of multiple branches as mergerfs does. It tries to handle errors in a way that would generally return meaningful values for that particular function.
### chmod, chown, removexattr, setxattr, truncate, utimens
1) if no error: return 0 (success)
2) if no successes: return first error
3) if one of the files acted on was the same as the related search function: return its value
4) return 0 (success)
While doing this increases the complexity and cost of error handling, particularly step 3, this provides probably the most reasonable return value.
### unlink, rmdir
1) if no errors: return 0 (success)
2) return first error
Older version of mergerfs would return success if any success occurred but for unlink and rmdir there are downstream assumptions that, while not impossible to occur, can confuse some software.
### others
For search functions there is always a single thing acted on and as such whatever return value that comes from the single function call is returned.
For create functions `mkdir`, `mknod`, and `symlink` which don't return a file descriptor and therefore can have `all` or `epall` policies it will return success if any of the calls succeed and an error otherwise.
# BUILD / UPDATE
**NOTE:** Prebuilt packages can be found at: https://github.com/trapexit/mergerfs/releases
**NOTE:** Prebuilt packages can be found at and recommended for most users: https://github.com/trapexit/mergerfs/releases
**NOTE:** Only tagged releases are supported. `master` and other branches should be considered works in progress.
First get the code from [github](https://github.com/trapexit/mergerfs).
@@ -424,7 +528,7 @@ $ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ve
$ cd mergerfs
$ sudo tools/install-build-pkgs
$ make deb
$ sudo dpkg -i ../mergerfs_version_arch.deb
$ sudo dpkg -i ../mergerfs_<version>_<arch>.deb
```
#### RHEL / CentOS /Fedora
@@ -458,43 +562,26 @@ make LTO=1 - build with link time optimization
```
# UPGRADE
mergerfs can be upgraded live by mounting on top of the previous instance. Simply install the new version of mergerfs and follow the instructions below.
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
```
$ sudo mount /mnt/mergerfs
$ mount | grep mergerfs
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
```
A problem with this approach is that the underlying instance will continue to run even if the software using it stop or are restarted. To work around this you can use a "lazy umount". Before mounting over top the mount point with the new instance of mergerfs issue: `umount -l <mergerfs_mountpoint>`.
# RUNTIME CONFIG
#### ioctl
The original runtime config API was via xattr calls. This however became an issue when needing to disable xattr. While slightly less convenient ioctl does not have the same problems and will be the main API going forward.
The keys are the same as the command line option arguments as well as the config file.
##### requests / commands
All commands take a 4096 byte char buffer.
* read keys: get a nul '\0' delimited list of option keys
* _IOWR(0xDF,0,char[4096]) = 0xD000DF00
* on success ioctl return value is the total length
* read value: get an option value
* _IOWR(0xDF,1,char[4096]) = 0xD000DF01
* the key is passed in via the char buffer as a nul '\0' terminated string
* on success ioctl return value is the total length
* write value: set an option value
* _IOW(0xDF,2,char[4096]) = 0x5000DF02
* the key and value is passed in via the char buffer as a nul '\0' terminated string in the format of `key=value`
* on success ioctl return value is 0
* file info: get mergerfs metadata info for a file
* _IOWR(0xDF,3,char[4096]) = 0xD000DF03
* the key is passed in via the char buffer as a nul '\0' terminated string
* on success the ioctl return value is the total length
* keys:
* basepath: the base mount point for the file according to the getattr policy
* relpath: the relative path of the file from the mount point
* fullpath: the full path of the underlying file according to the getattr policy
* allpaths: a NUL '\0' delimited list of full paths to all files found
#### .mergerfs pseudo file (deprecated) ####
NOTE: this interface will be removed in mergerfs 3.0
#### .mergerfs pseudo file ####
```
<mountpoint>/.mergerfs
@@ -507,7 +594,7 @@ Any changes made at runtime are **not** persisted. If you wish for values to per
##### Keys #####
Use `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
Use `getfattr -d /mountpoint/.mergerfs` or `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
##### Values #####
@@ -517,8 +604,6 @@ Same as the command line.
###### user.mergerfs.branches ######
**NOTE:** formerly `user.mergerfs.srcmounts` but said key is still supported.
Used to query or modify the list of branches. When modifying there are several shortcuts to easy manipulation of the list.
| Value | Description |
@@ -538,70 +623,30 @@ The `=NC`, `=RO`, `=RW` syntax works just as on the command line.
##### Example #####
```
[trapexit:/mnt/mergerfs] $ xattr -l .mergerfs
user.mergerfs.branches: /mnt/a=RW:/mnt/b=RW
user.mergerfs.minfreespace: 4294967295
user.mergerfs.moveonenospc: false
[trapexit:/mnt/mergerfs] $ getfattr -d .mergerfs
user.mergerfs.branches="/mnt/a=RW:/mnt/b=RW"
user.mergerfs.minfreespace="4294967295"
user.mergerfs.moveonenospc="false"
...
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
ff
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
user.mergerfs.category.search="ff"
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.category.search newest .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
newest
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches +/mnt/c .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
/mnt/a:/mnt/b:/mnt/c
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches =/mnt/c .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
/mnt/c
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches '+</mnt/a:/mnt/b' .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
/mnt/a:/mnt/b:/mnt/c
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.category.search -v newest .mergerfs
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
user.mergerfs.category.search="newest"
```
#### 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:
While they won't show up when using `getfattr` **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:/mnt/mergerfs] $ ls
A B C
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.fullpath A
/mnt/a/full/path/to/A
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.basepath A
/mnt/a
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.relpath A
/full/path/to/A
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.allpaths A | tr '\0' '\n'
/mnt/a/full/path/to/A
/mnt/b/full/path/to/A
```
# UPGRADE
mergerfs can be upgraded live by mounting on top of the previous version. Simply install the new version of mergerfs and follow the instructions below.
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
```
$ sudo mount /mnt/mergerfs
$ mount | grep mergerfs
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
```
# TOOLING
@@ -612,7 +657,6 @@ media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,defa
* mergerfs.dup: Ensure there are at least N copies of a file across the pool
* mergerfs.balance: Rebalance files across drives by moving them from the most filled to the least filled
* mergerfs.consolidate: move files within a single mergerfs directory to the drive with most free space
* 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 and keep track of files
* https://github.com/trapexit/bbf
@@ -655,7 +699,7 @@ Given the relatively high cost of FUSE due to the kernel <-> userspace round tri
When `cache.files` is enabled the default is for it to perform writethrough caching. This behavior won't help improve performance as each write still goes one for one through the filesystem. By enabling the FUSE writeback cache small writes may be aggregated by the kernel and then sent to mergerfs as one larger request. This can greatly improve the throughput for apps which write to files inefficiently. The amount the kernel can aggregate is limited by the size of a FUSE message. Read the `fuse_msg_size` section for more details.
There is a small side effect as a result of enabling wrtieback caching. Underlying files won't ever be opened with O_APPEND or O_WRONLY. The former because the kernel then manages append mode and the latter because the kernel may request file data from mergerfs to populate the write cache. The O_APPEND change means that if a file is changed outside of mergerfs it could lead to corruption as the kernel won't know the end of the file has changed. That said any time you use caching you should keep from using the same file outside of mergerfs at the same time.
There is a small side effect as a result of enabling writeback caching. Underlying files won't ever be opened with O_APPEND or O_WRONLY. The former because the kernel then manages append mode and the latter because the kernel may request file data from mergerfs to populate the write cache. The O_APPEND change means that if a file is changed outside of mergerfs it could lead to corruption as the kernel won't know the end of the file has changed. That said any time you use caching you should keep from using the same file outside of mergerfs at the same time.
Note that if an application is properly sizing writes then writeback caching will have little or no effect. It will only help with writes of sizes below the FUSE message size (128K on older kernels, 1M on newer).
@@ -695,7 +739,7 @@ MergerFS does not natively support any sort of tiered caching. Most users have n
1. Fast network, slow drives, many readers: You've a 10+Gbps network with many readers and your regular drives can't keep up.
2. Fast network, slow drives, small'ish bursty writes: You have a 10+Gbps network and wish to transfer amounts of data less than your cache drive but wish to do so quickly.
With #1 its arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
With #1 it's arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
With #2 one could use dm-cache as well but there is another solution which requires only mergerfs and a cronjob.
@@ -712,6 +756,9 @@ With #2 one could use dm-cache as well but there is another solution which requi
Move files from cache to backing pool based only on the last time the file was accessed. Replace `-atime` with `-amin` if you want minutes rather than days. May want to use the `fadvise` / `--drop-cache` version of rsync or run rsync with the tool "nocache".
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
```
#!/bin/bash
@@ -733,6 +780,8 @@ find "${CACHE}" -type f -atime +${N} -printf '%P\n' | \
Move the oldest file from the cache to the backing pool. Continue till below percentage threshold.
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
```
#!/bin/bash
@@ -753,7 +802,7 @@ do
head -n 1 | \
cut -d' ' -f2-)
test -n "${FILE}"
rsync -axqHAXWES --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
rsync -axqHAXWESR --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
done
```
@@ -780,6 +829,8 @@ NOTE: be sure to read about these features before changing them to understand wh
* use `symlinkify` if your data is largely static and read-only
* use tiered cache drives
* use LVM and LVM cache to place a SSD in front of your HDDs
* if `cache.files` is enabled increase readahead: `echo "1024" > /sys/class/bdi/0:$(stat -c%d /MOUNT)/read_ahead_kb`
* increase readahead on all devices: `ls -1 /sys/class/bdi/*/read_ahead_kb | xargs -n1 -I{} sh -c "echo 1024 > {}"`
If you come across a setting that significantly impacts performance please contact trapexit so he may investigate further.
@@ -814,16 +865,26 @@ $ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=
$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
```
### other benchmarks
If you are attempting to benchmark other behaviors you must ensure you clear kernel caches before runs. In fact it would be a good deal to run before the read and write benchmarks as well just in case.
```
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches
```
# TIPS / NOTES
* This document is very literal and thorough. Unless there is a bug things work as described. If a suspected feature isn't mentioned it doesn't exist. If certain libfuse arguments aren't listed they probably shouldn't be used.
* Ensure you're using the latest version. Few distros have the latest version.
* **use_ino** will only work when used with mergerfs 2.18.0 and above.
* Run mergerfs as `root` (with **allow_other**) 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 and files you expect in a merged point or policies seem to skip drives be sure the user has permission to all the underlying directories. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
* Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned exclusively and fully by the same user otherwise strange permission issues may arise. mergerfs is designed and intended to be run as `root`.
* If you don't see some directories and files you expect, policies seem to skip branches, you get strange permission errors, etc. be sure the underlying filesystems' permissions are all the same. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
* If you still have permission issues be sure you are using POSIX ACL compliant filesystems. mergerfs doesn't generally make exceptions for FAT, NTFS, or other non-POSIX filesystem.
* Do **not** use `cache.files=off` if you expect applications (such as rtorrent) to use [mmap](http://linux.die.net/man/2/mmap) files. Shared mmap is not currently supported in FUSE w/ page caching disabled. Enabling `dropcacheonclose` is recommended when `cache.files=partial|full|auto-full`.
* Since POSIX functions give only a singular error or success its difficult to determine the proper behavior when applying the function to multiple targets. **mergerfs** will return an error only if all attempts of an action fail. Any success will lead to a success returned. This means however that some odd situations may arise.
* [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org), etc. 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 default **getattr** policy of **ff** it's possible those programs 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.
* [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org), etc. 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 default **getattr** policy of **ff** it's possible those programs will miss an update on account of it returning the first directory found's **stat** info and it's 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.
* 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 the attempt to merge together multiple sources of data which could be out of sync due to the different policies.
* For consistency 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). However, the flexibility is there if needed.
@@ -834,6 +895,7 @@ $ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=
[https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs)
#### directory mtime is not being updated
Remember that the default policy for `getattr` is `ff`. The information for the first directory found will be returned. If it wasn't the directory which had been updated then it will appear outdated.
@@ -1010,17 +1072,35 @@ The default create policy is `epmfs`. That is a path preserving algorithm. With
This catches a lot of new users off guard but changing the default would break the setup for many existing users. If you do not care about path preservation and wish your files to be spread across all your drives change to `mfs` or similar policy as described above. If you do want path preservation you'll need to perform the manual act of creating paths on the drives you want the data to land on before transferring your data. Setting `func.mkdir=epall` can simplify managing path preservation for `create`. Or use `func.mkdir=rand` if you're interested in just grouping together directory content by drive.
#### Do hard links work?
#### Do hardlinks work?
Yes. You need to use `use_ino` to support proper reporting of inodes but they work regardless. See also the option `inodecalc`.
What mergerfs does not do is fake hard links across branches. Read the section "rename & link" for how it works.
Remember that hardlinks will NOT work across devices. That includes between the original filesystem and a mergerfs pool, between two separate pools of the same underlying filesystems, or bind mounts of paths within the mergerfs pool. The latter is common when using Docker or Podman. Multiple volumes (bind mounts) to the same underlying filesystem are considered different devices. There is no way to link between them. You should mount in the highest directory in the mergerfs pool that includes all the paths you need if you want links to work.
#### Does mergerfs support CoW / copy-on-write?
#### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
Yes. They are completely unrelated pieces of software.
#### Can mergerfs run via Docker, Podman, Kubernetes, etc.
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined` or similar with other container runtimes. You should also be running it as root or given sufficient caps to change user and group identity as well as have root like filesystem permissions.
Keep in mind that you **MUST** consider identity when using containers. For example: supplemental groups will be picked up from the container unless you properly manage users and groups by sharing relevant /etc files or by using some other means to share identity across containers. Similarly if you use "rootless" containers and user namespaces to do uid/gid translations you **MUST** consider that while managing shared files.
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs), with Docker you should probably be mounting with `bind-propagation` set to `slave`.
#### Does mergerfs support CoW / copy-on-write / writes to read-only filesystems?
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs or aufs sense. It does offer a [cow-shell](http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html) like hard link breaking (copy to temp file then rename over original) which can be useful when wanting to save space by hardlinking duplicate files but wish to treat each name as if it were a unique and separate file.
If you want to write to a read-only filesystem you should look at overlayfs. You can always include the overlayfs mount into a mergerfs pool.
#### Why can't I see my files / directories?
@@ -1031,6 +1111,32 @@ Whenever you run into a split permission issue (seeing some but not all files) t
If using a network filesystem such as NFS, SMB, CIFS (Samba) be sure to pay close attention to anything regarding permissioning and users. Root squashing and user translation for instance has bitten a few mergerfs users. Some of these also affect the use of mergerfs from container platforms such as Docker.
#### Why use FUSE? Why not a kernel based solution?
As with any two solutions to a problem there are advantages and disadvantages to each one.
A FUSE based solution has all the downsides of FUSE:
* Higher IO latency due to the trips in and out of kernel space
* Higher general overhead due to trips in and out of kernel space
* Double caching when using page caching
* Misc limitations due to FUSE's design
But FUSE also has a lot of upsides:
* Easier to offer a cross platform solution
* Easier forward and backward compatibility
* Easier updates for users
* Easier and faster release cadence
* Allows more flexibility in design and features
* Overall easier to write, secure, and maintain
* Ability to run without root access or need to change the kernel
* Much lower barrier to entry (getting code into the kernel takes a lot of time and effort initially)
FUSE was chosen because of all the advantages listed above. The negatives of FUSE do not outweigh the positives.
#### Is my OS's libfuse needed for mergerfs to work?
No. Normally `mount.fuse` is needed to get mergerfs (or any FUSE filesystem to mount using the `mount` command but in vendoring the libfuse library the `mount.fuse` app has been renamed to `mount.mergerfs` meaning the filesystem type in `fstab` can simply be `mergerfs`. That said there should be no harm in having it installed and continuing to using `fuse.mergerfs` as the type in `/etc/fstab`.
@@ -1048,7 +1154,7 @@ If `mergerfs` doesn't work as a type it could be due to how the `mount.mergerfs`
See above first.
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then its simply too much work to manage the differences across the versions.
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then it's simply too much work to manage the differences across the versions.
#### Why use mergerfs over mhddfs?
@@ -1155,7 +1261,7 @@ Yes.
#### I notice massive slowdowns of writes when enabling cache.files.
When file caching is enabled in any form (`cache.files!=off` or `direct_io=false`) it will issue `getxattr` requests for `security.capability` prior to *every single write*. This will usually result in a performance degregation, especially when using a network filesystem (such as NFS or CIFS/SMB/Samba.) Unfortunately at this moment the kernel is not caching the response.
When file caching is enabled in any form (`cache.files!=off` or `direct_io=false`) it will issue `getxattr` requests for `security.capability` prior to *every single write*. This will usually result in a performance degradation, especially when using a network filesystem (such as NFS or CIFS/SMB/Samba.) Unfortunately at this moment the kernel is not caching the response.
To work around this situation mergerfs offers a few solutions.
@@ -1185,48 +1291,40 @@ Filesystems are complex and difficult to debug. mergerfs, while being just a pro
**Please make sure you are using the [latest release](https://github.com/trapexit/mergerfs/releases) or have tried it in comparison. Old versions, which are often included in distros like Debian and Ubuntu, are not ever going to be updated and your bug may have been addressed already.**
**For commercial support or feature requests please contact me directly.**
#### Information to include in bug reports
* Version of mergerfs: `mergerfs -V`
* mergerfs settings: from `/etc/fstab` or command line execution
* Version of Linux: `uname -a`
* Versions of any additional software being used
* List of drives, their filesystems, and sizes (before and after issue): `df -h`
* **All** information about the relevant branches and paths: permissions, etc.
* mergerfs settings / arguments: from fstab, systemd unit, command line, etc.
* Version of the OS: `uname -a`
* List of branches, their filesystem types, sizes (before and after issue): `df -h`
* **All** information about the relevant branches and paths: permissions, ownership, etc.
* **All** information about the client app making the requests: version, uid/gid
* Runtime environment: mostly are things running inside containers or not
* A `strace` of the app having problems:
* `strace -f -o /tmp/app.strace.txt <cmd>`
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
* A `strace` of mergerfs while the program is trying to do whatever it's failing to do:
* `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* **Precise** directions on replicating the issue. Do not leave **anything** out.
* Try to recreate the problem in the simplist way using standard programs.
* Try to recreate the problem in the simplest way using standard programs: ln, mv, cp, ls, dd, etc.
#### Contact / Issue submission
* github.com: https://github.com/trapexit/mergerfs/issues
* email: trapexit@spawn.link
* discord: https://discord.gg/MpAr69V
* twitter: https://twitter.com/_trapexit
* reddit: https://www.reddit.com/user/trapexit
* discord: https://discord.gg/MpAr69V
#### Support development
This software is free to use and released under a very liberal license. That said if you like this software and would like to support its development donations are welcome.
This software is released under the very liberal ISC license and is therefore free to use for personal or commercial uses. That said if you like this software and have the means please consider supporting its development.
* PayPal: https://paypal.me/trapexit
* GitHub Sponsors: https://github.com/sponsors/trapexit
* Patreon: https://www.patreon.com/trapexit
* SubscribeStar: https://www.subscribestar.com/trapexit
* Ko-Fi: https://ko-fi.com/trapexit
* Bitcoin (BTC): 1DfoUd2m5WCxJAMvcFuvDpT4DR2gWX2PWb
* Bitcoin Cash (BCH): qrf257j0l09yxty4kur8dk2uma8p5vntdcpks72l8z
* Ethereum (ETH): 0xb486C0270fF75872Fc51d85879b9c15C380E66CA
* Litecoin (LTC): LW1rvHRPWtm2NUEMhJpP4DjHZY1FaJ1WYs
* Basic Attention Token (BAT): 0xE651d4900B4C305284Da43E2e182e9abE149A87A
* Zcash (ZEC): t1ZwTgmbQF23DJrzqbAmw8kXWvU2xUkkhTt
* Zcoin (XZC): a8L5Vz35KdCQe7Y7urK2pcCGau7JsqZ5Gw
https://github.com/trapexit/support
# LINKS
-3547
View File
File diff suppressed because it is too large Load Diff
+24 -10
View File
@@ -10,7 +10,7 @@ INSTALLUTILS :=
endif
ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g
OPT_FLAGS := -O0 -g -fsanitize=undefined
else
OPT_FLAGS := -O2
endif
@@ -29,16 +29,18 @@ INSTALLBINDIR = $(DESTDIR)$(BINDIR)
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
AR ?= ar
SRC = \
lib/buffer.c \
lib/cuse_lowlevel.c \
lib/fuse_dirents.c \
lib/crc32b.c \
lib/debug.c \
lib/fuse.c \
lib/fuse_kern_chan.c \
lib/fuse_loop.c \
lib/fuse_dirents.c \
lib/fuse_loop_mt.c \
lib/fuse_lowlevel.c \
lib/fuse_mt.c \
lib/fuse_node.c \
lib/fuse_opt.c \
lib/fuse_session.c \
lib/fuse_signals.c \
@@ -46,9 +48,17 @@ SRC = \
lib/mount.c
OBJS = $(SRC:lib/%.c=build/%.o)
DEPS = $(SRC:lib/%.c=build/%.d)
CFLAGS = \
$(OPT_FLAGS) \
CFLAGS ?= \
$(OPT_FLAGS)
CFLAGS := \
${CFLAGS} \
-std=gnu99 \
-Wall \
-pipe \
-MMD
CXXFLAGS := \
${CXXFLAGS} \
-std=c++17 \
-Wall \
-pipe \
-MMD
@@ -61,7 +71,8 @@ FUSE_FLAGS = \
-DFUSE_USE_VERSION=29 \
-DPACKAGE_VERSION=\"$(VERSION)\" \
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
LDFLAGS = \
LDFLAGS := \
${LDFLAGS} \
-lrt \
-pthread
@@ -78,7 +89,7 @@ objects: build/config.h
$(MAKE) $(OBJS)
build/libfuse.a: objects
ar rcs build/libfuse.a $(OBJS)
${AR} rcs build/libfuse.a $(OBJS)
utils: mergerfs-fusermount mount.mergerfs
@@ -95,6 +106,9 @@ mount.mergerfs: build/mount.mergerfs
build/%.o: lib/%.c
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
build/%.o: lib/%.cpp
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
clean:
rm -rf build
+7
View File
@@ -0,0 +1,7 @@
#include <malloc.h>
int
main()
{
malloc_trim(0);
}
-11
View File
@@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: fuse
Description: Filesystem in Userspace
Version: @VERSION@
Libs: -L${libdir} -lfuse -pthread
Libs.private: @libfuse_libs@
Cflags: -I${includedir}/fuse -D_FILE_OFFSET_BITS=64
-17
View File
@@ -1,17 +0,0 @@
## Process this file with automake to produce Makefile.in
fuseincludedir=$(includedir)/fuse
fuseinclude_HEADERS = \
fuse.h \
fuse_compat.h \
fuse_common.h \
fuse_common_compat.h \
fuse_lowlevel.h \
fuse_lowlevel_compat.h \
fuse_opt.h \
cuse_lowlevel.h
include_HEADERS = old/fuse.h ulockmgr.h
noinst_HEADERS = fuse_kernel.h
-87
View File
@@ -1,87 +0,0 @@
/*
CUSE: Character device in Userspace
Copyright (C) 2008-2009 SUSE Linux Products GmbH
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
Read example/cusexmp.c for usages.
*/
#ifndef _CUSE_LOWLEVEL_H_
#define _CUSE_LOWLEVEL_H_
#ifndef FUSE_USE_VERSION
#define FUSE_USE_VERSION 29
#endif
#include "fuse_lowlevel.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
struct fuse_session;
struct cuse_info {
unsigned dev_major;
unsigned dev_minor;
unsigned dev_info_argc;
const char **dev_info_argv;
unsigned flags;
};
/*
* Most ops behave almost identically to the matching fuse_lowlevel
* ops except that they don't take @ino.
*
* init_done : called after initialization is complete
* read/write : always direct IO, simultaneous operations allowed
* ioctl : might be in unrestricted mode depending on ci->flags
*/
struct cuse_lowlevel_ops {
void (*init) (void *userdata, struct fuse_conn_info *conn);
void (*init_done) (void *userdata);
void (*destroy) (void *userdata);
void (*open) (fuse_req_t req, struct fuse_file_info *fi);
void (*read) (fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi);
void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
struct fuse_file_info *fi);
void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
void (*release) (fuse_req_t req, struct fuse_file_info *fi);
void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
void (*ioctl) (fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz);
void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
struct fuse_pollhandle *ph);
};
struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop,
void *userdata);
struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop,
int *multithreaded, void *userdata);
void cuse_lowlevel_teardown(struct fuse_session *se);
int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop, void *userdata);
#ifdef __cplusplus
}
#endif
#endif /* _CUSE_LOWLEVEL_H_ */
+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
+465 -752
View File
File diff suppressed because it is too large Load Diff
+165 -207
View File
@@ -15,7 +15,10 @@
#ifndef _FUSE_COMMON_H_
#define _FUSE_COMMON_H_
#include "extern_c.h"
#include "fuse_opt.h"
#include "fuse_timeouts.h"
#include <stdint.h>
#include <sys/types.h>
@@ -36,24 +39,15 @@
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
#define FUSE_MAX_MAX_PAGES 256
#ifdef __cplusplus
extern "C" {
#endif
typedef struct fuse_timeouts_s fuse_timeouts_t;
struct fuse_timeouts_s
{
uint64_t entry;
uint64_t attr;
};
EXTERN_C_BEGIN
/**
* Information about open files
*
* Changed in version 2.5
*/
struct
fuse_file_info
typedef struct fuse_file_info_t fuse_file_info_t;
struct fuse_file_info_t
{
/** Open flags. Available in open() and release() */
int flags;
@@ -158,60 +152,61 @@ fuse_file_info
* value must usually be smaller than the indicated value.
*/
struct fuse_conn_info {
/**
* Major version of the protocol (read-only)
*/
unsigned proto_major;
/**
* Major version of the protocol (read-only)
*/
unsigned proto_major;
/**
* Minor version of the protocol (read-only)
*/
unsigned proto_minor;
/**
* Minor version of the protocol (read-only)
*/
unsigned proto_minor;
/**
* Maximum size of the write buffer
*/
unsigned max_write;
/**
* Maximum size of the write buffer
*/
unsigned max_write;
/**
* Maximum readahead
*/
unsigned max_readahead;
/**
* Maximum readahead
*/
unsigned max_readahead;
/**
* Capability flags, that the kernel supports
*/
unsigned capable;
/**
* Capability flags, that the kernel supports
*/
unsigned capable;
/**
* Capability flags, that the filesystem wants to enable
*/
unsigned want;
/**
* Capability flags, that the filesystem wants to enable
*/
unsigned want;
/**
* Maximum number of backgrounded requests
*/
unsigned max_background;
/**
* Maximum number of backgrounded requests
*/
unsigned max_background;
/**
* Kernel congestion threshold parameter
*/
unsigned congestion_threshold;
/**
* Kernel congestion threshold parameter
*/
unsigned congestion_threshold;
/**
* Max pages
*/
uint16_t max_pages;
/**
* Max pages
*/
uint16_t max_pages;
/**
* For future use.
*/
unsigned reserved[22];
/**
* For future use.
*/
unsigned reserved[22];
};
struct fuse_session;
struct fuse_chan;
struct fuse_pollhandle;
struct fuse_pollhandle_t;
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
/**
* Create a FUSE mountpoint
@@ -241,7 +236,6 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
*
* '-f' foreground
* '-d' '-odebug' foreground, but keep the debug option
* '-s' single threaded
* '-h' '--help' help
* '-ho' help without header
* '-ofsname=..' file system name, if not present, then set to the program
@@ -251,12 +245,12 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
*
* @param args argument vector
* @param mountpoint the returned mountpoint, should be freed after use
* @param multithreaded set to 1 unless the '-s' option is present
* @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 *multithreaded, int *foreground);
int fuse_parse_cmdline(struct fuse_args *args,
char **mountpoint,
int *foreground);
/**
* Go into the background
@@ -278,7 +272,7 @@ int fuse_version(void);
*
* @param ph the poll handle
*/
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
/* ----------------------------------------------------------- *
* Data buffer *
@@ -288,73 +282,73 @@ void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
* 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),
/**
* 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),
/**
* 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),
/**
* 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),
/**
* 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),
/**
* 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),
/**
* 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),
/**
* 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),
};
/**
@@ -364,36 +358,36 @@ enum fuse_buf_copy_flags {
* be supplied as a memory pointer or as a file descriptor
*/
struct fuse_buf {
/**
* Size of data in bytes
*/
size_t size;
/**
* Size of data in bytes
*/
size_t size;
/**
* Buffer flags
*/
enum fuse_buf_flags flags;
/**
* Buffer flags
*/
enum fuse_buf_flags flags;
/**
* Memory pointer
*
* Used unless FUSE_BUF_IS_FD flag is set.
*/
void *mem;
/**
* 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 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;
/**
* File position
*
* Used if FUSE_BUF_FD_SEEK flag is set.
*/
off_t pos;
};
/**
@@ -405,41 +399,41 @@ struct fuse_buf {
* Allocate dynamically to add more than one buffer.
*/
struct fuse_bufvec {
/**
* Number of buffers in the array
*/
size_t count;
/**
* Number of buffers in the array
*/
size_t count;
/**
* Index of current buffer within the array
*/
size_t idx;
/**
* Index of current buffer within the array
*/
size_t idx;
/**
* Current offset within the current buffer
*/
size_t off;
/**
* Current offset within the current buffer
*/
size_t off;
/**
* Array of buffers
*/
struct fuse_buf buf[1];
/**
* 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, \
} } \
} )
#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
@@ -458,7 +452,7 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv);
* @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);
enum fuse_buf_copy_flags flags);
/* ----------------------------------------------------------- *
* Signal handling *
@@ -485,42 +479,6 @@ int fuse_set_signal_handlers(struct fuse_session *se);
*/
void fuse_remove_signal_handlers(struct fuse_session *se);
/* ----------------------------------------------------------- *
* Compatibility stuff *
* ----------------------------------------------------------- */
#if FUSE_USE_VERSION < 26
# ifdef __FreeBSD__
# if FUSE_USE_VERSION < 25
# error On FreeBSD API version 25 or greater must be used
# endif
# endif
# include "fuse_common_compat.h"
# undef FUSE_MINOR_VERSION
# undef fuse_main
# define fuse_unmount fuse_unmount_compat22
# if FUSE_USE_VERSION == 25
# define FUSE_MINOR_VERSION 5
# define fuse_mount fuse_mount_compat25
# elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22
# define FUSE_MINOR_VERSION 4
# define fuse_mount fuse_mount_compat22
# elif FUSE_USE_VERSION == 21
# define FUSE_MINOR_VERSION 1
# define fuse_mount fuse_mount_compat22
# elif FUSE_USE_VERSION == 11
# warning Compatibility with API version 11 is deprecated
# undef FUSE_MAJOR_VERSION
# define FUSE_MAJOR_VERSION 1
# define FUSE_MINOR_VERSION 1
# define fuse_mount fuse_mount_compat1
# else
# error Compatibility with API version other than 21, 22, 24, 25 and 11 not supported
# endif
#endif
#ifdef __cplusplus
}
#endif
EXTERN_C_END
#endif /* _FUSE_COMMON_H_ */
-26
View File
@@ -1,26 +0,0 @@
/*
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.
*/
/* these definitions provide source compatibility to prior versions.
Do not include this file directly! */
struct fuse_file_info_compat {
int flags;
unsigned long fh;
int writepage;
unsigned int direct_io : 1;
unsigned int keep_cache : 1;
};
int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args);
int fuse_mount_compat22(const char *mountpoint, const char *opts);
int fuse_mount_compat1(const char *mountpoint, const char *args[]);
void fuse_unmount_compat22(const char *mountpoint);
-196
View File
@@ -1,196 +0,0 @@
/*
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.
*/
/* these definitions provide source compatibility to prior versions.
Do not include this file directly! */
struct fuse_operations_compat25 {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, struct fuse_file_info *);
int (*read) (const char *, char *, size_t, off_t,
struct fuse_file_info *);
int (*write) (const char *, const char *, size_t, off_t,
struct fuse_file_info *);
int (*statfs) (const char *, struct statvfs *);
int (*flush) (const char *, struct fuse_file_info *);
int (*release) (const char *, struct fuse_file_info *);
int (*fsync) (const char *, int, struct fuse_file_info *);
int (*setxattr) (const char *, const char *, const char *, size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info *);
int (*readdir) (const char *, void *, off_t,
struct fuse_file_info *);
int (*releasedir) (const char *, struct fuse_file_info *);
int (*fsyncdir) (const char *, int, struct fuse_file_info *);
void *(*init) (void);
void (*destroy) (void *);
int (*access) (const char *, int);
int (*create) (const char *, mode_t, struct fuse_file_info *);
int (*ftruncate) (const char *, off_t, struct fuse_file_info *);
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
};
struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
const struct fuse_operations_compat25 *op,
size_t op_size);
int fuse_main_real_compat25(int argc, char *argv[],
const struct fuse_operations_compat25 *op,
size_t op_size);
struct fuse *fuse_setup_compat25(int argc, char *argv[],
const struct fuse_operations_compat25 *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd);
void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
#include <sys/statfs.h>
struct fuse_operations_compat22 {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, struct fuse_file_info_compat *);
int (*read) (const char *, char *, size_t, off_t,
struct fuse_file_info_compat *);
int (*write) (const char *, const char *, size_t, off_t,
struct fuse_file_info_compat *);
int (*statfs) (const char *, struct statfs *);
int (*flush) (const char *, struct fuse_file_info_compat *);
int (*release) (const char *, struct fuse_file_info_compat *);
int (*fsync) (const char *, int, struct fuse_file_info_compat *);
int (*setxattr) (const char *, const char *, const char *, size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info_compat *);
int (*readdir) (const char *, void *, off_t,
struct fuse_file_info_compat *);
int (*releasedir) (const char *, struct fuse_file_info_compat *);
int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
void *(*init) (void);
void (*destroy) (void *);
};
struct fuse *fuse_new_compat22(int fd, const char *opts,
const struct fuse_operations_compat22 *op,
size_t op_size);
struct fuse *fuse_setup_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd);
int fuse_main_real_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size);
struct fuse_operations_compat2 {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, int);
int (*read) (const char *, char *, size_t, off_t);
int (*write) (const char *, const char *, size_t, off_t);
int (*statfs) (const char *, struct statfs *);
int (*flush) (const char *);
int (*release) (const char *, int);
int (*fsync) (const char *, int);
int (*setxattr) (const char *, const char *, const char *,
size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
};
int fuse_main_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op);
struct fuse *fuse_new_compat2(int fd, const char *opts,
const struct fuse_operations_compat2 *op);
struct fuse *fuse_setup_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op,
char **mountpoint, int *multithreaded, int *fd);
struct fuse_statfs_compat1 {
long block_size;
long blocks;
long blocks_free;
long files;
long files_free;
long namelen;
};
struct fuse_operations_compat1 {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, int);
int (*read) (const char *, char *, size_t, off_t);
int (*write) (const char *, const char *, size_t, off_t);
int (*statfs) (struct fuse_statfs_compat1 *);
int (*release) (const char *, int);
int (*fsync) (const char *, int);
};
#define FUSE_DEBUG_COMPAT1 (1 << 1)
struct fuse *fuse_new_compat1(int fd, int flags,
const struct fuse_operations_compat1 *op);
void fuse_main_compat1(int argc, char *argv[],
const struct fuse_operations_compat1 *op);
#endif /* __FreeBSD__ || __NetBSD__ */
+14 -18
View File
@@ -18,9 +18,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "extern_c.h"
EXTERN_C_BEGIN
#include "kvec.h"
#include "fuse_dirent.h"
@@ -36,21 +36,19 @@ extern "C" {
#include <unistd.h>
enum fuse_dirents_type_e
{
UNSET = 0,
NORMAL,
PLUS
};
{
UNSET = 0,
NORMAL,
PLUS
};
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
typedef struct fuse_dirents_s fuse_dirents_t;
struct fuse_dirents_s
typedef struct fuse_dirents_t fuse_dirents_t;
struct fuse_dirents_t
{
char *buf;
uint64_t buf_len;
uint64_t data_len;
kvec_t(uint32_t) offs;
fuse_dirents_type_t type;
kvec_t(char) data;
kvec_t(uint32_t) offs;
fuse_dirents_type_t type;
};
int fuse_dirents_init(fuse_dirents_t *d);
@@ -79,6 +77,4 @@ void *fuse_dirents_find(fuse_dirents_t *d,
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
#ifdef __cplusplus
}
#endif
EXTERN_C_END
+165 -22
View File
@@ -172,6 +172,31 @@
* - add FUSE_WRITE_KILL_PRIV flag
* - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
* - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
*
* 7.32
* - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
*
* 7.33
* - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
* - add FUSE_OPEN_KILL_SUIDGID
* - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
* - add FUSE_SETXATTR_ACL_KILL_SGID
*
* 7.34
* - add FUSE_SYNCFS
*
* 7.35
* - add FOPEN_NOFLUSH
*
* 7.36
* - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
* - add flags2 to fuse_init_in and fuse_init_out
* - add FUSE_SECURITY_CTX init flag
* - add security context to create, mkdir, symlink, and mknod requests
* - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
*
* 7.37
* - add FUSE_TMPFILE
*/
#ifndef _LINUX_FUSE_H
@@ -207,7 +232,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 31
#define FUSE_KERNEL_MINOR_VERSION 37
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -231,7 +256,7 @@ struct fuse_attr {
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t padding;
uint32_t flags;
};
struct fuse_kstatfs {
@@ -257,17 +282,18 @@ struct fuse_file_lock {
/**
* Bitmasks for fuse_setattr_in.valid
*/
#define FATTR_MODE (1 << 0)
#define FATTR_UID (1 << 1)
#define FATTR_GID (1 << 2)
#define FATTR_SIZE (1 << 3)
#define FATTR_ATIME (1 << 4)
#define FATTR_MTIME (1 << 5)
#define FATTR_FH (1 << 6)
#define FATTR_ATIME_NOW (1 << 7)
#define FATTR_MTIME_NOW (1 << 8)
#define FATTR_LOCKOWNER (1 << 9)
#define FATTR_CTIME (1 << 10)
#define FATTR_MODE (1 << 0)
#define FATTR_UID (1 << 1)
#define FATTR_GID (1 << 2)
#define FATTR_SIZE (1 << 3)
#define FATTR_ATIME (1 << 4)
#define FATTR_MTIME (1 << 5)
#define FATTR_FH (1 << 6)
#define FATTR_ATIME_NOW (1 << 7)
#define FATTR_MTIME_NOW (1 << 8)
#define FATTR_LOCKOWNER (1 << 9)
#define FATTR_CTIME (1 << 10)
#define FATTR_KILL_SUIDGID (1 << 11)
/**
* Flags returned by the OPEN request
@@ -277,12 +303,14 @@ struct fuse_file_lock {
* FOPEN_NONSEEKABLE: the file is not seekable
* FOPEN_CACHE_DIR: allow caching this directory
* FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
*/
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
#define FOPEN_NONSEEKABLE (1 << 2)
#define FOPEN_CACHE_DIR (1 << 3)
#define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5)
/**
* INIT request/reply flags
@@ -313,7 +341,21 @@ struct fuse_file_lock {
* FUSE_CACHE_SYMLINKS: cache READLINK responses
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
* FUSE_MAP_ALIGNMENT: map_alignment field is valid
* FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for
* foffset and moffset fields in struct
* fuse_setupmapping_out and fuse_removemapping_one.
* FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
* FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc.
* Upon write/truncate suid/sgid is only killed if caller
* does not have CAP_FSETID. Additionally upon
* write/truncate sgid is killed only if file has group
* execute permission. (Same as Linux VFS behavior).
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
* FUSE_INIT_EXT: extended fuse_init_in request
* FUSE_INIT_RESERVED: reserved, do not use
* FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
* mknod
* FUSE_HAS_INODE_DAX: use per inode DAX
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -342,6 +384,14 @@ struct fuse_file_lock {
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
#define FUSE_MAP_ALIGNMENT (1 << 26)
#define FUSE_SUBMOUNTS (1 << 27)
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
#define FUSE_SETXATTR_EXT (1 << 29)
#define FUSE_INIT_EXT (1 << 30)
#define FUSE_INIT_RESERVED (1 << 31)
/* bits 32..63 get shifted down 32 bits into the flags2 field */
#define FUSE_SECURITY_CTX (1ULL << 32)
#define FUSE_HAS_INODE_DAX (1ULL << 33)
/**
* CUSE INIT request/reply flags
@@ -371,11 +421,14 @@ struct fuse_file_lock {
*
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
* FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
* FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits
*/
#define FUSE_WRITE_CACHE (1 << 0)
#define FUSE_WRITE_LOCKOWNER (1 << 1)
#define FUSE_WRITE_KILL_PRIV (1 << 2)
#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
/* Obsolete alias; this flag implies killing suid/sgid only. */
#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
/**
* Read flags
@@ -417,6 +470,27 @@ struct fuse_file_lock {
*/
#define FUSE_FSYNC_FDATASYNC (1 << 0)
/**
* fuse_attr flags
*
* FUSE_ATTR_SUBMOUNT: Object is a submount root
* FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
*/
#define FUSE_ATTR_SUBMOUNT (1 << 0)
#define FUSE_ATTR_DAX (1 << 1)
/**
* Open flags
* FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable
*/
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
/**
* setxattr flags
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
*/
#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -465,6 +539,8 @@ enum fuse_opcode {
FUSE_COPY_FILE_RANGE = 47,
FUSE_SETUPMAPPING = 48,
FUSE_REMOVEMAPPING = 49,
FUSE_SYNCFS = 50,
FUSE_TMPFILE = 51,
/* CUSE specific operations */
CUSE_INIT = 4096,
@@ -578,14 +654,14 @@ struct fuse_setattr_in {
struct fuse_open_in {
uint32_t flags;
uint32_t unused;
uint32_t open_flags; /* FUSE_OPEN_... */
};
struct fuse_create_in {
uint32_t flags;
uint32_t mode;
uint32_t umask;
uint32_t padding;
uint32_t open_flags; /* FUSE_OPEN_... */
};
struct fuse_open_out {
@@ -647,9 +723,13 @@ struct fuse_fsync_in {
uint32_t padding;
};
#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
struct fuse_setxattr_in {
uint32_t size;
uint32_t flags;
uint32_t setxattr_flags;
uint32_t padding;
};
struct fuse_getxattr_in {
@@ -684,6 +764,8 @@ struct fuse_init_in {
uint32_t minor;
uint32_t max_readahead;
uint32_t flags;
uint32_t flags2;
uint32_t unused[11];
};
#define FUSE_COMPAT_INIT_OUT_SIZE 8
@@ -700,7 +782,8 @@ struct fuse_init_out {
uint32_t time_gran;
uint16_t max_pages;
uint16_t map_alignment;
uint32_t unused[8];
uint32_t flags2;
uint32_t unused[7];
};
#define CUSE_INIT_INFO_MAX 4096
@@ -808,9 +891,12 @@ struct fuse_dirent {
char name[];
};
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
#define FUSE_DIRENT_ALIGN(x) \
/* Align variable length records to 64bit boundary */
#define FUSE_REC_ALIGN(x) \
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
#define FUSE_DIRENT_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
@@ -869,7 +955,8 @@ struct fuse_notify_retrieve_in {
};
/* Device ioctls: */
#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
#define FUSE_DEV_IOC_MAGIC 229
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
struct fuse_lseek_in {
uint64_t fh;
@@ -892,4 +979,60 @@ struct fuse_copy_file_range_in {
uint64_t flags;
};
#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
struct fuse_setupmapping_in {
/* An already open handle */
uint64_t fh;
/* Offset into the file to start the mapping */
uint64_t foffset;
/* Length of mapping required */
uint64_t len;
/* Flags, FUSE_SETUPMAPPING_FLAG_* */
uint64_t flags;
/* Offset in Memory Window */
uint64_t moffset;
};
struct fuse_removemapping_in {
/* number of fuse_removemapping_one follows */
uint32_t count;
};
struct fuse_removemapping_one {
/* Offset into the dax window start the unmapping */
uint64_t moffset;
/* Length of mapping required */
uint64_t len;
};
#define FUSE_REMOVEMAPPING_MAX_ENTRY \
(PAGE_SIZE / sizeof(struct fuse_removemapping_one))
struct fuse_syncfs_in {
uint64_t padding;
};
/*
* For each security context, send fuse_secctx with size of security context
* fuse_secctx will be followed by security context name and this in turn
* will be followed by actual context label.
* fuse_secctx, name, context
*/
struct fuse_secctx {
uint32_t size;
uint32_t padding;
};
/*
* Contains the information about how many fuse_secctx structures are being
* sent and what's the total size of all security contexts (including
* size of fuse_secctx_header).
*
*/
struct fuse_secctx_header {
uint32_t size;
uint32_t nr_secctx;
};
#endif /* _LINUX_FUSE_H */
File diff suppressed because it is too large Load Diff
-155
View File
@@ -1,155 +0,0 @@
/*
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.
*/
/* these definitions provide source compatibility to prior versions.
Do not include this file directly! */
struct fuse_lowlevel_ops_compat25 {
void (*init) (void *userdata);
void (*destroy) (void *userdata);
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
void (*getattr) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int to_set, struct fuse_file_info *fi);
void (*readlink) (fuse_req_t req, fuse_ino_t ino);
void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, dev_t rdev);
void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode);
void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
const char *name);
void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname);
void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
const char *newname);
void (*open) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
size_t size, off_t off, struct fuse_file_info *fi);
void (*flush) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*release) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi);
void (*opendir) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi);
void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi);
void (*statfs) (fuse_req_t req);
void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags);
void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size);
void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi);
};
struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
const struct fuse_lowlevel_ops_compat25 *op,
size_t op_size, void *userdata);
size_t fuse_dirent_size(size_t namelen);
char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
off_t off);
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
#include <sys/statfs.h>
struct fuse_lowlevel_ops_compat {
void (*init) (void *userdata);
void (*destroy) (void *userdata);
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
void (*getattr) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int to_set, struct fuse_file_info_compat *fi);
void (*readlink) (fuse_req_t req, fuse_ino_t ino);
void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, dev_t rdev);
void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode);
void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
const char *name);
void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname);
void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
const char *newname);
void (*open) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info_compat *fi);
void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
size_t size, off_t off, struct fuse_file_info_compat *fi);
void (*flush) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*release) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info_compat *fi);
void (*opendir) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info_compat *fi);
void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info_compat *fi);
void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info_compat *fi);
void (*statfs) (fuse_req_t req);
void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags);
void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size);
void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info_compat *fi);
};
int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf);
int fuse_reply_open_compat(fuse_req_t req,
const struct fuse_file_info_compat *fi);
struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
const struct fuse_lowlevel_ops_compat *op,
size_t op_size, void *userdata);
#endif /* __FreeBSD__ || __NetBSD__ */
struct fuse_chan_ops_compat24 {
int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
int (*send)(struct fuse_chan *ch, const struct iovec iov[],
size_t count);
void (*destroy)(struct fuse_chan *ch);
};
struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
int fd, size_t bufsize, void *data);
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
struct fuse_chan *fuse_kern_chan_new(int fd);
+28 -28
View File
@@ -9,14 +9,14 @@
#ifndef _FUSE_OPT_H_
#define _FUSE_OPT_H_
#include "extern_c.h"
/** @file
*
* This file defines the option parsing interface of FUSE
*/
#ifdef __cplusplus
extern "C" {
#endif
EXTERN_C_BEGIN
/**
* Option description
@@ -73,21 +73,22 @@ extern "C" {
* If the format is "%s", memory is allocated for the string unlike
* with scanf().
*/
struct fuse_opt {
/** Matching template and optional parameter formatting */
const char *templ;
struct fuse_opt
{
/** Matching template and optional parameter formatting */
const char *templ;
/**
* Offset of variable within 'data' parameter of fuse_opt_parse()
* or -1
*/
unsigned long offset;
/**
* Offset of variable within 'data' parameter of fuse_opt_parse()
* or -1
*/
unsigned long offset;
/**
* Value to set the variable to, or to be passed as 'key' to the
* processing function. Ignored if template has a format
*/
int value;
/**
* Value to set the variable to, or to be passed as 'key' to the
* processing function. Ignored if template has a format
*/
int value;
};
/**
@@ -105,15 +106,16 @@ struct fuse_opt {
/**
* Argument list
*/
struct fuse_args {
/** Argument count */
int argc;
struct fuse_args
{
/** Argument count */
int argc;
/** Argument vector. NULL terminated */
char **argv;
/** Argument vector. NULL terminated */
char **argv;
/** Is 'argv' allocated? */
int allocated;
/** Is 'argv' allocated? */
int allocated;
};
/**
@@ -177,7 +179,7 @@ struct fuse_args {
* @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
*/
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
struct fuse_args *outargs);
struct fuse_args *outargs);
/**
* Option parsing function
@@ -200,7 +202,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
* @return -1 on error, 0 on success
*/
int fuse_opt_parse(struct fuse_args *args, void *data,
const struct fuse_opt opts[], fuse_opt_proc_t proc);
const struct fuse_opt opts[], fuse_opt_proc_t proc);
/**
* Add an option to a comma separated option list
@@ -263,8 +265,6 @@ void fuse_opt_free_args(struct fuse_args *args);
*/
int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
#ifdef __cplusplus
}
#endif
EXTERN_C_END
#endif /* _FUSE_OPT_H_ */
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
struct fuse_chan;
struct fuse_ll;
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
struct fuse_pollhandle_t
{
uint64_t kh;
struct fuse_chan *ch;
struct fuse_ll *f;
};
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
typedef struct fuse_timeouts_s fuse_timeouts_t;
struct fuse_timeouts_s
{
uint64_t entry;
uint64_t attr;
};
+4
View File
@@ -59,6 +59,10 @@
#define kv_pop(v) ((v).a[--(v).n])
#define kv_size(v) ((v).n)
#define kv_max(v) ((v).m)
#define kv_first(v) (kv_A(v,0))
#define kv_last(v) (kv_A(v,kv_size(v)-1))
#define kv_end(v) (kv_A(v,kv_size(v)))
#define kv_delete(v,i) (kv_A(v,i) = kv_pop(v))
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
-9
View File
@@ -1,9 +0,0 @@
/*
This header is for compatibility with older software using FUSE.
Please use 'pkg-config --cflags fuse' to set include path. The
correct usage is still '#include <fuse.h>', not '#include
<fuse/fuse.h>'.
*/
#include "fuse/fuse.h"
-24
View File
@@ -1,24 +0,0 @@
/*
libulockmgr: Userspace Lock Manager Library
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
*/
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h>
/**
* Perform POSIX locking operation
*
* @param fd the file descriptor
* @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
* @param lock the lock parameters
* @param owner the lock owner ID cookie
* @param owner_len length of the lock owner ID cookie
* @return 0 on success -errno on error
*/
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
size_t owner_len);
-42
View File
@@ -1,42 +0,0 @@
## Process this file with automake to produce Makefile.in
AUTOMAKE_OPTIONS = subdir-objects
AM_CPPFLAGS = -I$(top_srcdir)/include -DFUSERMOUNT_DIR=\"$(bindir)\" \
-D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=26
lib_LTLIBRARIES = libfuse.la libulockmgr.la
if BSD
mount_source = mount_bsd.c
else
mount_source = mount.c mount_util.c mount_util.h
endif
libfuse_la_SOURCES = \
fuse.c \
fuse_i.h \
fuse_kern_chan.c \
fuse_loop.c \
fuse_loop_mt.c \
fuse_lowlevel.c \
fuse_misc.h \
fuse_mt.c \
fuse_opt.c \
fuse_session.c \
fuse_signals.c \
buffer.c \
cuse_lowlevel.c \
helper.c \
$(mount_source)
libfuse_la_LDFLAGS = -pthread @libfuse_libs@ -version-number 2:9:7 \
-Wl,--version-script,$(srcdir)/fuse_versionscript
if NETBSD
libfuse_la_LIBADD = -lperfuse -lpuffs
endif
libulockmgr_la_SOURCES = ulockmgr.c
libulockmgr_la_LDFLAGS = -pthread -version-number 1:0:1
EXTRA_DIST = fuse_versionscript
+201 -202
View File
@@ -18,140 +18,140 @@
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
{
size_t i;
size_t size = 0;
size_t i;
size_t size = 0;
for (i = 0; i < bufv->count; i++) {
if (bufv->buf[i].size == SIZE_MAX)
size = SIZE_MAX;
else
size += bufv->buf[i].size;
}
for (i = 0; i < bufv->count; i++) {
if (bufv->buf[i].size == SIZE_MAX)
size = SIZE_MAX;
else
size += bufv->buf[i].size;
}
return size;
return size;
}
static size_t min_size(size_t s1, size_t s2)
{
return s1 < s2 ? s1 : s2;
return s1 < s2 ? s1 : s2;
}
static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (dst->flags & FUSE_BUF_FD_SEEK) {
res = pwrite(dst->fd, src->mem + src_off, len,
dst->pos + dst_off);
} else {
res = write(dst->fd, src->mem + src_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
while (len) {
if (dst->flags & FUSE_BUF_FD_SEEK) {
res = pwrite(dst->fd, src->mem + src_off, len,
dst->pos + dst_off);
} else {
res = write(dst->fd, src->mem + src_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(dst->flags & FUSE_BUF_FD_RETRY))
break;
copied += res;
if (!(dst->flags & FUSE_BUF_FD_RETRY))
break;
src_off += res;
dst_off += res;
len -= res;
}
src_off += res;
dst_off += res;
len -= res;
}
return copied;
return copied;
}
static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (src->flags & FUSE_BUF_FD_SEEK) {
res = pread(src->fd, dst->mem + dst_off, len,
src->pos + src_off);
} else {
res = read(src->fd, dst->mem + dst_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
while (len) {
if (src->flags & FUSE_BUF_FD_SEEK) {
res = pread(src->fd, dst->mem + dst_off, len,
src->pos + src_off);
} else {
res = read(src->fd, dst->mem + dst_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY))
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY))
break;
dst_off += res;
src_off += res;
len -= res;
}
dst_off += res;
src_off += res;
len -= res;
}
return copied;
return copied;
}
static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
char buf[4096];
struct fuse_buf tmp = {
.size = sizeof(buf),
.flags = 0,
};
ssize_t res;
size_t copied = 0;
char buf[4096];
struct fuse_buf tmp = {
.size = sizeof(buf),
.flags = 0,
};
ssize_t res;
size_t copied = 0;
tmp.mem = buf;
tmp.mem = buf;
while (len) {
size_t this_len = min_size(tmp.size, len);
size_t read_len;
while (len) {
size_t this_len = min_size(tmp.size, len);
size_t read_len;
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
read_len = res;
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
read_len = res;
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
copied += res;
copied += res;
if (res < this_len)
break;
if (res < this_len)
break;
dst_off += res;
src_off += res;
len -= res;
}
dst_off += res;
src_off += res;
len -= res;
}
return copied;
return copied;
}
#ifdef HAVE_SPLICE
@@ -159,64 +159,63 @@ static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int splice_flags = 0;
off_t *srcpos = NULL;
off_t *dstpos = NULL;
off_t srcpos_val;
off_t dstpos_val;
ssize_t res;
size_t copied = 0;
int splice_flags = 0;
off_t *srcpos = NULL;
off_t *dstpos = NULL;
off_t srcpos_val;
off_t dstpos_val;
ssize_t res;
size_t copied = 0;
if (flags & FUSE_BUF_SPLICE_MOVE)
splice_flags |= SPLICE_F_MOVE;
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
splice_flags |= SPLICE_F_NONBLOCK;
if (flags & FUSE_BUF_SPLICE_MOVE)
splice_flags |= SPLICE_F_MOVE;
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
splice_flags |= SPLICE_F_NONBLOCK;
if (src->flags & FUSE_BUF_FD_SEEK) {
srcpos_val = src->pos + src_off;
srcpos = &srcpos_val;
}
if (dst->flags & FUSE_BUF_FD_SEEK) {
dstpos_val = dst->pos + dst_off;
dstpos = &dstpos_val;
}
if (src->flags & FUSE_BUF_FD_SEEK) {
srcpos_val = src->pos + src_off;
srcpos = &srcpos_val;
}
if (dst->flags & FUSE_BUF_FD_SEEK) {
dstpos_val = dst->pos + dst_off;
dstpos = &dstpos_val;
}
while (len) {
res = splice(src->fd, srcpos, dst->fd, dstpos, len,
splice_flags);
if (res == -1) {
if (copied)
break;
while (len) {
res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags);
if (res == -1) {
if (copied)
break;
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
return -errno;
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
return -errno;
/* Maybe splice is not supported for this combination */
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
len);
}
if (res == 0)
break;
/* Maybe splice is not supported for this combination */
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
len);
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
!(dst->flags & FUSE_BUF_FD_RETRY)) {
break;
}
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
!(dst->flags & FUSE_BUF_FD_RETRY)) {
break;
}
len -= res;
}
len -= res;
}
return copied;
return copied;
}
#else
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
(void) flags;
(void) flags;
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
}
#endif
@@ -225,94 +224,94 @@ static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
if (!src_is_fd && !dst_is_fd) {
char *dstmem = dst->mem + dst_off;
char *srcmem = src->mem + src_off;
if (!src_is_fd && !dst_is_fd) {
char *dstmem = dst->mem + dst_off;
char *srcmem = src->mem + src_off;
if (dstmem != srcmem) {
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
memcpy(dstmem, srcmem, len);
else
memmove(dstmem, srcmem, len);
}
if (dstmem != srcmem) {
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
memcpy(dstmem, srcmem, len);
else
memmove(dstmem, srcmem, len);
}
return len;
} else if (!src_is_fd) {
return fuse_buf_write(dst, dst_off, src, src_off, len);
} else if (!dst_is_fd) {
return fuse_buf_read(dst, dst_off, src, src_off, len);
} else if (flags & FUSE_BUF_NO_SPLICE) {
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
} else {
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
}
return len;
} else if (!src_is_fd) {
return fuse_buf_write(dst, dst_off, src, src_off, len);
} else if (!dst_is_fd) {
return fuse_buf_read(dst, dst_off, src, src_off, len);
} else if (flags & FUSE_BUF_NO_SPLICE) {
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
} else {
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
}
}
static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
{
if (bufv->idx < bufv->count)
return &bufv->buf[bufv->idx];
else
return NULL;
if (bufv->idx < bufv->count)
return &bufv->buf[bufv->idx];
else
return NULL;
}
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
{
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
bufv->off += len;
assert(bufv->off <= buf->size);
if (bufv->off == buf->size) {
assert(bufv->idx < bufv->count);
bufv->idx++;
if (bufv->idx == bufv->count)
return 0;
bufv->off = 0;
}
return 1;
bufv->off += len;
assert(bufv->off <= buf->size);
if (bufv->off == buf->size) {
assert(bufv->idx < bufv->count);
bufv->idx++;
if (bufv->idx == bufv->count)
return 0;
bufv->off = 0;
}
return 1;
}
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
enum fuse_buf_copy_flags flags)
{
size_t copied = 0;
size_t copied = 0;
if (dstv == srcv)
return fuse_buf_size(dstv);
if (dstv == srcv)
return fuse_buf_size(dstv);
for (;;) {
const struct fuse_buf *src = fuse_bufvec_current(srcv);
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
size_t src_len;
size_t dst_len;
size_t len;
ssize_t res;
for (;;) {
const struct fuse_buf *src = fuse_bufvec_current(srcv);
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
size_t src_len;
size_t dst_len;
size_t len;
ssize_t res;
if (src == NULL || dst == NULL)
break;
if (src == NULL || dst == NULL)
break;
src_len = src->size - srcv->off;
dst_len = dst->size - dstv->off;
len = min_size(src_len, dst_len);
src_len = src->size - srcv->off;
dst_len = dst->size - dstv->off;
len = min_size(src_len, dst_len);
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
if (res < 0) {
if (!copied)
return res;
break;
}
copied += res;
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
if (res < 0) {
if (!copied)
return res;
break;
}
copied += res;
if (!fuse_bufvec_advance(srcv, res) ||
!fuse_bufvec_advance(dstv, res))
break;
if (!fuse_bufvec_advance(srcv, res) ||
!fuse_bufvec_advance(dstv, res))
break;
if (res < len)
break;
}
if (res < len)
break;
}
return copied;
return copied;
}
+112
View File
@@ -0,0 +1,112 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#include "crc32b.h"
static
const
crc32b_t CRC32BTABLE[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
crc32b_t
crc32b_start(void)
{
return 0xFFFFFFFF;
}
crc32b_t
crc32b_continue(const void *buf_,
const crc32b_t len_,
const crc32b_t crc_)
{
int i;
char *buf;
crc32b_t crc;
crc = crc_;
buf = (char*)buf_;
for(i = 0; i < len_; i++)
{
crc = (CRC32BTABLE[(crc ^ buf[i]) & 0xFFL] ^ (crc >> 8));
}
return crc;
}
crc32b_t
crc32b_finish(const crc32b_t crc_)
{
return (crc_ ^ 0xFFFFFFFF);
}
crc32b_t
crc32b(const void *buf_,
const crc32b_t len_)
{
crc32b_t crc;
crc = crc32b_start();
crc = crc32b_continue(buf_,len_,crc);
crc = crc32b_finish(crc);
return crc;
}
+32
View File
@@ -0,0 +1,32 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#ifndef CRC32B_H_INCLUDED
#define CRC32B_H_INCLUDED
typedef unsigned int crc32b_t;
crc32b_t crc32b_start(void);
crc32b_t crc32b_continue(const void *buf,
const crc32b_t len,
const crc32b_t crc);
crc32b_t crc32b_finish(const crc32b_t crc);
crc32b_t crc32b(const void *buf, crc32b_t len);
#endif
-371
View File
@@ -1,371 +0,0 @@
/*
CUSE: Character device in Userspace
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
*/
#include "cuse_lowlevel.h"
#include "fuse_kernel.h"
#include "fuse_i.h"
#include "fuse_opt.h"
#include "fuse_misc.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <unistd.h>
struct cuse_data {
struct cuse_lowlevel_ops clop;
unsigned max_read;
unsigned dev_major;
unsigned dev_minor;
unsigned flags;
unsigned dev_info_len;
char dev_info[];
};
static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
{
return &req->f->cuse_data->clop;
}
static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->open(req, fi);
}
static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->read(req, size, off, fi);
}
static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
size_t size, off_t off, struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->write(req, buf, size, off, fi);
}
static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->flush(req, fi);
}
static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->release(req, fi);
}
static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
(void)ino;
req_clop(req)->fsync(req, datasync, fi);
}
static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
(void)ino;
req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
out_bufsz);
}
static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi, struct fuse_pollhandle *ph)
{
(void)ino;
req_clop(req)->poll(req, fi, ph);
}
static size_t cuse_pack_info(int argc, const char **argv, char *buf)
{
size_t size = 0;
int i;
for (i = 0; i < argc; i++) {
size_t len;
len = strlen(argv[i]) + 1;
size += len;
if (buf) {
memcpy(buf, argv[i], len);
buf += len;
}
}
return size;
}
static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop)
{
struct cuse_data *cd;
size_t dev_info_len;
dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
NULL);
if (dev_info_len > CUSE_INIT_INFO_MAX) {
fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
dev_info_len, CUSE_INIT_INFO_MAX);
return NULL;
}
cd = calloc(1, sizeof(*cd) + dev_info_len);
if (!cd) {
fprintf(stderr, "cuse: failed to allocate cuse_data\n");
return NULL;
}
memcpy(&cd->clop, clop, sizeof(cd->clop));
cd->max_read = 131072;
cd->dev_major = ci->dev_major;
cd->dev_minor = ci->dev_minor;
cd->dev_info_len = dev_info_len;
cd->flags = ci->flags;
cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
return cd;
}
struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop,
void *userdata)
{
struct fuse_lowlevel_ops lop;
struct cuse_data *cd;
struct fuse_session *se;
struct fuse_ll *ll;
cd = cuse_prep_data(ci, clop);
if (!cd)
return NULL;
memset(&lop, 0, sizeof(lop));
lop.init = clop->init;
lop.destroy = clop->destroy;
lop.open = clop->open ? cuse_fll_open : NULL;
lop.read = clop->read ? cuse_fll_read : NULL;
lop.write = clop->write ? cuse_fll_write : NULL;
lop.flush = clop->flush ? cuse_fll_flush : NULL;
lop.release = clop->release ? cuse_fll_release : NULL;
lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
lop.poll = clop->poll ? cuse_fll_poll : NULL;
se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
if (!se) {
free(cd);
return NULL;
}
ll = se->data;
ll->cuse_data = cd;
return se;
}
static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
char *dev_info, unsigned dev_info_len)
{
struct iovec iov[3];
iov[1].iov_base = arg;
iov[1].iov_len = sizeof(struct cuse_init_out);
iov[2].iov_base = dev_info;
iov[2].iov_len = dev_info_len;
return fuse_send_reply_iov_nofree(req, 0, iov, 3);
}
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
struct cuse_init_out outarg;
struct fuse_ll *f = req->f;
struct cuse_data *cd = f->cuse_data;
size_t bufsize = fuse_chan_bufsize(req->ch);
struct cuse_lowlevel_ops *clop = req_clop(req);
(void) nodeid;
if (f->debug) {
fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
fprintf(stderr, "flags=0x%08x\n", arg->flags);
}
f->conn.proto_major = arg->major;
f->conn.proto_minor = arg->minor;
f->conn.capable = 0;
f->conn.want = 0;
if (arg->major < 7) {
fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
arg->major, arg->minor);
fuse_reply_err(req, EPROTO);
return;
}
if (bufsize < FUSE_MIN_READ_BUFFER) {
fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
bufsize);
bufsize = FUSE_MIN_READ_BUFFER;
}
bufsize -= 4096;
if (bufsize < f->conn.max_write)
f->conn.max_write = bufsize;
f->got_init = 1;
if (f->op.init)
f->op.init(f->userdata, &f->conn);
memset(&outarg, 0, sizeof(outarg));
outarg.major = FUSE_KERNEL_VERSION;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
outarg.flags = cd->flags;
outarg.max_read = cd->max_read;
outarg.max_write = f->conn.max_write;
outarg.dev_major = cd->dev_major;
outarg.dev_minor = cd->dev_minor;
if (f->debug) {
fprintf(stderr, " CUSE_INIT: %u.%u\n",
outarg.major, outarg.minor);
fprintf(stderr, " flags=0x%08x\n", outarg.flags);
fprintf(stderr, " max_read=0x%08x\n", outarg.max_read);
fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
fprintf(stderr, " dev_major=%u\n", outarg.dev_major);
fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor);
fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len,
cd->dev_info);
}
cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
if (clop->init_done)
clop->init_done(f->userdata);
fuse_free_req(req);
}
struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop,
int *multithreaded, void *userdata)
{
const char *devname = "/dev/cuse";
static const struct fuse_opt kill_subtype_opts[] = {
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_END
};
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_chan *ch;
int fd;
int foreground;
int res;
res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
if (res == -1)
goto err_args;
res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
if (res == -1)
goto err_args;
/*
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
* would ensue.
*/
do {
fd = open("/dev/null", O_RDWR);
if (fd > 2)
close(fd);
} while (fd >= 0 && fd <= 2);
se = cuse_lowlevel_new(&args, ci, clop, userdata);
fuse_opt_free_args(&args);
if (se == NULL)
goto err_args;
fd = open(devname, O_RDWR);
if (fd == -1) {
if (errno == ENODEV || errno == ENOENT)
fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
else
fprintf(stderr, "cuse: failed to open %s: %s\n",
devname, strerror(errno));
goto err_se;
}
ch = fuse_kern_chan_new(fd);
if (!ch) {
close(fd);
goto err_se;
}
fuse_session_add_chan(se, ch);
res = fuse_set_signal_handlers(se);
if (res == -1)
goto err_se;
res = fuse_daemonize(foreground);
if (res == -1)
goto err_sig;
return se;
err_sig:
fuse_remove_signal_handlers(se);
err_se:
fuse_session_destroy(se);
err_args:
fuse_opt_free_args(&args);
return NULL;
}
void cuse_lowlevel_teardown(struct fuse_session *se)
{
fuse_remove_signal_handlers(se);
fuse_session_destroy(se);
}
int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
const struct cuse_lowlevel_ops *clop, void *userdata)
{
struct fuse_session *se;
int multithreaded;
int res;
se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
userdata);
if (se == NULL)
return 1;
if (multithreaded)
res = fuse_session_loop_mt(se, 0);
else
res = fuse_session_loop(se);
cuse_lowlevel_teardown(se);
if (res == -1)
return 1;
return 0;
}
+1278
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
/*
ISC License
Copyright (c) 2021, 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 "fuse_kernel.h"
void debug_fuse_open_out(const uint64_t unique,
const struct fuse_open_out *arg,
const uint64_t argsize);
void debug_fuse_init_in(const struct fuse_init_in *arg);
void debug_fuse_init_out(const uint64_t unique,
const struct fuse_init_out *arg,
const uint64_t argsize);
void debug_fuse_entry_out(const uint64_t unique,
const struct fuse_entry_out *arg,
const uint64_t argsize);
void debug_fuse_attr_out(const uint64_t unique,
const struct fuse_attr_out *arg,
const uint64_t argsize);
void debug_fuse_entry_open_out(const uint64_t unique,
const struct fuse_entry_out *earg,
const struct fuse_open_out *oarg);
void debug_fuse_readlink(const uint64_t unique,
const char *linkname);
void debug_fuse_write_out(const uint64_t unique,
const struct fuse_write_out *arg);
void debug_fuse_statfs_out(const uint64_t unique,
const struct fuse_statfs_out *arg);
void debug_fuse_getxattr_out(const uint64_t unique,
const struct fuse_getxattr_out *arg);
void debug_fuse_lk_out(const uint64_t unique,
const struct fuse_lk_out *arg);
void debug_fuse_bmap_out(const uint64_t unique,
const struct fuse_bmap_out *arg);
void debug_fuse_in_header(const struct fuse_in_header *hdr);
+380
View File
@@ -0,0 +1,380 @@
/*
ISC License
Copyright (c) 2021, 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 "kvec.h"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#define ROUND_UP(N,S) ((((N) + (S) - 1) / (S)) * (S))
typedef kvec_t(void*) slab_kvec_t;
typedef struct mem_stack_t mem_stack_t;
struct mem_stack_t
{
mem_stack_t *next;
};
typedef struct fmp_t fmp_t;
struct fmp_t
{
mem_stack_t *objs;
slab_kvec_t slabs;
uint64_t avail_objs;
uint64_t obj_size;
uint64_t page_size;
uint64_t slab_size;
};
static
inline
uint64_t
fmp_page_size()
{
return sysconf(_SC_PAGESIZE);
}
static
inline
void
fmp_init(fmp_t *fmp_,
const uint64_t obj_size_,
const uint64_t page_multiple_)
{
kv_init(fmp_->slabs);
fmp_->objs = NULL;
fmp_->avail_objs = 0;
fmp_->obj_size = ROUND_UP(obj_size_,sizeof(void*));
fmp_->page_size = fmp_page_size();
fmp_->slab_size = (fmp_->page_size * page_multiple_);
}
static
inline
uint64_t
fmp_slab_count(fmp_t *fmp_)
{
return kv_size(fmp_->slabs);
}
static
inline
void*
fmp_slab_alloc_posix_memalign(fmp_t *fmp_)
{
int rv;
void *mem;
const size_t alignment = fmp_->page_size;
const size_t size = fmp_->slab_size;
rv = posix_memalign(&mem,alignment,size);
if(rv != 0)
return NULL;
return NULL;
}
static
inline
void*
fmp_slab_alloc_mmap(fmp_t *fmp_)
{
void *mem;
void *address = NULL;
const size_t length = fmp_->slab_size;
const int protect = PROT_READ|PROT_WRITE;
const int flags = MAP_PRIVATE|MAP_ANONYMOUS;
const int filedes = -1;
const off_t offset = 0;
mem = mmap(address,length,protect,flags,filedes,offset);
if(mem == MAP_FAILED)
return NULL;
return mem;
}
static
inline
void
fmp_slab_free_posix_memalign(fmp_t* fmp_,
void *mem_)
{
(void)fmp_;
free(mem_);
}
static
inline
void
fmp_slab_free_mmap(fmp_t* fmp_,
void *mem_)
{
void *addr = mem_;
size_t length = fmp_->slab_size;
(void)munmap(addr,length);
}
static
inline
int
fmp_slab_alloc(fmp_t *fmp_)
{
char *i;
void *mem;
mem = fmp_slab_alloc_mmap(fmp_);
if(mem == NULL)
return -ENOMEM;
kv_push(void*,fmp_->slabs,mem);
i = ((char*)mem + fmp_->slab_size - fmp_->obj_size);
while(i >= (char*)mem)
{
mem_stack_t *obj = (mem_stack_t*)i;
obj->next = fmp_->objs;
fmp_->objs = obj;
fmp_->avail_objs++;
i -= fmp_->obj_size;
}
return 0;
}
static
inline
void*
fmp_alloc(fmp_t *fmp_)
{
void *rv;
if(fmp_->objs == NULL)
fmp_slab_alloc(fmp_);
if(fmp_->objs == NULL)
return NULL;
rv = fmp_->objs;
fmp_->objs = fmp_->objs->next;
fmp_->avail_objs--;
return rv;
}
static
inline
void*
fmp_calloc(fmp_t *fmp_)
{
void *obj;
obj = fmp_alloc(fmp_);
if(obj == NULL)
return NULL;
memset(obj,0,fmp_->obj_size);
return obj;
}
static
inline
void
fmp_free(fmp_t *fmp_,
void *obj_)
{
mem_stack_t *obj = (mem_stack_t*)obj_;
obj->next = fmp_->objs;
fmp_->objs = obj;
fmp_->avail_objs++;
}
static
inline
void
fmp_clear(fmp_t *fmp_)
{
while(kv_size(fmp_->slabs))
{
void *slab = kv_pop(fmp_->slabs);
fmp_slab_free_mmap(fmp_,slab);
}
fmp_->objs = NULL;
fmp_->avail_objs = 0;
}
static
inline
void
fmp_destroy(fmp_t *fmp_)
{
fmp_clear(fmp_);
kv_destroy(fmp_->slabs);
}
static
inline
uint64_t
fmp_avail_objs(fmp_t *fmp_)
{
return fmp_->avail_objs;
}
static
inline
uint64_t
fmp_objs_per_slab(fmp_t *fmp_)
{
return (fmp_->slab_size / fmp_->obj_size);
}
static
inline
uint64_t
fmp_objs_in_slab(fmp_t *fmp_,
void *slab_)
{
char *slab;
uint64_t objs_per_slab;
uint64_t objs_in_slab;
slab = (char*)slab_;
objs_in_slab = 0;
objs_per_slab = fmp_objs_per_slab(fmp_);
for(mem_stack_t *stack = fmp_->objs; stack != NULL; stack = stack->next)
{
char *obj = (char*)stack;
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
objs_in_slab++;
if(objs_in_slab >= objs_per_slab)
break;
}
return objs_in_slab;
}
static
inline
void
fmp_remove_objs_in_slab(fmp_t *fmp_,
void *slab_)
{
char *slab;
uint64_t objs_per_slab;
uint64_t objs_in_slab;
mem_stack_t **p;
p = &fmp_->objs;
slab = (char*)slab_;
objs_in_slab = 0;
objs_per_slab = fmp_objs_per_slab(fmp_);
while((*p) != NULL)
{
char *obj = (char*)*p;
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
{
objs_in_slab++;
*p = (*p)->next;
fmp_->avail_objs--;
if(objs_in_slab >= objs_per_slab)
break;
continue;
}
p = &(*p)->next;
}
}
static
inline
int
fmp_gc_slab(fmp_t *fmp_,
uint64_t slab_idx_)
{
char *slab;
uint64_t objs_in_slab;
uint64_t objs_per_slab;
slab_idx_ = (slab_idx_ % kv_size(fmp_->slabs));
slab = kv_A(fmp_->slabs,slab_idx_);
objs_per_slab = fmp_objs_per_slab(fmp_);
objs_in_slab = fmp_objs_in_slab(fmp_,slab);
if(objs_in_slab != objs_per_slab)
return 0;
fmp_remove_objs_in_slab(fmp_,slab);
kv_delete(fmp_->slabs,slab_idx_);
fmp_slab_free_mmap(fmp_,slab);
return 1;
}
static
inline
int
fmp_gc(fmp_t *fmp_)
{
uint64_t slab_idx;
slab_idx = rand();
return fmp_gc_slab(fmp_,slab_idx);
}
static
inline
double
fmp_slab_usage_ratio(fmp_t *fmp_)
{
double avail_objs;
double objs_per_slab;
double nums_of_slabs;
avail_objs = fmp_->avail_objs;
objs_per_slab = fmp_objs_per_slab(fmp_);
nums_of_slabs = kv_size(fmp_->slabs);
return (avail_objs / (objs_per_slab * nums_of_slabs));
}
static
inline
uint64_t
fmp_total_allocated_memory(fmp_t *fmp_)
{
return (fmp_->slab_size * kv_size(fmp_->slabs));
}
+3290 -3930
View File
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -19,6 +19,14 @@
/* 32KB - same as glibc getdents buffer size */
#define DEFAULT_SIZE (1024 * 32)
static
uint64_t
round_up(const uint64_t number_,
const uint64_t multiple_)
{
return (((number_ + multiple_ - 1) / multiple_) * multiple_);
}
static
uint64_t
align_uint64_t(uint64_t v_)
@@ -57,16 +65,15 @@ int
fuse_dirents_buf_resize(fuse_dirents_t *d_,
uint64_t size_)
{
void *p;
if((d_->data_len + size_) >= d_->buf_len)
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
{
p = realloc(d_->buf,(d_->buf_len * 2));
if(p == NULL)
return -errno;
uint64_t new_size;
d_->buf = p;
d_->buf_len *= 2;
new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE);
kv_resize(char,d_->data,new_size);
if(d_->data.a == NULL)
return -ENOMEM;
}
return 0;
@@ -87,9 +94,8 @@ fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
if(rv)
return NULL;
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
d_->data_len += size;
d = (fuse_dirent_t*)&kv_end(d_->data);
kv_size(d_->data) += size;
return d;
}
@@ -109,9 +115,8 @@ fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
if(rv)
return NULL;
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
d_->data_len += size;
d = (fuse_dirent_t*)&kv_end(d_->data);
kv_size(d_->data) += size;
return d;
}
@@ -170,8 +175,8 @@ fuse_dirent_find(fuse_dirents_t *d_,
if(d_->type != NORMAL)
return NULL;
cur = (fuse_dirent_t*)&d_->buf[0];
end = (fuse_dirent_t*)&d_->buf[d_->data_len];
cur = (fuse_dirent_t*)&kv_first(d_->data);
end = (fuse_dirent_t*)&kv_end(d_->data);
while(cur < end)
{
if(cur->ino == ino_)
@@ -193,8 +198,8 @@ fuse_direntplus_find(fuse_dirents_t *d_,
if(d_->type != PLUS)
return NULL;
cur = (fuse_direntplus_t*)&d_->buf[0];
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
cur = (fuse_direntplus_t*)&kv_first(d_->data);
end = (fuse_direntplus_t*)&kv_end(d_->data);
while(cur < end)
{
if(cur->dirent.ino == ino_)
@@ -251,7 +256,7 @@ fuse_dirents_add(fuse_dirents_t *d_,
return -ENOMEM;
d->off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,d_->data_len);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->ino = dirent_->d_ino;
d->namelen = namelen_;
d->type = dirent_->d_type;
@@ -285,7 +290,7 @@ fuse_dirents_add_plus(fuse_dirents_t *d_,
return -ENOMEM;
d->dirent.off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,d_->data_len);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->dirent.ino = dirent_->d_ino;
d->dirent.namelen = namelen_;
d->dirent.type = dirent_->d_type;
@@ -321,7 +326,7 @@ fuse_dirents_add_linux(fuse_dirents_t *d_,
return -ENOMEM;
d->off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,d_->data_len);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->ino = dirent_->ino;
d->namelen = namelen_;
d->type = dirent_->type;
@@ -355,7 +360,7 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
return -ENOMEM;
d->dirent.off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,d_->data_len);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->dirent.ino = dirent_->ino;
d->dirent.namelen = namelen_;
d->dirent.type = dirent_->type;
@@ -371,25 +376,21 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
void
fuse_dirents_reset(fuse_dirents_t *d_)
{
d_->data_len = 0;
d_->type = UNSET;
kv_size(d_->data) = 0;
kv_size(d_->offs) = 1;
}
int
fuse_dirents_init(fuse_dirents_t *d_)
{
void *buf;
d_->type = UNSET;
buf = calloc(DEFAULT_SIZE,1);
if(buf == NULL)
kv_init(d_->data);
kv_resize(char,d_->data,DEFAULT_SIZE);
if(d_->data.a == NULL)
return -ENOMEM;
d_->buf = buf;
d_->buf_len = DEFAULT_SIZE;
d_->data_len = 0;
d_->type = UNSET;
kv_init(d_->offs);
kv_resize(uint32_t,d_->offs,64);
kv_push(uint32_t,d_->offs,0);
@@ -400,11 +401,6 @@ fuse_dirents_init(fuse_dirents_t *d_)
void
fuse_dirents_free(fuse_dirents_t *d_)
{
d_->buf_len = 0;
d_->data_len = 0;
d_->type = UNSET;
kv_destroy(d_->data);
kv_destroy(d_->offs);
free(d_->buf);
}
+68 -80
View File
@@ -6,106 +6,97 @@
See the file COPYING.LIB
*/
#pragma once
#include "fuse.h"
#include "fuse_lowlevel.h"
#include "extern_c.h"
struct fuse_chan;
struct fuse_ll;
struct fuse_session {
struct fuse_session_ops op;
struct fuse_session
{
int (*receive_buf)(struct fuse_session *se,
struct fuse_buf *buf,
struct fuse_chan *ch);
int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
struct fuse_chan **chp);
void (*process_buf)(void *data,
const void *buf,
struct fuse_chan *ch);
void (*process_buf)(void *data, const struct fuse_buf *buf,
struct fuse_chan *ch);
void (*destroy)(void *data);
void *data;
volatile int exited;
struct fuse_chan *ch;
void *data;
volatile int exited;
struct fuse_chan *ch;
};
struct fuse_req {
struct fuse_ll *f;
uint64_t unique;
int ctr;
pthread_mutex_t lock;
struct fuse_ctx ctx;
struct fuse_chan *ch;
int interrupted;
unsigned int ioctl_64bit : 1;
union {
struct {
uint64_t unique;
} i;
struct {
fuse_interrupt_func_t func;
void *data;
} ni;
} u;
struct fuse_req *next;
struct fuse_req *prev;
struct fuse_req
{
struct fuse_ll *f;
uint64_t unique;
struct fuse_ctx ctx;
struct fuse_chan *ch;
unsigned int ioctl_64bit : 1;
};
struct fuse_notify_req {
uint64_t unique;
void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
const void *, const struct fuse_buf *);
struct fuse_notify_req *next;
struct fuse_notify_req *prev;
struct fuse_notify_req
{
uint64_t unique;
void (*reply)(struct fuse_notify_req *,
fuse_req_t,
uint64_t,
const void *);
struct fuse_notify_req *next;
struct fuse_notify_req *prev;
};
struct fuse_ll {
int debug;
int allow_root;
int atomic_o_trunc;
int no_remote_posix_lock;
int no_remote_flock;
int big_writes;
int splice_write;
int splice_move;
int splice_read;
int no_splice_write;
int no_splice_move;
int no_splice_read;
struct fuse_lowlevel_ops op;
int got_init;
struct cuse_data *cuse_data;
void *userdata;
uid_t owner;
struct fuse_conn_info conn;
struct fuse_req list;
struct fuse_req interrupts;
pthread_mutex_t lock;
int got_destroy;
pthread_key_t pipe_key;
int broken_splice_nonblock;
uint64_t notify_ctr;
struct fuse_notify_req notify_list;
struct fuse_ll
{
int debug;
int no_remote_posix_lock;
int no_remote_flock;
int big_writes;
int splice_write;
int splice_move;
int splice_read;
int no_splice_write;
int no_splice_move;
int no_splice_read;
struct fuse_lowlevel_ops op;
int got_init;
void *userdata;
uid_t owner;
struct fuse_conn_info conn;
pthread_mutex_t lock;
int got_destroy;
pthread_key_t pipe_key;
int broken_splice_nonblock;
uint64_t notify_ctr;
struct fuse_notify_req notify_list;
};
struct fuse_cmd {
char *buf;
size_t buflen;
struct fuse_chan *ch;
struct fuse_cmd
{
char *buf;
size_t buflen;
struct fuse_chan *ch;
};
EXTERN_C_BEGIN
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op,
size_t op_size, void *user_data, int compat);
int fuse_sync_compat_args(struct fuse_args *args);
size_t op_size);
struct fuse_chan *fuse_kern_chan_new(int fd);
struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
void fuse_kern_unmount_compat22(const char *mountpoint);
int fuse_chan_clearfd(struct fuse_chan *ch);
void fuse_kern_unmount(const char *mountpoint, int fd);
@@ -120,11 +111,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
void *user_data,
int compat);
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
int *fd);
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
EXTERN_C_END
-103
View File
@@ -1,103 +0,0 @@
/*
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
*/
#include "fuse_lowlevel.h"
#include "fuse_kernel.h"
#include "fuse_i.h"
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
size_t size)
{
struct fuse_chan *ch = *chp;
int err;
ssize_t res;
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
restart:
res = read(fuse_chan_fd(ch), buf, size);
err = errno;
if (fuse_session_exited(se))
return 0;
if (res == -1) {
/* ENOENT means the operation was interrupted, it's safe
to restart */
if (err == ENOENT)
goto restart;
if (err == ENODEV) {
fuse_session_exit(se);
return 0;
}
/* Errors occurring during normal operation: EINTR (read
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
umounted) */
if (err != EINTR && err != EAGAIN)
perror("fuse: reading device");
return -err;
}
if ((size_t) res < sizeof(struct fuse_in_header)) {
fprintf(stderr, "short read on fuse device\n");
return -EIO;
}
return res;
}
static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
size_t count)
{
if (iov) {
ssize_t res = writev(fuse_chan_fd(ch), iov, count);
int err = errno;
if (res == -1) {
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
/* ENOENT means the operation was interrupted */
if (!fuse_session_exited(se) && err != ENOENT)
perror("fuse: writing device");
return -err;
}
}
return 0;
}
static void fuse_kern_chan_destroy(struct fuse_chan *ch)
{
int fd = fuse_chan_fd(ch);
if (fd != -1)
close(fd);
}
struct fuse_chan *
fuse_kern_chan_new(int fd_)
{
long pagesize;
size_t bufsize;
struct fuse_chan_ops op =
{
.receive = fuse_kern_chan_receive,
.send = fuse_kern_chan_send,
.destroy = fuse_kern_chan_destroy,
};
pagesize = sysconf(_SC_PAGESIZE);
bufsize = ((FUSE_MAX_MAX_PAGES * pagesize) + 0x1000);
return fuse_chan_new(&op, fd_, bufsize, NULL);
}
-46
View File
@@ -1,46 +0,0 @@
/*
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
*/
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int fuse_session_loop(struct fuse_session *se)
{
int res = 0;
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
size_t bufsize = fuse_chan_bufsize(ch);
char *buf = (char*)calloc(bufsize,1);
if (!buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
return -1;
}
while (!fuse_session_exited(se)) {
struct fuse_chan *tmpch = ch;
struct fuse_buf fbuf = {
.mem = buf,
.size = bufsize,
};
res = fuse_session_receive_buf(se, &fbuf, &tmpch);
if (res == -EINTR)
continue;
if (res <= 0)
break;
fuse_session_process_buf(se, &fbuf, tmpch);
}
free(buf);
fuse_session_reset(se);
return res < 0 ? -1 : 0;
}
-225
View File
@@ -1,225 +0,0 @@
/*
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.
*/
#include "fuse_lowlevel.h"
#include "fuse_misc.h"
#include "fuse_kernel.h"
#include "fuse_i.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
/* Environment var controlling the thread stack size */
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
struct fuse_worker {
struct fuse_worker *prev;
struct fuse_worker *next;
pthread_t thread_id;
size_t bufsize;
char *buf;
struct fuse_mt *mt;
};
struct fuse_mt {
struct fuse_session *se;
struct fuse_chan *prevch;
struct fuse_worker main;
sem_t finish;
int exit;
int error;
};
static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
{
struct fuse_worker *prev = next->prev;
w->next = next;
w->prev = prev;
prev->next = w;
next->prev = w;
}
static void list_del_worker(struct fuse_worker *w)
{
struct fuse_worker *prev = w->prev;
struct fuse_worker *next = w->next;
prev->next = next;
next->prev = prev;
}
static int fuse_loop_start_thread(struct fuse_mt *mt);
static void *fuse_do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
struct fuse_mt *mt = w->mt;
while (!fuse_session_exited(mt->se)) {
struct fuse_chan *ch = mt->prevch;
struct fuse_buf fbuf = {
.mem = w->buf,
.size = w->bufsize,
};
int res;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (res == -EINTR)
continue;
if (res <= 0) {
if (res < 0) {
fuse_session_exit(mt->se);
mt->error = -1;
}
break;
}
if (mt->exit)
return NULL;
fuse_session_process_buf(mt->se, &fbuf, ch);
}
sem_post(&mt->finish);
return NULL;
}
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
{
sigset_t oldset;
sigset_t newset;
int res;
pthread_attr_t attr;
char *stack_size;
/* Override default stack size */
pthread_attr_init(&attr);
stack_size = getenv(ENVNAME_THREAD_STACK);
if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
/* Disallow signal reception in worker threads */
sigemptyset(&newset);
sigaddset(&newset, SIGTERM);
sigaddset(&newset, SIGINT);
sigaddset(&newset, SIGHUP);
sigaddset(&newset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
res = pthread_create(thread_id, &attr, func, arg);
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
pthread_attr_destroy(&attr);
if (res != 0) {
fprintf(stderr, "fuse: error creating thread: %s\n",
strerror(res));
return -1;
}
return 0;
}
static int fuse_loop_start_thread(struct fuse_mt *mt)
{
int res;
struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
if (!w) {
fprintf(stderr, "fuse: failed to allocate worker structure\n");
return -1;
}
memset(w, 0, sizeof(struct fuse_worker));
w->bufsize = fuse_chan_bufsize(mt->prevch);
w->buf = calloc(w->bufsize,1);
w->mt = mt;
if (!w->buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
free(w);
return -1;
}
res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
if (res == -1) {
free(w->buf);
free(w);
return -1;
}
list_add_worker(w, &mt->main);
return 0;
}
static void fuse_join_worker(struct fuse_worker *w)
{
pthread_join(w->thread_id, NULL);
list_del_worker(w);
free(w->buf);
free(w);
}
static int number_of_threads(void)
{
#ifdef _SC_NPROCESSORS_ONLN
return sysconf(_SC_NPROCESSORS_ONLN);
#endif
return 4;
}
int fuse_session_loop_mt(struct fuse_session *se,
const int _threads)
{
int i;
int err;
int threads;
struct fuse_mt mt;
struct fuse_worker *w;
memset(&mt, 0, sizeof(struct fuse_mt));
mt.se = se;
mt.prevch = fuse_session_next_chan(se, NULL);
mt.error = 0;
mt.main.thread_id = pthread_self();
mt.main.prev = mt.main.next = &mt.main;
sem_init(&mt.finish, 0, 0);
threads = ((_threads > 0) ? _threads : number_of_threads());
if(_threads < 0)
threads /= -_threads;
if(threads == 0)
threads = 1;
err = 0;
for(i = 0; (i < threads) && !err; i++)
err = fuse_loop_start_thread(&mt);
if (!err) {
/* sem_wait() is interruptible */
while (!fuse_session_exited(se))
sem_wait(&mt.finish);
for (w = mt.main.next; w != &mt.main; w = w->next)
pthread_cancel(w->thread_id);
mt.exit = 1;
while (mt.main.next != &mt.main)
fuse_join_worker(mt.main.next);
err = mt.error;
}
sem_destroy(&mt.finish);
fuse_session_reset(se);
return err;
}
+239
View File
@@ -0,0 +1,239 @@
/*
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.
*/
#include "pool.hpp"
#include "fuse_i.h"
#include "fuse_kernel.h"
#include "fuse_lowlevel.h"
#include "fuse_misc.h"
#include <errno.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <unistd.h>
/* Environment var controlling the thread stack size */
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
struct fuse_worker
{
struct fuse_worker *prev;
struct fuse_worker *next;
pthread_t thread_id;
size_t bufsize;
char *buf;
struct fuse_mt *mt;
};
struct fuse_mt
{
struct fuse_session *se;
struct fuse_worker main;
sem_t finish;
int exit;
int error;
};
static
void
list_add_worker(struct fuse_worker *w,
struct fuse_worker *next)
{
struct fuse_worker *prev = next->prev;
w->next = next;
w->prev = prev;
prev->next = w;
next->prev = w;
}
static void list_del_worker(struct fuse_worker *w)
{
struct fuse_worker *prev = w->prev;
struct fuse_worker *next = w->next;
prev->next = next;
next->prev = prev;
}
static int fuse_loop_start_thread(struct fuse_mt *mt);
thread_pool tp;
static
void*
fuse_do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
struct fuse_mt *mt = w->mt;
while(!fuse_session_exited(mt->se))
{
int res;
struct fuse_buf fbuf = {0};
fbuf.mem = (char*)malloc(w->bufsize);
fbuf.size = w->bufsize;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
res = mt->se->receive_buf(mt->se,&fbuf,mt->se->ch);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if(res == -EINTR)
continue;
if(res <= 0)
{
if(res < 0)
{
mt->se->exited = 1;
mt->error = -1;
}
break;
}
if(mt->exit)
return NULL;
tp.enqueue_work([=] {
mt->se->process_buf(mt->se->data,fbuf.mem,mt->se->ch);
free(fbuf.mem);
});
}
sem_post(&mt->finish);
return NULL;
}
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
{
sigset_t oldset;
sigset_t newset;
int res;
pthread_attr_t attr;
char *stack_size;
/* Override default stack size */
pthread_attr_init(&attr);
stack_size = getenv(ENVNAME_THREAD_STACK);
if(stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
/* Disallow signal reception in worker threads */
sigfillset(&newset);
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
res = pthread_create(thread_id, &attr, func, arg);
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
pthread_attr_destroy(&attr);
if(res != 0) {
fprintf(stderr, "fuse: error creating thread: %s\n",
strerror(res));
return -1;
}
return 0;
}
static int fuse_loop_start_thread(struct fuse_mt *mt)
{
int res;
struct fuse_worker *w = (struct fuse_worker*)malloc(sizeof(struct fuse_worker));
if(!w) {
fprintf(stderr, "fuse: failed to allocate worker structure\n");
return -1;
}
memset(w, 0, sizeof(struct fuse_worker));
w->bufsize = fuse_chan_bufsize(mt->se->ch);
w->buf = (char*)calloc(w->bufsize,1);
w->mt = mt;
if(!w->buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
free(w);
return -1;
}
res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
if(res == -1) {
free(w->buf);
free(w);
return -1;
}
list_add_worker(w, &mt->main);
return 0;
}
static void fuse_join_worker(struct fuse_worker *w)
{
pthread_join(w->thread_id, NULL);
list_del_worker(w);
free(w->buf);
free(w);
}
static int number_of_threads(void)
{
#ifdef _SC_NPROCESSORS_ONLN
return sysconf(_SC_NPROCESSORS_ONLN);
#endif
return 4;
}
int
fuse_session_loop_mt(struct fuse_session *se_,
const int threads_)
{
int i;
int err;
int threads;
struct fuse_mt mt;
struct fuse_worker *w;
memset(&mt,0,sizeof(struct fuse_mt));
mt.se = se_;
mt.error = 0;
mt.main.thread_id = pthread_self();
mt.main.prev = mt.main.next = &mt.main;
sem_init(&mt.finish,0,0);
threads = ((threads_ > 0) ? threads_ : number_of_threads());
if(threads_ < 0)
threads /= -threads_;
if(threads == 0)
threads = 1;
err = 0;
for(i = 0; (i < threads) && !err; i++)
err = fuse_loop_start_thread(&mt);
if(!err)
{
/* sem_wait() is interruptible */
while(!fuse_session_exited(se_))
sem_wait(&mt.finish);
for(w = mt.main.next; w != &mt.main; w = w->next)
pthread_cancel(w->thread_id);
mt.exit = 1;
while(mt.main.next != &mt.main)
fuse_join_worker(mt.main.next);
err = mt.error;
}
sem_destroy(&mt.finish);
fuse_session_reset(se_);
return err;
}
+1786 -2544
View File
File diff suppressed because it is too large Load Diff
+10 -7
View File
@@ -6,13 +6,16 @@
See the file COPYING.LIB
*/
#pragma once
#include "config.h"
#include <pthread.h>
/*
Versioned symbols cannot be used in some cases because it
- confuse the dynamic linker in uClibc
- not supported on MacOSX (in MachO binary format)
- confuse the dynamic linker in uClibc
- not supported on MacOSX (in MachO binary format)
*/
#if (!defined(__UCLIBC__) && !defined(__APPLE__))
#define FUSE_SYMVER(x) __asm__(x)
@@ -26,11 +29,11 @@
/* Is this hack still needed? */
static inline void fuse_mutex_init(pthread_mutex_t *mut)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(mut, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(mut, &attr);
pthread_mutexattr_destroy(&attr);
}
#endif
+14 -103
View File
@@ -16,109 +16,20 @@
#include <pthread.h>
#include <assert.h>
struct procdata {
struct fuse *f;
struct fuse_chan *prevch;
struct fuse_session *prevse;
fuse_processor_t proc;
void *data;
};
static void mt_session_proc(void *data, const char *buf, size_t len,
struct fuse_chan *ch)
int
fuse_loop_mt(struct fuse *f)
{
struct procdata *pd = (struct procdata *) data;
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
if (f == NULL)
return -1;
(void) len;
(void) ch;
pd->proc(pd->f, cmd, pd->data);
int res = fuse_start_maintenance_thread(f);
if (res)
return -1;
res = fuse_session_loop_mt(fuse_get_session(f),
fuse_config_num_threads(f));
fuse_stop_maintenance_thread(f);
return res;
}
static void mt_session_exit(void *data, int val)
{
struct procdata *pd = (struct procdata *) data;
if (val)
fuse_session_exit(pd->prevse);
else
fuse_session_reset(pd->prevse);
}
static int mt_session_exited(void *data)
{
struct procdata *pd = (struct procdata *) data;
return fuse_session_exited(pd->prevse);
}
static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
{
struct fuse_cmd *cmd;
struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
assert(size >= sizeof(cmd));
cmd = fuse_read_cmd(pd->f);
if (cmd == NULL)
return 0;
*(struct fuse_cmd **) buf = cmd;
return sizeof(cmd);
}
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
{
int res;
struct procdata pd;
struct fuse_session *prevse = fuse_get_session(f);
struct fuse_session *se;
struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
struct fuse_chan *ch;
struct fuse_session_ops sop = {
.exit = mt_session_exit,
.exited = mt_session_exited,
.process = mt_session_proc,
};
struct fuse_chan_ops cop = {
.receive = mt_chan_receive,
};
pd.f = f;
pd.prevch = prevch;
pd.prevse = prevse;
pd.proc = proc;
pd.data = data;
se = fuse_session_new(&sop, &pd);
if (se == NULL)
return -1;
ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
sizeof(struct fuse_cmd *), &pd);
if (ch == NULL) {
fuse_session_destroy(se);
return -1;
}
fuse_session_add_chan(se, ch);
res = fuse_session_loop_mt(se,
fuse_config_num_threads(f));
fuse_session_destroy(se);
return res;
}
int fuse_loop_mt(struct fuse *f)
{
if (f == NULL)
return -1;
int res = fuse_start_cleanup_thread(f);
if (res)
return -1;
res = fuse_session_loop_mt(fuse_get_session(f),
fuse_config_num_threads(f));
fuse_stop_cleanup_thread(f);
return res;
}
FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
+224
View File
@@ -0,0 +1,224 @@
#include "fuse_node.h"
#include "khash.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdio.h> // for debugging
#define UNKNOWN_INO UINT64_MAX
#define ROOT_NODE_ID 0
#define ROOT_NODE_NAME "/"
typedef struct node_idname_t node_idname_t;
struct node_idname_t
{
uint64_t id;
const char *name;
};
static khint_t idname_hash_func(const node_idname_t idname);
static int idname_hash_equal(const node_idname_t idname0, const node_idname_t idname1);
KHASH_INIT(node,node_idname_t,fuse_node_t*,1,idname_hash_func,idname_hash_equal);
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
struct fuse_node_hashtable_t
{
kh_node_t *ht;
uint64_t id;
uint64_t generation;
};
static
inline
khint_t
idname_hash_func(const node_idname_t idname_)
{
if(idname_.name == NULL)
return idname_.id;
return (idname_.id ^ kh_str_hash_func(idname_.name));
}
static
inline
int
idname_hash_equal(const node_idname_t idname0_,
const node_idname_t idname1_)
{
return ((idname0_.id == idname1_.id) &&
((idname0_.name == idname1_.name) ||
(strcmp(idname0_.name,idname1_.name) == 0)));
}
static
inline
fuse_node_t*
fuse_node_alloc(const uint64_t id_,
const char *name_)
{
fuse_node_t *node;
node = (fuse_node_t*)calloc(1,sizeof(fuse_node_t));
node->id = id_;
node->name = strdup(name_);
node->ref_count = 1;
node->lookup_count = 1;
return node;
}
static
inline
void
fuse_node_free(fuse_node_t *node_)
{
free(node_->name);
free(node_);
}
static
inline
uint64_t
rand64()
{
uint64_t rv;
rv = rand();
rv <<= 32;
rv |= rand();
return rv;
}
static
inline
void
node_hashtable_gen_unique_id(fuse_node_hashtable_t *ht_)
{
do
{
ht_->id++;
if(ht_->id == 0)
ht_->generation++;
}
while((ht_->id == 0) || (ht_->id == UNKNOWN_INO));
}
static
inline
void
node_hashtable_put_root(fuse_node_hashtable_t *ht_)
{
int rv;
khint_t k;
fuse_node_t *root_node;
const node_idname_t idname0 = {ROOT_NODE_ID,""};
const node_idname_t idname1 = {ROOT_NODE_ID,ROOT_NODE_NAME};
root_node = fuse_node_alloc(ROOT_NODE_ID,ROOT_NODE_NAME);
k = kh_put_node(ht_->ht,idname0,&rv);
kh_value(ht_->ht,k) = root_node;
k = kh_put_node(ht_->ht,idname1,&rv);
kh_value(ht_->ht,k) = root_node;
}
static
inline
void
node_hashtable_set_id_gen(fuse_node_hashtable_t *ht_,
fuse_node_t *node_)
{
node_hashtable_gen_unique_id(ht_);
node_->id = ht_->id;
node_->generation = ht_->generation;
}
fuse_node_hashtable_t*
fuse_node_hashtable_init()
{
fuse_node_hashtable_t *ht;
ht = (fuse_node_hashtable_t*)calloc(sizeof(fuse_node_hashtable_t),1);
if(ht == NULL)
return NULL;
ht->ht = kh_init_node();
if(ht->ht == NULL)
{
free(ht);
return NULL;
}
srand(time(NULL));
ht->id = 0;
ht->generation = rand64();
node_hashtable_put_root(ht);
return ht;
}
fuse_node_t*
fuse_node_hashtable_put(fuse_node_hashtable_t *ht_,
const uint64_t parent_id_,
const uint64_t child_id_,
const char *child_name_)
{
int rv;
khint_t k;
fuse_node_t *child_node;
const node_idname_t p_idname = {parent_id_,""};
const node_idname_t c0_idname = {child_id_,child_name_};
const node_idname_t c1_idname = {parent_id_,child_name_};
child_node = fuse_node_alloc(child_id_,child_name_);
k = kh_get_node(ht_->ht,p_idname);
child_node->parent = kh_value(ht_->ht,k);
child_node->parent->ref_count++;
k = kh_put_node(ht_->ht,c0_idname,&rv);
kh_value(ht_->ht,k) = child_node;
k = kh_put_node(ht_->ht,c1_idname,&rv);
kh_value(ht_->ht,k) = child_node;
return child_node;
}
fuse_node_t*
fuse_node_hashtable_get(fuse_node_hashtable_t *ht_,
const uint64_t id_)
{
return fuse_node_hashtable_get_child(ht_,id_,"");
}
fuse_node_t*
fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht_,
const uint64_t parent_id_,
const char *child_name_)
{
khint_t k;
fuse_node_t *node;
const node_idname_t idname = {parent_id_,child_name_};
k = kh_get_node(ht_->ht,idname);
node = ((k != kh_end(ht_->ht)) ?
kh_value(ht_->ht,k) :
NULL);
return node;
}
void
fuse_node_hashtable_del(fuse_node_hashtable_t *ht_,
fuse_node_t *node_)
{
}
+36
View File
@@ -0,0 +1,36 @@
#include <stdint.h>
typedef struct fuse_node_t fuse_node_t;
struct fuse_node_t
{
uint64_t id;
uint64_t generation;
char *name;
fuse_node_t *parent;
uint32_t ref_count;
uint64_t lookup_count;
uint64_t open_count;
};
struct fuse_node_hashtable_t;
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
fuse_node_hashtable_t *fuse_node_hashtable_init();
fuse_node_t *fuse_node_hashtable_put(fuse_node_hashtable_t *ht,
const uint64_t parent_id,
const uint64_t child_id,
const char *child_name);
fuse_node_t* fuse_node_hashtable_get(fuse_node_hashtable_t *ht,
const uint64_t id);
fuse_node_t* fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht,
const uint64_t id,
const char *name);
void fuse_node_hashtable_del(fuse_node_hashtable_t *ht,
fuse_node_t *node);
void fuse_node_hashtable_get_path(fuse_node_hashtable_t *ht,
char *buf,
uint32_t buflen);
+275 -280
View File
@@ -14,413 +14,408 @@
#include <string.h>
#include <assert.h>
struct fuse_opt_context {
void *data;
const struct fuse_opt *opt;
fuse_opt_proc_t proc;
int argctr;
int argc;
char **argv;
struct fuse_args outargs;
char *opts;
int nonopt;
struct fuse_opt_context
{
void *data;
const struct fuse_opt *opt;
fuse_opt_proc_t proc;
int argctr;
int argc;
char **argv;
struct fuse_args outargs;
char *opts;
int nonopt;
};
void fuse_opt_free_args(struct fuse_args *args)
void
fuse_opt_free_args(struct fuse_args *args)
{
if (args) {
if (args->argv && args->allocated) {
int i;
for (i = 0; i < args->argc; i++)
free(args->argv[i]);
free(args->argv);
}
args->argc = 0;
args->argv = NULL;
args->allocated = 0;
}
if (args) {
if (args->argv && args->allocated) {
int i;
for (i = 0; i < args->argc; i++)
free(args->argv[i]);
free(args->argv);
}
args->argc = 0;
args->argv = NULL;
args->allocated = 0;
}
}
static int alloc_failed(void)
static
int
alloc_failed(void)
{
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
int
fuse_opt_add_arg(struct fuse_args *args, const char *arg)
{
char **newargv;
char *newarg;
char **newargv;
char *newarg;
assert(!args->argv || args->allocated);
assert(!args->argv || args->allocated);
newarg = strdup(arg);
if (!newarg)
return alloc_failed();
newarg = strdup(arg);
if (!newarg)
return alloc_failed();
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
if (!newargv) {
free(newarg);
return alloc_failed();
}
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
if (!newargv) {
free(newarg);
return alloc_failed();
}
args->argv = newargv;
args->allocated = 1;
args->argv[args->argc++] = newarg;
args->argv[args->argc] = NULL;
return 0;
args->argv = newargv;
args->allocated = 1;
args->argv[args->argc++] = newarg;
args->argv[args->argc] = NULL;
return 0;
}
static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
const char *arg)
{
assert(pos <= args->argc);
if (fuse_opt_add_arg(args, arg) == -1)
return -1;
assert(pos <= args->argc);
if (fuse_opt_add_arg(args, arg) == -1)
return -1;
if (pos != args->argc - 1) {
char *newarg = args->argv[args->argc - 1];
memmove(&args->argv[pos + 1], &args->argv[pos],
sizeof(char *) * (args->argc - pos - 1));
args->argv[pos] = newarg;
}
return 0;
if (pos != args->argc - 1) {
char *newarg = args->argv[args->argc - 1];
memmove(&args->argv[pos + 1], &args->argv[pos],
sizeof(char *) * (args->argc - pos - 1));
args->argv[pos] = newarg;
}
return 0;
}
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
{
return fuse_opt_insert_arg_common(args, pos, arg);
}
int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos,
const char *arg);
int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg)
{
return fuse_opt_insert_arg_common(args, pos, arg);
return fuse_opt_insert_arg_common(args, pos, arg);
}
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
{
if (ctx->argctr + 1 >= ctx->argc) {
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
return -1;
}
ctx->argctr++;
return 0;
if (ctx->argctr + 1 >= ctx->argc) {
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
return -1;
}
ctx->argctr++;
return 0;
}
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
{
return fuse_opt_add_arg(&ctx->outargs, arg);
return fuse_opt_add_arg(&ctx->outargs, arg);
}
static int add_opt_common(char **opts, const char *opt, int esc)
{
unsigned oldlen = *opts ? strlen(*opts) : 0;
char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
unsigned oldlen = *opts ? strlen(*opts) : 0;
char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
if (!d)
return alloc_failed();
if (!d)
return alloc_failed();
*opts = d;
if (oldlen) {
d += oldlen;
*d++ = ',';
}
*opts = d;
if (oldlen) {
d += oldlen;
*d++ = ',';
}
for (; *opt; opt++) {
if (esc && (*opt == ',' || *opt == '\\'))
*d++ = '\\';
*d++ = *opt;
}
*d = '\0';
for (; *opt; opt++) {
if (esc && (*opt == ',' || *opt == '\\'))
*d++ = '\\';
*d++ = *opt;
}
*d = '\0';
return 0;
return 0;
}
int fuse_opt_add_opt(char **opts, const char *opt)
{
return add_opt_common(opts, opt, 0);
return add_opt_common(opts, opt, 0);
}
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
{
return add_opt_common(opts, opt, 1);
return add_opt_common(opts, opt, 1);
}
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
{
return add_opt_common(&ctx->opts, opt, 1);
return add_opt_common(&ctx->opts, opt, 1);
}
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
int iso)
{
if (key == FUSE_OPT_KEY_DISCARD)
return 0;
if (key == FUSE_OPT_KEY_DISCARD)
return 0;
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
if (res == -1 || !res)
return res;
}
if (iso)
return add_opt(ctx, arg);
else
return add_arg(ctx, arg);
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
if (res == -1 || !res)
return res;
}
if (iso)
return add_opt(ctx, arg);
else
return add_arg(ctx, arg);
}
static int match_template(const char *t, const char *arg, unsigned *sepp)
{
int arglen = strlen(arg);
const char *sep = strchr(t, '=');
sep = sep ? sep : strchr(t, ' ');
if (sep && (!sep[1] || sep[1] == '%')) {
int tlen = sep - t;
if (sep[0] == '=')
tlen ++;
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
*sepp = sep - t;
return 1;
}
}
if (strcmp(t, arg) == 0) {
*sepp = 0;
return 1;
}
return 0;
int arglen = strlen(arg);
const char *sep = strchr(t, '=');
sep = sep ? sep : strchr(t, ' ');
if (sep && (!sep[1] || sep[1] == '%')) {
int tlen = sep - t;
if (sep[0] == '=')
tlen ++;
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
*sepp = sep - t;
return 1;
}
}
if (strcmp(t, arg) == 0) {
*sepp = 0;
return 1;
}
return 0;
}
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
const char *arg, unsigned *sepp)
{
for (; opt && opt->templ; opt++)
if (match_template(opt->templ, arg, sepp))
return opt;
return NULL;
for (; opt && opt->templ; opt++)
if (match_template(opt->templ, arg, sepp))
return opt;
return NULL;
}
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
{
unsigned dummy;
return find_opt(opts, opt, &dummy) ? 1 : 0;
unsigned dummy;
return find_opt(opts, opt, &dummy) ? 1 : 0;
}
static int process_opt_param(void *var, const char *format, const char *param,
const char *arg)
{
assert(format[0] == '%');
if (format[1] == 's') {
char *copy = strdup(param);
if (!copy)
return alloc_failed();
assert(format[0] == '%');
if (format[1] == 's') {
char *copy = strdup(param);
if (!copy)
return alloc_failed();
*(char **) var = copy;
} else {
if (sscanf(param, format, var) != 1) {
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
return -1;
}
}
return 0;
*(char **) var = copy;
} else {
if (sscanf(param, format, var) != 1) {
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
return -1;
}
}
return 0;
}
static int process_opt(struct fuse_opt_context *ctx,
const struct fuse_opt *opt, unsigned sep,
const char *arg, int iso)
{
if (opt->offset == -1U) {
if (call_proc(ctx, arg, opt->value, iso) == -1)
return -1;
} else {
void *var = ctx->data + opt->offset;
if (sep && opt->templ[sep + 1]) {
const char *param = arg + sep;
if (opt->templ[sep] == '=')
param ++;
if (process_opt_param(var, opt->templ + sep + 1,
param, arg) == -1)
return -1;
} else
*(int *)var = opt->value;
}
return 0;
if (opt->offset == -1U) {
if (call_proc(ctx, arg, opt->value, iso) == -1)
return -1;
} else {
void *var = ctx->data + opt->offset;
if (sep && opt->templ[sep + 1]) {
const char *param = arg + sep;
if (opt->templ[sep] == '=')
param ++;
if (process_opt_param(var, opt->templ + sep + 1,
param, arg) == -1)
return -1;
} else
*(int *)var = opt->value;
}
return 0;
}
static int process_opt_sep_arg(struct fuse_opt_context *ctx,
const struct fuse_opt *opt, unsigned sep,
const char *arg, int iso)
{
int res;
char *newarg;
char *param;
int res;
char *newarg;
char *param;
if (next_arg(ctx, arg) == -1)
return -1;
if (next_arg(ctx, arg) == -1)
return -1;
param = ctx->argv[ctx->argctr];
newarg = malloc(sep + strlen(param) + 1);
if (!newarg)
return alloc_failed();
param = ctx->argv[ctx->argctr];
newarg = malloc(sep + strlen(param) + 1);
if (!newarg)
return alloc_failed();
memcpy(newarg, arg, sep);
strcpy(newarg + sep, param);
res = process_opt(ctx, opt, sep, newarg, iso);
free(newarg);
memcpy(newarg, arg, sep);
strcpy(newarg + sep, param);
res = process_opt(ctx, opt, sep, newarg, iso);
free(newarg);
return res;
return res;
}
static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
{
unsigned sep;
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
if (opt) {
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
int res;
if (sep && opt->templ[sep] == ' ' && !arg[sep])
res = process_opt_sep_arg(ctx, opt, sep, arg,
iso);
else
res = process_opt(ctx, opt, sep, arg, iso);
if (res == -1)
return -1;
}
return 0;
} else
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
unsigned sep;
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
if (opt) {
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
int res;
if (sep && opt->templ[sep] == ' ' && !arg[sep])
res = process_opt_sep_arg(ctx, opt, sep, arg,
iso);
else
res = process_opt(ctx, opt, sep, arg, iso);
if (res == -1)
return -1;
}
return 0;
} else
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
}
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
{
char *s = opts;
char *d = s;
int end = 0;
char *s = opts;
char *d = s;
int end = 0;
while (!end) {
if (*s == '\0')
end = 1;
if (*s == ',' || end) {
int res;
while (!end) {
if (*s == '\0')
end = 1;
if (*s == ',' || end) {
int res;
*d = '\0';
res = process_gopt(ctx, opts, 1);
if (res == -1)
return -1;
d = opts;
} else {
if (s[0] == '\\' && s[1] != '\0') {
s++;
if (s[0] >= '0' && s[0] <= '3' &&
s[1] >= '0' && s[1] <= '7' &&
s[2] >= '0' && s[2] <= '7') {
*d++ = (s[0] - '0') * 0100 +
(s[1] - '0') * 0010 +
(s[2] - '0');
s += 2;
} else {
*d++ = *s;
}
} else {
*d++ = *s;
}
}
s++;
}
*d = '\0';
res = process_gopt(ctx, opts, 1);
if (res == -1)
return -1;
d = opts;
} else {
if (s[0] == '\\' && s[1] != '\0') {
s++;
if (s[0] >= '0' && s[0] <= '3' &&
s[1] >= '0' && s[1] <= '7' &&
s[2] >= '0' && s[2] <= '7') {
*d++ = (s[0] - '0') * 0100 +
(s[1] - '0') * 0010 +
(s[2] - '0');
s += 2;
} else {
*d++ = *s;
}
} else {
*d++ = *s;
}
}
s++;
}
return 0;
return 0;
}
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
{
int res;
char *copy = strdup(opts);
int res;
char *copy = strdup(opts);
if (!copy) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
res = process_real_option_group(ctx, copy);
free(copy);
return res;
if (!copy) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
res = process_real_option_group(ctx, copy);
free(copy);
return res;
}
static int process_one(struct fuse_opt_context *ctx, const char *arg)
{
if (ctx->nonopt || arg[0] != '-')
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
else if (arg[1] == 'o') {
if (arg[2])
return process_option_group(ctx, arg + 2);
else {
if (next_arg(ctx, arg) == -1)
return -1;
if (ctx->nonopt || arg[0] != '-')
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
else if (arg[1] == 'o') {
if (arg[2])
return process_option_group(ctx, arg + 2);
else {
if (next_arg(ctx, arg) == -1)
return -1;
return process_option_group(ctx,
ctx->argv[ctx->argctr]);
}
} else if (arg[1] == '-' && !arg[2]) {
if (add_arg(ctx, arg) == -1)
return -1;
ctx->nonopt = ctx->outargs.argc;
return 0;
} else
return process_gopt(ctx, arg, 0);
return process_option_group(ctx,
ctx->argv[ctx->argctr]);
}
} else if (arg[1] == '-' && !arg[2]) {
if (add_arg(ctx, arg) == -1)
return -1;
ctx->nonopt = ctx->outargs.argc;
return 0;
} else
return process_gopt(ctx, arg, 0);
}
static int opt_parse(struct fuse_opt_context *ctx)
{
if (ctx->argc) {
if (add_arg(ctx, ctx->argv[0]) == -1)
return -1;
}
if (ctx->argc) {
if (add_arg(ctx, ctx->argv[0]) == -1)
return -1;
}
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
return -1;
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
return -1;
if (ctx->opts) {
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
return -1;
}
if (ctx->opts) {
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
return -1;
}
/* If option separator ("--") is the last argument, remove it */
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
}
/* If option separator ("--") is the last argument, remove it */
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
}
return 0;
return 0;
}
int fuse_opt_parse(struct fuse_args *args, void *data,
const struct fuse_opt opts[], fuse_opt_proc_t proc)
{
int res;
struct fuse_opt_context ctx = {
.data = data,
.opt = opts,
.proc = proc,
};
int res;
struct fuse_opt_context ctx = {
.data = data,
.opt = opts,
.proc = proc,
};
if (!args || !args->argv || !args->argc)
return 0;
if (!args || !args->argv || !args->argc)
return 0;
ctx.argc = args->argc;
ctx.argv = args->argv;
ctx.argc = args->argc;
ctx.argv = args->argv;
res = opt_parse(&ctx);
if (res != -1) {
struct fuse_args tmp = *args;
*args = ctx.outargs;
ctx.outargs = tmp;
}
free(ctx.opts);
fuse_opt_free_args(&ctx.outargs);
return res;
res = opt_parse(&ctx);
if (res != -1) {
struct fuse_args tmp = *args;
*args = ctx.outargs;
ctx.outargs = tmp;
}
free(ctx.opts);
fuse_opt_free_args(&ctx.outargs);
return res;
}
/* This symbol version was mistakenly added to the version script */
FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");
+151 -162
View File
@@ -8,233 +8,222 @@
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_common_compat.h"
#include "fuse_lowlevel_compat.h"
#include "fuse_kernel.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/uio.h>
struct fuse_chan {
struct fuse_chan_ops op;
struct fuse_session *se;
int fd;
size_t bufsize;
void *data;
int compat;
struct fuse_chan
{
struct fuse_session *se;
int fd;
size_t bufsize;
};
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
struct fuse_session *fuse_session_new(void *data,
void *receive_buf,
void *process_buf,
void *destroy)
{
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
if (se == NULL) {
fprintf(stderr, "fuse: failed to allocate session\n");
return NULL;
}
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
if (se == NULL) {
fprintf(stderr, "fuse: failed to allocate session\n");
return NULL;
}
memset(se, 0, sizeof(*se));
se->op = *op;
se->data = data;
memset(se, 0, sizeof(*se));
se->data = data;
se->receive_buf = receive_buf;
se->process_buf = process_buf;
se->destroy = destroy;
return se;
return se;
}
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
{
assert(se->ch == NULL);
assert(ch->se == NULL);
se->ch = ch;
ch->se = se;
assert(se->ch == NULL);
assert(ch->se == NULL);
se->ch = ch;
ch->se = se;
}
void fuse_session_remove_chan(struct fuse_chan *ch)
{
struct fuse_session *se = ch->se;
if (se) {
assert(se->ch == ch);
se->ch = NULL;
ch->se = NULL;
}
struct fuse_session *se = ch->se;
if (se) {
assert(se->ch == ch);
se->ch = NULL;
ch->se = NULL;
}
}
struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
struct fuse_chan *ch)
void
fuse_session_destroy(struct fuse_session *se)
{
assert(ch == NULL || ch == se->ch);
if (ch == NULL)
return se->ch;
else
return NULL;
}
void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
struct fuse_chan *ch)
{
se->op.process(se->data, buf, len, ch);
}
void fuse_session_process_buf(struct fuse_session *se,
const struct fuse_buf *buf, struct fuse_chan *ch)
{
if (se->process_buf) {
se->process_buf(se->data, buf, ch);
} else {
assert(!(buf->flags & FUSE_BUF_IS_FD));
fuse_session_process(se->data, buf->mem, buf->size, ch);
}
}
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
struct fuse_chan **chp)
{
int res;
if (se->receive_buf) {
res = se->receive_buf(se, buf, chp);
} else {
res = fuse_chan_recv(chp, buf->mem, buf->size);
if (res > 0)
buf->size = res;
}
return res;
}
void fuse_session_destroy(struct fuse_session *se)
{
if (se->op.destroy)
se->op.destroy(se->data);
if (se->ch != NULL)
fuse_chan_destroy(se->ch);
free(se);
}
void fuse_session_exit(struct fuse_session *se)
{
if (se->op.exit)
se->op.exit(se->data, 1);
se->exited = 1;
se->destroy(se->data);
if(se->ch != NULL)
fuse_chan_destroy(se->ch);
free(se);
}
void fuse_session_reset(struct fuse_session *se)
{
if (se->op.exit)
se->op.exit(se->data, 0);
se->exited = 0;
se->exited = 0;
}
int fuse_session_exited(struct fuse_session *se)
int
fuse_session_exited(struct fuse_session *se)
{
if (se->op.exited)
return se->op.exited(se->data);
else
return se->exited;
return se->exited;
}
void
fuse_session_exit(struct fuse_session *se_)
{
se_->exited = 1;
}
void *fuse_session_data(struct fuse_session *se)
{
return se->data;
return se->data;
}
static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
size_t bufsize, void *data,
int compat)
struct fuse_chan *
fuse_chan_new(int fd,
size_t bufsize)
{
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
if (ch == NULL) {
fprintf(stderr, "fuse: failed to allocate channel\n");
return NULL;
}
struct fuse_chan *ch;
memset(ch, 0, sizeof(*ch));
ch->op = *op;
ch->fd = fd;
ch->bufsize = bufsize;
ch->data = data;
ch->compat = compat;
ch = (struct fuse_chan*)malloc(sizeof(*ch));
if(ch == NULL)
{
fprintf(stderr, "fuse: failed to allocate channel\n");
return NULL;
}
return ch;
}
memset(ch, 0, sizeof(*ch));
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
size_t bufsize, void *data)
{
return fuse_chan_new_common(op, fd, bufsize, data, 0);
}
ch->fd = fd;
ch->bufsize = bufsize;
struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
int fd, size_t bufsize, void *data)
{
return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize,
data, 24);
return ch;
}
int fuse_chan_fd(struct fuse_chan *ch)
{
return ch->fd;
return ch->fd;
}
int fuse_chan_clearfd(struct fuse_chan *ch)
{
int fd = ch->fd;
ch->fd = -1;
return fd;
int fd = ch->fd;
ch->fd = -1;
return fd;
}
size_t fuse_chan_bufsize(struct fuse_chan *ch)
{
return ch->bufsize;
}
void *fuse_chan_data(struct fuse_chan *ch)
{
return ch->data;
return ch->bufsize;
}
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
{
return ch->se;
return ch->se;
}
int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
int
fuse_chan_recv(struct fuse_chan *ch,
char *buf,
size_t size)
{
struct fuse_chan *ch = *chp;
if (ch->compat)
return ((struct fuse_chan_ops_compat24 *) &ch->op)
->receive(ch, buf, size);
else
return ch->op.receive(chp, buf, size);
int err;
ssize_t res;
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
restart:
res = read(fuse_chan_fd(ch), buf, size);
err = errno;
if(fuse_session_exited(se))
return 0;
if(res == -1)
{
/* ENOENT means the operation was interrupted, it's safe
to restart */
if (err == ENOENT)
goto restart;
if(err == ENODEV)
{
se->exited = 1;
return 0;
}
/* Errors occurring during normal operation: EINTR (read
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
umounted) */
if(err != EINTR && err != EAGAIN)
perror("fuse: reading device");
return -err;
}
if((size_t) res < sizeof(struct fuse_in_header))
{
fprintf(stderr, "short read on fuse device\n");
return -EIO;
}
return res;
}
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
int
fuse_chan_send(struct fuse_chan *ch,
const struct iovec iov[],
size_t count)
{
int res;
if(!iov)
return 0;
res = fuse_chan_recv(&ch, buf, size);
return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
int err;
ssize_t res;
res = writev(fuse_chan_fd(ch), iov, count);
err = errno;
if(res == -1)
{
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
/* ENOENT means the operation was interrupted */
if(!fuse_session_exited(se) && err != ENOENT)
perror("fuse: writing device");
return -err;
}
return 0;
}
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
void
fuse_chan_destroy(struct fuse_chan *ch)
{
return ch->op.send(ch, iov, count);
}
int fd;
void fuse_chan_destroy(struct fuse_chan *ch)
{
fuse_session_remove_chan(ch);
if (ch->op.destroy)
ch->op.destroy(ch);
free(ch);
}
fuse_session_remove_chan(ch);
#ifndef __FreeBSD__
FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
#endif
fd = fuse_chan_fd(ch);
if(fd != -1)
close(fd);
free(ch);
}
+35 -36
View File
@@ -16,57 +16,56 @@ static struct fuse_session *fuse_instance;
static void exit_handler(int sig)
{
(void) sig;
if (fuse_instance)
fuse_session_exit(fuse_instance);
(void) sig;
if (fuse_instance)
fuse_session_exit(fuse_instance);
}
static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
{
struct sigaction sa;
struct sigaction old_sa;
struct sigaction sa;
struct sigaction old_sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = remove ? SIG_DFL : handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = remove ? SIG_DFL : handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
if (sigaction(sig, NULL, &old_sa) == -1) {
perror("fuse: cannot get old signal handler");
return -1;
}
if (sigaction(sig, NULL, &old_sa) == -1) {
perror("fuse: cannot get old signal handler");
return -1;
}
if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
sigaction(sig, &sa, NULL) == -1) {
perror("fuse: cannot set signal handler");
return -1;
}
return 0;
if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
sigaction(sig, &sa, NULL) == -1) {
perror("fuse: cannot set signal handler");
return -1;
}
return 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
{
if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1)
return -1;
if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1)
return -1;
fuse_instance = se;
return 0;
fuse_instance = se;
return 0;
}
void fuse_remove_signal_handlers(struct fuse_session *se)
{
if (fuse_instance != se)
fprintf(stderr,
"fuse: fuse_remove_signal_handlers: unknown session\n");
else
fuse_instance = NULL;
if (fuse_instance != se)
fprintf(stderr,
"fuse: fuse_remove_signal_handlers: unknown session\n");
else
fuse_instance = NULL;
set_one_signal_handler(SIGHUP, exit_handler, 1);
set_one_signal_handler(SIGINT, exit_handler, 1);
set_one_signal_handler(SIGTERM, exit_handler, 1);
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
set_one_signal_handler(SIGHUP, exit_handler, 1);
set_one_signal_handler(SIGINT, exit_handler, 1);
set_one_signal_handler(SIGTERM, exit_handler, 1);
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
}
-207
View File
@@ -1,207 +0,0 @@
FUSE_2.2 {
global:
fuse_destroy;
fuse_exit;
fuse_exited;
fuse_invalidate;
fuse_is_lib_option;
fuse_loop;
fuse_loop_mt;
fuse_loop_mt_proc;
fuse_main;
fuse_main_compat1;
fuse_main_compat2;
fuse_mount_compat1;
fuse_new_compat1;
fuse_new_compat2;
fuse_process_cmd;
fuse_read_cmd;
fuse_set_getcontext_func;
fuse_setup_compat2;
};
FUSE_2.4 {
global:
fuse_add_dirent;
fuse_chan_bufsize;
fuse_chan_data;
fuse_chan_destroy;
fuse_chan_fd;
fuse_chan_receive;
fuse_chan_send;
fuse_chan_session;
fuse_dirent_size;
fuse_kern_chan_new;
fuse_lowlevel_is_lib_option;
fuse_reply_attr;
fuse_reply_buf;
fuse_reply_entry;
fuse_reply_err;
fuse_reply_none;
fuse_reply_readlink;
fuse_reply_write;
fuse_reply_xattr;
fuse_req_userdata;
fuse_session_add_chan;
fuse_session_destroy;
fuse_session_exit;
fuse_session_exited;
fuse_session_loop;
fuse_session_loop_mt;
fuse_session_new;
fuse_session_next_chan;
fuse_session_process;
fuse_session_reset;
} FUSE_2.2;
FUSE_2.5 {
global:
fuse_lowlevel_new_compat;
fuse_main_real_compat22;
fuse_mount_compat22;
fuse_new_compat22;
fuse_opt_parse;
fuse_opt_add_opt;
fuse_opt_add_arg;
fuse_opt_free_args;
fuse_opt_match;
fuse_parse_cmdline;
fuse_remove_signal_handlers;
fuse_reply_create;
fuse_reply_open;
fuse_reply_open_compat;
fuse_reply_statfs;
fuse_reply_statfs_compat;
fuse_setup_compat22;
fuse_set_signal_handlers;
} FUSE_2.4;
FUSE_2.6 {
global:
fuse_add_direntry;
fuse_chan_new;
fuse_chan_new_compat24;
fuse_chan_recv;
fuse_daemonize;
fuse_get_session;
fuse_interrupted;
fuse_lowlevel_new;
fuse_lowlevel_new_compat25;
fuse_main_real;
fuse_main_real_compat25;
fuse_mount;
fuse_mount_compat25;
fuse_new;
fuse_new_compat25;
fuse_opt_insert_arg;
fuse_reply_lock;
fuse_req_interrupt_func;
fuse_req_interrupted;
fuse_session_remove_chan;
fuse_setup;
fuse_setup_compat25;
fuse_teardown;
fuse_teardown_compat22;
fuse_unmount;
fuse_unmount_compat22;
} FUSE_2.5;
FUSE_2.7 {
global:
fuse_fs_access;
fuse_fs_bmap;
fuse_fs_chmod;
fuse_fs_chown;
fuse_fs_create;
fuse_fs_destroy;
fuse_fs_fgetattr;
fuse_fs_flush;
fuse_fs_fsync;
fuse_fs_fsyncdir;
fuse_fs_ftruncate;
fuse_fs_getattr;
fuse_fs_getxattr;
fuse_fs_init;
fuse_fs_link;
fuse_fs_listxattr;
fuse_fs_lock;
fuse_fs_mkdir;
fuse_fs_mknod;
fuse_fs_new;
fuse_fs_open;
fuse_fs_opendir;
fuse_fs_read;
fuse_fs_readdir;
fuse_fs_readlink;
fuse_fs_release;
fuse_fs_releasedir;
fuse_fs_removexattr;
fuse_fs_rename;
fuse_fs_rmdir;
fuse_fs_setxattr;
fuse_fs_statfs;
fuse_fs_symlink;
fuse_fs_truncate;
fuse_fs_unlink;
fuse_fs_utimens;
fuse_fs_write;
fuse_register_module;
fuse_reply_iov;
fuse_version;
} FUSE_2.6;
FUSE_2.7.5 {
global:
fuse_reply_bmap;
} FUSE_2.7;
FUSE_2.8 {
global:
cuse_lowlevel_new;
cuse_lowlevel_main;
cuse_lowlevel_setup;
cuse_lowlevel_teardown;
fuse_fs_ioctl;
fuse_fs_poll;
fuse_get_context;
fuse_getgroups;
fuse_lowlevel_notify_inval_entry;
fuse_lowlevel_notify_inval_inode;
fuse_lowlevel_notify_poll;
fuse_notify_poll;
fuse_opt_add_opt_escaped;
fuse_pollhandle_destroy;
fuse_reply_ioctl;
fuse_reply_ioctl_iov;
fuse_reply_ioctl_retry;
fuse_reply_poll;
fuse_req_ctx;
fuse_req_getgroups;
fuse_session_data;
} FUSE_2.7.5;
FUSE_2.9 {
global:
fuse_buf_copy;
fuse_buf_size;
fuse_fs_read_buf;
fuse_fs_write_buf;
fuse_lowlevel_notify_retrieve;
fuse_lowlevel_notify_store;
fuse_reply_data;
fuse_session_process_buf;
fuse_session_receive_buf;
fuse_start_cleanup_thread;
fuse_stop_cleanup_thread;
fuse_clean_cache;
fuse_lowlevel_notify_delete;
fuse_fs_flock;
} FUSE_2.8;
FUSE_2.9.1 {
global:
fuse_fs_fallocate;
local:
*;
} FUSE_2.9;
+205 -309
View File
@@ -11,7 +11,6 @@
#include "fuse_misc.h"
#include "fuse_opt.h"
#include "fuse_lowlevel.h"
#include "fuse_common_compat.h"
#include <stdio.h>
#include <stdlib.h>
@@ -23,14 +22,13 @@
#include <sys/param.h>
enum {
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_VERSION,
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_VERSION,
};
struct helper_opts
{
int singlethread;
int foreground;
int nodefault_subtype;
char *mountpoint;
@@ -42,115 +40,110 @@ static
const
struct fuse_opt fuse_helper_opts[] =
{
FUSE_HELPER_OPT("-d", foreground),
FUSE_HELPER_OPT("debug", foreground),
FUSE_HELPER_OPT("-f", foreground),
FUSE_HELPER_OPT("-s", singlethread),
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END
};
FUSE_HELPER_OPT("-f", foreground),
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END
};
static void usage(const char *progname)
{
fprintf(stderr,
"usage: %s mountpoint [options]\n\n", progname);
fprintf(stderr,
"general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n");
fprintf(stderr,
"usage: %s mountpoint [options]\n\n", progname);
fprintf(stderr,
"general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n");
}
static void helper_help(void)
{
fprintf(stderr,
"FUSE options:\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n"
);
fprintf(stderr,
"FUSE options:\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
"\n"
);
}
static void helper_version(void)
{
fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
}
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct helper_opts *hopts = data;
struct helper_opts *hopts = data;
switch (key) {
case KEY_HELP:
usage(outargs->argv[0]);
/* fall through */
switch (key) {
case KEY_HELP:
usage(outargs->argv[0]);
/* fall through */
case KEY_HELP_NOHEADER:
helper_help();
return fuse_opt_add_arg(outargs, "-h");
case KEY_HELP_NOHEADER:
helper_help();
return fuse_opt_add_arg(outargs, "-h");
case KEY_VERSION:
helper_version();
return 1;
case KEY_VERSION:
helper_version();
return 1;
case FUSE_OPT_KEY_NONOPT:
if (!hopts->mountpoint) {
char mountpoint[PATH_MAX];
if (realpath(arg, mountpoint) == NULL) {
fprintf(stderr,
"fuse: bad mount point `%s': %s\n",
arg, strerror(errno));
return -1;
}
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
} else {
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
return -1;
}
case FUSE_OPT_KEY_NONOPT:
if (!hopts->mountpoint) {
char mountpoint[PATH_MAX];
if (realpath(arg, mountpoint) == NULL) {
fprintf(stderr,
"fuse: bad mount point `%s': %s\n",
arg, strerror(errno));
return -1;
}
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
} else {
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
return -1;
}
default:
return 1;
}
default:
return 1;
}
}
static int add_default_subtype(const char *progname, struct fuse_args *args)
{
int res;
char *subtype_opt;
const char *basename = strrchr(progname, '/');
if (basename == NULL)
basename = progname;
else if (basename[1] != '\0')
basename++;
int res;
char *subtype_opt;
const char *basename = strrchr(progname, '/');
if (basename == NULL)
basename = progname;
else if (basename[1] != '\0')
basename++;
subtype_opt = (char *) malloc(strlen(basename) + 64);
if (subtype_opt == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
sprintf(subtype_opt, "-osubtype=%s", basename);
res = fuse_opt_add_arg(args, subtype_opt);
free(subtype_opt);
return res;
subtype_opt = (char *) malloc(strlen(basename) + 64);
if (subtype_opt == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
sprintf(subtype_opt, "-osubtype=%s", basename);
res = fuse_opt_add_arg(args, subtype_opt);
free(subtype_opt);
return res;
}
int
fuse_parse_cmdline(struct fuse_args *args_,
char **mountpoint_,
int *multithreaded_,
int *foreground_)
{
int res;
@@ -177,8 +170,6 @@ fuse_parse_cmdline(struct fuse_args *args_,
else
free(hopts.mountpoint);
if(multithreaded_)
*multithreaded_ = !hopts.singlethread;
if(foreground_)
*foreground_ = hopts.foreground;
@@ -191,54 +182,54 @@ fuse_parse_cmdline(struct fuse_args *args_,
int fuse_daemonize(int foreground)
{
if (!foreground) {
int nullfd;
int waiter[2];
char completed;
if (!foreground) {
int nullfd;
int waiter[2];
char completed;
if (pipe(waiter)) {
perror("fuse_daemonize: pipe");
return -1;
}
if (pipe(waiter)) {
perror("fuse_daemonize: pipe");
return -1;
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork()) {
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork()) {
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
if (setsid() == -1) {
perror("fuse_daemonize: setsid");
return -1;
}
if (setsid() == -1) {
perror("fuse_daemonize: setsid");
return -1;
}
(void) chdir("/");
(void) chdir("/");
nullfd = open("/dev/null", O_RDWR, 0);
if (nullfd != -1) {
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
nullfd = open("/dev/null", O_RDWR, 0);
if (nullfd != -1) {
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
}
return 0;
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
}
return 0;
}
static
@@ -246,8 +237,10 @@ struct fuse_chan *
fuse_mount_common(const char *mountpoint_,
struct fuse_args *args_)
{
struct fuse_chan *ch;
int fd;
long bufsize;
long pagesize;
struct fuse_chan *ch;
/*
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
@@ -260,11 +253,14 @@ fuse_mount_common(const char *mountpoint_,
close(fd);
} while(fd >= 0 && fd <= 2);
fd = fuse_mount_compat25(mountpoint_, args_);
fd = fuse_kern_mount(mountpoint_,args_);
if(fd == -1)
return NULL;
ch = fuse_kern_chan_new(fd);
pagesize = sysconf(_SC_PAGESIZE);
bufsize = ((FUSE_MAX_MAX_PAGES + 1) * pagesize);
ch = fuse_chan_new(fd,bufsize);
if(!ch)
fuse_kern_unmount(mountpoint_, fd);
@@ -280,232 +276,132 @@ fuse_mount(const char *mountpoint_,
static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
{
if (mountpoint) {
int fd = ch ? fuse_chan_clearfd(ch) : -1;
fuse_kern_unmount(mountpoint, fd);
if (ch)
fuse_chan_destroy(ch);
}
if (mountpoint) {
int fd = ch ? fuse_chan_clearfd(ch) : -1;
fuse_kern_unmount(mountpoint, fd);
if (ch)
fuse_chan_destroy(ch);
}
}
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
{
fuse_unmount_common(mountpoint, ch);
fuse_unmount_common(mountpoint, ch);
}
struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
void *user_data,
int compat)
struct fuse *
fuse_setup_common(int argc,
char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *fd)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
struct fuse *fuse;
int foreground;
int res;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
struct fuse *fuse;
int foreground;
int res;
res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
if (res == -1)
return NULL;
res = fuse_parse_cmdline(&args, mountpoint, &foreground);
if (res == -1)
return NULL;
ch = fuse_mount_common(*mountpoint, &args);
if (!ch) {
fuse_opt_free_args(&args);
goto err_free;
}
ch = fuse_mount_common(*mountpoint, &args);
if (!ch) {
fuse_opt_free_args(&args);
goto err_free;
}
fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat);
fuse_opt_free_args(&args);
if (fuse == NULL)
goto err_unmount;
fuse = fuse_new_common(ch, &args, op, op_size);
fuse_opt_free_args(&args);
if (fuse == NULL)
goto err_unmount;
res = fuse_daemonize(foreground);
if (res == -1)
goto err_unmount;
res = fuse_daemonize(foreground);
if (res == -1)
goto err_unmount;
res = fuse_set_signal_handlers(fuse_get_session(fuse));
if (res == -1)
goto err_unmount;
res = fuse_set_signal_handlers(fuse_get_session(fuse));
if (res == -1)
goto err_unmount;
if (fd)
*fd = fuse_chan_fd(ch);
if (fd)
*fd = fuse_chan_fd(ch);
return fuse;
return fuse;
err_unmount:
fuse_unmount_common(*mountpoint, ch);
if (fuse)
fuse_destroy(fuse);
err_free:
free(*mountpoint);
return NULL;
err_unmount:
fuse_unmount_common(*mountpoint, ch);
if (fuse)
fuse_destroy(fuse);
err_free:
free(*mountpoint);
return NULL;
}
struct fuse *fuse_setup(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
char **mountpoint, int *multithreaded, void *user_data)
char **mountpoint)
{
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
multithreaded, NULL, user_data, 0);
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
NULL);
}
static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
{
struct fuse_session *se = fuse_get_session(fuse);
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
fuse_remove_signal_handlers(se);
fuse_unmount_common(mountpoint, ch);
fuse_destroy(fuse);
free(mountpoint);
struct fuse_session *se = fuse_get_session(fuse);
struct fuse_chan *ch = se->ch;
fuse_remove_signal_handlers(se);
fuse_unmount_common(mountpoint, ch);
fuse_destroy(fuse);
free(mountpoint);
}
void fuse_teardown(struct fuse *fuse, char *mountpoint)
{
fuse_teardown_common(fuse, mountpoint);
fuse_teardown_common(fuse, mountpoint);
}
static int fuse_main_common(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
void *user_data, int compat)
const struct fuse_operations *op, size_t op_size)
{
struct fuse *fuse;
char *mountpoint;
int multithreaded;
int res;
struct fuse *fuse;
char *mountpoint;
int res;
fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
&multithreaded, NULL, user_data, compat);
if (fuse == NULL)
return 1;
fuse = fuse_setup_common(argc, argv, op, op_size,
&mountpoint,
NULL);
if (fuse == NULL)
return 1;
if (multithreaded)
res = fuse_loop_mt(fuse);
else
res = fuse_loop(fuse);
res = fuse_loop_mt(fuse);
fuse_teardown_common(fuse, mountpoint);
if (res == -1)
return 1;
fuse_teardown_common(fuse, mountpoint);
if (res == -1)
return 1;
return 0;
return 0;
}
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
size_t op_size, void *user_data)
int fuse_main_real(int argc,
char *argv[],
const struct fuse_operations *op,
size_t op_size)
{
return fuse_main_common(argc, argv, op, op_size, user_data, 0);
return fuse_main_common(argc, argv, op, op_size);
}
#undef fuse_main
int fuse_main(void);
int fuse_main(void)
{
fprintf(stderr, "fuse_main(): This function does not exist\n");
return -1;
fprintf(stderr, "fuse_main(): This function does not exist\n");
return -1;
}
int fuse_version(void)
{
return FUSE_VERSION;
return FUSE_VERSION;
}
#include "fuse_compat.h"
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
struct fuse *fuse_setup_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd)
{
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
op_size, mountpoint, multithreaded, fd, NULL,
22);
}
struct fuse *fuse_setup_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op,
char **mountpoint, int *multithreaded,
int *fd)
{
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat2),
mountpoint, multithreaded, fd, NULL, 21);
}
int fuse_main_real_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size)
{
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
op_size, NULL, 22);
}
void fuse_main_compat1(int argc, char *argv[],
const struct fuse_operations_compat1 *op)
{
fuse_main_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat1), NULL, 11);
}
int fuse_main_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op)
{
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat2), NULL,
21);
}
int fuse_mount_compat1(const char *mountpoint, const char *args[])
{
/* just ignore mount args for now */
(void) args;
return fuse_mount_compat22(mountpoint, NULL);
}
FUSE_SYMVER(".symver fuse_setup_compat2,__fuse_setup@");
FUSE_SYMVER(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
FUSE_SYMVER(".symver fuse_teardown,__fuse_teardown@");
FUSE_SYMVER(".symver fuse_main_compat2,fuse_main@");
FUSE_SYMVER(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
#endif /* __FreeBSD__ || __NetBSD__ */
struct fuse *fuse_setup_compat25(int argc, char *argv[],
const struct fuse_operations_compat25 *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd)
{
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
op_size, mountpoint, multithreaded, fd, NULL,
25);
}
int fuse_main_real_compat25(int argc, char *argv[],
const struct fuse_operations_compat25 *op,
size_t op_size)
{
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
op_size, NULL, 25);
}
void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint)
{
(void) fd;
fuse_teardown_common(fuse, mountpoint);
}
int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)
{
return fuse_kern_mount(mountpoint, args);
}
FUSE_SYMVER(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
FUSE_SYMVER(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
FUSE_SYMVER(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
FUSE_SYMVER(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
+627
View File
@@ -0,0 +1,627 @@
/* The MIT License
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
int ret, is_missing;
khiter_t k;
khash_t(32) *h = kh_init(32);
k = kh_put(32, h, 5, &ret);
kh_value(h, k) = 10;
k = kh_get(32, h, 10);
is_missing = (k == kh_end(h));
k = kh_get(32, h, 5);
kh_del(32, h, k);
for (k = kh_begin(h); k != kh_end(h); ++k)
if (kh_exist(h, k)) kh_value(h, k) = 1;
kh_destroy(32, h);
return 0;
}
*/
/*
2013-05-02 (0.2.8):
* Use quadratic probing. When the capacity is power of 2, stepping function
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
hashing on cache performance and is more robust than linear probing.
In theory, double hashing should be more robust than quadratic probing.
However, my implementation is probably not for large hash tables, because
the second hash function is closely tied to the first hash function,
which reduce the effectiveness of double hashing.
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
2011-09-16 (0.2.6):
* The capacity is a power of 2. This seems to dramatically improve the
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
- http://code.google.com/p/ulib/
- http://nothings.org/computer/judy/
* Allow to optionally use linear probing which usually has better
performance for random input. Double hashing is still the default as it
is more robust to certain non-random input.
* Added Wang's integer hash function (not used by default). This hash
function is more robust to certain non-random input.
2011-02-14 (0.2.5):
* Allow to declare global functions.
2009-09-26 (0.2.4):
* Improve portability
2008-09-19 (0.2.3):
* Corrected the example
* Improved interfaces
2008-09-11 (0.2.2):
* Improved speed a little in kh_put()
2008-09-10 (0.2.1):
* Added kh_clear()
* Fixed a compiling error
2008-09-02 (0.2.0):
* Changed to token concatenation which increases flexibility.
2008-08-31 (0.1.2):
* Fixed a bug in kh_get(), which has not been tested previously.
2008-08-31 (0.1.1):
* Added destructor
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
/*!
@header
Generic hash table library.
*/
#define AC_VERSION_KHASH_H "0.2.8"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* compiler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
#ifndef kh_inline
#ifdef _MSC_VER
#define kh_inline __inline
#else
#define kh_inline inline
#endif
#endif /* kh_inline */
#ifndef klib_unused
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
#define klib_unused __attribute__ ((__unused__))
#else
#define klib_unused
#endif
#endif /* klib_unused */
typedef khint32_t khint_t;
typedef khint_t khiter_t;
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
#ifndef kcalloc
#define kcalloc(N,Z) calloc(N,Z)
#endif
#ifndef kmalloc
#define kmalloc(Z) malloc(Z)
#endif
#ifndef krealloc
#define krealloc(P,Z) realloc(P,Z)
#endif
#ifndef kfree
#define kfree(P) free(P)
#endif
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct kh_##name##_s { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
kfree((void *)h->keys); kfree(h->flags); \
kfree((void *)h->vals); \
kfree(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (!new_keys) { kfree(new_flags); return -1; } \
h->keys = new_keys; \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
if (!new_vals) { kfree(new_flags); return -1; } \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
return 0; \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) { \
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
*ret = -1; return h->n_buckets; \
} \
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
*ret = -1; return h->n_buckets; \
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
/* --- BEGIN OF HASH FUNCTIONS --- */
/*! @function
@abstract Integer hash function
@param key The integer [khint32_t]
@return The hash value [khint_t]
*/
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
@abstract Integer comparison function
*/
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract 64-bit integer hash function
@param key The integer [khint64_t]
@return The hash value [khint_t]
*/
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
@abstract 64-bit integer comparison function
*/
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract const char* hash function
@param s Pointer to a null terminated string
@return The hash value
*/
static kh_inline khint_t __ac_X31_hash_string(const char *s)
{
khint_t h = (khint_t)*s;
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
return h;
}
/*! @function
@abstract Another interface to const char* hash function
@param key Pointer to a null terminated string [const char*]
@return The hash value [khint_t]
*/
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
@abstract Const char* comparison function
*/
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
static kh_inline khint_t __ac_Wang_hash(khint_t key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
/* --- END OF HASH FUNCTIONS --- */
/* Other convenient macros... */
/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: -1 if the operation failed;
0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
(vvar) = kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the values in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(vvar) = kh_val(h,__i); \
code; \
} }
/* More convenient interfaces */
/*! @function
@abstract Instantiate a hash set containing integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT(name) \
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT(name, khval_t) \
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash set containing 64-bit integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT64(name) \
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT64(name, khval_t) \
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
typedef const char *kh_cstr_t;
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_STR(name) \
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#endif /* __AC_KHASH_H */
+243
View File
@@ -0,0 +1,243 @@
/*
ISC License
Copyright (c) 2020, 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 "fmp.h"
#include <pthread.h>
typedef struct lfmp_t lfmp_t;
struct lfmp_t
{
fmp_t fmp;
pthread_mutex_t lock;
};
static
inline
void
lfmp_init(lfmp_t *lfmp_,
const uint64_t obj_size_,
const uint64_t page_multiple_)
{
fmp_init(&lfmp_->fmp,obj_size_,page_multiple_);
pthread_mutex_init(&lfmp_->lock,NULL);
}
static
inline
void
lfmp_lock(lfmp_t *lfmp_)
{
pthread_mutex_lock(&lfmp_->lock);
}
static
inline
void
lfmp_unlock(lfmp_t *lfmp_)
{
pthread_mutex_unlock(&lfmp_->lock);
}
static
inline
uint64_t
lfmp_slab_count(lfmp_t *lfmp_)
{
uint64_t rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_slab_count(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
int
lfmp_slab_alloc(lfmp_t *lfmp_)
{
int rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_slab_alloc(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void*
lfmp_alloc(lfmp_t *lfmp_)
{
void *rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_alloc(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void*
lfmp_calloc(lfmp_t *lfmp_)
{
void *rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_calloc(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void
lfmp_free(lfmp_t *lfmp_,
void *obj_)
{
pthread_mutex_lock(&lfmp_->lock);
fmp_free(&lfmp_->fmp,obj_);
pthread_mutex_unlock(&lfmp_->lock);
}
static
inline
void
lfmp_clear(lfmp_t *lfmp_)
{
pthread_mutex_lock(&lfmp_->lock);
fmp_clear(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
}
static
inline
void
lfmp_destroy(lfmp_t *lfmp_)
{
pthread_mutex_lock(&lfmp_->lock);
fmp_destroy(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
pthread_mutex_destroy(&lfmp_->lock);
}
static
inline
uint64_t
lfmp_avail_objs(lfmp_t *lfmp_)
{
uint64_t rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_avail_objs(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_objs_in_slab(lfmp_t *lfmp_,
void *slab_)
{
uint64_t rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_objs_in_slab(&lfmp_->fmp,slab_);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void
lfmp_remove_objs_in_slab(lfmp_t *lfmp_,
void *slab_)
{
pthread_mutex_lock(&lfmp_->lock);
fmp_remove_objs_in_slab(&lfmp_->fmp,slab_);
pthread_mutex_unlock(&lfmp_->lock);
}
static
inline
int
lfmp_gc(lfmp_t *lfmp_)
{
int rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_gc(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_objs_per_slab(lfmp_t *lfmp_)
{
uint64_t rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_objs_per_slab(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
double
lfmp_slab_usage_ratio(lfmp_t *lfmp_)
{
double rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_slab_usage_ratio(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_total_allocated_memory(lfmp_t *lfmp_)
{
uint64_t rv;
pthread_mutex_lock(&lfmp_->lock);
rv = fmp_total_allocated_memory(&lfmp_->fmp);
pthread_mutex_unlock(&lfmp_->lock);
return rv;
}
+206 -272
View File
@@ -28,364 +28,298 @@
#define FUSE_DEV_TRUNK "/dev/fuse"
enum {
KEY_ALLOW_ROOT,
KEY_RO,
KEY_HELP,
KEY_VERSION,
KEY_KERN
KEY_RO,
KEY_HELP,
KEY_VERSION,
KEY_KERN
};
struct mount_opts {
int allow_other;
int allow_root;
int ishelp;
char *kernel_opts;
int allow_other;
int ishelp;
char *kernel_opts;
};
#define FUSE_DUAL_OPT_KEY(templ, key) \
FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
static const struct fuse_opt fuse_mount_opts[] = {
{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
/* standard FreeBSD mount options */
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
FUSE_DUAL_OPT_KEY("async", KEY_KERN),
FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
FUSE_DUAL_OPT_KEY("union", KEY_KERN),
FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
FUSE_DUAL_OPT_KEY("force", KEY_KERN),
FUSE_DUAL_OPT_KEY("update", KEY_KERN),
FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
/* options supported under both Linux and FBSD */
FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
FUSE_OPT_KEY("max_read=", KEY_KERN),
FUSE_OPT_KEY("subtype=", KEY_KERN),
/* FBSD FUSE specific mount options */
FUSE_DUAL_OPT_KEY("private", KEY_KERN),
FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
/* stock FBSD mountopt parsing routine lets anything be negated... */
/*
* Linux specific mount options, but let just the mount util
* handle them
*/
FUSE_OPT_KEY("fsname=", KEY_KERN),
FUSE_OPT_KEY("nonempty", KEY_KERN),
FUSE_OPT_KEY("large_read", KEY_KERN),
FUSE_OPT_END
{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
/* standard FreeBSD mount options */
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
FUSE_DUAL_OPT_KEY("async", KEY_KERN),
FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
FUSE_DUAL_OPT_KEY("union", KEY_KERN),
FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
FUSE_DUAL_OPT_KEY("force", KEY_KERN),
FUSE_DUAL_OPT_KEY("update", KEY_KERN),
FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
/* options supported under both Linux and FBSD */
FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
FUSE_OPT_KEY("max_read=", KEY_KERN),
FUSE_OPT_KEY("subtype=", KEY_KERN),
/* FBSD FUSE specific mount options */
FUSE_DUAL_OPT_KEY("private", KEY_KERN),
FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
/* stock FBSD mountopt parsing routine lets anything be negated... */
/*
* Linux specific mount options, but let just the mount util
* handle them
*/
FUSE_OPT_KEY("fsname=", KEY_KERN),
FUSE_OPT_KEY("nonempty", KEY_KERN),
FUSE_OPT_KEY("large_read", KEY_KERN),
FUSE_OPT_END
};
static void mount_help(void)
{
fprintf(stderr,
" -o allow_root allow access to root\n"
);
system(FUSERMOUNT_PROG " --help");
fputc('\n', stderr);
system(FUSERMOUNT_PROG " --help");
fputc('\n', stderr);
}
static void mount_version(void)
{
system(FUSERMOUNT_PROG " --version");
system(FUSERMOUNT_PROG " --version");
}
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct mount_opts *mo = data;
struct mount_opts *mo = data;
switch (key) {
case KEY_ALLOW_ROOT:
if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
fuse_opt_add_arg(outargs, "-oallow_root") == -1)
return -1;
return 0;
switch (key) {
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_KERN:
return fuse_opt_add_opt(&mo->kernel_opts, arg);
case KEY_KERN:
return fuse_opt_add_opt(&mo->kernel_opts, arg);
case KEY_HELP:
mount_help();
mo->ishelp = 1;
break;
case KEY_HELP:
mount_help();
mo->ishelp = 1;
break;
case KEY_VERSION:
mount_version();
mo->ishelp = 1;
break;
}
return 1;
}
void fuse_unmount_compat22(const char *mountpoint)
{
char dev[128];
char *ssc, *umount_cmd;
FILE *sf;
int rv;
char seekscript[] =
/* error message is annoying in help output */
"exec 2>/dev/null; "
"/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
"/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
" { if ($3 == %d) print $10; }' | "
"/usr/bin/sort | "
"/usr/bin/uniq | "
"/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
(void) mountpoint;
/*
* If we don't know the fd, we have to resort to the scripted
* solution -- iterating over the fd-s is unpractical, as we
* don't know how many of open files we have. (This could be
* looked up in procfs -- however, that's optional on FBSD; or
* read out from the kmem -- however, that's bound to
* privileges (in fact, that's what happens when we call the
* setgid kmem fstat(1) utility).
*/
if (asprintf(&ssc, seekscript, getpid()) == -1)
return;
errno = 0;
sf = popen(ssc, "r");
free(ssc);
if (! sf)
return;
fgets(dev, sizeof(dev), sf);
rv = pclose(sf);
if (rv)
return;
if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
return;
system(umount_cmd);
free(umount_cmd);
case KEY_VERSION:
mount_version();
mo->ishelp = 1;
break;
}
return 1;
}
static void do_unmount(char *dev, int fd)
{
char device_path[SPECNAMELEN + 12];
const char *argv[4];
const char umount_cmd[] = "/sbin/umount";
pid_t pid;
char device_path[SPECNAMELEN + 12];
const char *argv[4];
const char umount_cmd[] = "/sbin/umount";
pid_t pid;
snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
argv[0] = umount_cmd;
argv[1] = "-f";
argv[2] = device_path;
argv[3] = NULL;
argv[0] = umount_cmd;
argv[1] = "-f";
argv[2] = device_path;
argv[3] = NULL;
pid = fork();
pid = fork();
if (pid == -1)
return;
if (pid == -1)
return;
if (pid == 0) {
close(fd);
execvp(umount_cmd, (char **)argv);
exit(1);
}
if (pid == 0) {
close(fd);
execvp(umount_cmd, (char **)argv);
exit(1);
}
waitpid(pid, NULL, 0);
waitpid(pid, NULL, 0);
}
void fuse_kern_unmount(const char *mountpoint, int fd)
{
char *ep, dev[128];
struct stat sbuf;
char *ep, dev[128];
struct stat sbuf;
(void)mountpoint;
(void)mountpoint;
if (fstat(fd, &sbuf) == -1)
goto out;
if (fstat(fd, &sbuf) == -1)
goto out;
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
if (strncmp(dev, "fuse", 4))
goto out;
if (strncmp(dev, "fuse", 4))
goto out;
strtol(dev + 4, &ep, 10);
if (*ep != '\0')
goto out;
strtol(dev + 4, &ep, 10);
if (*ep != '\0')
goto out;
do_unmount(dev, fd);
do_unmount(dev, fd);
out:
close(fd);
out:
close(fd);
}
/* Check if kernel is doing init in background */
static int init_backgrounded(void)
{
unsigned ibg, len;
unsigned ibg, len;
len = sizeof(ibg);
len = sizeof(ibg);
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
return 0;
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
return 0;
return ibg;
return ibg;
}
static int fuse_mount_core(const char *mountpoint, const char *opts)
{
const char *mountprog = FUSERMOUNT_PROG;
int fd;
char *fdnam, *dev;
pid_t pid, cpid;
int status;
const char *mountprog = FUSERMOUNT_PROG;
int fd;
char *fdnam, *dev;
pid_t pid, cpid;
int status;
fdnam = getenv("FUSE_DEV_FD");
fdnam = getenv("FUSE_DEV_FD");
if (fdnam) {
char *ep;
if (fdnam) {
char *ep;
fd = strtol(fdnam, &ep, 10);
fd = strtol(fdnam, &ep, 10);
if (*ep != '\0') {
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
return -1;
}
if (*ep != '\0') {
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
return -1;
}
if (fd < 0)
return -1;
if (fd < 0)
return -1;
goto mount;
}
goto mount;
}
dev = getenv("FUSE_DEV_NAME");
dev = getenv("FUSE_DEV_NAME");
if (! dev)
dev = (char *)FUSE_DEV_TRUNK;
if (! dev)
dev = (char *)FUSE_DEV_TRUNK;
if ((fd = open(dev, O_RDWR)) < 0) {
perror("fuse: failed to open fuse device");
return -1;
}
if ((fd = open(dev, O_RDWR)) < 0) {
perror("fuse: failed to open fuse device");
return -1;
}
mount:
if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
goto out;
mount:
if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
goto out;
pid = fork();
cpid = pid;
pid = fork();
cpid = pid;
if (pid == -1) {
perror("fuse: fork() failed");
close(fd);
return -1;
}
if (pid == -1) {
perror("fuse: fork() failed");
close(fd);
return -1;
}
if (pid == 0) {
if (! init_backgrounded()) {
/*
* If init is not backgrounded, we have to
* call the mount util backgrounded, to avoid
* deadlock.
*/
if (pid == 0) {
if (! init_backgrounded()) {
/*
* If init is not backgrounded, we have to
* call the mount util backgrounded, to avoid
* deadlock.
*/
pid = fork();
pid = fork();
if (pid == -1) {
perror("fuse: fork() failed");
close(fd);
exit(1);
}
}
if (pid == -1) {
perror("fuse: fork() failed");
close(fd);
exit(1);
}
}
if (pid == 0) {
const char *argv[32];
int a = 0;
if (pid == 0) {
const char *argv[32];
int a = 0;
if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
perror("fuse: failed to assemble mount arguments");
exit(1);
}
if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
perror("fuse: failed to assemble mount arguments");
exit(1);
}
argv[a++] = mountprog;
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
}
argv[a++] = fdnam;
argv[a++] = mountpoint;
argv[a++] = NULL;
execvp(mountprog, (char **) argv);
perror("fuse: failed to exec mount program");
exit(1);
}
argv[a++] = mountprog;
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
}
argv[a++] = fdnam;
argv[a++] = mountpoint;
argv[a++] = NULL;
execvp(mountprog, (char **) argv);
perror("fuse: failed to exec mount program");
exit(1);
}
exit(0);
}
exit(0);
}
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
perror("fuse: failed to mount file system");
close(fd);
return -1;
}
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
perror("fuse: failed to mount file system");
close(fd);
return -1;
}
out:
return fd;
out:
return fd;
}
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
{
struct mount_opts mo;
int res = -1;
struct mount_opts mo;
int res = -1;
memset(&mo, 0, sizeof(mo));
/* mount util should not try to spawn the daemon */
setenv("MOUNT_FUSEFS_SAFE", "1", 1);
/* to notify the mount util it's called from lib */
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
memset(&mo, 0, sizeof(mo));
/* mount util should not try to spawn the daemon */
setenv("MOUNT_FUSEFS_SAFE", "1", 1);
/* to notify the mount util it's called from lib */
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
if (args &&
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
return -1;
if (args &&
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
return -1;
if (mo.allow_other && mo.allow_root) {
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
goto out;
}
if (mo.ishelp)
return 0;
if (mo.ishelp)
return 0;
res = fuse_mount_core(mountpoint, mo.kernel_opts);
out:
free(mo.kernel_opts);
return res;
res = fuse_mount_core(mountpoint, mo.kernel_opts);
out:
free(mo.kernel_opts);
return res;
}
FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
File diff suppressed because it is too large Load Diff
+251 -251
View File
@@ -31,333 +31,333 @@
#else
static int mtab_needs_update(const char *mnt)
{
int res;
struct stat stbuf;
int res;
struct stat stbuf;
/* If mtab is within new mount, don't touch it */
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
_PATH_MOUNTED[strlen(mnt)] == '/')
return 0;
/* If mtab is within new mount, don't touch it */
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
_PATH_MOUNTED[strlen(mnt)] == '/')
return 0;
/*
* Skip mtab update if /etc/mtab:
*
* - doesn't exist,
* - is a symlink,
* - is on a read-only filesystem.
*/
res = lstat(_PATH_MOUNTED, &stbuf);
if (res == -1) {
if (errno == ENOENT)
return 0;
} else {
uid_t ruid;
int err;
/*
* Skip mtab update if /etc/mtab:
*
* - doesn't exist,
* - is a symlink,
* - is on a read-only filesystem.
*/
res = lstat(_PATH_MOUNTED, &stbuf);
if (res == -1) {
if (errno == ENOENT)
return 0;
} else {
uid_t ruid;
int err;
if (S_ISLNK(stbuf.st_mode))
return 0;
if (S_ISLNK(stbuf.st_mode))
return 0;
ruid = getuid();
if (ruid != 0)
setreuid(0, -1);
ruid = getuid();
if (ruid != 0)
setreuid(0, -1);
res = access(_PATH_MOUNTED, W_OK);
err = (res == -1) ? errno : 0;
if (ruid != 0)
setreuid(ruid, -1);
res = access(_PATH_MOUNTED, W_OK);
err = (res == -1) ? errno : 0;
if (ruid != 0)
setreuid(ruid, -1);
if (err == EROFS)
return 0;
}
if (err == EROFS)
return 0;
}
return 1;
return 1;
}
#endif /* __NetBSD__ */
static int add_mount(const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts)
const char *mnt, const char *type, const char *opts)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0)
res = -1;
if (status != 0)
res = -1;
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
return res;
}
int fuse_mnt_add_mount(const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts)
{
if (!mtab_needs_update(mnt))
return 0;
if (!mtab_needs_update(mnt))
return 0;
return add_mount(progname, fsname, mnt, type, opts);
return add_mount(progname, fsname, mnt, type, opts);
}
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
if (lazy) {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
"-l", NULL, &env);
} else {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
NULL, &env);
}
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
if (lazy) {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
"-l", NULL, &env);
} else {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
NULL, &env);
}
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0) {
res = -1;
}
if (status != 0) {
res = -1;
}
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
}
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
const char *rel_mnt, int lazy)
{
int res;
int res;
if (!mtab_needs_update(abs_mnt)) {
res = umount2(rel_mnt, lazy ? 2 : 0);
if (res == -1)
fprintf(stderr, "%s: failed to unmount %s: %s\n",
progname, abs_mnt, strerror(errno));
return res;
}
if (!mtab_needs_update(abs_mnt)) {
res = umount2(rel_mnt, lazy ? 2 : 0);
if (res == -1)
fprintf(stderr, "%s: failed to unmount %s: %s\n",
progname, abs_mnt, strerror(errno));
return res;
}
return exec_umount(progname, rel_mnt, lazy);
return exec_umount(progname, rel_mnt, lazy);
}
static int remove_mount(const char *progname, const char *mnt)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
"--fake", mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
"--fake", mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0)
res = -1;
if (status != 0)
res = -1;
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
}
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
{
if (!mtab_needs_update(mnt))
return 0;
if (!mtab_needs_update(mnt))
return 0;
return remove_mount(progname, mnt);
return remove_mount(progname, mnt);
}
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
{
char buf[PATH_MAX];
char *copy;
char *dst;
char *end;
char *lastcomp;
const char *toresolv;
char buf[PATH_MAX];
char *copy;
char *dst;
char *end;
char *lastcomp;
const char *toresolv;
if (!orig[0]) {
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
orig);
return NULL;
}
if (!orig[0]) {
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
orig);
return NULL;
}
copy = strdup(orig);
if (copy == NULL) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return NULL;
}
copy = strdup(orig);
if (copy == NULL) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return NULL;
}
toresolv = copy;
lastcomp = NULL;
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
if (end[0] != '/') {
char *tmp;
end[1] = '\0';
tmp = strrchr(copy, '/');
if (tmp == NULL) {
lastcomp = copy;
toresolv = ".";
} else {
lastcomp = tmp + 1;
if (tmp == copy)
toresolv = "/";
}
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
lastcomp = NULL;
toresolv = copy;
}
else if (tmp)
tmp[0] = '\0';
}
if (realpath(toresolv, buf) == NULL) {
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
strerror(errno));
free(copy);
return NULL;
}
if (lastcomp == NULL)
dst = strdup(buf);
else {
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
if (dst) {
unsigned buflen = strlen(buf);
if (buflen && buf[buflen-1] == '/')
sprintf(dst, "%s%s", buf, lastcomp);
else
sprintf(dst, "%s/%s", buf, lastcomp);
}
}
free(copy);
if (dst == NULL)
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return dst;
toresolv = copy;
lastcomp = NULL;
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
if (end[0] != '/') {
char *tmp;
end[1] = '\0';
tmp = strrchr(copy, '/');
if (tmp == NULL) {
lastcomp = copy;
toresolv = ".";
} else {
lastcomp = tmp + 1;
if (tmp == copy)
toresolv = "/";
}
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
lastcomp = NULL;
toresolv = copy;
}
else if (tmp)
tmp[0] = '\0';
}
if (realpath(toresolv, buf) == NULL) {
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
strerror(errno));
free(copy);
return NULL;
}
if (lastcomp == NULL)
dst = strdup(buf);
else {
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
if (dst) {
unsigned buflen = strlen(buf);
if (buflen && buf[buflen-1] == '/')
sprintf(dst, "%s%s", buf, lastcomp);
else
sprintf(dst, "%s/%s", buf, lastcomp);
}
}
free(copy);
if (dst == NULL)
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return dst;
}
int fuse_mnt_check_empty(const char *progname, const char *mnt,
mode_t rootmode, off_t rootsize)
{
int isempty = 1;
int isempty = 1;
if (S_ISDIR(rootmode)) {
struct dirent *ent;
DIR *dp = opendir(mnt);
if (dp == NULL) {
fprintf(stderr,
"%s: failed to open mountpoint for reading: %s\n",
progname, strerror(errno));
return -1;
}
while ((ent = readdir(dp)) != NULL) {
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0) {
isempty = 0;
break;
}
}
closedir(dp);
} else if (rootsize)
isempty = 0;
if (S_ISDIR(rootmode)) {
struct dirent *ent;
DIR *dp = opendir(mnt);
if (dp == NULL) {
fprintf(stderr,
"%s: failed to open mountpoint for reading: %s\n",
progname, strerror(errno));
return -1;
}
while ((ent = readdir(dp)) != NULL) {
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0) {
isempty = 0;
break;
}
}
closedir(dp);
} else if (rootsize)
isempty = 0;
if (!isempty) {
fprintf(stderr, "%s: mountpoint is not empty\n", progname);
fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
return -1;
}
return 0;
if (!isempty) {
fprintf(stderr, "%s: mountpoint is not empty\n", progname);
fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
return -1;
}
return 0;
}
int fuse_mnt_check_fuseblk(void)
{
char buf[256];
FILE *f = fopen("/proc/filesystems", "r");
if (!f)
return 1;
char buf[256];
FILE *f = fopen("/proc/filesystems", "r");
if (!f)
return 1;
while (fgets(buf, sizeof(buf), f))
if (strstr(buf, "fuseblk\n")) {
fclose(f);
return 1;
}
while (fgets(buf, sizeof(buf), f))
if (strstr(buf, "fuseblk\n")) {
fclose(f);
return 1;
}
fclose(f);
return 0;
fclose(f);
return 0;
}
+2
View File
@@ -6,6 +6,8 @@
See the file COPYING.LIB.
*/
#pragma once
#include <sys/types.h>
int fuse_mnt_add_mount(const char *progname, const char *fsname,
+161
View File
@@ -0,0 +1,161 @@
#pragma once
#include <tuple>
#include <atomic>
#include <vector>
#include <thread>
#include <memory>
#include <future>
#include <utility>
#include <stdexcept>
#include <functional>
#include <type_traits>
#include "queue.hpp"
class simple_thread_pool
{
public:
explicit simple_thread_pool(std::size_t thread_count = std::thread::hardware_concurrency())
{
if(!thread_count)
throw std::invalid_argument("bad thread count! must be non-zero!");
auto worker = [this]()
{
while(true)
{
proc_t f;
if(!m_queue.pop(f))
break;
f();
}
};
m_threads.reserve(thread_count);
while(thread_count--)
m_threads.emplace_back(worker);
}
~simple_thread_pool()
{
m_queue.unblock();
for(auto& thread : m_threads)
thread.join();
}
template<typename F, typename... Args>
void enqueue_work(F&& f, Args&&... args)
{
m_queue.push([p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); });
}
template<typename F, typename... Args>
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>
{
using task_return_type = std::invoke_result_t<F, Args...>;
using task_type = std::packaged_task<task_return_type()>;
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
auto result = task->get_future();
m_queue.push([=]() { (*task)(); });
return result;
}
private:
using proc_t = std::function<void(void)>;
using queue_t = unbounded_queue<proc_t>;
queue_t m_queue;
using threads_t = std::vector<std::thread>;
threads_t m_threads;
};
class thread_pool
{
public:
explicit thread_pool(std::size_t thread_count = std::thread::hardware_concurrency())
: m_queues(thread_count), m_count(thread_count)
{
if(!thread_count)
throw std::invalid_argument("bad thread count! must be non-zero!");
auto worker = [this](auto i)
{
while(true)
{
proc_t f;
for(std::size_t n = 0; n < m_count * K; ++n)
if(m_queues[(i + n) % m_count].try_pop(f))
break;
if(!f && !m_queues[i].pop(f))
break;
f();
}
};
m_threads.reserve(thread_count);
for(std::size_t i = 0; i < thread_count; ++i)
m_threads.emplace_back(worker, i);
}
~thread_pool()
{
for(auto& queue : m_queues)
queue.unblock();
for(auto& thread : m_threads)
thread.join();
}
template<typename F, typename... Args>
void enqueue_work(F&& f, Args&&... args)
{
auto work = [p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); };
auto i = m_index++;
for(std::size_t n = 0; n < m_count * K; ++n)
if(m_queues[(i + n) % m_count].try_push(work))
return;
m_queues[i % m_count].push(std::move(work));
}
template<typename F, typename... Args>
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>
{
using task_return_type = std::invoke_result_t<F, Args...>;
using task_type = std::packaged_task<task_return_type()>;
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
auto work = [=]() { (*task)(); };
auto result = task->get_future();
auto i = m_index++;
for(auto n = 0; n < m_count * K; ++n)
if(m_queues[(i + n) % m_count].try_push(work))
return result;
m_queues[i % m_count].push(std::move(work));
return result;
}
private:
using proc_t = std::function<void(void)>;
using queue_t = unbounded_queue<proc_t>;
using queues_t = std::vector<queue_t>;
queues_t m_queues;
using threads_t = std::vector<std::thread>;
threads_t m_threads;
const std::size_t m_count;
std::atomic_uint m_index = 0;
inline static const unsigned int K = 2;
};
+256
View File
@@ -0,0 +1,256 @@
#pragma once
#include <mutex>
#include <queue>
#include <utility>
#include <stdexcept>
#include <condition_variable>
template<typename T>
class unbounded_queue
{
public:
explicit unbounded_queue(bool block = true)
: m_block{ block } {}
void push(const T& item)
{
{
std::scoped_lock guard(m_queue_lock);
m_queue.push(item);
}
m_condition.notify_one();
}
void push(T&& item)
{
{
std::scoped_lock guard(m_queue_lock);
m_queue.push(std::move(item));
}
m_condition.notify_one();
}
template<typename... Args>
void emplace(Args&&... args)
{
{
std::scoped_lock guard(m_queue_lock);
m_queue.emplace(std::forward<Args>(args)...);
}
m_condition.notify_one();
}
bool try_push(const T& item)
{
{
std::unique_lock lock(m_queue_lock, std::try_to_lock);
if(!lock)
return false;
m_queue.push(item);
}
m_condition.notify_one();
return true;
}
bool try_push(T&& item)
{
{
std::unique_lock lock(m_queue_lock, std::try_to_lock);
if(!lock)
return false;
m_queue.push(std::move(item));
}
m_condition.notify_one();
return true;
}
bool pop(T& item)
{
std::unique_lock guard(m_queue_lock);
m_condition.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
if(m_queue.empty())
return false;
item = std::move(m_queue.front());
m_queue.pop();
return true;
}
bool try_pop(T& item)
{
std::unique_lock lock(m_queue_lock, std::try_to_lock);
if(!lock || m_queue.empty())
return false;
item = std::move(m_queue.front());
m_queue.pop();
return true;
}
std::size_t size() const
{
std::scoped_lock guard(m_queue_lock);
return m_queue.size();
}
bool empty() const
{
std::scoped_lock guard(m_queue_lock);
return m_queue.empty();
}
void block()
{
std::scoped_lock guard(m_queue_lock);
m_block = true;
}
void unblock()
{
{
std::scoped_lock guard(m_queue_lock);
m_block = false;
}
m_condition.notify_all();
}
bool blocking() const
{
std::scoped_lock guard(m_queue_lock);
return m_block;
}
private:
using queue_t = std::queue<T>;
queue_t m_queue;
bool m_block;
mutable std::mutex m_queue_lock;
std::condition_variable m_condition;
};
template<typename T>
class bounded_queue
{
public:
explicit bounded_queue(std::size_t max_size, bool block = true)
: m_block{ block }, m_max_size{ max_size }
{
if(!m_max_size)
throw std::invalid_argument("bad queue max-size! must be non-zero!");
}
bool push(const T& item)
{
{
std::unique_lock guard(m_queue_lock);
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
if(m_queue.size() == m_max_size)
return false;
m_queue.push(item);
}
m_condition_pop.notify_one();
return true;
}
bool push(T&& item)
{
{
std::unique_lock guard(m_queue_lock);
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
if(m_queue.size() == m_max_size)
return false;
m_queue.push(std::move(item));
}
m_condition_pop.notify_one();
return true;
}
template<typename... Args>
bool emplace(Args&&... args)
{
{
std::unique_lock guard(m_queue_lock);
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
if(m_queue.size() == m_max_size)
return false;
m_queue.emplace(std::forward<Args>(args)...);
}
m_condition_pop.notify_one();
return true;
}
bool pop(T& item)
{
{
std::unique_lock guard(m_queue_lock);
m_condition_pop.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
if(m_queue.empty())
return false;
item = std::move(m_queue.front());
m_queue.pop();
}
m_condition_push.notify_one();
return true;
}
std::size_t size() const
{
std::scoped_lock guard(m_queue_lock);
return m_queue.size();
}
std::size_t capacity() const
{
return m_max_size;
}
bool empty() const
{
std::scoped_lock guard(m_queue_lock);
return m_queue.empty();
}
bool full() const
{
std::scoped_lock lock(m_queue_lock);
return m_queue.size() == capacity();
}
void block()
{
std::scoped_lock guard(m_queue_lock);
m_block = true;
}
void unblock()
{
{
std::scoped_lock guard(m_queue_lock);
m_block = false;
}
m_condition_push.notify_all();
m_condition_pop.notify_all();
}
bool blocking() const
{
std::scoped_lock guard(m_queue_lock);
return m_block;
}
private:
using queue_t = std::queue<T>;
queue_t m_queue;
bool m_block;
const std::size_t m_max_size;
mutable std::mutex m_queue_lock;
std::condition_variable m_condition_push;
std::condition_variable m_condition_pop;
};
-444
View File
@@ -1,444 +0,0 @@
/*
libulockmgr: Userspace Lock Manager Library
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB
*/
/* #define DEBUG 1 */
#include "ulockmgr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
struct message {
unsigned intr : 1;
unsigned nofd : 1;
pthread_t thr;
int cmd;
int fd;
struct flock lock;
int error;
};
struct fd_store {
struct fd_store *next;
int fd;
int inuse;
};
struct owner {
struct owner *next;
struct owner *prev;
struct fd_store *fds;
void *id;
size_t id_len;
int cfd;
};
static pthread_mutex_t ulockmgr_lock;
static int ulockmgr_cfd = -1;
static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
#define MAX_SEND_FDS 2
static void list_del_owner(struct owner *owner)
{
struct owner *prev = owner->prev;
struct owner *next = owner->next;
prev->next = next;
next->prev = prev;
}
static void list_add_owner(struct owner *owner, struct owner *next)
{
struct owner *prev = next->prev;
owner->next = next;
owner->prev = prev;
prev->next = owner;
next->prev = owner;
}
/*
* There's a bug in the linux kernel (< 2.6.22) recv() implementation
* on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
* zero, even if data was available. Retrying the recv will return
* the data in this case.
*/
static int do_recv(int sock, void *buf, size_t len, int flags)
{
int res = recv(sock, buf, len, flags);
if (res == 0)
res = recv(sock, buf, len, flags);
return res;
}
static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
int *fdp, int numfds)
{
struct msghdr msg;
struct cmsghdr *p_cmsg;
struct iovec vec;
size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
int res;
assert(numfds <= MAX_SEND_FDS);
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
p_cmsg = CMSG_FIRSTHDR(&msg);
p_cmsg->cmsg_level = SOL_SOCKET;
p_cmsg->cmsg_type = SCM_RIGHTS;
p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
msg.msg_controllen = p_cmsg->cmsg_len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
vec.iov_base = buf;
vec.iov_len = buflen;
res = sendmsg(sock, &msg, MSG_NOSIGNAL);
if (res == -1) {
perror("libulockmgr: sendmsg");
return -1;
}
if ((size_t) res != buflen) {
fprintf(stderr, "libulockmgr: sendmsg short\n");
return -1;
}
return 0;
}
static int ulockmgr_start_daemon(void)
{
int sv[2];
int res;
char tmp[64];
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (res == -1) {
perror("libulockmgr: socketpair");
return -1;
}
snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
res = system(tmp);
close(sv[0]);
if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
close(sv[1]);
return -1;
}
ulockmgr_cfd = sv[1];
return 0;
}
static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
{
int sv[2];
int res;
char c = 'm';
struct owner *o;
if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
return NULL;
o = calloc(1, sizeof(struct owner) + id_len);
if (!o) {
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
return NULL;
}
o->id = o + 1;
o->id_len = id_len;
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (res == -1) {
perror("libulockmgr: socketpair");
goto out_free;
}
res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
close(sv[0]);
if (res == -1) {
close(ulockmgr_cfd);
ulockmgr_cfd = -1;
goto out_close;
}
o->cfd = sv[1];
memcpy(o->id, id, id_len);
list_add_owner(o, &owner_list);
return o;
out_close:
close(sv[1]);
out_free:
free(o);
return NULL;
}
static int ulockmgr_send_request(struct message *msg, const void *id,
size_t id_len)
{
int sv[2];
int cfd;
struct owner *o;
struct fd_store *f = NULL;
struct fd_store *newf = NULL;
struct fd_store **fp;
int fd = msg->fd;
int cmd = msg->cmd;
int res;
int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
msg->lock.l_start == 0 && msg->lock.l_len == 0);
for (o = owner_list.next; o != &owner_list; o = o->next)
if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
break;
if (o == &owner_list)
o = NULL;
if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
o = ulockmgr_new_owner(id, id_len);
if (!o) {
if (cmd == F_GETLK) {
res = fcntl(msg->fd, F_GETLK, &msg->lock);
return (res == -1) ? -errno : 0;
} else if (msg->lock.l_type == F_UNLCK)
return 0;
else
return -ENOLCK;
}
if (unlockall)
msg->nofd = 1;
else {
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
f = *fp;
if (f->fd == fd) {
msg->nofd = 1;
break;
}
}
}
if (!msg->nofd) {
newf = f = calloc(1, sizeof(struct fd_store));
if (!f) {
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
return -ENOLCK;
}
}
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (res == -1) {
perror("libulockmgr: socketpair");
free(newf);
return -ENOLCK;
}
cfd = sv[1];
sv[1] = msg->fd;
res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
msg->nofd ? 1 : 2);
close(sv[0]);
if (res == -1) {
free(newf);
close(cfd);
return -EIO;
}
if (newf) {
newf->fd = msg->fd;
newf->next = o->fds;
o->fds = newf;
}
if (f)
f->inuse++;
res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
if (res == -1) {
perror("libulockmgr: recv");
msg->error = EIO;
} else if (res != sizeof(struct message)) {
fprintf(stderr, "libulockmgr: recv short\n");
msg->error = EIO;
} else if (cmd == F_SETLKW && msg->error == EAGAIN) {
pthread_mutex_unlock(&ulockmgr_lock);
while (1) {
sigset_t old;
sigset_t unblock;
int errno_save;
sigemptyset(&unblock);
sigaddset(&unblock, SIGUSR1);
pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
res = do_recv(cfd, msg, sizeof(struct message),
MSG_WAITALL);
errno_save = errno;
pthread_sigmask(SIG_SETMASK, &old, NULL);
if (res == sizeof(struct message))
break;
else if (res >= 0) {
fprintf(stderr, "libulockmgr: recv short\n");
msg->error = EIO;
break;
} else if (errno_save != EINTR) {
errno = errno_save;
perror("libulockmgr: recv");
msg->error = EIO;
break;
}
msg->intr = 1;
res = send(o->cfd, msg, sizeof(struct message),
MSG_NOSIGNAL);
if (res == -1) {
perror("libulockmgr: send");
msg->error = EIO;
break;
}
if (res != sizeof(struct message)) {
fprintf(stderr, "libulockmgr: send short\n");
msg->error = EIO;
break;
}
}
pthread_mutex_lock(&ulockmgr_lock);
}
if (f)
f->inuse--;
close(cfd);
if (unlockall) {
for (fp = &o->fds; *fp;) {
f = *fp;
if (f->fd == fd && !f->inuse) {
*fp = f->next;
free(f);
} else
fp = &f->next;
}
if (!o->fds) {
list_del_owner(o);
close(o->cfd);
free(o);
}
/* Force OK on unlock-all, since it _will_ succeed once the
owner is deleted */
msg->error = 0;
}
return -msg->error;
}
#ifdef DEBUG
static uint32_t owner_hash(const unsigned char *id, size_t id_len)
{
uint32_t h = 0;
size_t i;
for (i = 0; i < id_len; i++)
h = ((h << 8) | (h >> 24)) ^ id[i];
return h;
}
#endif
static int ulockmgr_canonicalize(int fd, struct flock *lock)
{
off_t offset;
if (lock->l_whence == SEEK_CUR) {
offset = lseek(fd, 0, SEEK_CUR);
if (offset == (off_t) -1)
return -errno;
} else if (lock->l_whence == SEEK_END) {
struct stat stbuf;
int res = fstat(fd, &stbuf);
if (res == -1)
return -errno;
offset = stbuf.st_size;
} else
offset = 0;
lock->l_whence = SEEK_SET;
lock->l_start += offset;
if (lock->l_start < 0)
return -EINVAL;
if (lock->l_len < 0) {
lock->l_start += lock->l_len;
if (lock->l_start < 0)
return -EINVAL;
lock->l_len = -lock->l_len;
}
if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
return -EINVAL;
return 0;
}
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
size_t owner_len)
{
int err;
struct message msg;
sigset_t old;
sigset_t block;
if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
return -EINVAL;
if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
lock->l_type != F_UNLCK)
return -EINVAL;
if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
lock->l_whence != SEEK_END)
return -EINVAL;
#ifdef DEBUG
fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
owner_hash(owner, owner_len));
#endif
/* Unlock should never block anyway */
if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
cmd = F_SETLK;
memset(&msg, 0, sizeof(struct message));
msg.cmd = cmd;
msg.fd = fd;
msg.lock = *lock;
err = ulockmgr_canonicalize(fd, &msg.lock);
if (err)
return err;
sigemptyset(&block);
sigaddset(&block, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &block, &old);
pthread_mutex_lock(&ulockmgr_lock);
err = ulockmgr_send_request(&msg, owner, owner_len);
pthread_mutex_unlock(&ulockmgr_lock);
pthread_sigmask(SIG_SETMASK, &old, NULL);
if (!err && cmd == F_GETLK) {
if (msg.lock.l_type == F_UNLCK)
lock->l_type = F_UNLCK;
else
*lock = msg.lock;
}
return err;
}
-58
View File
@@ -1,58 +0,0 @@
## Process this file with automake to produce Makefile.in
AM_CPPFLAGS = -D_FILE_OFFSET_BITS=64
bin_PROGRAMS = fusermount ulockmgr_server
noinst_PROGRAMS = mount.fuse
# we re-use mount_util.c from the library, but do want to keep ourself
# as stand-alone as possible. in order to make an out-of-source build
# possible, we "generate" the file from its original location by
# copying it over.
fusermount_SOURCES = fusermount.c mount_util.c
fusermount_CPPFLAGS = -I$(top_srcdir)/lib
BUILT_SOURCES = mount_util.c
mount_util.c: $(top_srcdir)/lib/mount_util.c
@cp $(top_srcdir)/lib/mount_util.c .
mount_fuse_SOURCES = mount.fuse.c
ulockmgr_server_SOURCES = ulockmgr_server.c
ulockmgr_server_CPPFLAGS = -D_FILE_OFFSET_BITS=64 -D_REENTRANT
ulockmgr_server_LDFLAGS = -pthread
install-exec-hook:
-chmod u+s $(DESTDIR)$(bindir)/fusermount
@if test ! -e $(DESTDIR)/dev/fuse; then \
$(MKDIR_P) $(DESTDIR)/dev; \
echo "mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true"; \
mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true; \
fi
EXTRA_DIST = udev.rules init_script
MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@
UDEV_RULES_PATH = @UDEV_RULES_PATH@
INIT_D_PATH = @INIT_D_PATH@
install-exec-local:
$(MKDIR_P) $(DESTDIR)$(MOUNT_FUSE_PATH)
$(INSTALL_PROGRAM) $(builddir)/mount.fuse $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse
$(MKDIR_P) $(DESTDIR)$(INIT_D_PATH)
$(INSTALL_SCRIPT) $(srcdir)/init_script $(DESTDIR)$(INIT_D_PATH)/fuse
@if test -x /usr/sbin/update-rc.d; then \
echo "/usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true"; \
/usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true; \
fi
install-data-local:
$(MKDIR_P) $(DESTDIR)$(UDEV_RULES_PATH)
$(INSTALL_DATA) $(srcdir)/udev.rules $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules
uninstall-local:
rm -f $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse
rm -f $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules
rm -f $(DESTDIR)$(INIT_D_PATH)/fuse
@if test -x /usr/sbin/update-rc.d; then \
echo "/usr/sbin/update-rc.d fuse remove || true"; \
/usr/sbin/update-rc.d fuse remove || true; \
fi
+1 -3
View File
@@ -782,9 +782,7 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
skip_option = 1;
}
}
if (getuid() != 0 && !user_allow_other &&
(opt_eq(s, len, "allow_other") ||
opt_eq(s, len, "allow_root"))) {
if (getuid() != 0 && !user_allow_other && opt_eq(s, len, "allow_other")) {
fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
goto err;
}
-89
View File
@@ -1,89 +0,0 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: fuse
# Required-Start:
# Should-Start: udev
# Required-Stop:
# Default-Start: S
# Default-Stop:
# Short-Description: Start and stop fuse.
# Description: Load the fuse module and mount the fuse control
# filesystem.
### END INIT INFO
set -e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MOUNTPOINT=/sys/fs/fuse/connections
# Gracefully exit if the package has been removed.
which fusermount &>/dev/null || exit 5
case "$1" in
start|restart|force-reload)
if ! grep -qw fuse /proc/filesystems; then
echo -n "Loading fuse module"
if ! modprobe fuse >/dev/null 2>&1; then
echo " failed!"
exit 1
else
echo "."
fi
else
echo "Fuse filesystem already available."
fi
if grep -qw fusectl /proc/filesystems && \
! grep -qw $MOUNTPOINT /proc/mounts; then
echo -n "Mounting fuse control filesystem"
if ! mount -t fusectl fusectl $MOUNTPOINT >/dev/null 2>&1; then
echo " failed!"
exit 1
else
echo "."
fi
else
echo "Fuse control filesystem already available."
fi
;;
stop)
if ! grep -qw fuse /proc/filesystems; then
echo "Fuse filesystem not loaded."
exit 7
fi
if grep -qw $MOUNTPOINT /proc/mounts; then
echo -n "Unmounting fuse control filesystem"
if ! umount $MOUNTPOINT >/dev/null 2>&1; then
echo " failed!"
else
echo "."
fi
else
echo "Fuse control filesystem not mounted."
fi
if grep -qw "^fuse" /proc/modules; then
echo -n "Unloading fuse module"
if ! rmmod fuse >/dev/null 2>&1; then
echo " failed!"
else
echo "."
fi
else
echo "Fuse module not loaded."
fi
;;
status)
echo -n "Checking fuse filesystem"
if ! grep -qw fuse /proc/filesystems; then
echo " not available."
exit 3
else
echo " ok."
fi
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
-1
View File
@@ -1 +0,0 @@
KERNEL=="fuse", MODE="0666"
-425
View File
@@ -1,425 +0,0 @@
/*
ulockmgr_server: Userspace Lock Manager Server
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
/* #define DEBUG 1 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
struct message {
unsigned intr : 1;
unsigned nofd : 1;
pthread_t thr;
int cmd;
int fd;
struct flock lock;
int error;
};
struct fd_store {
struct fd_store *next;
int fd;
int origfd;
int inuse;
};
struct owner {
struct fd_store *fds;
pthread_mutex_t lock;
};
struct req_data {
struct owner *o;
int cfd;
struct fd_store *f;
struct message msg;
};
#define MAX_SEND_FDS 2
static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
int *numfds)
{
struct msghdr msg;
struct iovec iov;
size_t ccmsg[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
struct cmsghdr *cmsg;
int res;
int i;
assert(*numfds <= MAX_SEND_FDS);
iov.iov_base = buf;
iov.iov_len = buflen;
memset(&msg, 0, sizeof(msg));
memset(ccmsg, -1, sizeof(ccmsg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ccmsg;
msg.msg_controllen = sizeof(ccmsg);
res = recvmsg(sock, &msg, MSG_WAITALL);
if (!res) {
/* retry on zero return, see do_recv() in ulockmgr.c */
res = recvmsg(sock, &msg, MSG_WAITALL);
if (!res)
return 0;
}
if (res == -1) {
perror("ulockmgr_server: recvmsg");
return -1;
}
if ((size_t) res != buflen) {
fprintf(stderr, "ulockmgr_server: short message received\n");
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg) {
if (cmsg->cmsg_type != SCM_RIGHTS) {
fprintf(stderr,
"ulockmgr_server: unknown control message %d\n",
cmsg->cmsg_type);
return -1;
}
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
if (msg.msg_flags & MSG_CTRUNC) {
fprintf(stderr,
"ulockmgr_server: control message truncated\n");
for (i = 0; i < *numfds; i++)
close(fdp[i]);
*numfds = 0;
}
} else {
if (msg.msg_flags & MSG_CTRUNC) {
fprintf(stderr,
"ulockmgr_server: control message truncated(*)\n");
/* There's a bug in the Linux kernel, that if
not all file descriptors were allocated,
then the cmsg header is not filled in */
cmsg = (struct cmsghdr *) ccmsg;
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
for (i = 0; i < *numfds; i++)
close(fdp[i]);
}
*numfds = 0;
}
return res;
}
static int closefrom(int minfd)
{
DIR *dir = opendir("/proc/self/fd");
if (dir) {
int dfd = dirfd(dir);
struct dirent *ent;
while ((ent = readdir(dir))) {
char *end;
int fd = strtol(ent->d_name, &end, 10);
if (ent->d_name[0] && !end[0] && fd >= minfd &&
fd != dfd)
close(fd);
}
closedir(dir);
}
return 0;
}
static void send_reply(int cfd, struct message *msg)
{
int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
if (res == -1)
perror("ulockmgr_server: sending reply");
#ifdef DEBUG
fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
#endif
}
static void *process_request(void *d_)
{
struct req_data *d = d_;
int res;
assert(d->msg.cmd == F_SETLKW);
res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
if (res == -1 && errno == EAGAIN) {
d->msg.error = EAGAIN;
d->msg.thr = pthread_self();
send_reply(d->cfd, &d->msg);
res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
}
d->msg.error = (res == -1) ? errno : 0;
pthread_mutex_lock(&d->o->lock);
d->f->inuse--;
pthread_mutex_unlock(&d->o->lock);
send_reply(d->cfd, &d->msg);
close(d->cfd);
free(d);
return NULL;
}
static void process_message(struct owner *o, struct message *msg, int cfd,
int fd)
{
struct fd_store *f = NULL;
struct fd_store *newf = NULL;
struct fd_store **fp;
struct req_data *d;
pthread_t tid;
int res;
#ifdef DEBUG
fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
msg->cmd, msg->lock.l_type, msg->lock.l_whence,
msg->lock.l_start, msg->lock.l_len);
#endif
if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
msg->lock.l_start == 0 && msg->lock.l_len == 0) {
for (fp = &o->fds; *fp;) {
f = *fp;
if (f->origfd == msg->fd && !f->inuse) {
close(f->fd);
*fp = f->next;
free(f);
} else
fp = &f->next;
}
if (!msg->nofd)
close(fd);
msg->error = 0;
send_reply(cfd, msg);
close(cfd);
return;
}
if (msg->nofd) {
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
f = *fp;
if (f->origfd == msg->fd)
break;
}
if (!*fp) {
fprintf(stderr, "ulockmgr_server: fd %i not found\n",
msg->fd);
msg->error = EIO;
send_reply(cfd, msg);
close(cfd);
return;
}
} else {
newf = f = malloc(sizeof(struct fd_store));
if (!f) {
msg->error = ENOLCK;
send_reply(cfd, msg);
close(cfd);
return;
}
f->fd = fd;
f->origfd = msg->fd;
f->inuse = 0;
}
if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
msg->lock.l_type == F_UNLCK) {
res = fcntl(f->fd, msg->cmd, &msg->lock);
msg->error = (res == -1) ? errno : 0;
send_reply(cfd, msg);
close(cfd);
if (newf) {
newf->next = o->fds;
o->fds = newf;
}
return;
}
d = malloc(sizeof(struct req_data));
if (!d) {
msg->error = ENOLCK;
send_reply(cfd, msg);
close(cfd);
free(newf);
return;
}
f->inuse++;
d->o = o;
d->cfd = cfd;
d->f = f;
d->msg = *msg;
res = pthread_create(&tid, NULL, process_request, d);
if (res) {
msg->error = ENOLCK;
send_reply(cfd, msg);
close(cfd);
free(d);
f->inuse--;
free(newf);
return;
}
if (newf) {
newf->next = o->fds;
o->fds = newf;
}
pthread_detach(tid);
}
static void sigusr1_handler(int sig)
{
(void) sig;
/* Nothing to do */
}
static void process_owner(int cfd)
{
struct owner o;
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sigusr1_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("ulockmgr_server: cannot set sigusr1 signal handler");
exit(1);
}
memset(&o, 0, sizeof(struct owner));
pthread_mutex_init(&o.lock, NULL);
while (1) {
struct message msg;
int rfds[2];
int res;
int numfds = 2;
res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds);
if (!res)
break;
if (res == -1)
exit(1);
if (msg.intr) {
if (numfds != 0)
fprintf(stderr,
"ulockmgr_server: too many fds for intr\n");
pthread_kill(msg.thr, SIGUSR1);
} else {
if (numfds != 2)
continue;
pthread_mutex_lock(&o.lock);
process_message(&o, &msg, rfds[0], rfds[1]);
pthread_mutex_unlock(&o.lock);
}
}
if (o.fds)
fprintf(stderr,
"ulockmgr_server: open file descriptors on exit\n");
}
int main(int argc, char *argv[])
{
int nullfd;
char *end;
int cfd;
sigset_t empty;
if (argc != 2 || !argv[1][0])
goto out_inval;
cfd = strtol(argv[1], &end, 10);
if (*end)
goto out_inval;
/* demonize current process */
switch(fork()) {
case -1:
perror("ulockmgr_server: fork");
exit(1);
case 0:
break;
default:
_exit(0);
}
if (setsid() == -1) {
perror("ulockmgr_server: setsid");
exit(1);
}
(void) chdir("/");
sigemptyset(&empty);
sigprocmask(SIG_SETMASK, &empty, NULL);
if (dup2(cfd, 4) == -1) {
perror("ulockmgr_server: dup2");
exit(1);
}
cfd = 4;
nullfd = open("/dev/null", O_RDWR);
if (nullfd >= 0) {
dup2(nullfd, 0);
dup2(nullfd, 1);
}
close(3);
closefrom(5);
while (1) {
char c;
int sock;
int pid;
int numfds = 1;
int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds);
if (!res)
break;
if (res == -1)
exit(1);
assert(numfds == 1);
pid = fork();
if (pid == -1) {
perror("ulockmgr_server: fork");
close(sock);
continue;
}
if (pid == 0) {
close(cfd);
pid = fork();
if (pid == -1) {
perror("ulockmgr_server: fork");
_exit(1);
}
if (pid == 0)
process_owner(sock);
_exit(0);
}
waitpid(pid, NULL, 0);
close(sock);
}
return 0;
out_inval:
fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
return 1;
}
+1168 -991
View File
File diff suppressed because it is too large Load Diff
+59 -277
View File
@@ -18,298 +18,80 @@
#include "branch.hpp"
#include "ef.hpp"
#include "fs.hpp"
#include "fs_glob.hpp"
#include "str.hpp"
#include "errno.hpp"
#include "num.hpp"
#include <string>
#include <errno.h>
#include <fnmatch.h>
Branch::Branch(const uint64_t &default_minfreespace_)
: _default_minfreespace(&default_minfreespace_)
{
}
using std::string;
using std::vector;
int
Branch::from_string(const std::string &str_)
{
return -EINVAL;
}
std::string
Branch::to_string(void) const
{
std::string rv;
rv = path;
rv += '=';
switch(mode)
{
default:
case Branch::Mode::RW:
rv += "RW";
break;
case Branch::Mode::RO:
rv += "RO";
break;
case Branch::Mode::NC:
rv += "NC";
break;
}
if(_minfreespace.has_value())
{
rv += ',';
rv += num::humanize(_minfreespace.value());
}
return rv;
}
void
Branch::set_minfreespace(const uint64_t minfreespace_)
{
_minfreespace = minfreespace_;
}
uint64_t
Branch::minfreespace(void) const
{
if(_minfreespace.has_value())
return _minfreespace.value();
return *_default_minfreespace;
}
bool
Branch::ro(void) const
{
return (mode == Branch::RO);
return (mode == Branch::Mode::RO);
}
bool
Branch::nc(void) const
{
return (mode == Branch::NC);
return (mode == Branch::Mode::NC);
}
bool
Branch::ro_or_nc(void) const
{
return ((mode == Branch::RO) ||
(mode == Branch::NC));
}
static
void
split(const std::string &s_,
std::string *instr_,
std::string *values_)
{
uint64_t offset;
offset = s_.find_first_of('/');
*instr_ = s_.substr(0,offset);
if(offset != std::string::npos)
*values_ = s_.substr(offset);
}
Branches::Branches()
{
pthread_rwlock_init(&lock,NULL);
}
static
void
parse(const string &str_,
Branches &branches_)
{
string str;
Branch branch;
vector<string> globbed;
str = str_;
branch.mode = Branch::INVALID;
if(str::endswith(str,"=RO"))
branch.mode = Branch::RO;
ef(str::endswith(str,"=RW"))
branch.mode = Branch::RW;
ef(str::endswith(str,"=NC"))
branch.mode = Branch::NC;
if(branch.mode != Branch::INVALID)
str.resize(str.size() - 3);
else
branch.mode = Branch::RW;
fs::glob(str,globbed);
fs::realpathize(globbed);
for(size_t i = 0; i < globbed.size(); i++)
{
branch.path = globbed[i];
branches_.push_back(branch);
}
}
static
void
set(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
branches_.clear();
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches tmp;
parse(paths[i],tmp);
branches_.insert(branches_.end(),
tmp.begin(),
tmp.end());
}
}
static
void
add_begin(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches tmp;
parse(paths[i],tmp);
branches_.insert(branches_.begin(),
tmp.begin(),
tmp.end());
}
}
static
void
add_end(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches tmp;
parse(paths[i],tmp);
branches_.insert(branches_.end(),
tmp.begin(),
tmp.end());
}
}
static
void
erase_begin(Branches &branches_)
{
branches_.erase(branches_.begin());
}
static
void
erase_end(Branches &branches_)
{
branches_.pop_back();
}
static
void
erase_fnmatch(Branches &branches_,
const std::string &str_)
{
vector<string> patterns;
str::split(patterns,str_,':');
for(Branches::iterator i = branches_.begin();
i != branches_.end();)
{
int match = FNM_NOMATCH;
for(vector<string>::const_iterator pi = patterns.begin();
pi != patterns.end() && match != 0;
++pi)
{
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
}
i = ((match == 0) ? branches_.erase(i) : (i+1));
}
}
int
Branches::from_string(const std::string &s_)
{
rwlock::WriteGuard guard(&lock);
std::string instr;
std::string values;
::split(s_,&instr,&values);
if(instr == "+")
::add_end(*this,values);
ef(instr == "+<")
::add_begin(*this,values);
ef(instr == "+>")
::add_end(*this,values);
ef(instr == "-")
::erase_fnmatch(*this,values);
ef(instr == "-<")
::erase_begin(*this);
ef(instr == "->")
::erase_end(*this);
ef(instr == "=")
::set(*this,values);
ef(instr.empty())
::set(*this,values);
else
return -EINVAL;
return 0;
}
string
Branches::to_string(void) const
{
rwlock::ReadGuard guard(&lock);
string tmp;
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
tmp += branch.path;
tmp += '=';
switch(branch.mode)
{
default:
case Branch::RW:
tmp += "RW";
break;
case Branch::RO:
tmp += "RO";
break;
case Branch::NC:
tmp += "NC";
break;
}
tmp += ':';
}
if(*tmp.rbegin() == ':')
tmp.erase(tmp.size() - 1);
return tmp;
}
void
Branches::to_paths(vector<string> &vec_) const
{
rwlock::ReadGuard guard(&lock);
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
vec_.push_back(branch.path);
}
}
SrcMounts::SrcMounts(Branches &b_)
: _branches(b_)
{
}
int
SrcMounts::from_string(const std::string &s_)
{
return _branches.from_string(s_);
}
std::string
SrcMounts::to_string(void) const
{
rwlock::ReadGuard guard(&_branches.lock);
std::string rv;
for(uint64_t i = 0; i < _branches.size(); i++)
{
rv += _branches[i].path;
rv += ':';
}
if(*rv.rbegin() == ':')
rv.erase(rv.size() - 1);
return rv;
return ((mode == Branch::Mode::RO) ||
(mode == Branch::Mode::NC));
}
+24 -32
View File
@@ -18,58 +18,50 @@
#pragma once
#include "rwlock.hpp"
#include "nonstd/optional.hpp"
#include "strvec.hpp"
#include "tofrom_string.hpp"
#include <cstdint>
#include <string>
#include <vector>
#include <pthread.h>
class Branch
class Branch final : public ToFromString
{
public:
enum Mode
typedef std::vector<Branch> Vector;
public:
Branch(const uint64_t &default_minfreespace_);
public:
enum class Mode
{
INVALID,
RO,
RW,
NC
INVALID,
RO,
RW,
NC
};
Mode mode;
std::string path;
public:
bool ro(void) const;
bool nc(void) const;
bool ro_or_nc(void) const;
};
class Branches : public std::vector<Branch>, public ToFromString
{
public:
Branches();
public:
int from_string(const std::string &str);
std::string to_string(void) const;
int from_string(const std::string &str) final;
std::string to_string(void) const final;
public:
void to_paths(std::vector<std::string> &vec) const;
uint64_t minfreespace() const;
void set_minfreespace(const uint64_t);
public:
mutable pthread_rwlock_t lock;
};
class SrcMounts : public ToFromString
{
public:
SrcMounts(Branches &b_);
public:
int from_string(const std::string &str);
std::string to_string(void) const;
Mode mode;
std::string path;
private:
Branches &_branches;
nonstd::optional<uint64_t> _minfreespace;
const uint64_t *_default_minfreespace;
};
+431
View File
@@ -0,0 +1,431 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#include "branches.hpp"
#include "ef.hpp"
#include "errno.hpp"
#include "from_string.hpp"
#include "fs_glob.hpp"
#include "fs_realpathize.hpp"
#include "nonstd/optional.hpp"
#include "num.hpp"
#include "str.hpp"
#include <string>
#include <fnmatch.h>
using std::string;
using std::vector;
using nonstd::optional;
Branches::Impl::Impl(const uint64_t &default_minfreespace_)
: _default_minfreespace(default_minfreespace_)
{
}
Branches::Impl&
Branches::Impl::operator=(Branches::Impl &rval_)
{
auto this_base = dynamic_cast<Branch::Vector*>(this);
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
*this_base = *rval_base;
return *this;
}
Branches::Impl&
Branches::Impl::operator=(Branches::Impl &&rval_)
{
auto this_base = dynamic_cast<Branch::Vector*>(this);
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
*this_base = std::move(*rval_base);
return *this;
}
const
uint64_t&
Branches::Impl::minfreespace(void) const
{
return _default_minfreespace;
}
namespace l
{
static
void
split(const std::string &s_,
std::string *instr_,
std::string *values_)
{
uint64_t offset;
offset = s_.find_first_not_of("+<>-=");
if(offset > 1)
offset = 2;
*instr_ = s_.substr(0,offset);
if(offset != std::string::npos)
*values_ = s_.substr(offset);
}
static
int
parse_mode(const string &str_,
Branch::Mode *mode_)
{
if(str_ == "RW")
*mode_ = Branch::Mode::RW;
ef(str_ == "RO")
*mode_ = Branch::Mode::RO;
ef(str_ == "NC")
*mode_ = Branch::Mode::NC;
else
return -EINVAL;
return 0;
}
static
int
parse_minfreespace(const string &str_,
optional<uint64_t> *minfreespace_)
{
int rv;
uint64_t uint64;
rv = str::from(str_,&uint64);
if(rv < 0)
return rv;
*minfreespace_ = uint64;
return 0;
}
static
int
parse_branch(const string &str_,
string *glob_,
Branch::Mode *mode_,
optional<uint64_t> *minfreespace_)
{
int rv;
string options;
vector<string> v;
str::rsplit1(str_,'=',&v);
switch(v.size())
{
case 1:
*glob_ = v[0];
*mode_ = Branch::Mode::RW;
break;
case 2:
*glob_ = v[0];
options = v[1];
v.clear();
str::split(options,',',&v);
switch(v.size())
{
case 2:
rv = l::parse_minfreespace(v[1],minfreespace_);
if(rv < 0)
return rv;
case 1:
rv = l::parse_mode(v[0],mode_);
if(rv < 0)
return rv;
break;
case 0:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
static
int
parse(const string &str_,
Branches::Impl *branches_)
{
int rv;
string glob;
StrVec paths;
optional<uint64_t> minfreespace;
Branch branch(branches_->minfreespace());
rv = l::parse_branch(str_,&glob,&branch.mode,&minfreespace);
if(rv < 0)
return rv;
if(minfreespace.has_value())
branch.set_minfreespace(minfreespace.value());
fs::glob(glob,&paths);
fs::realpathize(&paths);
for(auto &path : paths)
{
branch.path = path;
branches_->push_back(branch);
}
return 0;
}
static
int
set(const std::string &str_,
Branches::Impl *branches_)
{
int rv;
StrVec paths;
Branches::Impl tmp_branches(branches_->minfreespace());
str::split(str_,':',&paths);
for(auto &path : paths)
{
rv = l::parse(path,&tmp_branches);
if(rv < 0)
return rv;
}
*branches_ = std::move(tmp_branches);
return 0;
}
static
int
add_begin(const std::string &str_,
Branches::Impl *branches_)
{
int rv;
vector<string> paths;
Branches::Impl tmp_branches(branches_->minfreespace());
str::split(str_,':',&paths);
for(auto &path : paths)
{
rv = l::parse(path,&tmp_branches);
if(rv < 0)
return rv;
}
branches_->insert(branches_->begin(),
tmp_branches.begin(),
tmp_branches.end());
return 0;
}
static
int
add_end(const std::string &str_,
Branches::Impl *branches_)
{
int rv;
StrVec paths;
Branches::Impl tmp_branches(branches_->minfreespace());
str::split(str_,':',&paths);
for(auto &path : paths)
{
rv = l::parse(path,&tmp_branches);
if(rv < 0)
return rv;
}
branches_->insert(branches_->end(),
tmp_branches.begin(),
tmp_branches.end());
return 0;
}
static
int
erase_begin(Branches::Impl *branches_)
{
branches_->erase(branches_->begin());
return 0;
}
static
int
erase_end(Branches::Impl *branches_)
{
branches_->pop_back();
return 0;
}
static
int
erase_fnmatch(const std::string &str_,
Branches::Impl *branches_)
{
StrVec patterns;
str::split(str_,':',&patterns);
for(auto i = branches_->begin(); i != branches_->end();)
{
int match = FNM_NOMATCH;
for(auto pi = patterns.cbegin(); pi != patterns.cend() && match != 0; ++pi)
{
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
}
i = ((match == 0) ? branches_->erase(i) : (i+1));
}
return 0;
}
}
int
Branches::Impl::from_string(const std::string &s_)
{
std::string instr;
std::string values;
l::split(s_,&instr,&values);
if(instr == "+")
return l::add_end(values,this);
if(instr == "+<")
return l::add_begin(values,this);
if(instr == "+>")
return l::add_end(values,this);
if(instr == "-")
return l::erase_fnmatch(values,this);
if(instr == "-<")
return l::erase_begin(this);
if(instr == "->")
return l::erase_end(this);
if(instr == "=")
return l::set(values,this);
if(instr.empty())
return l::set(values,this);
return -EINVAL;
}
std::string
Branches::Impl::to_string(void) const
{
string tmp;
if(empty())
return tmp;
for(auto &branch : *this)
{
tmp += branch.to_string();
tmp += ':';
}
tmp.pop_back();
return tmp;
}
void
Branches::Impl::to_paths(StrVec &paths_) const
{
for(auto &branch : *this)
{
paths_.push_back(branch.path);
}
}
int
Branches::from_string(const std::string &str_)
{
int rv;
Branches::Ptr impl;
Branches::Ptr new_impl;
{
std::lock_guard<std::mutex> lock_guard(_mutex);
impl = _impl;
}
new_impl = std::make_shared<Branches::Impl>(impl->minfreespace());
*new_impl = *impl;
rv = new_impl->from_string(str_);
if(rv < 0)
return rv;
{
std::lock_guard<std::mutex> lock_guard(_mutex);
_impl = new_impl;
}
return 0;
}
string
Branches::to_string(void) const
{
std::lock_guard<std::mutex> lock_guard(_mutex);
return _impl->to_string();
}
SrcMounts::SrcMounts(Branches &b_)
: _branches(b_)
{
}
int
SrcMounts::from_string(const std::string &s_)
{
return _branches.from_string(s_);
}
std::string
SrcMounts::to_string(void) const
{
std::string rv;
Branches::CPtr branches = _branches;
if(branches->empty())
return rv;
for(const auto &branch : *branches)
{
rv += branch.path;
rv += ':';
}
rv.pop_back();
return rv;
}
+94
View File
@@ -0,0 +1,94 @@
/*
ISC License
Copyright (c) 2021, 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 "branch.hpp"
#include "nonstd/optional.hpp"
#include "strvec.hpp"
#include "tofrom_string.hpp"
#include <cstdint>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
class Branches final : public ToFromString
{
public:
class Impl final : public ToFromString, public Branch::Vector
{
public:
typedef std::shared_ptr<Impl> Ptr;
typedef std::shared_ptr<const Impl> CPtr;
public:
Impl(const uint64_t &default_minfreespace_);
public:
int from_string(const std::string &str) final;
std::string to_string(void) const final;
public:
const uint64_t& minfreespace(void) const;
void to_paths(StrVec &strvec) const;
public:
Impl& operator=(Impl &impl_);
Impl& operator=(Impl &&impl_);
private:
const uint64_t &_default_minfreespace;
};
public:
typedef Branches::Impl::Ptr Ptr;
typedef Branches::Impl::CPtr CPtr;
public:
Branches(const uint64_t &default_minfreespace_)
: _impl(std::make_shared<Impl>(default_minfreespace_))
{}
public:
int from_string(const std::string &str) final;
std::string to_string(void) const final;
public:
operator CPtr() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
CPtr operator->() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
private:
mutable std::mutex _mutex;
Ptr _impl;
};
class SrcMounts : public ToFromString
{
public:
SrcMounts(Branches &b_);
public:
int from_string(const std::string &str) final;
std::string to_string(void) const final;
private:
Branches &_branches;
};
+7 -6
View File
@@ -17,21 +17,22 @@
#pragma once
#include <algorithm>
#include <map>
template<typename K,typename V>
class buildmap
{
public:
buildmap(const K &key,
const V &val)
buildmap(const K &key_,
const V &val_)
{
_map.insert(std::make_pair(key,val));
_map.insert(std::make_pair(key_,val_));
}
buildmap<K,V> &operator()(const K &key,
const V &val)
buildmap<K,V> &operator()(const K &key_,
const V &val_)
{
_map.insert(std::make_pair(key,val));
_map.insert(std::make_pair(key_,val_));
return *this;
}
+4 -4
View File
@@ -23,14 +23,14 @@ template<typename V, bool SORT = false>
class buildvector
{
public:
buildvector(const V &val)
buildvector(const V &val_)
{
_vector.push_back(val);
_vector.push_back(val_);
}
buildvector<V,SORT> &operator()(const V &val)
buildvector<V,SORT> &operator()(const V &val_)
{
_vector.push_back(val);
_vector.push_back(val_);
return *this;
}
+22 -32
View File
@@ -1,5 +1,7 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
ISC License
Copyright (c) 2020, 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
@@ -14,46 +16,34 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string>
#include <vector>
#include "category.hpp"
#include "buildvector.hpp"
#include "errno.hpp"
#include "str.hpp"
#define CATEGORY(X) Category(Category::Enum::X,#X)
#include <string>
const std::vector<Category> Category::_categories_ =
buildvector<Category,true>
(CATEGORY(invalid))
(CATEGORY(action))
(CATEGORY(create))
(CATEGORY(search));
const Category * const Category::categories = &_categories_[1];
const Category &Category::invalid = Category::categories[Category::Enum::invalid];
const Category &Category::action = Category::categories[Category::Enum::action];
const Category &Category::create = Category::categories[Category::Enum::create];
const Category &Category::search = Category::categories[Category::Enum::search];
const Category&
Category::find(const std::string &str)
int
Category::Base::from_string(const std::string &s_)
{
for(int i = Enum::BEGIN; i != Enum::END; ++i)
int rv;
for(auto func : funcs)
{
if(categories[i] == str)
return categories[i];
rv = func->from_string(s_);
if(rv < 0)
return rv;
}
return invalid;
return 0;
}
const Category&
Category::find(const Category::Enum::Type i)
std::string
Category::Base::to_string(void) const
{
if(i >= Category::Enum::BEGIN &&
i < Category::Enum::END)
return categories[i];
std::set<std::string> rv;
return invalid;
for(const auto func : funcs)
rv.insert(func->to_string());
return str::join(rv,',');
}
+79 -56
View File
@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2020, 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
@@ -16,69 +16,92 @@
#pragma once
#include <string>
#include <vector>
#include "tofrom_string.hpp"
#include "funcs.hpp"
#include "func.hpp"
class Category
#include <string>
namespace Category
{
public:
struct Enum
class Base : public ToFromString
{
enum Type
{
invalid = -1,
BEGIN = 0,
action = BEGIN,
create,
search,
END
};
public:
int from_string(const std::string &s) final;
std::string to_string() const final;
protected:
std::vector<ToFromString*> funcs;
};
class Action final : public Base
{
private:
Action();
public:
Action(Funcs &funcs_)
{
funcs.push_back(&funcs_.chmod);
funcs.push_back(&funcs_.chown);
funcs.push_back(&funcs_.link);
funcs.push_back(&funcs_.removexattr);
funcs.push_back(&funcs_.rename);
funcs.push_back(&funcs_.rmdir);
funcs.push_back(&funcs_.setxattr);
funcs.push_back(&funcs_.truncate);
funcs.push_back(&funcs_.unlink);
funcs.push_back(&funcs_.utimens);
}
};
class Create final : public Base
{
private:
Create();
public:
Create(Funcs &funcs_)
{
funcs.push_back(&funcs_.create);
funcs.push_back(&funcs_.mkdir);
funcs.push_back(&funcs_.mknod);
funcs.push_back(&funcs_.symlink);
}
};
class Search final : public Base
{
private:
Search();
public:
Search(Funcs &funcs_)
{
funcs.push_back(&funcs_.access);
funcs.push_back(&funcs_.getattr);
funcs.push_back(&funcs_.getxattr);
funcs.push_back(&funcs_.listxattr);
funcs.push_back(&funcs_.open);
funcs.push_back(&funcs_.readlink);
}
};
}
class Categories final
{
private:
Enum::Type _enum;
std::string _str;
Categories();
public:
Category()
: _enum(invalid),
_str(invalid)
{
}
Category(const Enum::Type enum_,
const std::string &str_)
: _enum(enum_),
_str(str_)
{
}
Categories(Funcs &funcs_)
: action(funcs_),
create(funcs_),
search(funcs_)
{}
public:
operator const Enum::Type() const { return _enum; }
operator const std::string&() const { return _str; }
operator const Category*() const { return this; }
bool operator==(const std::string &str_) const
{ return _str == str_; }
bool operator==(const Enum::Type enum_) const
{ return _enum == enum_; }
bool operator!=(const Category &r) const
{ return _enum != r._enum; }
bool operator<(const Category &r) const
{ return _enum < r._enum; }
public:
static const Category &find(const std::string&);
static const Category &find(const Enum::Type);
public:
static const std::vector<Category> _categories_;
static const Category * const categories;
static const Category &invalid;
static const Category &action;
static const Category &create;
static const Category &search;
Category::Action action;
Category::Create create;
Category::Search search;
};
+181 -56
View File
@@ -18,17 +18,20 @@
#include "ef.hpp"
#include "errno.hpp"
#include "from_string.hpp"
#include "fs.hpp"
#include "num.hpp"
#include "rwlock.hpp"
#include "str.hpp"
#include "to_string.hpp"
#include "version.hpp"
#include <algorithm>
#include <string>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
#include <stdint.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -38,6 +41,11 @@ using std::string;
#define IFERT(S) if(S == s_) return true
const std::string CONTROLFILE = "/.mergerfs";
Config Config::_singleton;
namespace l
{
static
@@ -61,49 +69,48 @@ namespace l
}
Config::Config()
:
open_cache(),
controlfile("/.mergerfs"),
async_read(true),
auto_cache(false),
branches(),
cache_attr(1),
cache_entry(1),
cache_files(CacheFiles::ENUM::LIBFUSE),
cache_negative_entry(0),
cache_readdir(false),
cache_statfs(0),
cache_symlinks(false),
category(func),
direct_io(false),
dropcacheonclose(false),
fsname(),
func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
ignorepponrename(false),
inodecalc("hybrid-hash"),
link_cow(false),
minfreespace(MINFREESPACE_DEFAULT),
mount(),
moveonenospc(false),
nfsopenhack(NFSOpenHack::ENUM::OFF),
nullrw(false),
pid(::getpid()),
posix_acl(false),
readdir(ReadDir::ENUM::POSIX),
readdirplus(false),
security_capability(true),
srcmounts(branches),
statfs(StatFS::ENUM::BASE),
statfs_ignore(StatFSIgnore::ENUM::NONE),
symlinkify(false),
symlinkify_timeout(3600),
threads(0),
version(MERGERFS_VERSION),
writeback_cache(false),
xattr(XAttr::ENUM::PASSTHROUGH)
: async_read(true),
auto_cache(false),
minfreespace(MINFREESPACE_DEFAULT),
branches(minfreespace),
cache_attr(1),
cache_entry(1),
cache_files(CacheFiles::ENUM::LIBFUSE),
cache_negative_entry(0),
cache_readdir(false),
cache_statfs(0),
cache_symlinks(false),
category(func),
direct_io(false),
dropcacheonclose(false),
fsname(),
follow_symlinks(FollowSymlinks::ENUM::NEVER),
func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
ignorepponrename(false),
inodecalc("hybrid-hash"),
link_cow(false),
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
log_metrics(false),
mount(),
moveonenospc(false),
nfsopenhack(NFSOpenHack::ENUM::OFF),
nullrw(false),
pid(::getpid()),
posix_acl(false),
readdir(ReadDir::ENUM::POSIX),
readdirplus(false),
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
security_capability(true),
srcmounts(branches),
statfs(StatFS::ENUM::BASE),
statfs_ignore(StatFSIgnore::ENUM::NONE),
symlinkify(false),
symlinkify_timeout(3600),
threads(0),
version(MERGERFS_VERSION),
writeback_cache(false),
xattr(XAttr::ENUM::PASSTHROUGH)
{
_map["async_read"] = &async_read;
_map["auto_cache"] = &auto_cache;
@@ -121,6 +128,7 @@ Config::Config()
_map["category.search"] = &category.search;
_map["direct_io"] = &direct_io;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod;
@@ -147,6 +155,8 @@ Config::Config()
_map["inodecalc"] = &inodecalc;
_map["kernel_cache"] = &kernel_cache;
_map["link_cow"] = &link_cow;
_map["link-exdev"] = &link_exdev;
_map["log.metrics"] = &log_metrics;
_map["minfreespace"] = &minfreespace;
_map["mount"] = &mount;
_map["moveonenospc"] = &moveonenospc;
@@ -156,6 +166,7 @@ Config::Config()
_map["posix_acl"] = &posix_acl;
// _map["readdir"] = &readdir;
_map["readdirplus"] = &readdirplus;
_map["rename-exdev"] = &rename_exdev;
_map["security_capability"] = &security_capability;
_map["srcmounts"] = &srcmounts;
_map["statfs"] = &statfs;
@@ -167,17 +178,22 @@ Config::Config()
_map["xattr"] = &xattr;
}
const
Config&
Config::ro(void)
Config::operator=(const Config &cfg_)
{
return *((Config*)fuse_get_context()->private_data);
}
int rv;
std::string val;
Config&
Config::rw(void)
{
return *((Config*)fuse_get_context()->private_data);
for(auto &kv : _map)
{
rv = cfg_.get(kv.first,&val);
if(rv)
continue;
kv.second->from_string(val);
}
return *this;
}
bool
@@ -255,11 +271,80 @@ Config::set(const std::string &key_,
const std::string &value_)
{
if(l::readonly(key_))
return -EINVAL;
return -EROFS;
return set_raw(key_,value_);
}
int
Config::set(const std::string &kv_)
{
std::string key;
std::string val;
str::splitkv(kv_,'=',&key,&val);
key = str::trim(key);
val = str::trim(val);
return set(key,val);
}
int
Config::from_stream(std::istream &istrm_,
ErrVec *errs_)
{
int rv;
std::string line;
std::string key;
std::string val;
Config newcfg;
newcfg = *this;
while(std::getline(istrm_,line,'\n'))
{
line = str::trim(line);
if(!line.empty() && (line[0] == '#'))
continue;
str::splitkv(line,'=',&key,&val);
key = str::trim(key);
val = str::trim(val);
rv = newcfg.set(key,val);
if(rv < 0)
errs_->push_back({rv,key});
}
if(!errs_->empty())
return -EINVAL;
*this = newcfg;
return 0;
}
int
Config::from_file(const std::string &filepath_,
ErrVec *errs_)
{
int rv;
std::ifstream ifstrm;
ifstrm.open(filepath_);
if(!ifstrm.good())
{
errs_->push_back({-errno,filepath_});
return -errno;
}
rv = from_stream(ifstrm,errs_);
ifstrm.close();
return rv;
}
std::ostream&
operator<<(std::ostream &os_,
const Config &c_)
@@ -269,7 +354,47 @@ operator<<(std::ostream &os_,
for(i = c_._map.begin(), ei = c_._map.end(); i != ei; ++i)
{
os_ << i->first << '=' << i->second << '\n';
os_ << i->first << '=' << i->second->to_string() << std::endl;
}
return os_;
}
static
std::string
err2str(const int err_)
{
switch(err_)
{
case 0:
return std::string();
case -EINVAL:
return "invalid value";
case -ENOATTR:
return "unknown option";
case -EROFS:
return "read-only option";
default:
return strerror(-err_);
}
return std::string();
}
std::ostream&
operator<<(std::ostream &os_,
const Config::ErrVec &ev_)
{
std::string errstr;
for(auto &err : ev_)
{
os_ << "* ERROR: ";
errstr = err2str(err.err);
if(!errstr.empty())
os_ << errstr << " - ";
os_ << err.str << std::endl;
}
return os_;
+96 -13
View File
@@ -16,29 +16,35 @@
#pragma once
#include "branch.hpp"
#include "branches.hpp"
#include "category.hpp"
#include "config_cachefiles.hpp"
#include "config_follow_symlinks.hpp"
#include "config_inodecalc.hpp"
#include "config_link_exdev.hpp"
#include "config_log_metrics.hpp"
#include "config_moveonenospc.hpp"
#include "config_nfsopenhack.hpp"
#include "config_readdir.hpp"
#include "config_rename_exdev.hpp"
#include "config_statfs.hpp"
#include "config_statfsignore.hpp"
#include "config_xattr.hpp"
#include "enum.hpp"
#include "errno.hpp"
#include "func_category.hpp"
#include "funcs.hpp"
#include "policy.hpp"
#include "policy_cache.hpp"
#include "rwlock.hpp"
#include "tofrom_wrapper.hpp"
#include <fuse.h>
#include "fuse.h"
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <stdint.h>
#include <sys/stat.h>
typedef ToFromWrapper<bool> ConfigBOOL;
@@ -47,20 +53,55 @@ typedef ToFromWrapper<int> ConfigINT;
typedef ToFromWrapper<std::string> ConfigSTR;
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
extern const std::string CONTROLFILE;
class Config
{
public:
struct Err
{
int err;
std::string str;
};
typedef std::vector<Err> ErrVec;
public:
class Read
{
public:
Read();
public:
inline const Config* operator->() const;
private:
const Config &_cfg;
};
public:
class Write
{
public:
Write();
public:
Config* operator->();
private:
Config &_cfg;
};
public:
Config();
public:
mutable PolicyCache open_cache;
public:
const std::string controlfile;
Config& operator=(const Config&);
public:
ConfigBOOL async_read;
ConfigBOOL auto_cache;
ConfigUINT64 minfreespace;
Branches branches;
ConfigUINT64 cache_attr;
ConfigUINT64 cache_entry;
@@ -69,17 +110,19 @@ public:
ConfigBOOL cache_readdir;
ConfigUINT64 cache_statfs;
ConfigBOOL cache_symlinks;
FuncCategories category;
Categories category;
ConfigBOOL direct_io;
ConfigBOOL dropcacheonclose;
ConfigSTR fsname;
FollowSymlinks follow_symlinks;
Funcs func;
ConfigUINT64 fuse_msg_size;
ConfigBOOL ignorepponrename;
InodeCalc inodecalc;
ConfigBOOL kernel_cache;
ConfigBOOL link_cow;
ConfigUINT64 minfreespace;
LinkEXDEV link_exdev;
LogMetrics log_metrics;
ConfigSTR mount;
MoveOnENOSPC moveonenospc;
NFSOpenHack nfsopenhack;
@@ -88,6 +131,7 @@ public:
ConfigBOOL posix_acl;
ReadDir readdir;
ConfigBOOL readdirplus;
RenameEXDEV rename_exdev;
ConfigBOOL security_capability;
SrcMounts srcmounts;
StatFS statfs;
@@ -112,11 +156,50 @@ public:
int get(const std::string &key, std::string *val) const;
int set_raw(const std::string &key, const std::string &val);
int set(const std::string &key, const std::string &val);
int set(const std::string &kv);
public:
static const Config &ro(void);
static Config &rw(void);
int from_stream(std::istream &istrm, ErrVec *errs);
int from_file(const std::string &filepath, ErrVec *errs);
private:
Str2TFStrMap _map;
private:
static Config _singleton;
public:
friend class Read;
friend class Write;
};
std::ostream& operator<<(std::ostream &s,const Config::ErrVec &ev);
inline
Config::Read::Read()
: _cfg(Config::_singleton)
{
}
inline
const
Config*
Config::Read::operator->() const
{
return &_cfg;
}
inline
Config::Write::Write()
: _cfg(Config::_singleton)
{
}
inline
Config*
Config::Write::operator->()
{
return &_cfg;
}
+1
View File
@@ -20,6 +20,7 @@
#include "enum.hpp"
enum class CacheFilesEnum
{
LIBFUSE,
+58
View File
@@ -0,0 +1,58 @@
/*
ISC License
Copyright (c) 2020, 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.
*/
#include "config_follow_symlinks.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
FollowSymlinks::to_string(void) const
{
switch(_data)
{
case FollowSymlinks::ENUM::NEVER:
return "never";
case FollowSymlinks::ENUM::DIRECTORY:
return "directory";
case FollowSymlinks::ENUM::REGULAR:
return "regular";
case FollowSymlinks::ENUM::ALL:
return "all";
}
return "invalid";
}
template<>
int
FollowSymlinks::from_string(const std::string &s_)
{
if(s_ == "never")
_data = FollowSymlinks::ENUM::NEVER;
ef(s_ == "directory")
_data = FollowSymlinks::ENUM::DIRECTORY;
ef(s_ == "regular")
_data = FollowSymlinks::ENUM::REGULAR;
ef(s_ == "all")
_data = FollowSymlinks::ENUM::ALL;
else
return -EINVAL;
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
/*
ISC License
Copyright (c) 2020, 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 "enum.hpp"
enum class FollowSymlinksEnum
{
NEVER,
DIRECTORY,
REGULAR,
ALL
};
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
+1
View File
@@ -19,6 +19,7 @@
#include "config_inodecalc.hpp"
#include "fs_inode.hpp"
InodeCalc::InodeCalc(const std::string &s_)
{
fs::inode::set_algo(s_);
+3 -2
View File
@@ -20,12 +20,13 @@
#include "tofrom_string.hpp"
class InodeCalc : public ToFromString
{
public:
InodeCalc(const std::string &);
public:
std::string to_string(void) const;
int from_string(const std::string &);
std::string to_string(void) const final;
int from_string(const std::string &) final;
};
+58
View File
@@ -0,0 +1,58 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#include "config_link_exdev.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
LinkEXDEV::to_string(void) const
{
switch(_data)
{
case LinkEXDEV::ENUM::PASSTHROUGH:
return "passthrough";
case LinkEXDEV::ENUM::REL_SYMLINK:
return "rel-symlink";
case LinkEXDEV::ENUM::ABS_BASE_SYMLINK:
return "abs-base-symlink";
case LinkEXDEV::ENUM::ABS_POOL_SYMLINK:
return "abs-pool-symlink";
}
return "invalid";
}
template<>
int
LinkEXDEV::from_string(const std::string &s_)
{
if(s_ == "passthrough")
_data = LinkEXDEV::ENUM::PASSTHROUGH;
ef(s_ == "rel-symlink")
_data = LinkEXDEV::ENUM::REL_SYMLINK;
ef(s_ == "abs-base-symlink")
_data = LinkEXDEV::ENUM::ABS_BASE_SYMLINK;
ef(s_ == "abs-pool-symlink")
_data = LinkEXDEV::ENUM::ABS_POOL_SYMLINK;
else
return -EINVAL;
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
/*
ISC License
Copyright (c) 2021, 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 "enum.hpp"
enum class LinkEXDEVEnum
{
PASSTHROUGH,
REL_SYMLINK,
ABS_BASE_SYMLINK,
ABS_POOL_SYMLINK
};
typedef Enum<LinkEXDEVEnum> LinkEXDEV;
+53
View File
@@ -0,0 +1,53 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#include "config_log_metrics.hpp"
#include "from_string.hpp"
#include "to_string.hpp"
#include "fuse.h"
LogMetrics::LogMetrics(const bool val_)
{
fuse_log_metrics_set(val_);
}
std::string
LogMetrics::to_string(void) const
{
bool val;
val = fuse_log_metrics_get();
return str::to(val);
}
int
LogMetrics::from_string(const std::string &s_)
{
int rv;
bool val;
rv = str::from(s_,&val);
if(rv < 0)
return rv;
fuse_log_metrics_set(val);
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
/*
ISC License
Copyright (c) 2021, 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 "tofrom_string.hpp"
class LogMetrics : public ToFromString
{
public:
LogMetrics(const bool);
public:
std::string to_string(void) const final;
int from_string(const std::string &) final;
};
+5 -4
View File
@@ -21,12 +21,13 @@
#include "errno.hpp"
#include "from_string.hpp"
int
MoveOnENOSPC::from_string(const std::string &s_)
{
int rv;
std::string s;
const Policy *tmp;
Policy::CreateImpl *tmp;
rv = str::from(s_,&enabled);
if((rv == 0) && (enabled == true))
@@ -36,8 +37,8 @@ MoveOnENOSPC::from_string(const std::string &s_)
else
return 0;
tmp = &Policy::find(s);
if(tmp == Policy::invalid)
tmp = Policies::Create::find(s);
if(tmp == NULL)
return -EINVAL;
policy = tmp;
@@ -50,6 +51,6 @@ std::string
MoveOnENOSPC::to_string(void) const
{
if(enabled)
return policy->to_string();
return policy.name();
return "false";
}
+7 -7
View File
@@ -19,26 +19,26 @@
#pragma once
#include "policy.hpp"
#include "policies.hpp"
#include "tofrom_string.hpp"
#include <string>
class MoveOnENOSPC : public ToFromString
{
public:
MoveOnENOSPC(const bool enabled_)
: enabled(enabled_)
: enabled(enabled_),
policy(&Policies::Create::mfs)
{
policy = (enabled ?
&Policy::mfs :
&Policy::invalid);
}
public:
int from_string(const std::string &s);
std::string to_string() const;
int from_string(const std::string &s) final;
std::string to_string() const final;
public:
bool enabled;
const Policy *policy;
Policy::Create policy;
};
+1
View File
@@ -20,6 +20,7 @@
#include "ef.hpp"
#include "errno.hpp"
template<>
int
NFSOpenHack::from_string(const std::string &s_)
+1
View File
@@ -20,6 +20,7 @@
#include "enum.hpp"
enum class NFSOpenHackEnum
{
OFF,
+1
View File
@@ -20,6 +20,7 @@
#include "ef.hpp"
#include "errno.hpp"
template<>
int
ReadDir::from_string(const std::string &s_)
+1
View File
@@ -20,6 +20,7 @@
#include "enum.hpp"
enum class ReadDirEnum
{
POSIX,
+54
View File
@@ -0,0 +1,54 @@
/*
ISC License
Copyright (c) 2021, 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.
*/
#include "config_rename_exdev.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
RenameEXDEV::to_string(void) const
{
switch(_data)
{
case RenameEXDEV::ENUM::PASSTHROUGH:
return "passthrough";
case RenameEXDEV::ENUM::REL_SYMLINK:
return "rel-symlink";
case RenameEXDEV::ENUM::ABS_SYMLINK:
return "abs-symlink";
}
return "invalid";
}
template<>
int
RenameEXDEV::from_string(const std::string &s_)
{
if(s_ == "passthrough")
_data = RenameEXDEV::ENUM::PASSTHROUGH;
ef(s_ == "rel-symlink")
_data = RenameEXDEV::ENUM::REL_SYMLINK;
ef(s_ == "abs-symlink")
_data = RenameEXDEV::ENUM::ABS_SYMLINK;
else
return -EINVAL;
return 0;
}
+29
View File
@@ -0,0 +1,29 @@
/*
ISC License
Copyright (c) 2021, 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 "enum.hpp"
enum class RenameEXDEVEnum
{
PASSTHROUGH,
REL_SYMLINK,
ABS_SYMLINK
};
typedef Enum<RenameEXDEVEnum> RenameEXDEV;
+1
View File
@@ -20,6 +20,7 @@
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
StatFS::to_string() const
+1
View File
@@ -20,6 +20,7 @@
#include "enum.hpp"
enum class StatFSEnum
{
BASE,

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