mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-20 00:17:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd7e9e2aa7 | ||
|
|
3fc0aa3809 | ||
|
|
b1dcc94e98 | ||
|
|
6d2c99e487 | ||
|
|
9d9eacfe7b | ||
|
|
b91cc1c191 | ||
|
|
6d2f26c60e | ||
|
|
0a94bd7cfc | ||
|
|
211e6c7115 | ||
|
|
335ca55a6e | ||
|
|
69de4de53f | ||
|
|
76c8d48dbd | ||
|
|
a927a15e9c | ||
|
|
0c555e71a0 | ||
|
|
82781b6ff8 | ||
|
|
b545352712 | ||
|
|
3d05999db0 | ||
|
|
f4a8105a42 | ||
|
|
004b616115 | ||
|
|
8d7017273f | ||
|
|
51b65cd126 | ||
|
|
0ed03a1535 | ||
|
|
b6d3c34d3f | ||
|
|
fdecabfaab | ||
|
|
3a1d8e03a1 | ||
|
|
e586d2fd1e | ||
|
|
e0087cd8a7 | ||
|
|
2377cb05b6 | ||
|
|
a8ffbc84f6 | ||
|
|
0aafdefc18 | ||
|
|
7221fded71 | ||
|
|
7a09f560f7 | ||
|
|
8b769dff7f | ||
|
|
6dcf6111af | ||
|
|
9849bcd3cf | ||
|
|
c92a10021e | ||
|
|
d817fa4e97 | ||
|
|
eb141c4c4e | ||
|
|
282ce08ddb | ||
|
|
51d97bb444 | ||
|
|
539f2221a3 | ||
|
|
2b1804d364 | ||
|
|
556c6522c0 | ||
|
|
543603bdfc | ||
|
|
7a86ed6508 | ||
|
|
707d298d7c | ||
|
|
36a4b7a23a | ||
|
|
22833bdfe2 | ||
|
|
775c3cad90 | ||
|
|
fa537961c5 | ||
|
|
ee13810f3b | ||
|
|
5d6c9cc56a | ||
|
|
832903378e | ||
|
|
2722f4f64d | ||
|
|
7cdd12bda4 | ||
|
|
5ab0fbcaee | ||
|
|
42836e0961 | ||
|
|
6a14a10e6c | ||
|
|
82430e1079 | ||
|
|
701f88de43 | ||
|
|
c72997f298 | ||
|
|
c681d6c2fe | ||
|
|
6bc3d77992 | ||
|
|
c266bae96d | ||
|
|
cdcfef2ceb | ||
|
|
7a62a629b9 | ||
|
|
0a64d74b4b | ||
|
|
26c8b5c642 | ||
|
|
26fd11cb4d | ||
|
|
cdceade1ae | ||
|
|
eebd6e9eb4 | ||
|
|
9744eab819 | ||
|
|
1c5b0f2f5f | ||
|
|
d9033b7713 | ||
|
|
b1d11779f0 | ||
|
|
be19b9ce71 | ||
|
|
9fcaf6a674 | ||
|
|
2dd4b788ce | ||
|
|
5152c63480 | ||
|
|
bd02bfd54c | ||
|
|
d04e04e89a | ||
|
|
10a9969a64 | ||
|
|
486c5d3734 | ||
|
|
55bd0ca25a | ||
|
|
ee2855bb38 | ||
|
|
65c004a309 | ||
|
|
a598f63235 | ||
|
|
6e845b337a | ||
|
|
e25fa1954e | ||
|
|
b48d368130 | ||
|
|
373d331f39 | ||
|
|
4a647beec1 | ||
|
|
84592a9f13 | ||
|
|
09fbb80f8c | ||
|
|
34310170ac | ||
|
|
e39ace0eba | ||
|
|
9effeb6869 | ||
|
|
b51a5970b2 | ||
|
|
2d9c915641 | ||
|
|
737e59d232 | ||
|
|
9adddfc66d | ||
|
|
9a6b7835f4 | ||
|
|
ac4e097511 | ||
|
|
3d73c6a596 | ||
|
|
9670fb9704 | ||
|
|
e2ece936e6 | ||
|
|
a4491169b0 | ||
|
|
0d566befba | ||
|
|
3e313cd605 | ||
|
|
5bf2a2be59 | ||
|
|
7ed91c1668 | ||
|
|
041e3f50d5 | ||
|
|
92ccfd3c89 | ||
|
|
24f2d3c94d | ||
|
|
a7113bb163 | ||
|
|
ec5ac0c517 | ||
|
|
fdec3355a1 | ||
|
|
16e9ba2f19 | ||
|
|
939eb3996a | ||
|
|
2d8d0f7ad6 | ||
|
|
3ee93d4c56 | ||
|
|
0b2afda30e | ||
|
|
d11807ef7c | ||
|
|
3e725a66e3 | ||
|
|
24423b8d2a | ||
|
|
c89fbde48d | ||
|
|
b65e52d56b | ||
|
|
6c2db76b92 | ||
|
|
094ad862e7 | ||
|
|
e81b1a6b9d | ||
|
|
a6ca96fa08 | ||
|
|
a449829d5f | ||
|
|
d14427ad9e | ||
|
|
ac1f4b4d52 | ||
|
|
ddf5e53aa4 | ||
|
|
a2a12afb34 | ||
|
|
7606430188 | ||
|
|
629806e896 | ||
|
|
50fbf14e1e | ||
|
|
4a67bbdc71 | ||
|
|
dba9c4451a | ||
|
|
fea499e220 | ||
|
|
9d056d609f | ||
|
|
9a839dd8ba | ||
|
|
32e2c9c48e | ||
|
|
83d7886a0a | ||
|
|
316abba0ac | ||
|
|
8001c58c36 | ||
|
|
3b0d7fb26e | ||
|
|
5d3c5002e0 | ||
|
|
9082285cc8 | ||
|
|
d1762b2bac | ||
|
|
46ce28b149 | ||
|
|
faadeb75e0 | ||
|
|
338e559e53 | ||
|
|
3db673410f | ||
|
|
77f098614f | ||
|
|
9589846e9a | ||
|
|
e2dda3c1b1 | ||
|
|
d9ab18282f | ||
|
|
14c2ff9ab8 | ||
|
|
77e6f400b5 | ||
|
|
b95ff8ba3a | ||
|
|
4bf8738e5c | ||
|
|
3f060f4512 | ||
|
|
c4c35a9ad0 | ||
|
|
c9a935835f | ||
|
|
015002ebd9 | ||
|
|
9ca10b2413 | ||
|
|
2d5c2c2b6e | ||
|
|
e3ab739a56 | ||
|
|
c784d06029 | ||
|
|
58803f4845 | ||
|
|
f159fe61f9 | ||
|
|
c43b464d6c | ||
|
|
34c684a4f5 | ||
|
|
e256c88e4c | ||
|
|
518b4a6b85 | ||
|
|
ee8532c70d | ||
|
|
c58da1ab9c | ||
|
|
68b3026264 | ||
|
|
810c5a40f7 | ||
|
|
18dead4d86 | ||
|
|
2082357d3a | ||
|
|
68719da828 | ||
|
|
31fd1ec82e | ||
|
|
c3845097ee | ||
|
|
d76934ee9d | ||
|
|
5f737cb7bf | ||
|
|
50ede1ba97 | ||
|
|
930dad31de | ||
|
|
88b91161e0 | ||
|
|
8150957a01 | ||
|
|
fee97efafb | ||
|
|
80856cf7ac | ||
|
|
c929781d08 | ||
|
|
313aa2a1d3 | ||
|
|
021ce3d97c | ||
|
|
4ea0de3ef2 | ||
|
|
6b5c484fbf | ||
|
|
b165332486 | ||
|
|
f598d3bc38 | ||
|
|
4daa9e4eb9 | ||
|
|
122f9c1d5d | ||
|
|
2da5cda4b3 | ||
|
|
5263a65dcf | ||
|
|
1255d3d1b2 | ||
|
|
8def16a811 | ||
|
|
7d1683b607 | ||
|
|
43a6d66e3c | ||
|
|
8b33f9a766 | ||
|
|
df721eb253 | ||
|
|
0547517a99 | ||
|
|
54e85007a2 | ||
|
|
6e53f3b698 | ||
|
|
1aaf742c13 | ||
|
|
3754e20049 | ||
|
|
b4c031d1f0 | ||
|
|
3b8d495c43 | ||
|
|
61c2187c14 | ||
|
|
192e4a4af8 | ||
|
|
749f0290e9 | ||
|
|
7013ff9aeb | ||
|
|
3ce0dc56e4 | ||
|
|
8e47bf4946 | ||
|
|
098f353bf1 | ||
|
|
859b4da3dc | ||
|
|
80f30999fa | ||
|
|
3fdd322a5c | ||
|
|
c40f42e57e | ||
|
|
18e87980a4 | ||
|
|
44efbaabe6 | ||
|
|
a02f3a6856 | ||
|
|
9573a053d6 | ||
|
|
9a5d691619 | ||
|
|
495afeffb1 | ||
|
|
4acc1defe2 | ||
|
|
58b4eb9089 | ||
|
|
ca4b4c6d35 | ||
|
|
9a90609428 | ||
|
|
3e691149f5 | ||
|
|
8adebc9489 | ||
|
|
50daf84ec0 | ||
|
|
d337574665 | ||
|
|
39d98f1eac | ||
|
|
cbea2ba91b | ||
|
|
f6e37672b8 | ||
|
|
55bd9a06b1 | ||
|
|
6a9f7e6afc | ||
|
|
f2158d3e66 | ||
|
|
3bc189cfc8 | ||
|
|
d0c5ff7f87 | ||
|
|
dfb544fed8 | ||
|
|
ae893ca051 | ||
|
|
538467b86d | ||
|
|
46de43d4ad | ||
|
|
3808e2683e | ||
|
|
52342a76c1 | ||
|
|
e9e17baf5e | ||
|
|
2e4c6c5fd1 | ||
|
|
f50812a9ce | ||
|
|
861edff49a | ||
|
|
5e08357f0e | ||
|
|
411ee36c90 | ||
|
|
44a98910ba | ||
|
|
8a335d8a6e | ||
|
|
3900543970 | ||
|
|
2e34c7598a | ||
|
|
7edd3c6cf3 | ||
|
|
ddf0075938 | ||
|
|
1b26f4908e | ||
|
|
2d8fab534a | ||
|
|
c3fffefbc1 | ||
|
|
eaa2593a4b | ||
|
|
7e583e3f48 | ||
|
|
2ad4891617 | ||
|
|
8990e24c0f | ||
|
|
88b80234c7 | ||
|
|
f6e37c57fe | ||
|
|
562295be90 | ||
|
|
04844420dc | ||
|
|
9c352ac555 | ||
|
|
5a3184359d | ||
|
|
24f03b04eb | ||
|
|
610c75fa90 | ||
|
|
dbc4774e8e | ||
|
|
73e8ed72cd | ||
|
|
e694585561 | ||
|
|
0b1af2b8ef | ||
|
|
668ac4000a | ||
|
|
767039c492 | ||
|
|
b1c6d4864f | ||
|
|
fc3453932a | ||
|
|
98c49b8001 | ||
|
|
27bd39f5ac | ||
|
|
9f84f29e12 | ||
|
|
6311df774d | ||
|
|
9424784fad | ||
|
|
15fb751f65 | ||
|
|
3534fb1128 | ||
|
|
046844083f | ||
|
|
7ff995631e | ||
|
|
8675fce04a | ||
|
|
7fd629ebf3 | ||
|
|
2fe20b888e | ||
|
|
2696079601 | ||
|
|
ec15872a1f | ||
|
|
6cc6524997 | ||
|
|
4c711159df | ||
|
|
7e173108fd | ||
|
|
17925c4c93 | ||
|
|
90081f4e61 | ||
|
|
f488debf96 | ||
|
|
678626ef82 | ||
|
|
0bc6711a95 | ||
|
|
a925fbe59c | ||
|
|
dc1b698847 | ||
|
|
3c761b708b | ||
|
|
3bfdd78434 | ||
|
|
5f12fb6a5f | ||
|
|
f9b831eb1a | ||
|
|
da07bc9637 | ||
|
|
badf7e1c60 | ||
|
|
1f35b65725 | ||
|
|
3b382624e5 | ||
|
|
5f3a4cb0f1 | ||
|
|
0371b047f9 | ||
|
|
3f593d9c3b | ||
|
|
1fe32e9cd5 | ||
|
|
58a8f8326d | ||
|
|
30d13b7f77 | ||
|
|
124299e185 | ||
|
|
5989d41a86 | ||
|
|
c97102bc9d | ||
|
|
1d2a1ba31a | ||
|
|
159c2366c7 | ||
|
|
e639f3d375 | ||
|
|
23f870d8f2 | ||
|
|
173193df74 | ||
|
|
d1d21ead1f | ||
|
|
0709b2e447 | ||
|
|
50d967221e | ||
|
|
15a0aedd25 | ||
|
|
8052039c6f | ||
|
|
8afe72d371 | ||
|
|
0682ebf103 | ||
|
|
a93bd9f7fa | ||
|
|
6f76080c6b | ||
|
|
139e61efaa | ||
|
|
b50a128777 | ||
|
|
93218a343a | ||
|
|
5a0568495b | ||
|
|
3ec137c4ad | ||
|
|
d4eecf3b56 | ||
|
|
3a6738475a | ||
|
|
468d42088a | ||
|
|
4a4db4be84 | ||
|
|
08d267fc61 | ||
|
|
f2d4fbfdb2 | ||
|
|
c4a85f5fad | ||
|
|
c3f4d2914f | ||
|
|
c0990644e8 | ||
|
|
b1f30b703f | ||
|
|
62899565e6 | ||
|
|
ddb435a44b | ||
|
|
d699a97928 | ||
|
|
06660ca25e | ||
|
|
8ed3a1f36a | ||
|
|
8948fc2935 | ||
|
|
b4397f7f2e | ||
|
|
cb74573fc2 | ||
|
|
dbdd3e22fc | ||
|
|
4160a8e2f8 | ||
|
|
afb07b170d | ||
|
|
ebcf98ac2d | ||
|
|
54c41c4994 | ||
|
|
8d989777f8 | ||
|
|
a0c1c1a997 | ||
|
|
bed493e8dd | ||
|
|
add588fdea | ||
|
|
a61d420224 | ||
|
|
eb78c512ee | ||
|
|
ee5851bb1c | ||
|
|
c6bce81747 | ||
|
|
e0bb4e407a | ||
|
|
aad2257387 | ||
|
|
831dba3ef6 | ||
|
|
eff15c72a5 | ||
|
|
edb50e5411 | ||
|
|
7c37a69d25 | ||
|
|
be3499ee56 | ||
|
|
698c4147fc | ||
|
|
b5fae4b356 | ||
|
|
0888ee12a5 | ||
|
|
7e8635b24b | ||
|
|
8ba1abafec | ||
|
|
14d26ba434 | ||
|
|
3a46ec9fab | ||
|
|
ada90eab57 | ||
|
|
9b2634a1e5 | ||
|
|
4cade5685c | ||
|
|
62873d2d3b | ||
|
|
d119807adb | ||
|
|
10e8bd9fa3 | ||
|
|
5ce428ca59 | ||
|
|
2f769e75c4 | ||
|
|
a646fe016d | ||
|
|
f45a52d621 | ||
|
|
576ff3694e | ||
|
|
4cb866cee9 | ||
|
|
43ddff6ca9 | ||
|
|
bf4b39065b | ||
|
|
4b9f3de814 | ||
|
|
903d39f968 | ||
|
|
e6c8fa3fad | ||
|
|
9952c58a4a | ||
|
|
be8bb5d2a7 | ||
|
|
66f0d9e649 | ||
|
|
7b355b279d | ||
|
|
0f2f78c5b8 | ||
|
|
eb57425315 | ||
|
|
489ab23e89 | ||
|
|
4c55eb0cc0 | ||
|
|
389785265b | ||
|
|
a6717990a8 | ||
|
|
f4b8efce20 | ||
|
|
8cdb7174c4 | ||
|
|
4625a3cf43 | ||
|
|
55245e290a | ||
|
|
322fa5778a | ||
|
|
5eb3c8cbef | ||
|
|
5d827566f6 | ||
|
|
6058ac0716 | ||
|
|
4d82ed9324 | ||
|
|
026dc5f750 | ||
|
|
08e1bef5a9 | ||
|
|
b5f6cd70de | ||
|
|
58ab7f7fa8 | ||
|
|
c46c41c740 | ||
|
|
7a84555db8 | ||
|
|
0b0f0a1b9e | ||
|
|
0fffabfbe3 | ||
|
|
6b3f703ba9 | ||
|
|
50ad648ce4 | ||
|
|
2dbcd2b07a | ||
|
|
752a15953a | ||
|
|
23d365f67b | ||
|
|
f77d1d9929 | ||
|
|
4204c807bc | ||
|
|
e6a6ab967b | ||
|
|
52e4d60e7c | ||
|
|
38919add61 | ||
|
|
a7244184bd | ||
|
|
86bda61aa7 | ||
|
|
10e5829b8e | ||
|
|
4c4c27a93e | ||
|
|
72ac1fc0d1 | ||
|
|
5ca928e436 | ||
|
|
5d013f4a18 | ||
|
|
7cbd88ac81 | ||
|
|
24af276542 | ||
|
|
8cb7195c3e | ||
|
|
c8e178bf71 | ||
|
|
529a953e30 | ||
|
|
02b657edff | ||
|
|
ddf6a2f105 | ||
|
|
40a4762cac | ||
|
|
2323c16316 | ||
|
|
bac4b33ef8 | ||
|
|
1baa706d37 | ||
|
|
9e3331dab4 | ||
|
|
61cded5b34 | ||
|
|
060cae6270 | ||
|
|
229d8517c0 | ||
|
|
cc9381f578 | ||
|
|
5f2221155c | ||
|
|
7da8dd6b7a | ||
|
|
2b019b84b0 | ||
|
|
741c559df1 | ||
|
|
3a66a68edb | ||
|
|
af1c0d78e8 | ||
|
|
80d56ac94e | ||
|
|
41881f9d05 | ||
|
|
1ca70521a4 |
+512
@@ -0,0 +1,512 @@
|
||||
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:
|
||||
image_family: freebsd-12-1
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- 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"
|
||||
container:
|
||||
image: alpine:3.11
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.12"
|
||||
container:
|
||||
image: alpine:3.12
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.13"
|
||||
container:
|
||||
image: alpine:3.13
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.14"
|
||||
container:
|
||||
image: alpine:3.14
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.15"
|
||||
container:
|
||||
image: alpine:3.15
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.16"
|
||||
container:
|
||||
image: alpine:3.16
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.17"
|
||||
container:
|
||||
image: alpine:3.17
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:latest"
|
||||
container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine-arm:latest"
|
||||
arm_container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:7"
|
||||
container:
|
||||
image: centos:7
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "rocky:8"
|
||||
container:
|
||||
image: rocky:8
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:30"
|
||||
container:
|
||||
image: fedora:30
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:31"
|
||||
container:
|
||||
image: fedora:31
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:32"
|
||||
container:
|
||||
image: fedora:32
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:33"
|
||||
container:
|
||||
image: fedora:33
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:34"
|
||||
container:
|
||||
image: fedora:34
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:35"
|
||||
container:
|
||||
image: fedora:35
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:36"
|
||||
container:
|
||||
image: fedora:36
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:37"
|
||||
container:
|
||||
image: fedora:37
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:38"
|
||||
container:
|
||||
image: fedora:38
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:latest"
|
||||
container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora-arm:latest"
|
||||
arm_container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:14.04"
|
||||
container:
|
||||
image: ubuntu:14.04
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:16.04"
|
||||
container:
|
||||
image: ubuntu:16.04
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:18.04"
|
||||
container:
|
||||
image: ubuntu:18.04
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:20.04"
|
||||
container:
|
||||
image: ubuntu:20.04
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:22.04"
|
||||
container:
|
||||
image: ubuntu:22.04
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:latest"
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- 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: 4G
|
||||
timeout_in: 15m
|
||||
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: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:8"
|
||||
container:
|
||||
image: debian:8
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:9"
|
||||
container:
|
||||
image: debian:9
|
||||
cpu: 4
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
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: 4G
|
||||
timeout_in: 15m
|
||||
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: 4G
|
||||
timeout_in: 15m
|
||||
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: 4G
|
||||
timeout_in: 15m
|
||||
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: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
@@ -0,0 +1,7 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [trapexit]
|
||||
patreon: trapexit
|
||||
custom: ['https://paypal.me/trapexit','https://buymeacoff.ee/trapexit']
|
||||
ko_fi: trapexit
|
||||
open_collective: trapexit
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: For the reporting of behavior not as described
|
||||
title: ''
|
||||
labels: bug, investigating
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A clear and concise description of the unexpected behavior.
|
||||
|
||||
**Please be sure to use latest release of mergerfs to ensure the issue still exists. Not your distro's latest but the latest official release.**
|
||||
|
||||
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
|
||||
|
||||
Please simplify the reproduction as much as possible.
|
||||
- Unless it is dependenat on multiple branches, use a single branch
|
||||
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A clear and concise description of the expected behavior.
|
||||
|
||||
|
||||
**System information:**
|
||||
|
||||
- OS, kernel version: `uname -a`
|
||||
- mergerfs version: `mergerfs -V`
|
||||
- mergerfs settings
|
||||
- List of drives, filesystems, & sizes:
|
||||
- `df -h`
|
||||
- `lsblk -f`
|
||||
- A strace of the application having a problem:
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
|
||||
- strace of mergerfs while app tried to do it's thing:
|
||||
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature, investigating
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Question
|
||||
about: When you're looking for information or general support
|
||||
title: ''
|
||||
labels: investigating, question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
For general questions and support please use [GitHub Discussions](https://github.com/trapexit/mergerfs/discussions) or [Discord](https://discord.gg/MpAr69V).
|
||||
@@ -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`
|
||||
|
||||
@@ -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 }}"
|
||||
-34
@@ -1,34 +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
|
||||
|
||||
script:
|
||||
- sudo -E apt-get install fakeroot
|
||||
- sudo -E make install-build-pkgs
|
||||
- make
|
||||
- make deb
|
||||
@@ -0,0 +1,10 @@
|
||||
## Libraries
|
||||
|
||||
### included in repo
|
||||
|
||||
* libfuse: https://github.com/libfuse/libfuse (heavily modified fork of v2.x)
|
||||
* wyhash: https://github.com/wangyi-fudan/wyhash
|
||||
* ghc::filesystem: https://github.com/gulrak/filesystem
|
||||
* nonstd::optional: https://github.com/martinmoene/optional-lite
|
||||
* fmt: https://github.com/fmtlib/fmt
|
||||
* concurrentqueue: https://github.com/cameron314/concurrentqueue
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2023, 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,13 +37,12 @@ endif
|
||||
endif
|
||||
|
||||
USE_XATTR = 1
|
||||
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -Ilibfuse/include
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
DEBUG_FLAGS := -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
DEBUG_FLAGS :=
|
||||
OPT_FLAGS := -O2 -DNDEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(STATIC),1)
|
||||
@@ -58,29 +57,42 @@ else
|
||||
LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
OPTS = -O2
|
||||
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 += \
|
||||
$(OPTS) \
|
||||
$(DEBUG_FLAGS) \
|
||||
CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++11 \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Wall \
|
||||
-Wno-unused-result \
|
||||
$(FUSE_CFLAGS) \
|
||||
-DFUSE_USE_VERSION=29 \
|
||||
-MMD \
|
||||
-Wno-unused-result \
|
||||
-MMD
|
||||
FUSE_FLAGS = \
|
||||
-Ilibfuse/include \
|
||||
-D_FILE_OFFSET_BITS=64
|
||||
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
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
@@ -107,13 +119,18 @@ help:
|
||||
objects: version build/stamp
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
tests-objects:
|
||||
$(MAKE) $(TESTS_OBJS)
|
||||
|
||||
build/mergerfs: libfuse objects
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
$(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
|
||||
|
||||
build/mount.mergerfs: build/mergerfs
|
||||
$(LN) -fs "$<" "$@"
|
||||
tests: build/tests
|
||||
|
||||
changelog:
|
||||
ifeq ($(GIT_REPO),1)
|
||||
@@ -127,11 +144,15 @@ version:
|
||||
tools/update-version
|
||||
|
||||
build/stamp:
|
||||
$(MKDIR) -p build
|
||||
$(MKDIR) -p build/.src build/.tests
|
||||
$(TOUCH) $@
|
||||
|
||||
build/%.o: src/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
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
|
||||
@@ -145,15 +166,15 @@ ifeq ($(GIT_REPO),1)
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
install: install-base install-mount.mergerfs install-man
|
||||
install: install-base install-mount-tools install-man
|
||||
|
||||
install-base: build/mergerfs
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(INSTALL) -v -m 0755 build/mergerfs "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
install-mount.mergerfs: build/mount.mergerfs
|
||||
install-mount-tools: install-base
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(CP) -a build/mount.mergerfs "$(INSTALLBINDIR)/mount.mergerfs"
|
||||
$(MAKE) -C libfuse install
|
||||
|
||||
install-man: $(MANPAGE)
|
||||
$(MKDIR) -p "$(INSTALLMAN1DIR)"
|
||||
@@ -233,6 +254,6 @@ install-build-pkgs:
|
||||
|
||||
.PHONY: libfuse
|
||||
libfuse:
|
||||
$(MAKE) -C libfuse
|
||||
$(MAKE) DEBUG=$(DEBUG) -C libfuse
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "${1}" in
|
||||
configure)
|
||||
chmod 4755 /usr/bin/mergerfs-fusermount
|
||||
;;
|
||||
esac
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@ --parallel
|
||||
|
||||
-3547
File diff suppressed because it is too large
Load Diff
+106
-43
@@ -1,50 +1,98 @@
|
||||
VERSION = "2.9.7-mergerfs_2.26.0"
|
||||
OPT = -O2
|
||||
VERSION = 2.9.7-mergerfs_2.30.0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
DEBUG_FLAGS := -g
|
||||
OS := $(shell uname -s)
|
||||
ifeq ($(OS),Linux)
|
||||
UTILS := utils
|
||||
INSTALLUTILS := install-utils
|
||||
else
|
||||
DEBUG_FLAGS :=
|
||||
UTILS :=
|
||||
INSTALLUTILS :=
|
||||
endif
|
||||
|
||||
DESTDIR = "/"
|
||||
BINDIR = "/bin"
|
||||
SBINDIR = "/sbin"
|
||||
SRC = \
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
||||
ifeq ($(LTO),1)
|
||||
LTO_FLAGS := -flto
|
||||
else
|
||||
LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
DATADIR = $(DATAROOTDIR)
|
||||
BINDIR = $(EXEC_PREFIX)/bin
|
||||
SBINDIR = /sbin
|
||||
MANDIR = $(DATAROOTDIR)/man
|
||||
MAN1DIR = $(MANDIR)/man1
|
||||
|
||||
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
|
||||
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
|
||||
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
AR ?= ar
|
||||
|
||||
SRC_C = \
|
||||
lib/buffer.c \
|
||||
lib/cuse_lowlevel.c \
|
||||
lib/crc32b.c \
|
||||
lib/debug.c \
|
||||
lib/fuse.c \
|
||||
lib/fuse_kern_chan.c \
|
||||
lib/fuse_loop.c \
|
||||
lib/fuse_loop_mt.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse_lowlevel.c \
|
||||
lib/fuse_mt.c \
|
||||
lib/node.c \
|
||||
lib/fuse_node.c \
|
||||
lib/fuse_opt.c \
|
||||
lib/fuse_session.c \
|
||||
lib/fuse_signals.c \
|
||||
lib/helper.c \
|
||||
lib/mount.c
|
||||
OBJS = $(SRC:lib/%.c=build/%.o)
|
||||
DEPS = $(SRC:lib/%.c=build/%.d)
|
||||
|
||||
CFLAGS += \
|
||||
$(OPT) \
|
||||
$(DEBUG_FLAGS) \
|
||||
lib/mount.c \
|
||||
lib/syslog.c
|
||||
SRC_CPP = \
|
||||
lib/format.cpp \
|
||||
lib/os.cpp \
|
||||
lib/cpu.cpp \
|
||||
lib/fuse_config.cpp \
|
||||
lib/fuse_loop.cpp \
|
||||
lib/fuse_msgbuf.cpp
|
||||
OBJS_C = $(SRC_C:lib/%.c=build/%.o)
|
||||
OBJS_CPP = $(SRC_CPP:lib/%.cpp=build/%.o)
|
||||
DEPS_C = $(SRC_C:lib/%.c=build/%.d)
|
||||
DEPS_CPP = $(SRC_CPP:lib/%.cpp=build/%.d)
|
||||
CFLAGS ?= \
|
||||
$(OPT_FLAGS)
|
||||
CFLAGS := \
|
||||
${CFLAGS} \
|
||||
$(LTO_FLAGS) \
|
||||
-std=gnu99 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
'-D_FILE_OFFSET_BITS=64' \
|
||||
'-D_REENTRANT' \
|
||||
'-DFUSE_USE_VERSION=29' \
|
||||
'-DFUSERMOUNT_DIR="/usr/local/bin"' \
|
||||
'-DPACKAGE_VERSION=$(VERSION)' \
|
||||
-MMD
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
$(LTO_FLAGS) \
|
||||
-std=c++11 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
FUSERMOUNT_DIR = $(BINDIR)
|
||||
FUSE_FLAGS = \
|
||||
-Iinclude \
|
||||
-Ibuild \
|
||||
-MMD
|
||||
LDFLAGS += \
|
||||
-D_REENTRANT \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-DPACKAGE_VERSION=\"$(VERSION)\" \
|
||||
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-lrt \
|
||||
-pthread
|
||||
|
||||
all: build/libfuse.a
|
||||
all: build/libfuse.a $(UTILS)
|
||||
|
||||
build/config.h: build/stamp
|
||||
ecfd/build | tee build/config.h
|
||||
@@ -54,31 +102,46 @@ build/stamp:
|
||||
touch $@
|
||||
|
||||
objects: build/config.h
|
||||
$(MAKE) $(OBJS)
|
||||
$(MAKE) $(OBJS_C) $(OBJS_CPP)
|
||||
|
||||
build/libfuse.a: objects
|
||||
ar rcs build/libfuse.a $(OBJS)
|
||||
${AR} rcs build/libfuse.a $(OBJS_C) $(OBJS_CPP)
|
||||
|
||||
build/mergerfs-mount: build/config.h util/fusermount.c lib/mount_util.c
|
||||
$(CC) $(CFLAGS) -Ilib -o mergerfs-mount util/fusermount.c lib/mount_util.c
|
||||
utils: mergerfs-fusermount mount.mergerfs
|
||||
|
||||
build/mount.mergerfs: build/libfuse.a util/mount.fuse.c
|
||||
$(CC) $(CFLAGS) -o mount.mergerfs util/mount.fuse.c build/libfuse.a $(LDFLAGS)
|
||||
build/mergerfs-fusermount: build/config.h util/fusermount.c lib/mount_util.c
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -Ilib -o build/mergerfs-fusermount util/fusermount.c lib/mount_util.c
|
||||
|
||||
mergerfs-fusermount: build/mergerfs-fusermount
|
||||
|
||||
build/mount.mergerfs: build/libfuse.a util/mount.mergerfs.c
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o build/mount.mergerfs util/mount.mergerfs.c build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mount.mergerfs: build/mount.mergerfs
|
||||
|
||||
build/%.o: lib/%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
build/%.o: lib/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
distclean: clean
|
||||
|
||||
install: build/mergerfs-mount build/mount.mergerfs
|
||||
install -D build/mergerfs-mount "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
chown root:root "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
chmod u+s "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
install -D build/mount.mergerfs "$(DESTDIR)$(SBINDIR)/mount.mergerfs"
|
||||
strip:
|
||||
strip --strip-all build/mount.mergerfs
|
||||
strip --strip-all build/mergerfs-fusermount
|
||||
|
||||
.PHONY: objects
|
||||
install-utils: mergerfs-fusermount mount.mergerfs strip
|
||||
install -D build/mergerfs-fusermount "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
install -D build/mount.mergerfs "$(INSTALLSBINDIR)/mount.mergerfs"
|
||||
chown root "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
chmod u+s "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
|
||||
-include $(DEPS)
|
||||
install: $(INSTALLUTILS)
|
||||
|
||||
.PHONY: objects strip utils install install-utils
|
||||
|
||||
-include $(DEPS_C) $(DEPS_CPP)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <malloc.h>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
malloc_trim(0);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char *argv[])
|
||||
{
|
||||
(void)splice;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char *argv[])
|
||||
{
|
||||
(void)vmsplice;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
+486
-793
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_attr_s fuse_attr_t;
|
||||
struct fuse_attr_s
|
||||
{
|
||||
uint64_t ino;
|
||||
uint64_t size;
|
||||
uint64_t blocks;
|
||||
uint64_t atime;
|
||||
uint64_t mtime;
|
||||
uint64_t ctime;
|
||||
uint32_t atimensec;
|
||||
uint32_t mtimensec;
|
||||
uint32_t ctimensec;
|
||||
uint32_t mode;
|
||||
uint32_t nlink;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t rdev;
|
||||
uint32_t blksize;
|
||||
uint32_t _padding;
|
||||
};
|
||||
+222
-246
@@ -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>
|
||||
|
||||
@@ -33,58 +36,62 @@
|
||||
#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
|
||||
#define FUSE_MAX_MAX_PAGES 256
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Information about open files
|
||||
*
|
||||
* Changed in version 2.5
|
||||
*/
|
||||
struct fuse_file_info {
|
||||
/** Open flags. Available in open() and release() */
|
||||
int flags;
|
||||
typedef struct fuse_file_info_t fuse_file_info_t;
|
||||
struct fuse_file_info_t
|
||||
{
|
||||
/** Open flags. Available in open() and release() */
|
||||
int flags;
|
||||
|
||||
/** Old file handle, don't use */
|
||||
unsigned long fh_old;
|
||||
/** In case of a write operation indicates if this was caused by a
|
||||
writepage */
|
||||
uint32_t writepage : 1;
|
||||
|
||||
/** In case of a write operation indicates if this was caused by a
|
||||
writepage */
|
||||
int writepage;
|
||||
/** Can be filled in by open, to use direct I/O on this file.
|
||||
Introduced in version 2.4 */
|
||||
uint32_t direct_io : 1;
|
||||
|
||||
/** Can be filled in by open, to use direct I/O on this file.
|
||||
Introduced in version 2.4 */
|
||||
unsigned int direct_io : 1;
|
||||
/** Can be filled in by open, to indicate, that cached file data
|
||||
need not be invalidated. Introduced in version 2.4 */
|
||||
uint32_t keep_cache : 1;
|
||||
|
||||
/** Can be filled in by open, to indicate, that cached file data
|
||||
need not be invalidated. Introduced in version 2.4 */
|
||||
unsigned int keep_cache : 1;
|
||||
/** Indicates a flush operation. Set in flush operation, also
|
||||
maybe set in highlevel lock operation and lowlevel release
|
||||
operation. Introduced in version 2.6 */
|
||||
uint32_t flush : 1;
|
||||
|
||||
/** Indicates a flush operation. Set in flush operation, also
|
||||
maybe set in highlevel lock operation and lowlevel release
|
||||
operation. Introduced in version 2.6 */
|
||||
unsigned int flush : 1;
|
||||
/** Can be filled in by open, to indicate that the file is not
|
||||
seekable. Introduced in version 2.8 */
|
||||
uint32_t nonseekable : 1;
|
||||
|
||||
/** Can be filled in by open, to indicate that the file is not
|
||||
seekable. Introduced in version 2.8 */
|
||||
unsigned int nonseekable : 1;
|
||||
/* Indicates that flock locks for this file should be
|
||||
released. If set, lock_owner shall contain a valid value.
|
||||
May only be set in ->release(). Introduced in version
|
||||
2.9 */
|
||||
uint32_t flock_release : 1;
|
||||
|
||||
/* Indicates that flock locks for this file should be
|
||||
released. If set, lock_owner shall contain a valid value.
|
||||
May only be set in ->release(). Introduced in version
|
||||
2.9 */
|
||||
unsigned int flock_release : 1;
|
||||
/* Requests the kernel to cache entries returned by readdir */
|
||||
uint32_t cache_readdir : 1;
|
||||
|
||||
/** Padding. Do not use*/
|
||||
unsigned int padding : 27;
|
||||
uint32_t auto_cache : 1;
|
||||
|
||||
/** File handle. May be filled in by filesystem in open().
|
||||
Available in all other file operations */
|
||||
uint64_t fh;
|
||||
uint32_t parallel_direct_writes:1;
|
||||
|
||||
/** Lock owner id. Available in locking operations and flush */
|
||||
uint64_t lock_owner;
|
||||
/** File handle. May be filled in by filesystem in open().
|
||||
Available in all other file operations */
|
||||
uint64_t fh;
|
||||
|
||||
/** Lock owner id. Available in locking operations and flush */
|
||||
uint64_t lock_owner;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,22 +103,26 @@ struct fuse_file_info {
|
||||
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
|
||||
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
|
||||
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
|
||||
* FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
|
||||
* FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
|
||||
* FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
|
||||
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
|
||||
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
|
||||
*/
|
||||
#define FUSE_CAP_ASYNC_READ (1 << 0)
|
||||
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_CAP_BIG_WRITES (1 << 5)
|
||||
#define FUSE_CAP_DONT_MASK (1 << 6)
|
||||
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_CAP_SPLICE_READ (1 << 9)
|
||||
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_CAP_ASYNC_READ (1 << 0)
|
||||
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_CAP_BIG_WRITES (1 << 5)
|
||||
#define FUSE_CAP_DONT_MASK (1 << 6)
|
||||
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_CAP_READDIR_PLUS (1 << 13)
|
||||
#define FUSE_CAP_READDIR_PLUS_AUTO (1 << 14)
|
||||
#define FUSE_CAP_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
|
||||
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_CAP_POSIX_ACL (1 << 19)
|
||||
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
|
||||
#define FUSE_CAP_MAX_PAGES (1 << 21)
|
||||
#define FUSE_CAP_SETXATTR_EXT (1 << 22)
|
||||
|
||||
/**
|
||||
* Ioctl flags
|
||||
@@ -138,60 +149,61 @@ struct 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;
|
||||
|
||||
/**
|
||||
* Is asynchronous read supported (read-write)
|
||||
*/
|
||||
unsigned async_read;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* For future use.
|
||||
*/
|
||||
unsigned reserved[23];
|
||||
/**
|
||||
* 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
|
||||
@@ -203,7 +215,8 @@ struct fuse_pollhandle;
|
||||
* @param args argument vector
|
||||
* @return the communication channel on success, NULL on failure
|
||||
*/
|
||||
struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args);
|
||||
struct fuse_chan *fuse_mount(const char *mountpoint,
|
||||
struct fuse_args *args);
|
||||
|
||||
/**
|
||||
* Umount a FUSE mountpoint
|
||||
@@ -220,7 +233,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
|
||||
@@ -230,12 +242,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
|
||||
@@ -257,7 +269,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 *
|
||||
@@ -267,73 +279,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),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -343,36 +355,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -384,41 +396,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
|
||||
@@ -437,7 +449,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 *
|
||||
@@ -464,42 +476,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_ */
|
||||
|
||||
@@ -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);
|
||||
@@ -1,201 +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 (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_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 *, fuse_fill_dir_t, 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 (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_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 *, fuse_fill_dir_t, 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);
|
||||
|
||||
typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
|
||||
struct fuse_operations_compat2 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
|
||||
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 (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
|
||||
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__ */
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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 <string>
|
||||
|
||||
int fuse_config_get_read_thread_count();
|
||||
int fuse_config_get_process_thread_count();
|
||||
int fuse_config_get_process_thread_queue_depth();
|
||||
std::string fuse_config_get_pin_threads();
|
||||
|
||||
void fuse_config_set_read_thread_count(int const);
|
||||
void fuse_config_set_process_thread_count(int const);
|
||||
void fuse_config_set_process_thread_queue_depth(int const);
|
||||
void fuse_config_set_pin_threads(std::string const);
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_dirent_s fuse_dirent_t;
|
||||
struct fuse_dirent_s
|
||||
{
|
||||
uint64_t ino;
|
||||
uint64_t off;
|
||||
uint32_t namelen;
|
||||
uint32_t type;
|
||||
char name[];
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_entry.h"
|
||||
|
||||
typedef struct fuse_direntplus_s fuse_direntplus_t;
|
||||
struct fuse_direntplus_s
|
||||
{
|
||||
fuse_entry_t entry;
|
||||
fuse_attr_t attr;
|
||||
fuse_dirent_t dirent;
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#include "kvec.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum fuse_dirents_type_e
|
||||
{
|
||||
UNSET = 0,
|
||||
NORMAL,
|
||||
PLUS
|
||||
};
|
||||
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
|
||||
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
struct fuse_dirents_t
|
||||
{
|
||||
kvec_t(char) data;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
void fuse_dirents_free(fuse_dirents_t *d);
|
||||
void fuse_dirents_reset(fuse_dirents_t *d);
|
||||
|
||||
int fuse_dirents_add(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_plus(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
int fuse_dirents_add_linux(fuse_dirents_t *d,
|
||||
const struct linux_dirent64 *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
|
||||
const struct linux_dirent64 *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
|
||||
void *fuse_dirents_find(fuse_dirents_t *d,
|
||||
const uint64_t ino);
|
||||
|
||||
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_entry_s fuse_entry_t;
|
||||
struct fuse_entry_s
|
||||
{
|
||||
uint64_t nodeid;
|
||||
uint64_t generation;
|
||||
uint64_t entry_valid;
|
||||
uint64_t attr_valid;
|
||||
uint32_t entry_valid_nsec;
|
||||
uint32_t attr_valid_nsec;
|
||||
};
|
||||
+333
-49
@@ -38,12 +38,50 @@
|
||||
*
|
||||
* Protocol changelog:
|
||||
*
|
||||
* 7.1:
|
||||
* - add the following messages:
|
||||
* FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
|
||||
* FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
|
||||
* FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
|
||||
* FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
|
||||
* FUSE_RELEASEDIR
|
||||
* - add padding to messages to accommodate 32-bit servers on 64-bit kernels
|
||||
*
|
||||
* 7.2:
|
||||
* - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
|
||||
* - add FUSE_FSYNCDIR message
|
||||
*
|
||||
* 7.3:
|
||||
* - add FUSE_ACCESS message
|
||||
* - add FUSE_CREATE message
|
||||
* - add filehandle to fuse_setattr_in
|
||||
*
|
||||
* 7.4:
|
||||
* - add frsize to fuse_kstatfs
|
||||
* - clean up request size limit checking
|
||||
*
|
||||
* 7.5:
|
||||
* - add flags and max_write to fuse_init_out
|
||||
*
|
||||
* 7.6:
|
||||
* - add max_readahead to fuse_init_in and fuse_init_out
|
||||
*
|
||||
* 7.7:
|
||||
* - add FUSE_INTERRUPT message
|
||||
* - add POSIX file lock support
|
||||
*
|
||||
* 7.8:
|
||||
* - add lock_owner and flags fields to fuse_release_in
|
||||
* - add FUSE_BMAP message
|
||||
* - add FUSE_DESTROY message
|
||||
*
|
||||
* 7.9:
|
||||
* - new fuse_getattr_in input argument of GETATTR
|
||||
* - add lk_flags in fuse_lk_in
|
||||
* - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
|
||||
* - add blksize field to fuse_attr
|
||||
* - add file flags field to fuse_read_in and fuse_write_in
|
||||
* - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
|
||||
*
|
||||
* 7.10
|
||||
* - add nonseekable open flag
|
||||
@@ -54,7 +92,7 @@
|
||||
* - add POLL message and NOTIFY_POLL notification
|
||||
*
|
||||
* 7.12
|
||||
* - add umask flag to input argument of open, mknod and mkdir
|
||||
* - add umask flag to input argument of create, mknod and mkdir
|
||||
* - add notification messages for invalidation of inodes and
|
||||
* directory entries
|
||||
*
|
||||
@@ -119,6 +157,55 @@
|
||||
*
|
||||
* 7.28
|
||||
* - add FUSE_COPY_FILE_RANGE
|
||||
* - add FOPEN_CACHE_DIR
|
||||
* - add FUSE_MAX_PAGES, add max_pages to init_out
|
||||
* - add FUSE_CACHE_SYMLINKS
|
||||
*
|
||||
* 7.29
|
||||
* - add FUSE_NO_OPENDIR_SUPPORT flag
|
||||
*
|
||||
* 7.30
|
||||
* - add FUSE_EXPLICIT_INVAL_DATA
|
||||
* - add FUSE_IOCTL_COMPAT_X32
|
||||
*
|
||||
* 7.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
|
||||
*
|
||||
* 7.38
|
||||
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
|
||||
* - add FOPEN_PARALLEL_DIRECT_WRITES
|
||||
* - add total_extlen to fuse_in_header
|
||||
* - add FUSE_MAX_NR_SECCTX
|
||||
* - add extension header
|
||||
* - add FUSE_EXT_GROUPS
|
||||
* - add FUSE_CREATE_SUPP_GROUP
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -154,7 +241,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 27
|
||||
#define FUSE_KERNEL_MINOR_VERSION 38
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -178,7 +265,7 @@ struct fuse_attr {
|
||||
uint32_t gid;
|
||||
uint32_t rdev;
|
||||
uint32_t blksize;
|
||||
uint32_t padding;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct fuse_kstatfs {
|
||||
@@ -204,17 +291,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
|
||||
@@ -222,10 +310,18 @@ struct fuse_file_lock {
|
||||
* FOPEN_DIRECT_IO: bypass page cache for this open file
|
||||
* FOPEN_KEEP_CACHE: don't invalidate the data cache on open
|
||||
* 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)
|
||||
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
|
||||
*/
|
||||
#define FOPEN_DIRECT_IO (1 << 0)
|
||||
#define FOPEN_KEEP_CACHE (1 << 1)
|
||||
#define FOPEN_NONSEEKABLE (1 << 2)
|
||||
#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)
|
||||
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
|
||||
|
||||
/**
|
||||
* INIT request/reply flags
|
||||
@@ -252,29 +348,64 @@ struct fuse_file_lock {
|
||||
* FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
|
||||
* FUSE_POSIX_ACL: filesystem supports posix acls
|
||||
* FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
|
||||
* FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
|
||||
* 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: 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
|
||||
* FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir,
|
||||
* symlink and mknod (single group that matches parent)
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_FILE_OPS (1 << 2)
|
||||
#define FUSE_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_BIG_WRITES (1 << 5)
|
||||
#define FUSE_DONT_MASK (1 << 6)
|
||||
#define FUSE_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_SPLICE_READ (1 << 9)
|
||||
#define FUSE_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_HAS_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_AUTO_INVAL_DATA (1 << 12)
|
||||
#define FUSE_DO_READDIRPLUS (1 << 13)
|
||||
#define FUSE_READDIRPLUS_AUTO (1 << 14)
|
||||
#define FUSE_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_WRITEBACK_CACHE (1 << 16)
|
||||
#define FUSE_NO_OPEN_SUPPORT (1 << 17)
|
||||
#define FUSE_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_HANDLE_KILLPRIV (1 << 19)
|
||||
#define FUSE_POSIX_ACL (1 << 20)
|
||||
#define FUSE_ABORT_ERROR (1 << 21)
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_FILE_OPS (1 << 2)
|
||||
#define FUSE_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_BIG_WRITES (1 << 5)
|
||||
#define FUSE_DONT_MASK (1 << 6)
|
||||
#define FUSE_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_SPLICE_READ (1 << 9)
|
||||
#define FUSE_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_HAS_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_AUTO_INVAL_DATA (1 << 12)
|
||||
#define FUSE_DO_READDIRPLUS (1 << 13)
|
||||
#define FUSE_READDIRPLUS_AUTO (1 << 14)
|
||||
#define FUSE_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_WRITEBACK_CACHE (1 << 16)
|
||||
#define FUSE_NO_OPEN_SUPPORT (1 << 17)
|
||||
#define FUSE_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_HANDLE_KILLPRIV (1 << 19)
|
||||
#define FUSE_POSIX_ACL (1 << 20)
|
||||
#define FUSE_ABORT_ERROR (1 << 21)
|
||||
#define FUSE_MAX_PAGES (1 << 22)
|
||||
#define FUSE_CACHE_SYMLINKS (1 << 23)
|
||||
#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)
|
||||
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@@ -286,8 +417,8 @@ struct fuse_file_lock {
|
||||
/**
|
||||
* Release flags
|
||||
*/
|
||||
#define FUSE_RELEASE_FLUSH (1 << 0)
|
||||
#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
|
||||
#define FUSE_RELEASE_FLUSH (1 << 0)
|
||||
#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
|
||||
|
||||
/**
|
||||
* Getattr flags
|
||||
@@ -304,9 +435,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_SUIDGID: kill suid and sgid bits
|
||||
*/
|
||||
#define FUSE_WRITE_CACHE (1 << 0)
|
||||
#define FUSE_WRITE_LOCKOWNER (1 << 1)
|
||||
#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
|
||||
@@ -321,6 +457,7 @@ struct fuse_file_lock {
|
||||
* FUSE_IOCTL_RETRY: retry with new iovecs
|
||||
* FUSE_IOCTL_32BIT: 32bit ioctl
|
||||
* FUSE_IOCTL_DIR: is a directory
|
||||
* FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t)
|
||||
*
|
||||
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
||||
*/
|
||||
@@ -329,6 +466,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_IOCTL_RETRY (1 << 2)
|
||||
#define FUSE_IOCTL_32BIT (1 << 3)
|
||||
#define FUSE_IOCTL_DIR (1 << 4)
|
||||
#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
|
||||
|
||||
#define FUSE_IOCTL_MAX_IOV 256
|
||||
|
||||
@@ -339,6 +477,51 @@ struct fuse_file_lock {
|
||||
*/
|
||||
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
|
||||
|
||||
/**
|
||||
* Fsync flags
|
||||
*
|
||||
* FUSE_FSYNC_FDATASYNC: Sync data only, not metadata
|
||||
*/
|
||||
#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)
|
||||
|
||||
/**
|
||||
* notify_inval_entry flags
|
||||
* FUSE_EXPIRE_ONLY
|
||||
*/
|
||||
#define FUSE_EXPIRE_ONLY (1 << 0)
|
||||
|
||||
/**
|
||||
* extension type
|
||||
* FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx
|
||||
* FUSE_EXT_GROUPS: &fuse_supp_groups extension
|
||||
*/
|
||||
enum fuse_ext_type {
|
||||
/* Types 0..31 are reserved for fuse_secctx_header */
|
||||
FUSE_MAX_NR_SECCTX = 31,
|
||||
FUSE_EXT_GROUPS = 32,
|
||||
};
|
||||
|
||||
enum fuse_opcode {
|
||||
FUSE_LOOKUP = 1,
|
||||
FUSE_FORGET = 2, /* no reply */
|
||||
@@ -385,9 +568,17 @@ enum fuse_opcode {
|
||||
FUSE_RENAME2 = 45,
|
||||
FUSE_LSEEK = 46,
|
||||
FUSE_COPY_FILE_RANGE = 47,
|
||||
FUSE_SETUPMAPPING = 48,
|
||||
FUSE_REMOVEMAPPING = 49,
|
||||
FUSE_SYNCFS = 50,
|
||||
FUSE_TMPFILE = 51,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
|
||||
/* Reserved opcodes: helpful to detect structure endian-ness */
|
||||
CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
|
||||
FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
|
||||
};
|
||||
|
||||
enum fuse_notify_code {
|
||||
@@ -494,14 +685,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 {
|
||||
@@ -563,9 +754,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 {
|
||||
@@ -600,6 +795,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
|
||||
@@ -614,7 +811,10 @@ struct fuse_init_out {
|
||||
uint16_t congestion_threshold;
|
||||
uint32_t max_write;
|
||||
uint32_t time_gran;
|
||||
uint32_t unused[9];
|
||||
uint16_t max_pages;
|
||||
uint16_t map_alignment;
|
||||
uint32_t flags2;
|
||||
uint32_t unused[7];
|
||||
};
|
||||
|
||||
#define CUSE_INIT_INFO_MAX 4096
|
||||
@@ -705,7 +905,8 @@ struct fuse_in_header {
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t pid;
|
||||
uint32_t padding;
|
||||
uint16_t total_extlen; /* length of extensions in 8byte units */
|
||||
uint16_t padding;
|
||||
};
|
||||
|
||||
struct fuse_out_header {
|
||||
@@ -722,9 +923,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)
|
||||
|
||||
@@ -747,7 +951,7 @@ struct fuse_notify_inval_inode_out {
|
||||
struct fuse_notify_inval_entry_out {
|
||||
uint64_t parent;
|
||||
uint32_t namelen;
|
||||
uint32_t padding;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct fuse_notify_delete_out {
|
||||
@@ -783,7 +987,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;
|
||||
@@ -806,4 +1011,83 @@ 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fuse_ext_header - extension header
|
||||
* @size: total size of this extension including this header
|
||||
* @type: type of extension
|
||||
*
|
||||
* This is made compatible with fuse_secctx_header by using type values >
|
||||
* FUSE_MAX_NR_SECCTX
|
||||
*/
|
||||
struct fuse_ext_header {
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fuse_supp_groups - Supplementary group extension
|
||||
* @nr_groups: number of supplementary groups
|
||||
* @groups: flexible array of group IDs
|
||||
*/
|
||||
struct fuse_supp_groups {
|
||||
uint32_t nr_groups;
|
||||
uint32_t groups[];
|
||||
};
|
||||
|
||||
#endif /* _LINUX_FUSE_H */
|
||||
|
||||
+937
-1270
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_msgbuf_t fuse_msgbuf_t;
|
||||
struct fuse_msgbuf_t
|
||||
{
|
||||
uint32_t size;
|
||||
char *mem;
|
||||
};
|
||||
+28
-28
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 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 "kvec.h"
|
||||
int main() {
|
||||
kvec_t(int) array;
|
||||
kv_init(array);
|
||||
kv_push(int, array, 10); // append
|
||||
kv_a(int, array, 20) = 5; // dynamic
|
||||
kv_A(array, 20) = 4; // static
|
||||
kv_destroy(array);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2008-09-22 (0.1.0):
|
||||
|
||||
* The initial version.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef AC_KVEC_H
|
||||
#define AC_KVEC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
|
||||
#define kvec_t(type) struct { size_t n, m; type *a; }
|
||||
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
|
||||
#define kv_destroy(v) free((v).a)
|
||||
#define kv_A(v, i) ((v).a[(i)])
|
||||
#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))
|
||||
|
||||
#define kv_copy(type, v1, v0) do { \
|
||||
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
|
||||
(v1).n = (v0).n; \
|
||||
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
|
||||
} while (0) \
|
||||
|
||||
#define kv_push(type, v, x) do { \
|
||||
if ((v).n == (v).m) { \
|
||||
(v).m = (v).m? (v).m<<1 : 2; \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
|
||||
} \
|
||||
(v).a[(v).n++] = (x); \
|
||||
} while (0)
|
||||
|
||||
#define kv_pushp(type, v) (((v).n == (v).m)? \
|
||||
((v).m = ((v).m? (v).m<<1 : 2), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: 0), ((v).a + ((v).n++))
|
||||
|
||||
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
|
||||
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
|
||||
: 0), (v).a[(i)])
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct linux_dirent64
|
||||
{
|
||||
uint64_t ino;
|
||||
int64_t off;
|
||||
uint16_t reclen;
|
||||
uint8_t type;
|
||||
char name[];
|
||||
};
|
||||
@@ -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"
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||
#define ST_ATIM_NSEC(ST) ((ST)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(ST) ((ST)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(ST) ((ST)->st_mtim.tv_nsec)
|
||||
#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC
|
||||
#define ST_ATIM_NSEC(ST) ((ST)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(ST) ((ST)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(ST) ((ST)->st_mtimespec.tv_nsec)
|
||||
#else
|
||||
#define ST_ATIM_NSEC(ST) 0
|
||||
#define ST_CTIM_NSEC(ST) 0
|
||||
#define ST_MTIM_NSEC(ST) 0
|
||||
#endif
|
||||
@@ -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);
|
||||
@@ -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
@@ -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) {
|
||||
void *dstmem = dst->mem + dst_off;
|
||||
void *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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "cpu.hpp"
|
||||
#include "ghc/filesystem.hpp"
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
int
|
||||
CPU::getaffinity(const pthread_t thread_id_,
|
||||
cpu_set_t *cpuset_)
|
||||
{
|
||||
CPU_ZERO(cpuset_);
|
||||
return sched_getaffinity(thread_id_,
|
||||
sizeof(cpu_set_t),
|
||||
cpuset_);
|
||||
}
|
||||
|
||||
int
|
||||
CPU::setaffinity(const pthread_t thread_id_,
|
||||
cpu_set_t *cpuset_)
|
||||
{
|
||||
return pthread_setaffinity_np(thread_id_,
|
||||
sizeof(cpu_set_t),
|
||||
cpuset_);
|
||||
}
|
||||
|
||||
int
|
||||
CPU::setaffinity(const pthread_t thread_id_,
|
||||
const int cpu_)
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpu_,&cpuset);
|
||||
|
||||
return CPU::setaffinity(thread_id_,&cpuset);
|
||||
}
|
||||
|
||||
int
|
||||
CPU::setaffinity(const pthread_t thread_id_,
|
||||
const std::set<int> cpus_)
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
for(auto const cpu : cpus_)
|
||||
CPU_SET(cpu,&cpuset);
|
||||
|
||||
return CPU::setaffinity(thread_id_,&cpuset);
|
||||
}
|
||||
|
||||
static
|
||||
ghc::filesystem::path
|
||||
generate_cpu_core_id_path(const int cpu_id_)
|
||||
{
|
||||
const ghc::filesystem::path basepath{"/sys/devices/system/cpu"};
|
||||
|
||||
return basepath / fmt::format("cpu{}",cpu_id_) / "topology" / "core_id";
|
||||
}
|
||||
|
||||
int
|
||||
CPU::count()
|
||||
{
|
||||
int rv;
|
||||
cpu_set_t cpuset;
|
||||
|
||||
rv = CPU::getaffinity(0,&cpuset);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return CPU_COUNT(&cpuset);
|
||||
}
|
||||
|
||||
CPU::CPUVec
|
||||
CPU::cpus()
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
CPU::CPUVec cpuvec;
|
||||
|
||||
CPU::getaffinity(0,&cpuset);
|
||||
|
||||
for(int i = 0; i < CPU_SETSIZE; i++)
|
||||
{
|
||||
if(!CPU_ISSET(i,&cpuset))
|
||||
continue;
|
||||
|
||||
cpuvec.push_back(i);
|
||||
}
|
||||
|
||||
return cpuvec;
|
||||
}
|
||||
|
||||
CPU::CPU2CoreMap
|
||||
CPU::cpu2core()
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
CPU::CPU2CoreMap c2c;
|
||||
|
||||
CPU::getaffinity(0,&cpuset);
|
||||
|
||||
for(int i = 0; i < CPU_SETSIZE; i++)
|
||||
{
|
||||
int core_id;
|
||||
std::ifstream ifs;
|
||||
ghc::filesystem::path path;
|
||||
|
||||
if(!CPU_ISSET(i,&cpuset))
|
||||
continue;
|
||||
|
||||
path = ::generate_cpu_core_id_path(i);
|
||||
|
||||
ifs.open(path);
|
||||
if(!ifs)
|
||||
break;
|
||||
|
||||
ifs >> core_id;
|
||||
|
||||
c2c[i] = core_id;
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
return c2c;
|
||||
}
|
||||
|
||||
CPU::Core2CPUsMap
|
||||
CPU::core2cpus()
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
CPU::Core2CPUsMap c2c;
|
||||
|
||||
CPU::getaffinity(0,&cpuset);
|
||||
|
||||
for(int i = 0; i < CPU_SETSIZE; i++)
|
||||
{
|
||||
int core_id;
|
||||
std::ifstream ifs;
|
||||
ghc::filesystem::path path;
|
||||
|
||||
if(!CPU_ISSET(i,&cpuset))
|
||||
continue;
|
||||
|
||||
path = ::generate_cpu_core_id_path(i);
|
||||
|
||||
ifs.open(path);
|
||||
if(!ifs)
|
||||
break;
|
||||
|
||||
ifs >> core_id;
|
||||
|
||||
c2c[core_id].insert(i);
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
return c2c;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class CPU
|
||||
{
|
||||
public:
|
||||
typedef std::vector<pthread_t> ThreadIdVec;
|
||||
typedef std::vector<int> CPUVec;
|
||||
typedef std::unordered_map<int,int> CPU2CoreMap;
|
||||
typedef std::unordered_map<int,std::set<int>> Core2CPUsMap;
|
||||
|
||||
public:
|
||||
static int count();
|
||||
static int getaffinity(const pthread_t thread_id, cpu_set_t *cpuset);
|
||||
static int setaffinity(const pthread_t thread_id, cpu_set_t *cpuset);
|
||||
static int setaffinity(const pthread_t thread_id, const int cpu);
|
||||
static int setaffinity(const pthread_t thread_id, const std::set<int> cpus);
|
||||
|
||||
static CPU::CPUVec cpus();
|
||||
static CPU::CPU2CoreMap cpu2core();
|
||||
static CPU::Core2CPUsMap core2cpus();
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -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));
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
// Formatting library for C++ - dynamic format arguments
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_ARGS_H_
|
||||
#define FMT_ARGS_H_
|
||||
|
||||
#include <functional> // std::reference_wrapper
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <vector>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||
|
||||
template <typename T> const T& unwrap(const T& v) { return v; }
|
||||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
||||
return static_cast<const T&>(v);
|
||||
}
|
||||
|
||||
class dynamic_arg_list {
|
||||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||
// templates it doesn't complain about inability to deduce single translation
|
||||
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||
template <typename = void> struct node {
|
||||
virtual ~node() = default;
|
||||
std::unique_ptr<node<>> next;
|
||||
};
|
||||
|
||||
template <typename T> struct typed_node : node<> {
|
||||
T value;
|
||||
|
||||
template <typename Arg>
|
||||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||
: value(arg.data(), arg.size()) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<node<>> head_;
|
||||
|
||||
public:
|
||||
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||
auto& value = new_node->value;
|
||||
new_node->next = std::move(head_);
|
||||
head_ = std::move(new_node);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
\rst
|
||||
A dynamic version of `fmt::format_arg_store`.
|
||||
It's equipped with a storage to potentially temporary objects which lifetimes
|
||||
could be shorter than the format arguments object.
|
||||
|
||||
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
||||
into type-erased formatting functions such as `~fmt::vformat`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context>
|
||||
class dynamic_format_arg_store
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround a GCC template argument substitution bug.
|
||||
: public basic_format_args<Context>
|
||||
#endif
|
||||
{
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
template <typename T> struct need_copy {
|
||||
static constexpr detail::type mapped_type =
|
||||
detail::mapped_type_constant<T, Context>::value;
|
||||
|
||||
enum {
|
||||
value = !(detail::is_reference_wrapper<T>::value ||
|
||||
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||
(mapped_type != detail::type::cstring_type &&
|
||||
mapped_type != detail::type::string_type &&
|
||||
mapped_type != detail::type::custom_type))
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using stored_type = conditional_t<
|
||||
std::is_convertible<T, std::basic_string<char_type>>::value &&
|
||||
!detail::is_reference_wrapper<T>::value,
|
||||
std::basic_string<char_type>, T>;
|
||||
|
||||
// Storage of basic_format_arg must be contiguous.
|
||||
std::vector<basic_format_arg<Context>> data_;
|
||||
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||
|
||||
// Storage of arguments not fitting into basic_format_arg must grow
|
||||
// without relocation because items in data_ refer to it.
|
||||
detail::dynamic_arg_list dynamic_args_;
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
|
||||
unsigned long long get_types() const {
|
||||
return detail::is_unpacked_bit | data_.size() |
|
||||
(named_info_.empty()
|
||||
? 0ULL
|
||||
: static_cast<unsigned long long>(detail::has_named_args_bit));
|
||||
}
|
||||
|
||||
const basic_format_arg<Context>* data() const {
|
||||
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||
}
|
||||
|
||||
template <typename T> void emplace_arg(const T& arg) {
|
||||
data_.emplace_back(detail::make_arg<Context>(arg));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||
if (named_info_.empty()) {
|
||||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
|
||||
data_.insert(data_.begin(), {zero_ptr, 0});
|
||||
}
|
||||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
|
||||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||
data->pop_back();
|
||||
};
|
||||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||
guard{&data_, pop_one};
|
||||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
|
||||
guard.release();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr dynamic_format_arg_store() = default;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds an argument into the dynamic store for later passing to a formatting
|
||||
function.
|
||||
|
||||
Note that custom types and string types (but not string views) are copied
|
||||
into the store dynamically allocating memory if necessary.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(42);
|
||||
store.push_back("abc");
|
||||
store.push_back(1.5f);
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value))
|
||||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||
else
|
||||
emplace_arg(detail::unwrap(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds a reference to the argument into the dynamic store for later passing to
|
||||
a formatting function.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
char band[] = "Rolling Stones";
|
||||
store.push_back(std::cref(band));
|
||||
band[9] = 'c'; // Changing str affects the output.
|
||||
std::string result = fmt::vformat("{}", store);
|
||||
// result == "Rolling Scones"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||
static_assert(
|
||||
need_copy<T>::value,
|
||||
"objects of built-in types and string views are always copied");
|
||||
emplace_arg(arg.get());
|
||||
}
|
||||
|
||||
/**
|
||||
Adds named argument into the dynamic store for later passing to a formatting
|
||||
function. ``std::reference_wrapper`` is supported to avoid copying of the
|
||||
argument. The name is always copied into the store.
|
||||
*/
|
||||
template <typename T>
|
||||
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||
const char_type* arg_name =
|
||||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||
if (detail::const_check(need_copy<T>::value)) {
|
||||
emplace_arg(
|
||||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
|
||||
} else {
|
||||
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||
}
|
||||
}
|
||||
|
||||
/** Erase all elements from the store */
|
||||
void clear() {
|
||||
data_.clear();
|
||||
named_info_.clear();
|
||||
dynamic_args_ = detail::dynamic_arg_list();
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Reserves space to store at least *new_cap* arguments including
|
||||
*new_cap_named* named arguments.
|
||||
\endrst
|
||||
*/
|
||||
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||
FMT_ASSERT(new_cap >= new_cap_named,
|
||||
"Set of arguments includes set of named arguments");
|
||||
data_.reserve(new_cap);
|
||||
named_info_.reserve(new_cap_named);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_ARGS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,651 @@
|
||||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
underline = 1 << 3,
|
||||
blink = 1 << 4,
|
||||
reverse = 1 << 5,
|
||||
conceal = 1 << 6,
|
||||
strikethrough = 1 << 7,
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
|
||||
: is_rgb(), value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
union color_union {
|
||||
uint8_t term_color;
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
/** A text style consisting of foreground and background colors and emphasis. */
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const noexcept {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const noexcept {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const noexcept {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_background() const noexcept {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) noexcept
|
||||
: set_foreground_color(), set_background_color(), ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
|
||||
|
||||
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
/** Creates a text style from the foreground (text) color. */
|
||||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
|
||||
return text_style(true, foreground);
|
||||
}
|
||||
|
||||
/** Creates a text style from the background color. */
|
||||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
|
||||
return text_style(false, background);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||
const char* esc) noexcept {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == string_view("\x1b[48;2;");
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[index++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
buffer[index++] = static_cast<Char>('\0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
|
||||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t num_emphases = 8;
|
||||
Char buffer[7u + 3u * num_emphases + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) noexcept {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
detail::color_type foreground) noexcept {
|
||||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
detail::color_type background) noexcept {
|
||||
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
|
||||
int result = std::fputs(chars, stream);
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
|
||||
int result = std::fputws(chars, stream);
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) {
|
||||
fputs("\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) {
|
||||
fputs(L"\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename T> struct styled_arg {
|
||||
const T& value;
|
||||
text_style style;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
detail::vformat_to(buf, format_str, args, {});
|
||||
if (has_style) detail::reset_color<Char>(buf);
|
||||
}
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, detail::to_string_view(format), args);
|
||||
if (detail::is_utf8()) {
|
||||
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
|
||||
} else {
|
||||
buf.push_back(Char(0));
|
||||
detail::fputs(buf.data(), f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/color.h>
|
||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return fmt::vformat(ts, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string with the given text_style and writes the output to ``out``.
|
||||
*/
|
||||
template <typename OutputIt, typename Char,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||
OutputIt vformat_to(
|
||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, ts, format_str, args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments with the given text_style, writes the result to the output
|
||||
iterator ``out`` and returns the iterator past the end of the output range.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::vector<char> out;
|
||||
fmt::format_to(std::back_inserter(out),
|
||||
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||
detail::is_string<S>::value>
|
||||
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, ts, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
|
||||
template <typename FormatContext>
|
||||
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
const auto& ts = arg.style;
|
||||
const auto& value = arg.value;
|
||||
auto out = ctx.out();
|
||||
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
out = std::copy(emphasis.begin(), emphasis.end(), out);
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground =
|
||||
detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
out = std::copy(foreground.begin(), foreground.end(), out);
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background =
|
||||
detail::make_background_color<Char>(ts.get_background());
|
||||
out = std::copy(background.begin(), background.end(), out);
|
||||
}
|
||||
out = formatter<T, Char>::format(value, ctx);
|
||||
if (has_style) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
out = std::copy(reset_color.begin(), reset_color.end(), out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an argument that will be formatted using ANSI escape sequences,
|
||||
to be used in a formatting function.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("Elapsed time: {0:.2f} seconds",
|
||||
fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
||||
fmt::bg(fmt::color::blue)));
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
|
||||
-> detail::styled_arg<remove_cvref_t<T>> {
|
||||
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
@@ -0,0 +1,611 @@
|
||||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename Char, typename InputIt>
|
||||
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||
counting_iterator it) {
|
||||
return it + (end - begin);
|
||||
}
|
||||
|
||||
template <typename OutputIt> class truncating_iterator_base {
|
||||
protected:
|
||||
OutputIt out_;
|
||||
size_t limit_;
|
||||
size_t count_ = 0;
|
||||
|
||||
truncating_iterator_base() : out_(), limit_(0) {}
|
||||
|
||||
truncating_iterator_base(OutputIt out, size_t limit)
|
||||
: out_(out), limit_(limit) {}
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
|
||||
|
||||
OutputIt base() const { return out_; }
|
||||
size_t count() const { return count_; }
|
||||
};
|
||||
|
||||
// An output iterator that truncates the output and counts the number of objects
|
||||
// written to it.
|
||||
template <typename OutputIt,
|
||||
typename Enable = typename std::is_void<
|
||||
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||
class truncating_iterator;
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::false_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||
|
||||
public:
|
||||
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
truncating_iterator& operator++() {
|
||||
if (this->count_++ < this->limit_) ++this->out_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
value_type& operator*() const {
|
||||
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::true_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
public:
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
template <typename T> truncating_iterator& operator=(T val) {
|
||||
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator& operator++() { return *this; }
|
||||
truncating_iterator& operator++(int) { return *this; }
|
||||
truncating_iterator& operator*() { return *this; }
|
||||
};
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts a string literal *s* into a format string that will be parsed at
|
||||
compile time and converted into efficient formatting code. Requires C++17
|
||||
``constexpr if`` compiler support.
|
||||
|
||||
**Example**::
|
||||
|
||||
// Converts 42 into std::string using the most efficient method and no
|
||||
// runtime format string processing.
|
||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
# define FMT_COMPILE(s) \
|
||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||
#else
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
using char_type = Char;
|
||||
explicit constexpr operator basic_string_view<char_type>() const {
|
||||
return {Str.data, N - 1};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
const T& first(const T& value, const Tail&...) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||
[[maybe_unused]] const Args&... rest) {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return detail::get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <typename Char, typename... Args>
|
||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||
type_list<Args...>) {
|
||||
return get_arg_index_by_name<Args...>(name);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type =
|
||||
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, data);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||
size_t size) {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char> struct code_unit {
|
||||
Char value;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
};
|
||||
|
||||
// This ensures that the argument type is convertible to `const T&`.
|
||||
template <typename T, int N, typename... Args>
|
||||
constexpr const T& get_arg_checked(const Args&... args) {
|
||||
const auto& arg = detail::get<N>(args...);
|
||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||
return arg.value;
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument with name.
|
||||
template <typename Char> struct runtime_named_field {
|
||||
using char_type = Char;
|
||||
basic_string_view<Char> name;
|
||||
|
||||
template <typename OutputIt, typename T>
|
||||
constexpr static bool try_format_argument(
|
||||
OutputIt& out,
|
||||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
||||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||
if (arg_name == arg.name) {
|
||||
out = write<Char>(out, arg.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
bool found = (try_format_argument(out, name, args) || ...);
|
||||
if (!found) {
|
||||
FMT_THROW(format_error("argument with specified name is not found"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N and has format specifiers.
|
||||
template <typename Char, typename T, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
formatter<T, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||
const Args&... args) const {
|
||||
const auto& vargs =
|
||||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S format_str) {
|
||||
if constexpr (POS !=
|
||||
basic_string_view<typename S::char_type>(format_str).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Char> struct parse_specs_result {
|
||||
formatter<T, Char> fmt;
|
||||
size_t end;
|
||||
int next_arg_id;
|
||||
};
|
||||
|
||||
constexpr int manual_indexing_id = -1;
|
||||
|
||||
template <typename T, typename Char>
|
||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||
size_t pos, int next_arg_id) {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
|
||||
next_arg_id);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
|
||||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||
}
|
||||
|
||||
template <typename Char> struct arg_id_handler {
|
||||
arg_ref<Char> arg_id;
|
||||
|
||||
constexpr int operator()() {
|
||||
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||
return 0;
|
||||
}
|
||||
constexpr int operator()(int id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
constexpr int operator()(basic_string_view<Char> id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr void on_error(const char* message) {
|
||||
FMT_THROW(format_error(message));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct parse_arg_id_result {
|
||||
arg_ref<Char> arg_id;
|
||||
const Char* arg_id_end;
|
||||
};
|
||||
|
||||
template <int ID, typename Char>
|
||||
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
|
||||
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void> struct field_type {
|
||||
using type = remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||
using type = remove_cvref_t<decltype(T::value)>;
|
||||
};
|
||||
|
||||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||
typename S>
|
||||
constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||
format_str);
|
||||
} else if constexpr (c != ':') {
|
||||
FMT_THROW(format_error("expected ':'"));
|
||||
} else {
|
||||
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||
if constexpr (result.end >= str.size() || str[result.end] != '}') {
|
||||
FMT_THROW(format_error("expected '}'"));
|
||||
return 0;
|
||||
} else {
|
||||
return parse_tail<Args, result.end + 1, result.next_arg_id>(
|
||||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||
result.fmt},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||
if constexpr (str[POS] == '{') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||
static_assert(ID != manual_indexing_id,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||
POS + 1, ID, next_id>(
|
||||
format_str);
|
||||
} else {
|
||||
constexpr auto arg_id_result =
|
||||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||
constexpr char_type c =
|
||||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
|
||||
static_assert(
|
||||
ID == manual_indexing_id || ID == 0,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
constexpr auto arg_index = arg_id_result.arg_id.val.index;
|
||||
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||
Args, arg_id_end_pos,
|
||||
arg_index, manual_indexing_id>(
|
||||
format_str);
|
||||
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
|
||||
constexpr auto arg_index =
|
||||
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
|
||||
if constexpr (arg_index != invalid_arg_index) {
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<
|
||||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||
arg_index, next_id>(format_str);
|
||||
} else {
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
|
||||
format_str);
|
||||
} else if constexpr (c == ':') {
|
||||
return unknown_format(); // no type info for specs parsing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
if constexpr (end - POS > 1) {
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
} else {
|
||||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||
if constexpr (str.size() == 0) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto s = std::basic_string<Char>();
|
||||
cf.format(std::back_inserter(s), args...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... args) {
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||
const auto& first = detail::first(args...);
|
||||
if constexpr (detail::is_named_arg<
|
||||
remove_cvref_t<decltype(first)>>::value) {
|
||||
return fmt::to_string(first.value);
|
||||
} else {
|
||||
return fmt::to_string(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format(
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return fmt::format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format_to(
|
||||
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const S& format_str, Args&&... args) {
|
||||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
|
||||
format_str, std::forward<Args>(args)...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
|
||||
const Args&... args) {
|
||||
return fmt::format_to(detail::counting_iterator(), format_str, args...)
|
||||
.count();
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||
memory_buffer buffer;
|
||||
fmt::format_to(std::back_inserter(buffer), format_str, args...);
|
||||
detail::print(f, {buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
print(stdout, format_str, args...);
|
||||
}
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
inline namespace literals {
|
||||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
|
||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||
Str>();
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,478 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// UWP doesn't provide _pipe.
|
||||
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
# endif
|
||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || \
|
||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
# else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) ::call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
template <typename Char> struct formatter<std::error_code, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write_bytes(out, ec.category().name(),
|
||||
basic_format_specs<Char>());
|
||||
out = detail::write<Char>(out, Char(':'));
|
||||
out = detail::write<Char>(out, ec.value());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
FMT_API const std::error_category& system_category() noexcept;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(basic_string_view<wchar_t> s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
const char* message) noexcept;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
format_args args);
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`std::system_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a system_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
std::system_error windows_error(int error_code, string_view message,
|
||||
const Args&... args) {
|
||||
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
|
||||
#else
|
||||
inline const std::error_category& system_category() noexcept {
|
||||
return std::system_category();
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// std::system is not available on some platforms such as iOS (#2248).
|
||||
#ifdef __OSX__
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
void say(const S& format_str, Args&&... args) {
|
||||
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() noexcept : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() noexcept;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const noexcept { return file_; }
|
||||
|
||||
FMT_API int descriptor() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with noexcept may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class FMT_API file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
|
||||
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() noexcept : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
// Move assignment is not noexcept because close may throw.
|
||||
file& operator=(file&& other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
~file() noexcept;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const noexcept { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
size_t read(void* buffer, size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
size_t write(const void* buffer, size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd, std::error_code& ec) noexcept;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
struct buffer_size {
|
||||
buffer_size() = default;
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
|
||||
// Intel has a bug that results in failure to deduce a constructor
|
||||
// for empty parameter packs.
|
||||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||
# endif
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
// Added {} below to work around default constructor error known to
|
||||
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||
constexpr detail::buffer_size buffer_size{};
|
||||
|
||||
/** A fast output stream which is not thread-safe. */
|
||||
class FMT_API ostream final : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
void grow(size_t) override;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: file_(path, params.oflag) {
|
||||
set(new char[params.buffer_size], params.buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.clear();
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
flush();
|
||||
delete[] data();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
output to the file.
|
||||
*/
|
||||
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||
vformat_to(detail::buffer_appender<char>(*this), fmt,
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Opens a file for writing. Supported parameters passed in *params*:
|
||||
|
||||
* ``<integer>``: Flags passed to `open
|
||||
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
|
||||
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
|
||||
* ``buffer_size=<integer>``: Output buffer size
|
||||
|
||||
**Example**::
|
||||
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline ostream output_file(cstring_view path, T... params) {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
@@ -0,0 +1,237 @@
|
||||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
#if defined(_WIN32) && defined(__GLIBCXX__)
|
||||
# include <ext/stdio_filebuf.h>
|
||||
# include <ext/stdio_sync_filebuf.h>
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
# include <__std_stream>
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Checks if T has a user-defined operator<<.
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static auto test(int)
|
||||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||
<< std::declval<U>()) != 0>;
|
||||
|
||||
template <typename> static auto test(...) -> std::false_type;
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
is_streamable() = default;
|
||||
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Formatting of built-in types and arrays is intentionally disabled because
|
||||
// it's handled by standard (non-ostream) formatters.
|
||||
template <typename T, typename Char>
|
||||
struct is_streamable<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
|
||||
std::is_same<T, std_string_view<Char>>::value ||
|
||||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||
: std::false_type {};
|
||||
|
||||
// Generate a unique explicit instantion in every translation unit using a tag
|
||||
// type in an anonymous namespace.
|
||||
namespace {
|
||||
struct file_access_tag {};
|
||||
} // namespace
|
||||
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr>
|
||||
class file_access {
|
||||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||
};
|
||||
|
||||
#if FMT_MSC_VERSION
|
||||
template class file_access<file_access_tag, std::filebuf,
|
||||
&std::filebuf::_Myfile>;
|
||||
auto get_file(std::filebuf&) -> FILE*;
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
template class file_access<file_access_tag, std::__stdoutbuf<char>,
|
||||
&std::__stdoutbuf<char>::__file_>;
|
||||
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
|
||||
#endif
|
||||
|
||||
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
|
||||
#if FMT_MSC_VERSION
|
||||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
||||
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||
#elif defined(_WIN32) && defined(__GLIBCXX__)
|
||||
auto* rdbuf = os.rdbuf();
|
||||
FILE* c_file;
|
||||
if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
|
||||
c_file = fbuf->file();
|
||||
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
|
||||
c_file = fbuf->file();
|
||||
else
|
||||
return false;
|
||||
if (c_file) return write_console(c_file, data);
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
|
||||
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||
#else
|
||||
ignore_unused(os, data);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
inline bool write_ostream_unicode(std::wostream&,
|
||||
fmt::basic_string_view<wchar_t>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the content of buf to os.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output << value;
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
}
|
||||
|
||||
template <typename T> struct streamed_view { const T& value; };
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename Char>
|
||||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||
void set_debug_format() = delete;
|
||||
|
||||
template <typename T, typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
||||
-> OutputIt {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
{buffer.data(), buffer.size()}, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
using ostream_formatter = basic_ostream_formatter<char>;
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::streamed_view<T>, Char>
|
||||
: basic_ostream_formatter<Char> {
|
||||
template <typename OutputIt>
|
||||
auto format(detail::streamed_view<T> view,
|
||||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||
return basic_ostream_formatter<Char>::format(view.value, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns a view that formats `value` via an ostream ``operator<<``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("Current thread id: {}\n",
|
||||
fmt::streamed(std::this_thread::get_id()));
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
auto streamed(const T& value) -> detail::streamed_view<T> {
|
||||
return {value};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: basic_ostream_formatter<Char> {
|
||||
using basic_ostream_formatter<Char>::format;
|
||||
};
|
||||
|
||||
inline void vprint_directly(std::ostream& os, string_view format_str,
|
||||
format_args args) {
|
||||
auto buffer = memory_buffer();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os,
|
||||
basic_string_view<type_identity_t<Char>> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT template <typename... T>
|
||||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||
const auto& vargs = fmt::make_format_args(args...);
|
||||
if (detail::is_utf8())
|
||||
vprint(os, fmt, vargs);
|
||||
else
|
||||
detail::vprint_directly(os, fmt, vargs);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename... Args>
|
||||
void print(std::wostream& os,
|
||||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||
Args&&... args) {
|
||||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
@@ -0,0 +1,640 @@
|
||||
// Formatting library for C++ - legacy printf implementation
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
||||
|
||||
template <typename Char>
|
||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||
private:
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_printf_context> args_;
|
||||
|
||||
public:
|
||||
using char_type = Char;
|
||||
using format_arg = basic_format_arg<basic_printf_context>;
|
||||
using parse_context_type = basic_printf_parse_context<Char>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a ``printf_context`` object. References to the arguments are
|
||||
stored in the context object so make sure they have appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(OutputIt out,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args) {}
|
||||
|
||||
OutputIt out() { return out_; }
|
||||
void advance_to(OutputIt it) { out_ = it; }
|
||||
|
||||
detail::locale_ref locale() { return {}; }
|
||||
|
||||
format_arg arg(int id) const { return args_.get(id); }
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
detail::error_handler().on_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned> struct int_checker {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
unsigned max = max_value<int>();
|
||||
return value <= max;
|
||||
}
|
||||
static bool fits_in_int(bool) { return true; }
|
||||
};
|
||||
|
||||
template <> struct int_checker<true> {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
return value >= (std::numeric_limits<int>::min)() &&
|
||||
value <= max_value<int>();
|
||||
}
|
||||
static bool fits_in_int(int) { return true; }
|
||||
};
|
||||
|
||||
class printf_precision_handler {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
int operator()(T value) {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
return (std::max)(static_cast<int>(value), 0);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
int operator()(T) {
|
||||
FMT_THROW(format_error("precision is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// An argument visitor that returns true iff arg is a zero integer.
|
||||
class is_zero_int {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
bool operator()(T value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
bool operator()(T) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||
|
||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||
|
||||
template <typename T, typename Context> class arg_converter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_format_arg<Context>& arg_;
|
||||
char_type type_;
|
||||
|
||||
public:
|
||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||
: arg_(arg), type_(type) {}
|
||||
|
||||
void operator()(bool value) {
|
||||
if (type_ != 's') operator()<bool>(value);
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||
void operator()(U value) {
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<int>(static_cast<target_type>(value)));
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||
}
|
||||
} else {
|
||||
if (is_signed) {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
||||
} else {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||
void operator()(U) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Converts an integer argument to T for printf, if T is an integral type.
|
||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
template <typename Context> class char_converter {
|
||||
private:
|
||||
basic_format_arg<Context>& arg_;
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename Context::char_type>(value));
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// An argument visitor that return a pointer to a C string if argument is a
|
||||
// string or null otherwise.
|
||||
template <typename Char> struct get_cstring {
|
||||
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||
const Char* operator()(const Char* s) { return s; }
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
private:
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
format_specs& specs_;
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
unsigned operator()(T value) {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (detail::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
unsigned int_max = max_value<int>();
|
||||
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
||||
return static_cast<unsigned>(width);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
unsigned operator()(T) {
|
||||
FMT_THROW(format_error("width is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
OutputIt write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs;
|
||||
s.type = presentation_type::none;
|
||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
public:
|
||||
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
|
||||
: base{iter, s, locale_ref()}, context_(ctx) {}
|
||||
|
||||
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (std::is_same<T, Char>::value) {
|
||||
format_specs fmt_specs = this->specs;
|
||||
if (fmt_specs.type != presentation_type::none &&
|
||||
fmt_specs.type != presentation_type::chr) {
|
||||
return (*this)(static_cast<int>(value));
|
||||
}
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||
// align::numeric needs to be overwritten here since the '0' flag is
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
OutputIt operator()(const char* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
OutputIt operator()(const wchar_t* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
OutputIt operator()(basic_string_view<Char> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a pointer. */
|
||||
OutputIt operator()(const void* value) {
|
||||
return value ? base::operator()(value) : write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
auto parse_ctx =
|
||||
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||
handle.format(parse_ctx, context_);
|
||||
return this->out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
specs.align = align::left;
|
||||
break;
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '0':
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
if (specs.sign != sign::plus) {
|
||||
specs.sign = sign::space;
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename GetArg>
|
||||
int parse_header(const Char*& it, const Char* end,
|
||||
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||
int arg_index = -1;
|
||||
Char c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
int value = parse_nonnegative_int(it, end, -1);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value != -1 ? value : max_value<int>();
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(specs, it, end);
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
specs.width = parse_nonnegative_int(it, end, -1);
|
||||
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
using OutputIt = buffer_appender<Char>;
|
||||
auto out = OutputIt(buf);
|
||||
auto context = basic_printf_context<OutputIt, Char>(out, args);
|
||||
auto parse_ctx = basic_printf_parse_context<Char>(format);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||
// argument.
|
||||
auto get_arg = [&](int arg_index) {
|
||||
if (arg_index < 0)
|
||||
arg_index = parse_ctx.next_arg_id();
|
||||
else
|
||||
parse_ctx.check_arg_id(--arg_index);
|
||||
return detail::get_arg(context, arg_index);
|
||||
};
|
||||
|
||||
const Char* start = parse_ctx.begin();
|
||||
const Char* end = parse_ctx.end();
|
||||
auto it = start;
|
||||
while (it != end) {
|
||||
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||
break;
|
||||
}
|
||||
Char c = *it++;
|
||||
if (it != end && *it == c) {
|
||||
out = detail::write(
|
||||
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
out = detail::write(out, basic_string_view<Char>(
|
||||
start, detail::to_unsigned(it - 1 - start)));
|
||||
|
||||
basic_format_specs<Char> specs;
|
||||
specs.align = align::right;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs, get_arg);
|
||||
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto arg = get_arg(arg_index);
|
||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||
// specified, the '0' flag is ignored
|
||||
if (specs.precision >= 0 && arg.is_integral())
|
||||
specs.fill[0] =
|
||||
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
||||
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||
basic_string_view<Char>(
|
||||
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||
: specs.precision)));
|
||||
}
|
||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic() && specs.align != align::left)
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||
// flag is also present.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
Char t = it != end ? *it : 0;
|
||||
using detail::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
// need to do the same.
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||
char type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(
|
||||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specs.type = parse_presentation_type(type);
|
||||
if (specs.type == presentation_type::none)
|
||||
parse_ctx.on_error("invalid type specifier");
|
||||
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
out = visit_format_arg(
|
||||
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
|
||||
}
|
||||
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||
}
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char>
|
||||
using basic_printf_context_t =
|
||||
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||
|
||||
using printf_context = basic_printf_context_t<char>;
|
||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||
|
||||
using printf_args = basic_format_args<printf_context>;
|
||||
using wprintf_args = basic_format_args<wprintf_context>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto make_printf_args(const T&... args)
|
||||
-> format_arg_store<printf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto make_wprintf_args(const T&... args)
|
||||
-> format_arg_store<wprintf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vsprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, detail::to_string_view(fmt), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vsprintf(detail::to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vfprintf(
|
||||
std::FILE* f, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, detail::to_string_view(fmt), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the file *f*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
return vfprintf(stdout, detail::to_string_view(fmt), args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to ``stdout``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||
return vprintf(
|
||||
detail::to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
@@ -0,0 +1,722 @@
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
//
|
||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(wchar_t ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||
template <typename T> class is_std_string_like {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename T> class is_map {
|
||||
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T> class is_set {
|
||||
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
|
||||
|
||||
# define FMT_DECLTYPE_RETURN(val) \
|
||||
->decltype(val) { return val; } \
|
||||
static_assert( \
|
||||
true, "") // This makes it so that a semicolon is required after the
|
||||
// macro, which helps clang-format handle the formatting.
|
||||
|
||||
// C array overload
|
||||
template <typename T, std::size_t N>
|
||||
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||
return arr;
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
auto range_end(const T (&arr)[N]) -> const T* {
|
||||
return arr + N;
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_member_fn_begin_end_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
// Member function overload
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
|
||||
|
||||
// ADL overload. Only participates in overload resolution if member functions
|
||||
// are not found.
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng)
|
||||
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(begin(static_cast<T&&>(rng)))> {
|
||||
return begin(static_cast<T&&>(rng));
|
||||
}
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(end(static_cast<T&&>(rng)))> {
|
||||
return end(static_cast<T&&>(rng));
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_const_begin_end : std::false_type {};
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_mutable_begin_end : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_const_begin_end<
|
||||
T,
|
||||
void_t<
|
||||
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_mutable_begin_end<
|
||||
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||
decltype(detail::range_end(std::declval<T>())),
|
||||
enable_if_t<std::is_copy_constructible<T>::value>>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_range_<T, void>
|
||||
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||
has_mutable_begin_end<T>::value)> {};
|
||||
# undef FMT_DECLTYPE_RETURN
|
||||
#endif
|
||||
|
||||
// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||
|
||||
template <typename T, size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
|
||||
template <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
|
||||
|
||||
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
||||
class is_tuple_formattable_ {
|
||||
public:
|
||||
static constexpr const bool value = false;
|
||||
};
|
||||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||
template <std::size_t... I>
|
||||
static std::true_type check2(index_sequence<I...>,
|
||||
integer_sequence<bool, (I == I)...>);
|
||||
static std::false_type check2(...);
|
||||
template <std::size_t... I>
|
||||
static decltype(check2(
|
||||
index_sequence<I...>{},
|
||||
integer_sequence<
|
||||
bool, (is_formattable<typename std::tuple_element<I, T>::type,
|
||||
C>::value)...>{})) check(index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(tuple_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
|
||||
// Older MSVC doesn't get the reference type correctly for arrays.
|
||||
template <typename R> struct range_reference_type_impl {
|
||||
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
|
||||
using type = T&;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using range_reference_type = typename range_reference_type_impl<T>::type;
|
||||
#else
|
||||
template <typename Range>
|
||||
using range_reference_type =
|
||||
decltype(*detail::range_begin(std::declval<Range&>()));
|
||||
#endif
|
||||
|
||||
// We don't use the Range's value_type for anything, but we do need the Range's
|
||||
// reference type, with cv-ref stripped.
|
||||
template <typename Range>
|
||||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||
|
||||
template <typename Range>
|
||||
using uncvref_first_type =
|
||||
remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
|
||||
|
||||
template <typename Range>
|
||||
using uncvref_second_type = remove_cvref_t<
|
||||
decltype(std::declval<range_reference_type<Range>>().second)>;
|
||||
|
||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||
*out++ = ',';
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||
return write_escaped_string(out, str);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||
auto sv = std_string_view<Char>(str);
|
||||
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||
return write_escaped_char(out, v);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||
!std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_tuple_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char,
|
||||
enable_if_t<fmt::is_tuple_like<TupleT>::value &&
|
||||
fmt::is_tuple_formattable<TupleT, Char>::value>> {
|
||||
private:
|
||||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||
basic_string_view<Char> opening_bracket_ =
|
||||
detail::string_literal<Char, '('>{};
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ')'>{};
|
||||
|
||||
// C++11 generic lambda for format().
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) out = detail::copy_str<Char>(separator, out);
|
||||
out = detail::write_range_entry<Char>(out, v);
|
||||
++i;
|
||||
}
|
||||
int i;
|
||||
typename FormatContext::iterator& out;
|
||||
basic_string_view<Char> separator;
|
||||
};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR formatter() {}
|
||||
|
||||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||
separator_ = sep;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||
basic_string_view<Char> close) {
|
||||
opening_bracket_ = open;
|
||||
closing_bracket_ = close;
|
||||
}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||
detail::for_each(values, format_each<FormatContext>{0, out, separator_});
|
||||
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static constexpr const bool value =
|
||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_convertible<T, detail::std_string_view<Char>>::value;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Context> struct range_mapper {
|
||||
using mapper = arg_mapper<Context>;
|
||||
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||
static auto map(T&& value) -> T&& {
|
||||
return static_cast<T&&>(value);
|
||||
}
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||
static auto map(T&& value)
|
||||
-> decltype(mapper().map(static_cast<T&&>(value))) {
|
||||
return mapper().map(static_cast<T&&>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Element>
|
||||
using range_formatter_type = conditional_t<
|
||||
is_formattable<Element, Char>::value,
|
||||
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
|
||||
std::declval<Element>()))>,
|
||||
Char>,
|
||||
fallback_formatter<Element, Char>>;
|
||||
|
||||
template <typename R>
|
||||
using maybe_const_range =
|
||||
conditional_t<has_const_begin_end<R>::value, const R, R>;
|
||||
|
||||
// Workaround a bug in MSVC 2015 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||
template <typename R, typename Char>
|
||||
struct is_formattable_delayed
|
||||
: disjunction<
|
||||
is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
|
||||
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
struct range_formatter;
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct range_formatter<
|
||||
T, Char,
|
||||
enable_if_t<conjunction<
|
||||
std::is_same<T, remove_cvref_t<T>>,
|
||||
disjunction<is_formattable<T, Char>,
|
||||
detail::has_fallback_formatter<T, Char>>>::value>> {
|
||||
private:
|
||||
detail::range_formatter_type<Char, T> underlying_;
|
||||
bool custom_specs_ = false;
|
||||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||
basic_string_view<Char> opening_bracket_ =
|
||||
detail::string_literal<Char, '['>{};
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ']'>{};
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
|
||||
-> decltype(u.set_debug_format()) {
|
||||
u.set_debug_format();
|
||||
}
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
||||
|
||||
FMT_CONSTEXPR void maybe_set_debug_format() {
|
||||
maybe_set_debug_format(underlying_, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR range_formatter() {}
|
||||
|
||||
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
|
||||
return underlying_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||
separator_ = sep;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||
basic_string_view<Char> close) {
|
||||
opening_bracket_ = open;
|
||||
closing_bracket_ = close;
|
||||
}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
if (it == end || *it == '}') {
|
||||
maybe_set_debug_format();
|
||||
return it;
|
||||
}
|
||||
|
||||
if (*it == 'n') {
|
||||
set_brackets({}, {});
|
||||
++it;
|
||||
}
|
||||
|
||||
if (*it == '}') {
|
||||
maybe_set_debug_format();
|
||||
return it;
|
||||
}
|
||||
|
||||
if (*it != ':')
|
||||
FMT_THROW(format_error("no other top-level range formatters supported"));
|
||||
|
||||
custom_specs_ = true;
|
||||
++it;
|
||||
ctx.advance_to(it);
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename R, class FormatContext>
|
||||
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
detail::range_mapper<buffer_context<Char>> mapper;
|
||||
auto out = ctx.out();
|
||||
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||
int i = 0;
|
||||
auto it = detail::range_begin(range);
|
||||
auto end = detail::range_end(range);
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) out = detail::copy_str<Char>(separator_, out);
|
||||
;
|
||||
ctx.advance_to(out);
|
||||
out = underlying_.format(mapper.map(*it), ctx);
|
||||
++i;
|
||||
}
|
||||
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
enum class range_format { disabled, map, set, sequence, string, debug_string };
|
||||
|
||||
namespace detail {
|
||||
template <typename T> struct range_format_kind_ {
|
||||
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
|
||||
? range_format::disabled
|
||||
: is_map<T>::value ? range_format::map
|
||||
: is_set<T>::value ? range_format::set
|
||||
: range_format::sequence;
|
||||
};
|
||||
|
||||
template <range_format K, typename R, typename Char, typename Enable = void>
|
||||
struct range_default_formatter;
|
||||
|
||||
template <range_format K>
|
||||
using range_format_constant = std::integral_constant<range_format, K>;
|
||||
|
||||
template <range_format K, typename R, typename Char>
|
||||
struct range_default_formatter<
|
||||
K, R, Char,
|
||||
enable_if_t<(K == range_format::sequence || K == range_format::map ||
|
||||
K == range_format::set)>> {
|
||||
using range_type = detail::maybe_const_range<R>;
|
||||
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
|
||||
|
||||
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
|
||||
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||
detail::string_literal<Char, '}'>{});
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
|
||||
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||
detail::string_literal<Char, '}'>{});
|
||||
underlying_.underlying().set_brackets({}, {});
|
||||
underlying_.underlying().set_separator(
|
||||
detail::string_literal<Char, ':', ' '>{});
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(range_type& range, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return underlying_.format(range, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
struct range_format_kind
|
||||
: conditional_t<
|
||||
is_range<T, Char>::value, detail::range_format_kind_<T>,
|
||||
std::integral_constant<range_format, range_format::disabled>> {};
|
||||
|
||||
template <typename R, typename Char>
|
||||
struct formatter<
|
||||
R, Char,
|
||||
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
|
||||
range_format::disabled>
|
||||
// Workaround a bug in MSVC 2015 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||
,
|
||||
detail::is_formattable_delayed<R, Char>
|
||||
#endif
|
||||
>::value>>
|
||||
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
|
||||
Char> {
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||
|
||||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||
// support in tuple_join. It is disabled by default because of issues with
|
||||
// the dynamic width and precision.
|
||||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||
#endif
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const tuple_join_view<Char, T...>& value,
|
||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||
return do_format(value, ctx,
|
||||
std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, 0>)
|
||||
-> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename ParseContext, size_t N>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, N>)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto end = ctx.begin();
|
||||
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||
if (N > 1) {
|
||||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||
if (end != end1)
|
||||
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||
}
|
||||
#endif
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||
std::integral_constant<size_t, 0>) const ->
|
||||
typename FormatContext::iterator {
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, size_t N>
|
||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
std::integral_constant<size_t, N>) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||
if (N > 1) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||
-> tuple_join_view<char, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||
basic_string_view<wchar_t> sep)
|
||||
-> tuple_join_view<wchar_t, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `initializer_list` with elements separated by
|
||||
`sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||
// Output: "1, 2, 3"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
auto join(std::initializer_list<T> list, string_view sep)
|
||||
-> join_view<const T*, const T*> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
@@ -0,0 +1,171 @@
|
||||
// Formatting library for C++ - formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_STD_H_
|
||||
#define FMT_STD_H_
|
||||
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
#if FMT_HAS_INCLUDE(<version>)
|
||||
# include <version>
|
||||
#endif
|
||||
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
|
||||
#if FMT_CPLUSPLUS >= 201703L
|
||||
# if FMT_HAS_INCLUDE(<filesystem>)
|
||||
# include <filesystem>
|
||||
# endif
|
||||
# if FMT_HAS_INCLUDE(<variant>)
|
||||
# include <variant>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Char>
|
||||
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
|
||||
}
|
||||
# ifdef _WIN32
|
||||
template <>
|
||||
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
auto s = p.u8string();
|
||||
write_escaped_string<char>(
|
||||
std::back_inserter(quoted),
|
||||
string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
|
||||
}
|
||||
# endif
|
||||
template <>
|
||||
inline void write_escaped_path<std::filesystem::path::value_type>(
|
||||
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<std::filesystem::path::value_type>(
|
||||
std::back_inserter(quoted), p.native());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Char>
|
||||
struct formatter<std::filesystem::path, Char>
|
||||
: formatter<basic_string_view<Char>> {
|
||||
template <typename FormatContext>
|
||||
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
|
||||
typename FormatContext::iterator {
|
||||
basic_memory_buffer<Char> quoted;
|
||||
detail::write_escaped_path(quoted, p);
|
||||
return formatter<basic_string_view<Char>>::format(
|
||||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char>
|
||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#ifdef __cpp_lib_variant
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write<Char>(out, "monostate");
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using variant_index_sequence =
|
||||
std::make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
// variant_size and variant_alternative check.
|
||||
template <typename T, typename U = void>
|
||||
struct is_variant_like_ : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
|
||||
: std::true_type {};
|
||||
|
||||
// formattable element check
|
||||
template <typename T, typename C> class is_variant_formattable_ {
|
||||
template <std::size_t... I>
|
||||
static std::conjunction<
|
||||
is_formattable<std::variant_alternative_t<I, T>, C>...>
|
||||
check(std::index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(variant_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
|
||||
if constexpr (is_string<T>::value)
|
||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||
else if constexpr (std::is_same_v<T, Char>)
|
||||
return write_escaped_char(out, v);
|
||||
else
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_variant_like {
|
||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_variant_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_variant_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename Variant, typename Char>
|
||||
struct formatter<
|
||||
Variant, Char,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Variant& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
|
||||
out = detail::write<Char>(out, "variant(");
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
out = detail::write_variant_alternative<Char>(out, v);
|
||||
},
|
||||
value);
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif // FMT_STD_H_
|
||||
@@ -0,0 +1,229 @@
|
||||
// Formatting library for C++ - optional wchar_t and exotic character support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_XCHAR_H_
|
||||
#define FMT_XCHAR_H_
|
||||
|
||||
#include <cwchar>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
using wstring_view = basic_string_view<wchar_t>;
|
||||
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||
using wformat_context = buffer_context<wchar_t>;
|
||||
using wformat_args = basic_format_args<wformat_context>;
|
||||
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround broken conversion on older gcc.
|
||||
template <typename... Args> using wformat_string = wstring_view;
|
||||
inline auto runtime(wstring_view s) -> wstring_view { return s; }
|
||||
#else
|
||||
template <typename... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; }
|
||||
#endif
|
||||
|
||||
template <> struct is_char<wchar_t> : std::true_type {};
|
||||
template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||
template <> struct is_char<char16_t> : std::true_type {};
|
||||
template <> struct is_char<char32_t> : std::true_type {};
|
||||
|
||||
template <typename... Args>
|
||||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||
return {s};
|
||||
}
|
||||
#endif
|
||||
} // namespace literals
|
||||
|
||||
template <typename It, typename Sentinel>
|
||||
auto join(It begin, Sentinel end, wstring_view sep)
|
||||
-> join_view<It, Sentinel, wchar_t> {
|
||||
return {begin, end, sep};
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
auto join(Range&& range, wstring_view sep)
|
||||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
|
||||
wchar_t> {
|
||||
return join(std::begin(range), std::end(range), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto join(std::initializer_list<T> list, wstring_view sep)
|
||||
-> join_view<const T*, const T*, wchar_t> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
auto vformat(basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
// Pass char_t as a default template parameter instead of using
|
||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
|
||||
!std::is_same<Char, wchar_t>::value)>
|
||||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
||||
return vformat(detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat(
|
||||
const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, detail::to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
auto vformat_to(OutputIt out, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, detail::to_string_view(format_str), args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
|
||||
return vformat_to(out, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to(
|
||||
OutputIt out, const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
vformat_to(buf, detail::to_string_view(format_str), args,
|
||||
detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OutputIt, typename Locale, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
|
||||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to_n(
|
||||
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||
n);
|
||||
detail::vformat_to(buf, format_str, args);
|
||||
return {buf.out(), buf.count()};
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
|
||||
const Args&... args) -> format_to_n_result<OutputIt> {
|
||||
return vformat_to_n(out, n, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
|
||||
detail::counting_buffer<Char> buf;
|
||||
detail::vformat_to(buf, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
|
||||
wmemory_buffer buffer;
|
||||
detail::vformat_to(buffer, fmt, args);
|
||||
buffer.push_back(L'\0');
|
||||
if (std::fputws(buffer.data(), f) == -1)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
inline void vprint(wstring_view fmt, wformat_args args) {
|
||||
vprint(stdout, fmt, args);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
||||
*/
|
||||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||
return format(FMT_STRING(L"{}"), value);
|
||||
}
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_XCHAR_H_
|
||||
@@ -0,0 +1,47 @@
|
||||
// Formatting library for C++
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format-inl.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
||||
-> dragonbox::decimal_fp<float>;
|
||||
template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
||||
-> dragonbox::decimal_fp<double>;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
||||
#endif
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||
-> thousands_sep_result<char>;
|
||||
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
||||
|
||||
template FMT_API void buffer<char>::append(const char*, const char*);
|
||||
|
||||
// DEPRECATED!
|
||||
// There is no correspondent extern template in format.h because of
|
||||
// incompatibility between clang and gcc (#2377).
|
||||
template FMT_API void vformat_to(buffer<char>&, string_view,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>,
|
||||
locale_ref);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||
-> thousands_sep_result<wchar_t>;
|
||||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||
|
||||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
||||
|
||||
} // namespace detail
|
||||
FMT_END_NAMESPACE
|
||||
+3945
-4602
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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 <string>
|
||||
|
||||
static int g_READ_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_QUEUE_DEPTH = -1;
|
||||
static std::string g_PIN_THREADS = {};
|
||||
|
||||
|
||||
int
|
||||
fuse_config_get_read_thread_count()
|
||||
{
|
||||
return g_READ_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_read_thread_count(int const v_)
|
||||
{
|
||||
g_READ_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_count()
|
||||
{
|
||||
return g_PROCESS_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_count(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_queue_depth()
|
||||
{
|
||||
return g_PROCESS_THREAD_QUEUE_DEPTH;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_queue_depth(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_QUEUE_DEPTH = v_;
|
||||
}
|
||||
|
||||
std::string
|
||||
fuse_config_get_pin_threads()
|
||||
{
|
||||
return g_PIN_THREADS;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_pin_threads(std::string const v_)
|
||||
{
|
||||
g_PIN_THREADS = v_;
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_dirents.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
#include "stat_utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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_)
|
||||
{
|
||||
return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1));
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_dirent_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_dirent_t,name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_direntplus_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_direntplus_t,dirent.name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
uint64_t size_)
|
||||
{
|
||||
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
|
||||
{
|
||||
uint64_t new_size;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_dirent_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_direntplus_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_dirents_fill_attr(fuse_attr_t *attr_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
attr_->ino = st_->st_ino;
|
||||
attr_->size = st_->st_size;
|
||||
attr_->blocks = st_->st_blocks;
|
||||
attr_->atime = st_->st_atime;
|
||||
attr_->mtime = st_->st_mtime;
|
||||
attr_->ctime = st_->st_ctime;
|
||||
attr_->atimensec = ST_ATIM_NSEC(st_);
|
||||
attr_->mtimensec = ST_MTIM_NSEC(st_);
|
||||
attr_->ctimensec = ST_CTIM_NSEC(st_);
|
||||
attr_->mode = st_->st_mode;
|
||||
attr_->nlink = st_->st_nlink;
|
||||
attr_->uid = st_->st_uid;
|
||||
attr_->gid = st_->st_gid;
|
||||
attr_->rdev = st_->st_rdev;
|
||||
attr_->blksize = st_->st_blksize;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_next(fuse_dirent_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_dirent_size(cur_->namelen);
|
||||
|
||||
return (fuse_dirent_t*)buf;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_next(fuse_direntplus_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_direntplus_size(cur_->dirent.namelen);
|
||||
|
||||
return (fuse_direntplus_t*)buf;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_dirent_t *cur;
|
||||
fuse_dirent_t *end;
|
||||
|
||||
if(d_->type != NORMAL)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_dirent_t*)&kv_first(d_->data);
|
||||
end = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_dirent_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_direntplus_t *cur;
|
||||
fuse_direntplus_t *end;
|
||||
|
||||
if(d_->type != PLUS)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_direntplus_t*)&kv_first(d_->data);
|
||||
end = (fuse_direntplus_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->dirent.ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_direntplus_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void*
|
||||
fuse_dirents_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
switch(d_->type)
|
||||
{
|
||||
default:
|
||||
case UNSET:
|
||||
return NULL;
|
||||
case NORMAL:
|
||||
return fuse_dirent_find(d_,ino_);
|
||||
case PLUS:
|
||||
return fuse_direntplus_find(d_,ino_);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_convert_plus2normal(fuse_dirents_t *d_)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->d_type;
|
||||
memcpy(d->name,dirent_->d_name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
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;
|
||||
memcpy(d->dirent.name,dirent_->d_name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||
const struct linux_dirent64 *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->type;
|
||||
memcpy(d->name,dirent_->name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
const struct linux_dirent64 *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->type;
|
||||
memcpy(d->dirent.name,dirent_->name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->type = UNSET;
|
||||
kv_size(d_->data) = 0;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_init(d_->data);
|
||||
kv_resize(char,d_->data,DEFAULT_SIZE);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kv_init(d_->offs);
|
||||
kv_resize(uint32_t,d_->offs,64);
|
||||
kv_push(uint32_t,d_->offs,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
kv_destroy(d_->data);
|
||||
kv_destroy(d_->offs);
|
||||
}
|
||||
+61
-80
@@ -6,106 +6,90 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_msgbuf.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,
|
||||
fuse_msgbuf_t *msgbuf);
|
||||
|
||||
int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp);
|
||||
void (*process_buf)(struct fuse_session *se,
|
||||
const fuse_msgbuf_t *msgbuf);
|
||||
|
||||
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;
|
||||
struct fuse_ll *f;
|
||||
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;
|
||||
struct fuse_lowlevel_ops op;
|
||||
void *userdata;
|
||||
uid_t owner;
|
||||
struct fuse_conn_info conn;
|
||||
pthread_mutex_t lock;
|
||||
int got_init;
|
||||
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 +104,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
|
||||
|
||||
@@ -1,98 +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);
|
||||
}
|
||||
|
||||
#define MIN_BUFSIZE 0x21000
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd)
|
||||
{
|
||||
struct fuse_chan_ops op = {
|
||||
.receive = fuse_kern_chan_receive,
|
||||
.send = fuse_kern_chan_send,
|
||||
.destroy = fuse_kern_chan_destroy,
|
||||
};
|
||||
size_t bufsize = getpagesize() + 0x1000;
|
||||
bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
|
||||
return fuse_chan_new(&op, fd, bufsize, NULL);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_msgbuf.h"
|
||||
|
||||
int fuse_receive_buf(struct fuse_session *se,
|
||||
fuse_msgbuf_t *msgbuf);
|
||||
void fuse_process_buf(void *data,
|
||||
const fuse_msgbuf_t *msgbuf,
|
||||
struct fuse_chan *ch);
|
||||
@@ -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 *) malloc(bufsize);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "cpu.hpp"
|
||||
#include "fmt/core.h"
|
||||
#include "make_unique.hpp"
|
||||
#include "scope_guard.hpp"
|
||||
#include "syslog.h"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_misc.h"
|
||||
|
||||
#include "fuse_config.hpp"
|
||||
#include "fuse_msgbuf.hpp"
|
||||
#include "fuse_ll.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
static
|
||||
bool
|
||||
retriable_receive_error(const int err_)
|
||||
{
|
||||
switch(err_)
|
||||
{
|
||||
case -EINTR:
|
||||
case -EAGAIN:
|
||||
case -ENOENT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
fatal_receive_error(const int err_)
|
||||
{
|
||||
return (err_ < 0);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handle_receive_error(const int rv_,
|
||||
fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_free(msgbuf_);
|
||||
|
||||
fmt::print(stderr,
|
||||
"mergerfs: error reading from /dev/fuse - {} ({})\n",
|
||||
strerror(-rv_),
|
||||
-rv_);
|
||||
}
|
||||
|
||||
struct AsyncWorker
|
||||
{
|
||||
fuse_session *_se;
|
||||
sem_t *_finished;
|
||||
std::shared_ptr<ThreadPool> _process_tp;
|
||||
|
||||
AsyncWorker(fuse_session *se_,
|
||||
sem_t *finished_,
|
||||
std::shared_ptr<ThreadPool> process_tp_)
|
||||
: _se(se_),
|
||||
_finished(finished_),
|
||||
_process_tp(process_tp_)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
DEFER{ fuse_session_exit(_se); };
|
||||
DEFER{ sem_post(_finished); };
|
||||
|
||||
moodycamel::ProducerToken ptok(_process_tp->ptoken());
|
||||
while(!fuse_session_exited(_se))
|
||||
{
|
||||
int rv;
|
||||
fuse_msgbuf_t *msgbuf;
|
||||
|
||||
msgbuf = msgbuf_alloc();
|
||||
|
||||
do
|
||||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
|
||||
rv = _se->receive_buf(_se,msgbuf);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
|
||||
if(rv == 0)
|
||||
return;
|
||||
if(retriable_receive_error(rv))
|
||||
continue;
|
||||
if(fatal_receive_error(rv))
|
||||
return handle_receive_error(rv,msgbuf);
|
||||
} while(false);
|
||||
|
||||
auto const func = [=]
|
||||
{
|
||||
_se->process_buf(_se,msgbuf);
|
||||
msgbuf_free(msgbuf);
|
||||
};
|
||||
|
||||
_process_tp->enqueue_work(ptok,func);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SyncWorker
|
||||
{
|
||||
fuse_session *_se;
|
||||
sem_t *_finished;
|
||||
|
||||
SyncWorker(fuse_session *se_,
|
||||
sem_t *finished_)
|
||||
|
||||
: _se(se_),
|
||||
_finished(finished_)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
DEFER{ fuse_session_exit(_se); };
|
||||
DEFER{ sem_post(_finished); };
|
||||
|
||||
while(!fuse_session_exited(_se))
|
||||
{
|
||||
int rv;
|
||||
fuse_msgbuf_t *msgbuf;
|
||||
|
||||
msgbuf = msgbuf_alloc();
|
||||
|
||||
do
|
||||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
|
||||
rv = _se->receive_buf(_se,msgbuf);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
|
||||
if(rv == 0)
|
||||
return;
|
||||
if(retriable_receive_error(rv))
|
||||
continue;
|
||||
if(fatal_receive_error(rv))
|
||||
return handle_receive_error(rv,msgbuf);
|
||||
} while(false);
|
||||
|
||||
_se->process_buf(_se,msgbuf);
|
||||
msgbuf_free(msgbuf);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int
|
||||
fuse_start_thread(pthread_t *thread_id,
|
||||
void *(*func)(void *),
|
||||
void *arg)
|
||||
{
|
||||
int res;
|
||||
sigset_t oldset;
|
||||
sigset_t newset;
|
||||
|
||||
sigfillset(&newset);
|
||||
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
|
||||
res = pthread_create(thread_id,NULL,func,arg);
|
||||
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
|
||||
|
||||
if(res != 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"fuse: error creating thread: %s\n",
|
||||
strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
calculate_thread_count(const int raw_thread_count_)
|
||||
{
|
||||
int thread_count;
|
||||
|
||||
thread_count = 4;
|
||||
if(raw_thread_count_ == 0)
|
||||
thread_count = std::thread::hardware_concurrency();
|
||||
else if(raw_thread_count_ < 0)
|
||||
thread_count = (std::thread::hardware_concurrency() / -raw_thread_count_);
|
||||
else if(raw_thread_count_ > 0)
|
||||
thread_count = raw_thread_count_;
|
||||
|
||||
if(thread_count <= 0)
|
||||
thread_count = 1;
|
||||
|
||||
return thread_count;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
calculate_thread_counts(int *read_thread_count_,
|
||||
int *process_thread_count_,
|
||||
int *process_thread_queue_depth_)
|
||||
{
|
||||
if((*read_thread_count_ == -1) && (*process_thread_count_ == -1))
|
||||
{
|
||||
int nproc;
|
||||
|
||||
nproc = std::thread::hardware_concurrency();
|
||||
*read_thread_count_ = 2;
|
||||
*process_thread_count_ = std::max(2,(nproc - 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
*read_thread_count_ = ::calculate_thread_count(*read_thread_count_);
|
||||
if(*process_thread_count_ != -1)
|
||||
*process_thread_count_ = ::calculate_thread_count(*process_thread_count_);
|
||||
}
|
||||
|
||||
if(*process_thread_queue_depth_ <= 0)
|
||||
*process_thread_queue_depth_ = *process_thread_count_;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_R1L(const CPU::ThreadIdVec read_threads_)
|
||||
{
|
||||
CPU::CPUVec cpus;
|
||||
|
||||
cpus = CPU::cpus();
|
||||
if(cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,cpus.front());
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_R1P(const CPU::ThreadIdVec read_threads_)
|
||||
{
|
||||
CPU::Core2CPUsMap core2cpus;
|
||||
|
||||
core2cpus = CPU::core2cpus();
|
||||
if(core2cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_RP1L(const CPU::ThreadIdVec read_threads_,
|
||||
const CPU::ThreadIdVec process_threads_)
|
||||
{
|
||||
CPU::CPUVec cpus;
|
||||
|
||||
cpus = CPU::cpus();
|
||||
if(cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,cpus.front());
|
||||
for(auto const thread_id : process_threads_)
|
||||
CPU::setaffinity(thread_id,cpus.front());
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_RP1P(const CPU::ThreadIdVec read_threads_,
|
||||
const CPU::ThreadIdVec process_threads_)
|
||||
{
|
||||
CPU::Core2CPUsMap core2cpus;
|
||||
|
||||
core2cpus = CPU::core2cpus();
|
||||
if(core2cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
for(auto const thread_id : process_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_R1LP1L(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_)
|
||||
{
|
||||
CPU::CPUVec cpus;
|
||||
|
||||
cpus = CPU::cpus();
|
||||
if(cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,cpus.front());
|
||||
|
||||
for(auto const thread_id : process_threads_)
|
||||
CPU::setaffinity(thread_id,cpus.back());
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_R1PP1P(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_)
|
||||
{
|
||||
CPU::Core2CPUsMap core2cpus;
|
||||
|
||||
core2cpus = CPU::core2cpus();
|
||||
if(core2cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
|
||||
if(core2cpus.size() > 1)
|
||||
core2cpus.erase(core2cpus.begin());
|
||||
|
||||
for(auto const thread_id : process_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_RPSL(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_)
|
||||
{
|
||||
CPU::CPUVec cpus;
|
||||
|
||||
cpus = CPU::cpus();
|
||||
if(cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
{
|
||||
if(cpus.empty())
|
||||
cpus = CPU::cpus();
|
||||
CPU::setaffinity(thread_id,cpus.back());
|
||||
cpus.pop_back();
|
||||
}
|
||||
|
||||
for(auto const thread_id : process_threads_)
|
||||
{
|
||||
if(cpus.empty())
|
||||
cpus = CPU::cpus();
|
||||
CPU::setaffinity(thread_id,cpus.back());
|
||||
cpus.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_RPSP(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_)
|
||||
{
|
||||
CPU::Core2CPUsMap core2cpus;
|
||||
|
||||
core2cpus = CPU::core2cpus();
|
||||
if(core2cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
{
|
||||
if(core2cpus.empty())
|
||||
core2cpus = CPU::core2cpus();
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
core2cpus.erase(core2cpus.begin());
|
||||
}
|
||||
|
||||
for(auto const thread_id : process_threads_)
|
||||
{
|
||||
if(core2cpus.empty())
|
||||
core2cpus = CPU::core2cpus();
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
core2cpus.erase(core2cpus.begin());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads_R1PPSP(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_)
|
||||
{
|
||||
CPU::Core2CPUsMap core2cpus;
|
||||
CPU::Core2CPUsMap leftover;
|
||||
|
||||
core2cpus = CPU::core2cpus();
|
||||
if(core2cpus.empty())
|
||||
return;
|
||||
|
||||
for(auto const thread_id : read_threads_)
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
|
||||
core2cpus.erase(core2cpus.begin());
|
||||
if(core2cpus.empty())
|
||||
core2cpus = CPU::core2cpus();
|
||||
leftover = core2cpus;
|
||||
|
||||
for(auto const thread_id : process_threads_)
|
||||
{
|
||||
if(core2cpus.empty())
|
||||
core2cpus = leftover;
|
||||
CPU::setaffinity(thread_id,core2cpus.begin()->second);
|
||||
core2cpus.erase(core2cpus.begin());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pin_threads(const std::vector<pthread_t> read_threads_,
|
||||
const std::vector<pthread_t> process_threads_,
|
||||
const std::string type_)
|
||||
{
|
||||
if(type_.empty() || (type_ == "false"))
|
||||
return;
|
||||
if(type_ == "R1L")
|
||||
return ::pin_threads_R1L(read_threads_);
|
||||
if(type_ == "R1P")
|
||||
return ::pin_threads_R1P(read_threads_);
|
||||
if(type_ == "RP1L")
|
||||
return ::pin_threads_RP1L(read_threads_,process_threads_);
|
||||
if(type_ == "RP1P")
|
||||
return ::pin_threads_RP1P(read_threads_,process_threads_);
|
||||
if(type_ == "R1LP1L")
|
||||
return ::pin_threads_R1LP1L(read_threads_,process_threads_);
|
||||
if(type_ == "R1PP1P")
|
||||
return ::pin_threads_R1PP1P(read_threads_,process_threads_);
|
||||
if(type_ == "RPSL")
|
||||
return ::pin_threads_RPSL(read_threads_,process_threads_);
|
||||
if(type_ == "RPSP")
|
||||
return ::pin_threads_RPSP(read_threads_,process_threads_);
|
||||
if(type_ == "R1PPSP")
|
||||
return ::pin_threads_R1PPSP(read_threads_,process_threads_);
|
||||
|
||||
syslog_warning("Invalid pin-threads value, ignoring: %s",type_.c_str());
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
wait(fuse_session *se_,
|
||||
sem_t *finished_sem_)
|
||||
{
|
||||
while(!fuse_session_exited(se_))
|
||||
sem_wait(finished_sem_);
|
||||
}
|
||||
|
||||
int
|
||||
fuse_session_loop_mt(struct fuse_session *se_,
|
||||
const int raw_read_thread_count_,
|
||||
const int raw_process_thread_count_,
|
||||
const int raw_process_thread_queue_depth_,
|
||||
const std::string pin_threads_type_)
|
||||
{
|
||||
sem_t finished;
|
||||
int read_thread_count;
|
||||
int process_thread_count;
|
||||
int process_thread_queue_depth;
|
||||
std::vector<pthread_t> read_threads;
|
||||
std::vector<pthread_t> process_threads;
|
||||
std::unique_ptr<ThreadPool> read_tp;
|
||||
std::shared_ptr<ThreadPool> process_tp;
|
||||
|
||||
sem_init(&finished,0,0);
|
||||
|
||||
read_thread_count = raw_read_thread_count_;
|
||||
process_thread_count = raw_process_thread_count_;
|
||||
process_thread_queue_depth = raw_process_thread_queue_depth_;
|
||||
::calculate_thread_counts(&read_thread_count,
|
||||
&process_thread_count,
|
||||
&process_thread_queue_depth);
|
||||
|
||||
if(process_thread_count > 0)
|
||||
process_tp = std::make_shared<ThreadPool>(process_thread_count,
|
||||
process_thread_queue_depth,
|
||||
"fuse.process");
|
||||
|
||||
read_tp = std::make_unique<ThreadPool>(read_thread_count,1,"fuse.read");
|
||||
if(process_tp)
|
||||
{
|
||||
for(auto i = 0; i < read_thread_count; i++)
|
||||
read_tp->enqueue_work(AsyncWorker(se_,&finished,process_tp));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto i = 0; i < read_thread_count; i++)
|
||||
read_tp->enqueue_work(SyncWorker(se_,&finished));
|
||||
}
|
||||
|
||||
if(read_tp)
|
||||
read_threads = read_tp->threads();
|
||||
if(process_tp)
|
||||
process_threads = process_tp->threads();
|
||||
|
||||
::pin_threads(read_threads,process_threads,pin_threads_type_);
|
||||
|
||||
syslog_info("read-thread-count=%d; "
|
||||
"process-thread-count=%d; "
|
||||
"process-thread-queue-depth=%d; "
|
||||
"pin-threads=%s;"
|
||||
,
|
||||
read_thread_count,
|
||||
process_thread_count,
|
||||
process_thread_queue_depth,
|
||||
pin_threads_type_.c_str());
|
||||
|
||||
::wait(se_,&finished);
|
||||
|
||||
sem_destroy(&finished);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_loop_mt(struct fuse *f)
|
||||
{
|
||||
if(f == NULL)
|
||||
return -1;
|
||||
|
||||
int res = fuse_start_maintenance_thread(f);
|
||||
if(res)
|
||||
return -1;
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(f),
|
||||
fuse_config_get_read_thread_count(),
|
||||
fuse_config_get_process_thread_count(),
|
||||
fuse_config_get_process_thread_queue_depth(),
|
||||
fuse_config_get_pin_threads());
|
||||
|
||||
fuse_stop_maintenance_thread(f);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -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 = malloc(w->bufsize);
|
||||
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;
|
||||
}
|
||||
+1776
-2760
File diff suppressed because it is too large
Load Diff
+25
-19
@@ -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,32 +29,35 @@
|
||||
/* 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
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||
/* Linux */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) ((stbuf)->st_atim.tv_nsec = (val))
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) ((stbuf)->st_ctim.tv_nsec = (val))
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) ((stbuf)->st_mtim.tv_nsec = (val))
|
||||
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
|
||||
/* FreeBSD */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) ((stbuf)->st_atimespec.tv_nsec = (val))
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) ((stbuf)->st_ctimespec.tv_nsec = (val))
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) ((stbuf)->st_mtimespec.tv_nsec = (val))
|
||||
#else
|
||||
#define ST_ATIM_NSEC(stbuf) 0
|
||||
#define ST_CTIM_NSEC(stbuf) 0
|
||||
#define ST_MTIM_NSEC(stbuf) 0
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2022, 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 "fuse_msgbuf.hpp"
|
||||
#include "fuse.h"
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
|
||||
static std::uint32_t g_PAGESIZE = 0;
|
||||
static std::uint32_t g_BUFSIZE = 0;
|
||||
|
||||
static std::atomic<std::uint_fast64_t> g_MSGBUF_ALLOC_COUNT;
|
||||
|
||||
static std::mutex g_MUTEX;
|
||||
static std::vector<fuse_msgbuf_t*> g_MSGBUF_STACK;
|
||||
|
||||
uint64_t
|
||||
msgbuf_get_bufsize()
|
||||
{
|
||||
return g_BUFSIZE;
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_set_bufsize(const uint32_t size_in_pages_)
|
||||
{
|
||||
g_BUFSIZE = ((size_in_pages_ + 1) * g_PAGESIZE);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
msgbuf_->mem -= sizeof(struct fuse_in_header);
|
||||
msgbuf_->mem -= sizeof(struct fuse_write_in);
|
||||
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
__attribute__((constructor))
|
||||
void
|
||||
msgbuf_constructor()
|
||||
{
|
||||
g_PAGESIZE = sysconf(_SC_PAGESIZE);
|
||||
// FUSE_MAX_MAX_PAGES for payload + 1 for message header
|
||||
msgbuf_set_bufsize(FUSE_MAX_MAX_PAGES + 1);
|
||||
}
|
||||
|
||||
static
|
||||
__attribute__((destructor))
|
||||
void
|
||||
msgbuf_destroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
page_aligned_malloc(const uint64_t size_)
|
||||
{
|
||||
int rv;
|
||||
void *buf = NULL;
|
||||
|
||||
rv = posix_memalign(&buf,g_PAGESIZE,size_);
|
||||
if(rv != 0)
|
||||
return NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
typedef void (*msgbuf_setup_func_t)(fuse_msgbuf_t*);
|
||||
|
||||
static
|
||||
fuse_msgbuf_t*
|
||||
_msgbuf_alloc(msgbuf_setup_func_t setup_func_)
|
||||
{
|
||||
fuse_msgbuf_t *msgbuf;
|
||||
|
||||
g_MUTEX.lock();
|
||||
if(g_MSGBUF_STACK.empty())
|
||||
{
|
||||
g_MUTEX.unlock();
|
||||
|
||||
msgbuf = (fuse_msgbuf_t*)page_aligned_malloc(g_BUFSIZE);
|
||||
if(msgbuf == NULL)
|
||||
return NULL;
|
||||
|
||||
g_MSGBUF_ALLOC_COUNT.fetch_add(1,std::memory_order_relaxed);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgbuf = g_MSGBUF_STACK.back();
|
||||
g_MSGBUF_STACK.pop_back();
|
||||
g_MUTEX.unlock();
|
||||
}
|
||||
|
||||
setup_func_(msgbuf);
|
||||
|
||||
return msgbuf;
|
||||
}
|
||||
|
||||
// Offset the memory so write request payload will be placed on page
|
||||
// boundry so O_DIRECT can work. No impact on other message types
|
||||
// except for `read` which will require using `msgbuf_page_align`.
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_write_align);
|
||||
}
|
||||
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc_page_aligned()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_page_align);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_destroy(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
// free(msgbuf_->mem);
|
||||
free(msgbuf_);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_free(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
|
||||
if(msgbuf_->size != (g_BUFSIZE - g_PAGESIZE))
|
||||
{
|
||||
msgbuf_destroy(msgbuf_);
|
||||
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
|
||||
g_MSGBUF_STACK.emplace_back(msgbuf_);
|
||||
|
||||
}
|
||||
|
||||
uint64_t
|
||||
msgbuf_alloc_count()
|
||||
{
|
||||
return g_MSGBUF_ALLOC_COUNT;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
msgbuf_avail_count()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
|
||||
return g_MSGBUF_STACK.size();
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_gc_10percent()
|
||||
{
|
||||
std::vector<fuse_msgbuf_t*> togc;
|
||||
|
||||
{
|
||||
std::size_t size;
|
||||
std::size_t ten_percent;
|
||||
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
|
||||
size = g_MSGBUF_STACK.size();
|
||||
ten_percent = (size / 10);
|
||||
|
||||
for(std::size_t i = 0; i < ten_percent; i++)
|
||||
{
|
||||
togc.push_back(g_MSGBUF_STACK.back());
|
||||
g_MSGBUF_STACK.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
for(auto msgbuf : togc)
|
||||
{
|
||||
msgbuf_destroy(msgbuf);
|
||||
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_gc()
|
||||
{
|
||||
std::vector<fuse_msgbuf_t*> oldstack;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
oldstack.swap(g_MSGBUF_STACK);
|
||||
}
|
||||
|
||||
for(auto msgbuf: oldstack)
|
||||
{
|
||||
msgbuf_destroy(msgbuf);
|
||||
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2022, 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_msgbuf.h"
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
void msgbuf_set_bufsize(const uint32_t size);
|
||||
uint64_t msgbuf_get_bufsize();
|
||||
|
||||
fuse_msgbuf_t *msgbuf_alloc();
|
||||
fuse_msgbuf_t *msgbuf_alloc_page_aligned();
|
||||
void msgbuf_free(fuse_msgbuf_t *msgbuf);
|
||||
|
||||
void msgbuf_gc();
|
||||
void msgbuf_gc_10percent();
|
||||
|
||||
uint64_t msgbuf_alloc_count();
|
||||
uint64_t msgbuf_avail_count();
|
||||
|
||||
void msgbuf_page_align(fuse_msgbuf_t *msgbuf);
|
||||
void msgbuf_write_align(fuse_msgbuf_t *msgbuf);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -1,124 +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_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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)
|
||||
{
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
|
||||
|
||||
(void) len;
|
||||
(void) ch;
|
||||
pd->proc(pd->f, cmd, pd->data);
|
||||
}
|
||||
|
||||
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@");
|
||||
@@ -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_)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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
@@ -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");
|
||||
|
||||
+86
-171
@@ -8,233 +8,148 @@
|
||||
|
||||
#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->f = 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->f);
|
||||
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_data(struct fuse_session *se)
|
||||
void
|
||||
fuse_session_exit(struct fuse_session *se_)
|
||||
{
|
||||
return se->data;
|
||||
se_->exited = 1;
|
||||
}
|
||||
|
||||
static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data,
|
||||
int compat)
|
||||
void*
|
||||
fuse_session_data(struct fuse_session *se)
|
||||
{
|
||||
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
|
||||
if (ch == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
ch->op = *op;
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
ch->data = data;
|
||||
ch->compat = compat;
|
||||
|
||||
return ch;
|
||||
return se->f;
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data)
|
||||
struct fuse_chan *
|
||||
fuse_chan_new(int fd,
|
||||
size_t bufsize)
|
||||
{
|
||||
return fuse_chan_new_common(op, fd, bufsize, data, 0);
|
||||
}
|
||||
struct fuse_chan *ch;
|
||||
|
||||
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);
|
||||
ch = (struct fuse_chan*)malloc(sizeof(*ch));
|
||||
if(ch == NULL)
|
||||
{
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
|
||||
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)
|
||||
void
|
||||
fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
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 fd;
|
||||
|
||||
fuse_session_remove_chan(ch);
|
||||
|
||||
fd = fuse_chan_fd(ch);
|
||||
if(fd != -1)
|
||||
close(fd);
|
||||
|
||||
free(ch);
|
||||
}
|
||||
|
||||
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = fuse_chan_recv(&ch, buf, size);
|
||||
return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
|
||||
}
|
||||
|
||||
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
|
||||
{
|
||||
return ch->op.send(ch, iov, count);
|
||||
}
|
||||
|
||||
void fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
fuse_session_remove_chan(ch);
|
||||
if (ch->op.destroy)
|
||||
ch->op.destroy(ch);
|
||||
free(ch);
|
||||
}
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
|
||||
#endif
|
||||
|
||||
+38
-37
@@ -14,59 +14,60 @@
|
||||
|
||||
static struct fuse_session *fuse_instance;
|
||||
|
||||
static void exit_handler(int sig)
|
||||
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(SIGINT, exit_handler, 0) == -1) ||
|
||||
(set_one_signal_handler(SIGTERM, exit_handler, 0) == -1) ||
|
||||
(set_one_signal_handler(SIGQUIT, 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(SIGINT, exit_handler, 1);
|
||||
set_one_signal_handler(SIGTERM, exit_handler, 1);
|
||||
set_one_signal_handler(SIGQUIT, exit_handler, 1);
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
File diff suppressed because it is too large
Load Diff
+270
-357
@@ -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,472 +22,386 @@
|
||||
#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;
|
||||
struct helper_opts
|
||||
{
|
||||
int foreground;
|
||||
int nodefault_subtype;
|
||||
char *mountpoint;
|
||||
};
|
||||
|
||||
#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
|
||||
|
||||
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
|
||||
};
|
||||
static
|
||||
const
|
||||
struct fuse_opt fuse_helper_opts[] =
|
||||
{
|
||||
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
|
||||
fuse_parse_cmdline(struct fuse_args *args_,
|
||||
char **mountpoint_,
|
||||
int *foreground_)
|
||||
{
|
||||
int res;
|
||||
struct helper_opts hopts;
|
||||
int res;
|
||||
struct helper_opts hopts;
|
||||
|
||||
memset(&hopts, 0, sizeof(hopts));
|
||||
res = fuse_opt_parse(args, &hopts, fuse_helper_opts,
|
||||
fuse_helper_opt_proc);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
memset(&hopts, 0, sizeof(hopts));
|
||||
|
||||
if (!hopts.nodefault_subtype) {
|
||||
res = add_default_subtype(args->argv[0], args);
|
||||
if (res == -1)
|
||||
goto err;
|
||||
}
|
||||
if (mountpoint)
|
||||
*mountpoint = hopts.mountpoint;
|
||||
else
|
||||
free(hopts.mountpoint);
|
||||
res = fuse_opt_parse(args_,
|
||||
&hopts,
|
||||
fuse_helper_opts,
|
||||
fuse_helper_opt_proc);
|
||||
if(res == -1)
|
||||
return -1;
|
||||
|
||||
if (multithreaded)
|
||||
*multithreaded = !hopts.singlethread;
|
||||
if (foreground)
|
||||
*foreground = hopts.foreground;
|
||||
return 0;
|
||||
if(!hopts.nodefault_subtype)
|
||||
{
|
||||
res = add_default_subtype(args_->argv[0], args_);
|
||||
if(res == -1)
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
free(hopts.mountpoint);
|
||||
return -1;
|
||||
if(mountpoint_)
|
||||
*mountpoint_ = hopts.mountpoint;
|
||||
else
|
||||
free(hopts.mountpoint);
|
||||
|
||||
if(foreground_)
|
||||
*foreground_ = hopts.foreground;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free(hopts.mountpoint);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 struct fuse_chan *fuse_mount_common(const char *mountpoint,
|
||||
struct fuse_args *args)
|
||||
static
|
||||
struct fuse_chan *
|
||||
fuse_mount_common(const char *mountpoint_,
|
||||
struct fuse_args *args_)
|
||||
{
|
||||
struct fuse_chan *ch;
|
||||
int fd;
|
||||
int fd;
|
||||
long bufsize;
|
||||
long pagesize;
|
||||
struct fuse_chan *ch;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
fd = fuse_mount_compat25(mountpoint, args);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
fd = fuse_kern_mount(mountpoint_,args_);
|
||||
if(fd == -1)
|
||||
return NULL;
|
||||
|
||||
ch = fuse_kern_chan_new(fd);
|
||||
if (!ch)
|
||||
fuse_kern_unmount(mountpoint, fd);
|
||||
pagesize = sysconf(_SC_PAGESIZE);
|
||||
bufsize = ((FUSE_MAX_MAX_PAGES + 1) * pagesize);
|
||||
|
||||
return ch;
|
||||
ch = fuse_chan_new(fd,bufsize);
|
||||
if(!ch)
|
||||
fuse_kern_unmount(mountpoint_, fd);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
|
||||
struct fuse_chan *
|
||||
fuse_mount(const char *mountpoint_,
|
||||
struct fuse_args *args_)
|
||||
{
|
||||
return fuse_mount_common(mountpoint, args);
|
||||
return fuse_mount_common(mountpoint_,args_);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct lock_s lock_t;
|
||||
struct lock_s
|
||||
{
|
||||
int type;
|
||||
off_t start;
|
||||
off_t end;
|
||||
pid_t pid;
|
||||
uint64_t owner;
|
||||
lock_t *next;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class T> struct _Unique_if
|
||||
{
|
||||
typedef unique_ptr<T> _Single_object;
|
||||
};
|
||||
|
||||
template<class T> struct _Unique_if<T[]>
|
||||
{
|
||||
typedef unique_ptr<T[]> _Unknown_bound;
|
||||
};
|
||||
|
||||
template<class T, size_t N> struct _Unique_if<T[N]>
|
||||
{
|
||||
typedef void _Known_bound;
|
||||
};
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Single_object
|
||||
make_unique(Args&&... args)
|
||||
{
|
||||
return unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename _Unique_if<T>::_Unknown_bound
|
||||
make_unique(size_t n)
|
||||
{
|
||||
typedef typename remove_extent<T>::type U;
|
||||
return unique_ptr<T>(new U[n]());
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Known_bound
|
||||
make_unique(Args&&...) = delete;
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
|
||||
// ©2015-2020 Cameron Desrochers. Distributed under the terms of the simplified
|
||||
// BSD license, available at the top of concurrentqueue.h.
|
||||
// Also dual-licensed under the Boost Software License (see LICENSE.md)
|
||||
// Uses Jeff Preshing's semaphore implementation (under the terms of its
|
||||
// separate zlib license, see lightweightsemaphore.h).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
#include "lightweightsemaphore.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <cerrno>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
namespace moodycamel
|
||||
{
|
||||
// This is a blocking version of the queue. It has an almost identical interface to
|
||||
// the normal non-blocking version, with the addition of various wait_dequeue() methods
|
||||
// and the removal of producer-specific dequeue methods.
|
||||
template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
|
||||
class BlockingConcurrentQueue
|
||||
{
|
||||
private:
|
||||
typedef ::moodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
|
||||
typedef ::moodycamel::LightweightSemaphore LightweightSemaphore;
|
||||
|
||||
public:
|
||||
typedef typename ConcurrentQueue::producer_token_t producer_token_t;
|
||||
typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
|
||||
|
||||
typedef typename ConcurrentQueue::index_t index_t;
|
||||
typedef typename ConcurrentQueue::size_t size_t;
|
||||
typedef typename std::make_signed<size_t>::type ssize_t;
|
||||
|
||||
static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
|
||||
static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
|
||||
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
|
||||
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
|
||||
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
|
||||
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
|
||||
static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
|
||||
|
||||
public:
|
||||
// Creates a queue with at least `capacity` element slots; note that the
|
||||
// actual number of elements that can be inserted without additional memory
|
||||
// allocation depends on the number of producers and the block size (e.g. if
|
||||
// the block size is equal to `capacity`, only a single block will be allocated
|
||||
// up-front, which means only a single producer will be able to enqueue elements
|
||||
// without an extra allocation -- blocks aren't shared between producers).
|
||||
// This method is not thread safe -- it is up to the user to ensure that the
|
||||
// queue is fully constructed before it starts being used by other threads (this
|
||||
// includes making the memory effects of construction visible, possibly with a
|
||||
// memory barrier).
|
||||
explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
|
||||
: inner(capacity), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
|
||||
{
|
||||
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
|
||||
if (!sema) {
|
||||
MOODYCAMEL_THROW(std::bad_alloc());
|
||||
}
|
||||
}
|
||||
|
||||
BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
|
||||
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
|
||||
{
|
||||
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
|
||||
if (!sema) {
|
||||
MOODYCAMEL_THROW(std::bad_alloc());
|
||||
}
|
||||
}
|
||||
|
||||
// Disable copying and copy assignment
|
||||
BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
|
||||
BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
// Moving is supported, but note that it is *not* a thread-safe operation.
|
||||
// Nobody can use the queue while it's being moved, and the memory effects
|
||||
// of that move must be propagated to other threads before they can use it.
|
||||
// Note: When a queue is moved, its tokens are still valid but can only be
|
||||
// used with the destination queue (i.e. semantically they are moved along
|
||||
// with the queue itself).
|
||||
BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
|
||||
: inner(std::move(other.inner)), sema(std::move(other.sema))
|
||||
{ }
|
||||
|
||||
inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
return swap_internal(other);
|
||||
}
|
||||
|
||||
// Swaps this queue's state with the other's. Not thread-safe.
|
||||
// Swapping two queues does not invalidate their tokens, however
|
||||
// the tokens that were created for one queue must be used with
|
||||
// only the swapped queue (i.e. the tokens are tied to the
|
||||
// queue's movable state, not the object itself).
|
||||
inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
swap_internal(other);
|
||||
}
|
||||
|
||||
private:
|
||||
BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inner.swap(other.inner);
|
||||
sema.swap(other.sema);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
// Enqueues a single item (by copying it).
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
|
||||
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T const& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
|
||||
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T&& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(std::move(item)))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(producer_token_t const& token, T const& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(token, item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(producer_token_t const& token, T&& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(token, std::move(item)))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
|
||||
// is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue_bulk(std::forward<It>(itemFirst), count))) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails
|
||||
// (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue_bulk(token, std::forward<It>(itemFirst), count))) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it).
|
||||
// Does not allocate memory. Fails if not enough room to enqueue (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
|
||||
// is 0).
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T const& item)
|
||||
{
|
||||
if (inner.try_enqueue(item)) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
// Does not allocate memory (except for one-time implicit producer).
|
||||
// Fails if not enough room to enqueue (or implicit production is
|
||||
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T&& item)
|
||||
{
|
||||
if (inner.try_enqueue(std::move(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(producer_token_t const& token, T const& item)
|
||||
{
|
||||
if (inner.try_enqueue(token, item)) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(producer_token_t const& token, T&& item)
|
||||
{
|
||||
if (inner.try_enqueue(token, std::move(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items.
|
||||
// Does not allocate memory (except for one-time implicit producer).
|
||||
// Fails if not enough room to enqueue (or implicit production is
|
||||
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool try_enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if (inner.try_enqueue_bulk(std::forward<It>(itemFirst), count)) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
|
||||
{
|
||||
if (inner.try_enqueue_bulk(token, std::forward<It>(itemFirst), count)) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Attempts to dequeue from the queue.
|
||||
// Returns false if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool try_dequeue(U& item)
|
||||
{
|
||||
if (sema->tryWait()) {
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to dequeue from the queue using an explicit consumer token.
|
||||
// Returns false if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool try_dequeue(consumer_token_t& token, U& item)
|
||||
{
|
||||
if (sema->tryWait()) {
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued.
|
||||
// Returns 0 if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t try_dequeue_bulk(It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued.
|
||||
// Returns 0 if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Blocks the current thread until there's something to dequeue, then
|
||||
// dequeues it.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline void wait_dequeue(U& item)
|
||||
{
|
||||
while (!sema->wait()) {
|
||||
continue;
|
||||
}
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout (specified in microseconds) expires. Returns false
|
||||
// without setting `item` if the timeout expires, otherwise assigns
|
||||
// to `item` and returns true.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
|
||||
{
|
||||
if (!sema->wait(timeout_usecs)) {
|
||||
return false;
|
||||
}
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout expires. Returns false without setting `item` if the
|
||||
// timeout expires, otherwise assigns to `item` and returns true.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U, typename Rep, typename Period>
|
||||
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Blocks the current thread until there's something to dequeue, then
|
||||
// dequeues it using an explicit consumer token.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline void wait_dequeue(consumer_token_t& token, U& item)
|
||||
{
|
||||
while (!sema->wait()) {
|
||||
continue;
|
||||
}
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout (specified in microseconds) expires. Returns false
|
||||
// without setting `item` if the timeout expires, otherwise assigns
|
||||
// to `item` and returns true.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
|
||||
{
|
||||
if (!sema->wait(timeout_usecs)) {
|
||||
return false;
|
||||
}
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout expires. Returns false without setting `item` if the
|
||||
// timeout expires, otherwise assigns to `item` and returns true.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U, typename Rep, typename Period>
|
||||
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_timed(token, item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which will
|
||||
// always be at least one (this method blocks until the queue
|
||||
// is non-empty) and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue_bulk.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It, typename Rep, typename Period>
|
||||
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_bulk_timed<It&>(itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which will
|
||||
// always be at least one (this method blocks until the queue
|
||||
// is non-empty) and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue_bulk.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It, typename Rep, typename Period>
|
||||
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_bulk_timed<It&>(token, itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
|
||||
// Returns an estimate of the total number of elements currently in the queue. This
|
||||
// estimate is only accurate if the queue has completely stabilized before it is called
|
||||
// (i.e. all enqueue and dequeue operations have completed and their memory effects are
|
||||
// visible on the calling thread, and no further operations start while this method is
|
||||
// being called).
|
||||
// Thread-safe.
|
||||
inline size_t size_approx() const
|
||||
{
|
||||
return (size_t)sema->availableApprox();
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the underlying atomic variables used by
|
||||
// the queue are lock-free (they should be on most platforms).
|
||||
// Thread-safe.
|
||||
static constexpr bool is_lock_free()
|
||||
{
|
||||
return ConcurrentQueue::is_lock_free();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template<typename U, typename A1, typename A2>
|
||||
static inline U* create(A1&& a1, A2&& a2)
|
||||
{
|
||||
void* p = (Traits::malloc)(sizeof(U));
|
||||
return p != nullptr ? new (p) U(std::forward<A1>(a1), std::forward<A2>(a2)) : nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
static inline void destroy(U* p)
|
||||
{
|
||||
if (p != nullptr) {
|
||||
p->~U();
|
||||
}
|
||||
(Traits::free)(p);
|
||||
}
|
||||
|
||||
private:
|
||||
ConcurrentQueue inner;
|
||||
std::unique_ptr<LightweightSemaphore, void (*)(LightweightSemaphore*)> sema;
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename Traits>
|
||||
inline void swap(BlockingConcurrentQueue<T, Traits>& a, BlockingConcurrentQueue<T, Traits>& b) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // end namespace moodycamel
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,427 @@
|
||||
// Provides an efficient implementation of a semaphore (LightweightSemaphore).
|
||||
// This is an extension of Jeff Preshing's sempahore implementation (licensed
|
||||
// under the terms of its separate zlib license) that has been adapted and
|
||||
// extended by Cameron Desrochers.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // For std::size_t
|
||||
#include <atomic>
|
||||
#include <type_traits> // For std::make_signed<T>
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Avoid including windows.h in a header; we only need a handful of
|
||||
// items, so we'll redeclare them here (this is relatively safe since
|
||||
// the API generally has to remain stable between Windows versions).
|
||||
// I know this is an ugly hack but it still beats polluting the global
|
||||
// namespace with thousands of generic names or adding a .cpp for nothing.
|
||||
extern "C" {
|
||||
struct _SECURITY_ATTRIBUTES;
|
||||
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
|
||||
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
|
||||
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
|
||||
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
|
||||
}
|
||||
#elif defined(__MACH__)
|
||||
#include <mach/mach.h>
|
||||
#elif defined(__MVS__)
|
||||
#include <zos-semaphore.h>
|
||||
#elif defined(__unix__)
|
||||
#include <semaphore.h>
|
||||
|
||||
#if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
|
||||
#if __GLIBC_PREREQ(2,30)
|
||||
#define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace moodycamel
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
|
||||
// portable + lightweight semaphore implementations, originally from
|
||||
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
|
||||
// LICENSE:
|
||||
// Copyright (c) 2015 Jeff Preshing
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgement in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
#if defined(_WIN32)
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
void* m_hSema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
const long maxLong = 0x7fffffff;
|
||||
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
|
||||
assert(m_hSema);
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
CloseHandle(m_hSema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
const unsigned long infinite = 0xffffffff;
|
||||
return WaitForSingleObject(m_hSema, infinite) == 0;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return WaitForSingleObject(m_hSema, 0) == 0;
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t usecs)
|
||||
{
|
||||
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
|
||||
}
|
||||
|
||||
void signal(int count = 1)
|
||||
{
|
||||
while (!ReleaseSemaphore(m_hSema, count, nullptr));
|
||||
}
|
||||
};
|
||||
#elif defined(__MACH__)
|
||||
//---------------------------------------------------------
|
||||
// Semaphore (Apple iOS and OSX)
|
||||
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
|
||||
//---------------------------------------------------------
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
semaphore_t m_sema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
|
||||
assert(rc == KERN_SUCCESS);
|
||||
(void)rc;
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
semaphore_destroy(mach_task_self(), m_sema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
return semaphore_wait(m_sema) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return timed_wait(0);
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t timeout_usecs)
|
||||
{
|
||||
mach_timespec_t ts;
|
||||
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
|
||||
ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
|
||||
|
||||
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
|
||||
kern_return_t rc = semaphore_timedwait(m_sema, ts);
|
||||
return rc == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
void signal()
|
||||
{
|
||||
while (semaphore_signal(m_sema) != KERN_SUCCESS);
|
||||
}
|
||||
|
||||
void signal(int count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
{
|
||||
while (semaphore_signal(m_sema) != KERN_SUCCESS);
|
||||
}
|
||||
}
|
||||
};
|
||||
#elif defined(__unix__) || defined(__MVS__)
|
||||
//---------------------------------------------------------
|
||||
// Semaphore (POSIX, Linux, zOS)
|
||||
//---------------------------------------------------------
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
sem_t m_sema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
|
||||
assert(rc == 0);
|
||||
(void)rc;
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
sem_destroy(&m_sema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
|
||||
int rc;
|
||||
do {
|
||||
rc = sem_wait(&m_sema);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
int rc;
|
||||
do {
|
||||
rc = sem_trywait(&m_sema);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t usecs)
|
||||
{
|
||||
struct timespec ts;
|
||||
const int usecs_in_1_sec = 1000000;
|
||||
const int nsecs_in_1_sec = 1000000000;
|
||||
#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
#endif
|
||||
ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
|
||||
ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
|
||||
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
|
||||
// so we have to clean things up before passing it in
|
||||
if (ts.tv_nsec >= nsecs_in_1_sec) {
|
||||
ts.tv_nsec -= nsecs_in_1_sec;
|
||||
++ts.tv_sec;
|
||||
}
|
||||
|
||||
int rc;
|
||||
do {
|
||||
#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
|
||||
rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts);
|
||||
#else
|
||||
rc = sem_timedwait(&m_sema, &ts);
|
||||
#endif
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
void signal()
|
||||
{
|
||||
while (sem_post(&m_sema) == -1);
|
||||
}
|
||||
|
||||
void signal(int count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
{
|
||||
while (sem_post(&m_sema) == -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
#else
|
||||
#error Unsupported platform! (No semaphore wrapper available)
|
||||
#endif
|
||||
|
||||
} // end namespace details
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// LightweightSemaphore
|
||||
//---------------------------------------------------------
|
||||
class LightweightSemaphore
|
||||
{
|
||||
public:
|
||||
typedef std::make_signed<std::size_t>::type ssize_t;
|
||||
|
||||
private:
|
||||
std::atomic<ssize_t> m_count;
|
||||
details::Semaphore m_sema;
|
||||
int m_maxSpins;
|
||||
|
||||
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
|
||||
{
|
||||
ssize_t oldCount;
|
||||
int spin = m_maxSpins;
|
||||
while (--spin >= 0)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_relaxed);
|
||||
if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return true;
|
||||
std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
|
||||
}
|
||||
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
|
||||
if (oldCount > 0)
|
||||
return true;
|
||||
if (timeout_usecs < 0)
|
||||
{
|
||||
if (m_sema.wait())
|
||||
return true;
|
||||
}
|
||||
if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
|
||||
return true;
|
||||
// At this point, we've timed out waiting for the semaphore, but the
|
||||
// count is still decremented indicating we may still be waiting on
|
||||
// it. So we have to re-adjust the count, but only if the semaphore
|
||||
// wasn't signaled enough times for us too since then. If it was, we
|
||||
// need to release the semaphore too.
|
||||
while (true)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_acquire);
|
||||
if (oldCount >= 0 && m_sema.try_wait())
|
||||
return true;
|
||||
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
|
||||
{
|
||||
assert(max > 0);
|
||||
ssize_t oldCount;
|
||||
int spin = m_maxSpins;
|
||||
while (--spin >= 0)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_relaxed);
|
||||
if (oldCount > 0)
|
||||
{
|
||||
ssize_t newCount = oldCount > max ? oldCount - max : 0;
|
||||
if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return oldCount - newCount;
|
||||
}
|
||||
std::atomic_signal_fence(std::memory_order_acquire);
|
||||
}
|
||||
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
|
||||
if (oldCount <= 0)
|
||||
{
|
||||
if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_acquire);
|
||||
if (oldCount >= 0 && m_sema.try_wait())
|
||||
break;
|
||||
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max > 1)
|
||||
return 1 + tryWaitMany(max - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
public:
|
||||
LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
assert(maxSpins >= 0);
|
||||
}
|
||||
|
||||
bool tryWait()
|
||||
{
|
||||
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
|
||||
while (oldCount > 0)
|
||||
{
|
||||
if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
return tryWait() || waitWithPartialSpinning();
|
||||
}
|
||||
|
||||
bool wait(std::int64_t timeout_usecs)
|
||||
{
|
||||
return tryWait() || waitWithPartialSpinning(timeout_usecs);
|
||||
}
|
||||
|
||||
// Acquires between 0 and (greedily) max, inclusive
|
||||
ssize_t tryWaitMany(ssize_t max)
|
||||
{
|
||||
assert(max >= 0);
|
||||
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
|
||||
while (oldCount > 0)
|
||||
{
|
||||
ssize_t newCount = oldCount > max ? oldCount - max : 0;
|
||||
if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return oldCount - newCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Acquires at least one, and (greedily) at most max
|
||||
ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
assert(max >= 0);
|
||||
ssize_t result = tryWaitMany(max);
|
||||
if (result == 0 && max > 0)
|
||||
result = waitManyWithPartialSpinning(max, timeout_usecs);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t waitMany(ssize_t max)
|
||||
{
|
||||
ssize_t result = waitMany(max, -1);
|
||||
assert(result > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void signal(ssize_t count = 1)
|
||||
{
|
||||
assert(count >= 0);
|
||||
ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
|
||||
ssize_t toRelease = -oldCount < count ? -oldCount : count;
|
||||
if (toRelease > 0)
|
||||
{
|
||||
m_sema.signal((int)toRelease);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t availableApprox() const
|
||||
{
|
||||
ssize_t count = m_count.load(std::memory_order_relaxed);
|
||||
return count > 0 ? static_cast<std::size_t>(count) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace moodycamel
|
||||
+205
-272
@@ -28,364 +28,297 @@
|
||||
#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("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");
|
||||
|
||||
+419
-463
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user