mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-04-21 12:31:43 +00:00
Compare commits
667
Commits
2.20.0
...
debug-output
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e83821f32 | ||
|
|
c96bac9cb8 | ||
|
|
5fc35874ac | ||
|
|
be82e83cf3 | ||
|
|
031a72d7ad | ||
|
|
84bdc8a21a | ||
|
|
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 | ||
|
|
ee0d87d712 | ||
|
|
c21aa34221 | ||
|
|
1ad79067e4 | ||
|
|
810871d2df | ||
|
|
6404939156 | ||
|
|
e052446713 | ||
|
|
5e16851c79 | ||
|
|
5883020784 | ||
|
|
726c1c850c | ||
|
|
374580a85d | ||
|
|
4293cb7253 | ||
|
|
825fcf7f35 | ||
|
|
85e2a3e71b | ||
|
|
3288d834a9 | ||
|
|
209dc2252a | ||
|
|
df0d0552b2 | ||
|
|
05f33c84e4 | ||
|
|
6ecc618d83 | ||
|
|
46be946672 | ||
|
|
2a075ead92 | ||
|
|
8bd09628ef | ||
|
|
b69819e4b2 | ||
|
|
2ca0cb0fbf | ||
|
|
61d764d0fd | ||
|
|
8b007ada61 | ||
|
|
b0b265b8e5 | ||
|
|
cab7033d5b | ||
|
|
1be9900733 | ||
|
|
3c2a9cd4f8 | ||
|
|
38037feab7 | ||
|
|
99d44cfc5d | ||
|
|
2af0dcd273 | ||
|
|
d9f430fda8 | ||
|
|
0918dfd117 | ||
|
|
a64c7c97ad | ||
|
|
95c0cc741d | ||
|
|
8b0342067c | ||
|
|
1d48dba0b7 | ||
|
|
e8622fdd64 | ||
|
|
7bf607bb6b | ||
|
|
a80f9a0b29 | ||
|
|
940c323251 | ||
|
|
babe8bcf47 | ||
|
|
5ab7d2dd72 | ||
|
|
401502579b | ||
|
|
65d3bfe84d | ||
|
|
cfd5d31338 | ||
|
|
9d9ee7b71d | ||
|
|
5a1a60742e | ||
|
|
c5b2415daf | ||
|
|
a0bb2f8480 | ||
|
|
f4a06cae18 | ||
|
|
21d690a210 | ||
|
|
5be7e007ce | ||
|
|
f42bd8666e | ||
|
|
7a057daa0c | ||
|
|
8e0aa1d9a6 | ||
|
|
a13b822543 | ||
|
|
40319081e0 | ||
|
|
9fd3b968fc | ||
|
|
24a7973955 | ||
|
|
a57c680cc8 | ||
|
|
11be067895 | ||
|
|
3a1213435f | ||
|
|
56f7df028b | ||
|
|
87b2795f2b | ||
|
|
c36bc5320d | ||
|
|
7eefb58ff7 | ||
|
|
6a247501e1 | ||
|
|
0b5a0d1228 | ||
|
|
8686eadcf4 | ||
|
|
aa56e12040 | ||
|
|
adcbf0eef0 | ||
|
|
10f7f8bdea | ||
|
|
90e422c113 | ||
|
|
dfa1c1ad6f | ||
|
|
bd792757e0 | ||
|
|
40969402e8 | ||
|
|
fee9a449ba | ||
|
|
6de8e4471c | ||
|
|
f0f64f8ed1 | ||
|
|
6ae68465cf | ||
|
|
3c1f19e85a | ||
|
|
2a8a6b8fe0 | ||
|
|
7d9458fdd1 | ||
|
|
43b676a6e2 | ||
|
|
606d9c4d3f | ||
|
|
c46134c03a | ||
|
|
0abb2a960c | ||
|
|
3631ab7df2 | ||
|
|
b8b3ab06a7 | ||
|
|
680f8194f9 | ||
|
|
3d33428a4e | ||
|
|
7524e57262 | ||
|
|
1195e66a8a | ||
|
|
b55ebba4ed | ||
|
|
8dd0dc11ee | ||
|
|
8eacb008b3 | ||
|
|
9c16ab6999 | ||
|
|
9e0ab1f518 | ||
|
|
47bf58eae9 | ||
|
|
8a48b74cb4 | ||
|
|
570c93ec2c | ||
|
|
85026d5780 | ||
|
|
bb2e6eb605 | ||
|
|
1885a82cce | ||
|
|
602a37b231 | ||
|
|
65f482e483 | ||
|
|
6ee44e451c | ||
|
|
8d1a1564bf | ||
|
|
4af9248eba | ||
|
|
93f7d7d927 | ||
|
|
19a7d43b31 | ||
|
|
9afefef032 | ||
|
|
8c3462caf2 | ||
|
|
340f3c85c9 | ||
|
|
ff04098319 | ||
|
|
f8563369bc | ||
|
|
4152769182 | ||
|
|
057eafef7f | ||
|
|
c7e93720ff | ||
|
|
587ab62583 | ||
|
|
a894360402 | ||
|
|
756d4ff0ff | ||
|
|
ec6adaa376 | ||
|
|
dee2c3e23b | ||
|
|
fc52f89ddc | ||
|
|
fdbafa63ba | ||
|
|
e987ff46c2 | ||
|
|
b1ecc58114 | ||
|
|
39d5ab8e83 | ||
|
|
4b1f9afe1e | ||
|
|
c005463111 | ||
|
|
50384f4e86 | ||
|
|
73e8867400 | ||
|
|
0545da67ff | ||
|
|
24e690b995 | ||
|
|
89200a0ab8 | ||
|
|
e521833cec | ||
|
|
737d941941 | ||
|
|
9830e29945 | ||
|
|
6418b00ac3 | ||
|
|
cfe9c28a10 | ||
|
|
aa6e4c1865 | ||
|
|
f48c16d162 | ||
|
|
62ae871d51 | ||
|
|
906702de77 | ||
|
|
db27a91a58 | ||
|
|
b4a003c5a8 | ||
|
|
b48054fa2c | ||
|
|
d8f05ac8ae | ||
|
|
8843ace757 | ||
|
|
7d93d5917e | ||
|
|
8846a135bc | ||
|
|
7207e7fdd6 | ||
|
|
a8e6128034 | ||
|
|
f72bb1e1ed | ||
|
|
cf5d28ea4a | ||
|
|
bd14f33c31 | ||
|
|
73561c1861 | ||
|
|
43d50ca471 | ||
|
|
8ea0b395b2 | ||
|
|
1d6d22727c | ||
|
|
b3c10f5bdf | ||
|
|
821d1b6964 | ||
|
|
6067478e00 | ||
|
|
ebe737e995 | ||
|
|
e2a9509896 | ||
|
|
9104f25c09 | ||
|
|
713c911264 | ||
|
|
c1d85ef825 | ||
|
|
43eaf29df5 | ||
|
|
cc1f07ef39 | ||
|
|
c35a4dadee | ||
|
|
75ed37a11a | ||
|
|
8043829815 | ||
|
|
deb70c7a4f | ||
|
|
0708110ec1 | ||
|
|
dae637925b | ||
|
|
a7b126cb48 | ||
|
|
d66774d3f3 | ||
|
|
a2bddec8c7 | ||
|
|
b95fbd76ec | ||
|
|
aea2b40dda | ||
|
|
fa3ff40218 | ||
|
|
bfd410def2 | ||
|
|
721bd4d337 | ||
|
|
7296d3d90d | ||
|
|
a7cddefcc9 | ||
|
|
de0985c135 | ||
|
|
986f230ba7 | ||
|
|
87c2f2f9dc | ||
|
|
63f84b255f | ||
|
|
6a7675f2bf | ||
|
|
25ce9938c6 | ||
|
|
8ba3a08f40 | ||
|
|
23dbe48324 | ||
|
|
ccaa458739 | ||
|
|
f95b07f83b | ||
|
|
162b99e6b8 | ||
|
|
3c8afc1bb0 | ||
|
|
f15437c713 | ||
|
|
d9be1c4841 | ||
|
|
5f3aa6eee5 | ||
|
|
1089af9077 | ||
|
|
2fbeb67dd9 | ||
|
|
c4ad239171 | ||
|
|
8b976ab3c1 | ||
|
|
1499ccf3fb | ||
|
|
1a1fa06aa3 | ||
|
|
e3e764ecae | ||
|
|
617195d71e | ||
|
|
8cfb1cbee4 | ||
|
|
be3eb7e3ed | ||
|
|
1c3b72de09 | ||
|
|
0600734942 | ||
|
|
e6dc8c2cb3 | ||
|
|
9d0798dbfb | ||
|
|
581356a0c3 | ||
|
|
e2acffe55b | ||
|
|
998966e6e6 | ||
|
|
42d454ac27 | ||
|
|
ad6062b9a8 | ||
|
|
0b2bf17cd7 | ||
|
|
292b850e07 | ||
|
|
e20d566f96 | ||
|
|
776ff3e3f1 | ||
|
|
1539aca3d8 | ||
|
|
3244314310 | ||
|
|
9d799ffc9f | ||
|
|
9486bc9b21 | ||
|
|
c043ef95a3 | ||
|
|
3728e39655 | ||
|
|
215f129a2a | ||
|
|
af9a71f84c | ||
|
|
bb4ec913c6 |
+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).
|
||||
@@ -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 }}"
|
||||
@@ -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
|
||||
|
||||
@@ -12,75 +12,87 @@
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
GIT = git
|
||||
TAR = tar
|
||||
MKDIR = mkdir
|
||||
TOUCH = touch
|
||||
CP = cp
|
||||
RM = rm
|
||||
LN = ln
|
||||
FIND = find
|
||||
INSTALL = install
|
||||
MKTEMP = mktemp
|
||||
STRIP = strip
|
||||
PANDOC = pandoc
|
||||
SED = sed
|
||||
RPMBUILD = rpmbuild
|
||||
GIT2DEBCL = ./tools/git2debcl
|
||||
PKGCONFIG = pkg-config
|
||||
|
||||
PKGCONFIG = $(shell which pkg-config)
|
||||
GIT = $(shell which git)
|
||||
TAR = $(shell which tar)
|
||||
MKDIR = $(shell which mkdir)
|
||||
TOUCH = $(shell which touch)
|
||||
CP = $(shell which cp)
|
||||
RM = $(shell which rm)
|
||||
LN = $(shell which ln)
|
||||
FIND = $(shell which find)
|
||||
INSTALL = $(shell which install)
|
||||
MKTEMP = $(shell which mktemp)
|
||||
STRIP = $(shell which strip)
|
||||
PANDOC = $(shell which pandoc)
|
||||
SED = $(shell which sed)
|
||||
GZIP = $(shell which gzip)
|
||||
RPMBUILD = $(shell which rpmbuild)
|
||||
GIT2DEBCL = ./tools/git2debcl
|
||||
CPPFIND = ./tools/cppfind
|
||||
|
||||
ifeq ($(PKGCONFIG),"")
|
||||
$(error "pkg-config not installed")
|
||||
GIT_REPO = 0
|
||||
ifneq ($(shell $(GIT) --version 2> /dev/null),)
|
||||
ifeq ($(shell test -e .git; echo $$?),0)
|
||||
GIT_REPO = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PANDOC),"")
|
||||
$(warning "pandoc does not appear available: manpage won't be buildable")
|
||||
endif
|
||||
|
||||
XATTR_AVAILABLE = $(shell test ! -e /usr/include/attr/xattr.h; echo $$?)
|
||||
|
||||
FUSE_AVAILABLE = $(shell ! pkg-config --exists fuse; echo $$?)
|
||||
|
||||
ifeq ($(FUSE_AVAILABLE),0)
|
||||
FUSE_AVAILABLE = $(shell test ! -e /usr/include/fuse.h; echo $$?)
|
||||
endif
|
||||
|
||||
ifeq ($(FUSE_AVAILABLE),0)
|
||||
$(error "FUSE development package doesn't appear available")
|
||||
endif
|
||||
|
||||
FLAG_NOPATH = $(shell $(CPPFIND) "flag_nopath")
|
||||
FALLOCATE = $(shell $(CPPFIND) "fuse_fs_fallocate")
|
||||
FLOCK = $(shell $(CPPFIND) "fuse_fs_flock")
|
||||
READ_BUF = $(shell $(CPPFIND) "fuse_fs_read_buf")
|
||||
WRITE_BUF = $(shell $(CPPFIND) "fuse_fs_write_buf")
|
||||
|
||||
USE_XATTR = 1
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
OPTS = -O2
|
||||
SRC = $(wildcard src/*.cpp)
|
||||
OBJ = $(SRC:src/%.cpp=obj/%.o)
|
||||
DEPS = $(OBJ:obj/%.o=obj/%.d)
|
||||
TARGET = mergerfs
|
||||
MANPAGE = $(TARGET).1
|
||||
FUSE_CFLAGS = $(shell $(PKGCONFIG) --cflags fuse)
|
||||
CFLAGS = -g -Wall \
|
||||
$(OPTS) \
|
||||
-Wno-unused-result \
|
||||
$(FUSE_CFLAGS) \
|
||||
-DFUSE_USE_VERSION=29 \
|
||||
-MMD \
|
||||
-DFLAG_NOPATH=$(FLAG_NOPATH) \
|
||||
-DFALLOCATE=$(FALLOCATE) \
|
||||
-DFLOCK=$(FLOCK) \
|
||||
-DREAD_BUF=$(READ_BUF) \
|
||||
-DWRITE_BUF=$(WRITE_BUF) \
|
||||
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
|
||||
LDFLAGS = $(shell $(PKGCONFIG) fuse --libs)
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
||||
ifeq ($(STATIC),1)
|
||||
STATIC_FLAGS := -static
|
||||
else
|
||||
STATIC_FLAGS :=
|
||||
endif
|
||||
|
||||
ifeq ($(LTO),1)
|
||||
LTO_FLAGS := -flto
|
||||
else
|
||||
LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
SRC = $(wildcard src/*.cpp)
|
||||
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 ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++11 \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Wall \
|
||||
-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)
|
||||
TESTS_FLAGS = \
|
||||
-Isrc \
|
||||
-DTESTS
|
||||
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-pthread \
|
||||
-lrt
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
@@ -94,77 +106,88 @@ INSTALLBINDIR = $(DESTDIR)$(BINDIR)
|
||||
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
|
||||
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
ifeq ($(XATTR_AVAILABLE),0)
|
||||
$(warning "xattr not available: disabling")
|
||||
CFLAGS += -DWITHOUT_XATTR
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
.PHONY: all
|
||||
all: mergerfs
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "usage: make"
|
||||
@echo "make XATTR_AVAILABLE=0 - to build program without xattrs functionality (auto discovered otherwise)"
|
||||
@echo "usage: make\n"
|
||||
@echo "make USE_XATTR=0 - build program without xattrs functionality"
|
||||
@echo "make STATIC=1 - build static binary"
|
||||
@echo "make LTO=1 - build with link time optimization"
|
||||
|
||||
$(TARGET): src/version.hpp obj/obj-stamp $(OBJ)
|
||||
$(CXX) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
|
||||
objects: version build/stamp
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
mount.mergerfs: $(TARGET)
|
||||
$(LN) -fs "$<" "$@"
|
||||
tests-objects:
|
||||
$(MAKE) $(TESTS_OBJS)
|
||||
|
||||
build/mergerfs: libfuse objects
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
build/tests: build/mergerfs tests-objects
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mergerfs: build/mergerfs
|
||||
|
||||
tests: build/tests
|
||||
|
||||
changelog:
|
||||
$(GIT2DEBCL) --name $(TARGET) > ChangeLog
|
||||
ifeq ($(GIT_REPO),1)
|
||||
$(GIT2DEBCL) --name mergerfs > ChangeLog
|
||||
else
|
||||
@echo "WARNING: need git repo to generate ChangeLog"
|
||||
endif
|
||||
|
||||
authors:
|
||||
$(GIT) log --format='%aN <%aE>' | sort -f | uniq > AUTHORS
|
||||
.PHONY: version
|
||||
version:
|
||||
tools/update-version
|
||||
|
||||
src/version.hpp:
|
||||
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
|
||||
@echo "#ifndef _VERSION_HPP" > src/version.hpp
|
||||
@echo "#define _VERSION_HPP" >> src/version.hpp
|
||||
@echo "static const char MERGERFS_VERSION[] = \"$(VERSION)\";" >> src/version.hpp
|
||||
@echo "#endif" >> src/version.hpp
|
||||
|
||||
obj/obj-stamp:
|
||||
$(MKDIR) -p obj
|
||||
build/stamp:
|
||||
$(MKDIR) -p build/.src build/.tests
|
||||
$(TOUCH) $@
|
||||
|
||||
obj/%.o: src/%.cpp
|
||||
$(CXX) $(CFLAGS) -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
|
||||
ifneq ($(GIT),)
|
||||
ifeq ($(shell test -e .git; echo $$?),0)
|
||||
$(RM) -f src/version.hpp
|
||||
endif
|
||||
endif
|
||||
$(RM) -rf obj
|
||||
$(RM) -f "$(TARGET)" mount.mergerfs
|
||||
$(RM) -rf build
|
||||
$(FIND) . -name "*~" -delete
|
||||
$(MAKE) -C libfuse clean
|
||||
|
||||
distclean: clean
|
||||
$(GIT) clean -fd
|
||||
ifeq ($(GIT_REPO),1)
|
||||
$(GIT) clean -xfd
|
||||
endif
|
||||
|
||||
install: install-base install-mount.mergerfs install-man
|
||||
.PHONY: install
|
||||
install: install-base install-mount-tools install-man
|
||||
|
||||
install-base: $(TARGET)
|
||||
install-base: build/mergerfs
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(INSTALL) -v -m 0755 "$(TARGET)" "$(INSTALLBINDIR)/$(TARGET)"
|
||||
$(INSTALL) -v -m 0755 build/mergerfs "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
install-mount.mergerfs: mount.mergerfs
|
||||
install-mount-tools: install-base
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(CP) -a "$<" "$(INSTALLBINDIR)/$<"
|
||||
$(MAKE) -C libfuse install
|
||||
|
||||
install-man: $(MANPAGE)
|
||||
$(MKDIR) -p "$(INSTALLMAN1DIR)"
|
||||
$(INSTALL) -v -m 0644 "man/$(MANPAGE)" "$(INSTALLMAN1DIR)/$(MANPAGE)"
|
||||
|
||||
install-strip: install-base
|
||||
$(STRIP) "$(INSTALLBINDIR)/$(TARGET)"
|
||||
$(STRIP) "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall: uninstall-base uninstall-mount.mergerfs uninstall-man
|
||||
|
||||
uninstall-base:
|
||||
$(RM) -f "$(INSTALLBINDIR)/$(TARGET)"
|
||||
$(RM) -f "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
uninstall-mount.mergerfs:
|
||||
$(RM) -f "$(INSTALLBINDIR)/mount.mergerfs"
|
||||
@@ -173,16 +196,19 @@ uninstall-man:
|
||||
$(RM) -f "$(INSTALLMAN1DIR)/$(MANPAGE)"
|
||||
|
||||
$(MANPAGE): README.md
|
||||
ifneq (,$(PANDOC))
|
||||
ifneq ($(shell $(PANDOC) --version 2> /dev/null),)
|
||||
$(PANDOC) -s -t man -o "man/$(MANPAGE)" README.md
|
||||
else
|
||||
$(warning "pandoc does not appear available: unable to build manpage")
|
||||
endif
|
||||
|
||||
man: $(MANPAGE)
|
||||
|
||||
tarball: clean man changelog authors src/version.hpp
|
||||
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
|
||||
.PHONY: tarball
|
||||
tarball: man changelog version
|
||||
$(eval VERSION := $(shell cat VERSION))
|
||||
$(eval VERSION := $(subst -,_,$(VERSION)))
|
||||
$(eval FILENAME := $(TARGET)-$(VERSION))
|
||||
$(eval FILENAME := mergerfs-$(VERSION))
|
||||
$(eval TMPDIR := $(shell $(MKTEMP) --tmpdir -d .$(FILENAME).XXXXXXXX))
|
||||
$(MKDIR) $(TMPDIR)/$(FILENAME)
|
||||
$(CP) -ar . $(TMPDIR)/$(FILENAME)
|
||||
@@ -190,27 +216,44 @@ tarball: clean man changelog authors src/version.hpp
|
||||
$(RM) -rf $(TMPDIR)
|
||||
|
||||
debian-changelog:
|
||||
$(GIT2DEBCL) --name $(TARGET) > debian/changelog
|
||||
ifeq ($(GIT_REPO),1)
|
||||
$(GIT2DEBCL) --name mergerfs > debian/changelog
|
||||
else
|
||||
cp ChangeLog debian/changelog
|
||||
endif
|
||||
|
||||
signed-deb: debian-changelog
|
||||
dpkg-buildpackage
|
||||
signed-deb:
|
||||
$(MAKE) distclean
|
||||
$(MAKE) debian-changelog
|
||||
dpkg-source -b .
|
||||
dpkg-buildpackage -nc
|
||||
|
||||
deb: debian-changelog
|
||||
dpkg-buildpackage -uc -us
|
||||
deb:
|
||||
$(MAKE) distclean
|
||||
$(MAKE) debian-changelog
|
||||
dpkg-source -b .
|
||||
dpkg-buildpackage -nc -uc -us
|
||||
|
||||
.PHONY: rpm-clean
|
||||
rpm-clean:
|
||||
$(RM) -rf rpmbuild
|
||||
|
||||
rpm: tarball
|
||||
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
|
||||
$(eval VERSION := $(shell cat VERSION))
|
||||
$(eval VERSION := $(subst -,_,$(VERSION)))
|
||||
$(MKDIR) -p rpmbuild/BUILD rpmbuild/RPMS rpmbuild/SOURCES
|
||||
$(SED) 's/__VERSION__/$(VERSION)/g' $(TARGET).spec > \
|
||||
rpmbuild/SOURCES/$(TARGET).spec
|
||||
cp -ar $(TARGET)-$(VERSION).tar.gz rpmbuild/SOURCES
|
||||
$(RPMBUILD) -ba rpmbuild/SOURCES/$(TARGET).spec \
|
||||
$(SED) 's/__VERSION__/$(VERSION)/g' mergerfs.spec > \
|
||||
rpmbuild/SOURCES/mergerfs.spec
|
||||
cp -ar mergerfs-$(VERSION).tar.gz rpmbuild/SOURCES
|
||||
$(RPMBUILD) -ba rpmbuild/SOURCES/mergerfs.spec \
|
||||
--define "_topdir $(CURDIR)/rpmbuild"
|
||||
|
||||
.PHONY: all clean install help
|
||||
.PHONY: install-build-pkgs
|
||||
install-build-pkgs:
|
||||
tools/install-build-pkgs
|
||||
|
||||
include $(wildcard obj/*.d)
|
||||
.PHONY: libfuse
|
||||
libfuse:
|
||||
$(MAKE) DEBUG=$(DEBUG) -C libfuse
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
Vendored
+5
-8
@@ -2,10 +2,7 @@ Source: mergerfs
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Build-Depends: debhelper (>= 8.0.0),
|
||||
libfuse-dev,
|
||||
libattr1-dev,
|
||||
pkg-config
|
||||
Build-Depends: debhelper (>= 8.0.0)
|
||||
Standards-Version: 3.9.4
|
||||
Homepage: http://github.com/trapexit/mergerfs
|
||||
|
||||
@@ -13,8 +10,8 @@ Package: mergerfs
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
libfuse2
|
||||
fuse [linux-any] | fuse4bsd [kfreebsd-any]
|
||||
Description: another FUSE union filesystem
|
||||
mergerfs is similar to mhddfs, unionfs, and aufs. Like mhddfs in that it too
|
||||
uses FUSE. Like aufs in that it provides multiple policies for how to handle
|
||||
behavior.
|
||||
mergerfs is a union filesystem geared towards simplifying storage and
|
||||
management of files across numerous commodity storage devices. It is
|
||||
similar to mhddfs, unionfs, and aufs.
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "${1}" in
|
||||
configure)
|
||||
chmod 4755 /usr/bin/mergerfs-fusermount
|
||||
;;
|
||||
esac
|
||||
Vendored
+2
-2
@@ -2,10 +2,10 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@
|
||||
dh $@ --parallel
|
||||
|
||||
override_dh_auto_install:
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/mergerfs PREFIX=/usr install
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
Current Maintainer
|
||||
------------------
|
||||
|
||||
Nikolaus Rath <Nikolaus@rath.org>
|
||||
|
||||
|
||||
Past Maintainers
|
||||
----------------
|
||||
|
||||
Miklos Szeredi <miklos@szeredi.hu> (until 12/2015)
|
||||
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
CUSE has been written by Tejun Heo <teheo@suse.de>. Furthermore, the
|
||||
following people have contributed patches (autogenerated list):
|
||||
|
||||
Anatol Pomozov <anatol.pomozov@gmail.com>
|
||||
Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Christopher Harrison <ch12@sanger.ac.uk>
|
||||
Csaba Henk <csaba.henk@creo.hu>
|
||||
cvs2git <>
|
||||
Dalvik Khertel <khertel@outlook.com>
|
||||
Daniel Thau <danthau@bedrocklinux.org>
|
||||
David McNab <david@rebirthing.co.nz>
|
||||
David Sheets <sheets@alum.mit.edu>
|
||||
Emmanuel Dreyfus <manu@netbsd.org>
|
||||
Enke Chen <enkechen@yahoo.com>
|
||||
Eric Engestrom <eric@engestrom.ch>
|
||||
Eric Wong <normalperson@yhbt.net>
|
||||
Fabrice Bauzac <fbauzac@amadeus.com>
|
||||
Feng Shuo <steve.shuo.feng@gmail.com>
|
||||
Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
||||
Ikey Doherty <michael.i.doherty@intel.com>
|
||||
Jan Blumschein <jan@jan-blumschein.de>
|
||||
Joachim Schiele <joachim.schiele@daimler.com>
|
||||
Joachim Schiele <js@lastlog.de>
|
||||
John Muir <john@jmuir.com>
|
||||
Laszlo Papp <ext-laszlo.papp@nokia.com>
|
||||
Madan Valluri <mvalluri@cumulus-systems.com>
|
||||
Mark Glines <mark@glines.org>
|
||||
Max Krasnyansky <maxk@kernel.org>
|
||||
Michael Grigoriev <mag@luminal.org>
|
||||
Miklos Szeredi <miklos@szeredi.hu>
|
||||
Miklos Szeredi <mszeredi@suse.cz>
|
||||
mkmm@gmx-topmail.de <mkmm@gmx-topmail.de>
|
||||
Natanael Copa <ncopa@alpinelinux.org>
|
||||
Nikolaus Rath <Nikolaus@rath.org>
|
||||
Olivier Blin <olivier.blin@softathome.com>
|
||||
Ratna_Bolla@dell.com <Ratna_Bolla@dell.com>
|
||||
Reuben Hawkins <reubenhwk@gmail.com>
|
||||
Richard W.M. Jones <rjones@redhat.com>
|
||||
Riku Voipio <riku.voipio@linaro.org>
|
||||
Roland Bauerschmidt <rb@debian.org>
|
||||
Sam Stuewe <halosghost@archlinux.info>
|
||||
Sebastian Pipping <sebastian@pipping.org>
|
||||
therealneworld@gmail.com <therealneworld@gmail.com>
|
||||
Winfried Koehler <w_scan@gmx-topmail.de>
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -0,0 +1,137 @@
|
||||
VERSION = 2.9.7-mergerfs_2.30.0
|
||||
|
||||
OS := $(shell uname -s)
|
||||
ifeq ($(OS),Linux)
|
||||
UTILS := utils
|
||||
INSTALLUTILS := install-utils
|
||||
else
|
||||
UTILS :=
|
||||
INSTALLUTILS :=
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
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/crc32b.c \
|
||||
lib/debug.c \
|
||||
lib/fuse.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse_lowlevel.c \
|
||||
lib/fuse_mt.c \
|
||||
lib/fuse_node.c \
|
||||
lib/fuse_opt.c \
|
||||
lib/fuse_session.c \
|
||||
lib/fuse_signals.c \
|
||||
lib/helper.c \
|
||||
lib/mount.c
|
||||
SRC_CPP = \
|
||||
lib/format.cpp \
|
||||
lib/os.cpp \
|
||||
lib/cpu.cpp \
|
||||
lib/fuse_loop_mt.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} \
|
||||
-std=gnu99 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++11 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
FUSERMOUNT_DIR = $(BINDIR)
|
||||
FUSE_FLAGS = \
|
||||
-Iinclude \
|
||||
-Ibuild \
|
||||
-D_REENTRANT \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-DPACKAGE_VERSION=\"$(VERSION)\" \
|
||||
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-lrt \
|
||||
-pthread
|
||||
|
||||
all: build/libfuse.a $(UTILS)
|
||||
|
||||
build/config.h: build/stamp
|
||||
ecfd/build | tee build/config.h
|
||||
|
||||
build/stamp:
|
||||
mkdir -p build
|
||||
touch $@
|
||||
|
||||
objects: build/config.h
|
||||
$(MAKE) $(OBJS_C) $(OBJS_CPP)
|
||||
|
||||
build/libfuse.a: objects
|
||||
${AR} rcs build/libfuse.a $(OBJS_C) $(OBJS_CPP)
|
||||
|
||||
utils: mergerfs-fusermount mount.mergerfs
|
||||
|
||||
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) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
build/%.o: lib/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
distclean: clean
|
||||
|
||||
strip:
|
||||
strip --strip-all build/mount.mergerfs
|
||||
strip --strip-all build/mergerfs-fusermount
|
||||
|
||||
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"
|
||||
|
||||
install: $(INSTALLUTILS)
|
||||
|
||||
.PHONY: objects strip utils install install-utils
|
||||
|
||||
-include $(DEPS_C) $(DEPS_CPP)
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
What is new in 2.9
|
||||
|
||||
- Add "zero copy" support for kernel 2.6.35 or newer
|
||||
|
||||
- Make maximum background requests tunable on kernel 2.6.32 or newer
|
||||
|
||||
- Require --no-canonicalize in (u)mount (util-linux version 2.18 or
|
||||
newer) to fix security problems with fusermount
|
||||
|
||||
- Use dynamically sized hash tables in high level library
|
||||
|
||||
- Memory use of filesystem daemon can shrink more easily
|
||||
|
||||
- Add "auto_unmount" option
|
||||
|
||||
- Add "remember" option
|
||||
|
||||
- Add man pages for fusermount, mount.fuse and ulockmgr_server
|
||||
|
||||
- API changes:
|
||||
|
||||
o Introduce "store" and "retrieve" for accessing kernel buffers on
|
||||
kernel 2.6.36 or newer
|
||||
|
||||
o Introduce abstract buffer for zero copy operations
|
||||
|
||||
o Allow path calculation to be omitted on certain operations
|
||||
|
||||
o Allow batching forget requests
|
||||
|
||||
o Add "flock" method
|
||||
|
||||
o Add support for ioctl on directories
|
||||
|
||||
o Add delete notification
|
||||
|
||||
o Add fallocate operation (linux kernel 3.5 or newer)
|
||||
|
||||
- Bug fixes and small improvements
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.8
|
||||
|
||||
- More scalable directory tree locking
|
||||
|
||||
- Atomic open(O_TRUNC) support
|
||||
|
||||
- Support big write requests on kernels 2.6.26 and newer
|
||||
|
||||
- Out-of-tree fuse module removed
|
||||
|
||||
- Better NFS exporting support
|
||||
|
||||
- New ioctl and poll requests
|
||||
|
||||
- New CUSE (Character Device in Userspace) interface
|
||||
|
||||
- Allow umask processing in userspace
|
||||
|
||||
- Added cache invalidation notifications
|
||||
|
||||
- Bugfixes and small improvements
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.7
|
||||
|
||||
- Stacking support for the high level API
|
||||
|
||||
- Add filename charset conversion module
|
||||
|
||||
- Improved mounting
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.6
|
||||
|
||||
- Improved read characteristics (asynchronous reads)
|
||||
|
||||
- Support for aborting filesystem connection
|
||||
|
||||
- POSIX file locking support
|
||||
|
||||
- Request interruption support
|
||||
|
||||
- Building module for Linux kernels earlier than 2.6.9 not supported
|
||||
|
||||
- Allow block device based filesystems to support swap files
|
||||
|
||||
- Several bugs fixed, including a rare system hang on SMP
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.5
|
||||
|
||||
- Merge library part of FreeBSD port
|
||||
|
||||
- New atomic create+open, access and ftruncate operations
|
||||
|
||||
- On filesystems implementing the new create+open operation, and
|
||||
running on Linux kernels 2.6.15 or later, the 'cp' operation will
|
||||
work correctly when copying read-only files.
|
||||
|
||||
- New option parsing interface added to the library
|
||||
|
||||
- Lots of minor improvements and fixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.4
|
||||
|
||||
- Simplify device opening. Now '/dev/fuse' is a requirement
|
||||
|
||||
- Allow module auto-loading if user has access to '/dev/fuse'
|
||||
|
||||
- Allow mounting over a regular file for unprivileged users
|
||||
|
||||
- Allow mounting of arbitrary FUSE filesystems from /etc/fstab
|
||||
|
||||
- New mount options: 'umask=M', 'uid=N', 'gid=N'
|
||||
|
||||
- Check for non-empty mountpoint, and refuse mount by default. New
|
||||
mount option: 'nonempty'
|
||||
|
||||
- Low level (inode based) API added
|
||||
|
||||
- Allow 'direct_io' and 'keep_cache' options to be set on a
|
||||
case-by-case basis on open.
|
||||
|
||||
- Add 'attr_timeout' and 'entry_timeout' mount options to the
|
||||
high-level library. Until now these timeouts were fixed at 1 sec.
|
||||
|
||||
- Some bugfixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.3
|
||||
|
||||
- Add new directory related operations: opendir(), readdir(),
|
||||
releasedir() and fsyncdir()
|
||||
|
||||
- Add init() and destroy() operations which are called before the
|
||||
event loop is started and after it has exited
|
||||
|
||||
- Update kernel ABI so that on dual architectures (e.g. AMD64) 32bit
|
||||
binaries work under a 64bit kernel
|
||||
|
||||
- Bugfixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.2
|
||||
|
||||
Userspace changes:
|
||||
|
||||
- Add fuse_file_info structure to file operations, this allows the
|
||||
filesystem to return a file handle in open() which is passed to
|
||||
read(), write(), flush(), fsync() and release().
|
||||
|
||||
- Add source compatibility with 2.1 and 1.4 releases
|
||||
|
||||
- Binary compatibility with 2.1 release is retained
|
||||
|
||||
Kernel changes:
|
||||
|
||||
- Make requests interruptible. This prevents the filesystem to go
|
||||
into an unbreakable deadlock with itself.
|
||||
|
||||
- Make readpages() synchronous. Asynchronous requests are deadlock
|
||||
prone, since they cannot be interrupted (see above)
|
||||
|
||||
- Remove shared-writeable mapping support, which could deadlock the
|
||||
machine
|
||||
|
||||
- Remove INVALIDATE userspace initiated request
|
||||
|
||||
- Update ABI to be independent of sizeof(long), so dual-size archs
|
||||
don't cause problems
|
||||
|
||||
- Remove /sys/fs/fuse/version. Version checking is now done through
|
||||
the fuse device
|
||||
|
||||
- Replace directory reading method on the kernel interface. Instead
|
||||
of passing an open file descriptor to the kernel, send data through
|
||||
the FUSE device, like all other operations.
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 2.1
|
||||
|
||||
* Bug fixes
|
||||
|
||||
* Improved support for filesystems implementing a custom event-loop
|
||||
|
||||
* Add 'pkg-config' support
|
||||
|
||||
* Kernel module can be compiled separately
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 1.9
|
||||
|
||||
* Lots of bugs fixed
|
||||
|
||||
* Minor modifications to the library API
|
||||
|
||||
* Improvements to the kernel/userspace interface
|
||||
|
||||
* Mounting by non-root made more secure
|
||||
|
||||
* Build shared library in addition to the static one
|
||||
|
||||
* Consolidated mount options
|
||||
|
||||
* Optimized reading under 2.6 kernels
|
||||
|
||||
* Direct I/O support
|
||||
|
||||
* Support file I/O on deleted files
|
||||
|
||||
* Extended attributes support
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 1.3
|
||||
|
||||
* Thanks to user bugreports and stress testing with LTP and sfx-linux
|
||||
a number of bugs were fixed, some quite serious.
|
||||
|
||||
* Fix compile problems with recent SuSE kernles
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 1.2
|
||||
|
||||
* Fix mount problems on recent 2.6 kernels with SELinux enabled
|
||||
|
||||
* Fixed writing files lager than 2GBytes
|
||||
|
||||
* Other bugfixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 1.1
|
||||
|
||||
* Support for the 2.6 kernels
|
||||
|
||||
* Support for exporting filesystem over NFS in 2.6 kernels
|
||||
|
||||
* Read efficiency improvements: read in 64k blocks instead of 4k
|
||||
(Michael Grigoriev). Can be turned on with '-l' option of fusermount
|
||||
|
||||
* Lazy automatic unmount
|
||||
|
||||
* Added 'fsync()' VFS call to the FUSE interface
|
||||
|
||||
* Bugfixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 1.0
|
||||
|
||||
* Cleanups and bugfixes
|
||||
|
||||
* Added 'release()' VFS call to the FUSE interface
|
||||
|
||||
* 64 bit file offsets (handling of > 4 GByte files)
|
||||
|
||||
* libfuse is now under LGPL
|
||||
|
||||
* New 'statfs' call (Mark Glines)
|
||||
|
||||
* Cleaned up mount procedure (mostly by Mark Glines)
|
||||
|
||||
NOTE: Binaries linked with with a previous version of libavfs may
|
||||
not work with the new version of the fusermount program. In such
|
||||
case recompile the program after installing the new libavfs library.
|
||||
|
||||
* Fix for problems under linux kernel 2.4.19
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 0.95
|
||||
|
||||
* Optimized read/write operations. Raw throughput has increased to
|
||||
about 60Mbyte/s on a Celeron/360
|
||||
|
||||
* Python bindings by Jeff Epler
|
||||
|
||||
* Perl bindings by Mark Glines
|
||||
|
||||
* Improved multithreaded operation
|
||||
|
||||
* Simplified library interface
|
||||
|
||||
* Bugfixes
|
||||
|
||||
============================================================================
|
||||
|
||||
What is new in 0.9:
|
||||
|
||||
* Everything
|
||||
@@ -0,0 +1,33 @@
|
||||
NFS exporting is supported in Linux kernels 2.6.27 or later.
|
||||
|
||||
You need to add an fsid=NNN option to /etc/exports to make exporting a
|
||||
FUSE directory work.
|
||||
|
||||
Filesystem support
|
||||
------------------
|
||||
|
||||
NFS exporting works to some extent on all fuse filesystems, but not
|
||||
perfectly. This is due to the stateless nature of the protocol, the
|
||||
server has no way of knowing whether the client is keeping a reference
|
||||
to a file or not, and hence that file may be removed from the server's
|
||||
cache. In that case there has to be a way to look up that object
|
||||
using the inode number, otherwise an ESTALE error will be returned.
|
||||
|
||||
1) low-level interface
|
||||
|
||||
Filesystems need to implement special lookups for the names "." and
|
||||
"..". The former may be requested on any inode, including
|
||||
non-directories, while the latter is only requested for directories.
|
||||
Otherwise these special lookups should behave identically to ordinary
|
||||
lookups.
|
||||
|
||||
2) high-level interface
|
||||
|
||||
Because the high-level interface is path based, it is not possible to
|
||||
delegate looking up by inode to the filesystem.
|
||||
|
||||
To work around this, currently a "noforget" option is provided, which
|
||||
makes the library remember nodes forever. This will make the NFS
|
||||
server happy, but also results in an ever growing memory footprint for
|
||||
the filesystem. For this reason if the filesystem is large (or the
|
||||
memory is small), then this option is not recommended.
|
||||
@@ -0,0 +1,108 @@
|
||||
libfuse
|
||||
=======
|
||||
|
||||
Warning: unresolved security issue
|
||||
----------------------------------
|
||||
|
||||
Be aware that FUSE has an unresolved security bug
|
||||
([bug #15](https://github.com/libfuse/libfuse/issues/15)): the
|
||||
permission check for accessing a cached directory is only done once
|
||||
when the directory entry is first loaded into the cache. Subsequent
|
||||
accesses will re-use the results of the first check, even if the
|
||||
directory permissions have since changed, and even if the subsequent
|
||||
access is made by a different user.
|
||||
|
||||
This bug needs to be fixed in the Linux kernel and has been known
|
||||
since 2006 but unfortunately no fix has been applied yet. If you
|
||||
depend on correct permission handling for FUSE file systems, the only
|
||||
workaround is to completely disable caching of directory
|
||||
entries. Alternatively, the severity of the bug can be somewhat
|
||||
reduced by not using the `allow_other` mount option.
|
||||
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
FUSE (Filesystem in Userspace) is an interface for userspace programs
|
||||
to export a filesystem to the Linux kernel. The FUSE project consists
|
||||
of two components: the *fuse* kernel module (maintained in the regular
|
||||
kernel repositories) and the *libfuse* userspace library (maintained
|
||||
in this repository). libfuse provides the reference implementation
|
||||
for communicating with the FUSE kernel module.
|
||||
|
||||
A FUSE file system is typically implemented as a standalone
|
||||
application that links with libfuse. libfuse provides functions to
|
||||
mount the file system, unmount it, read requests from the kernel, and
|
||||
send responses back. libfuse offers two APIs: a "high-level",
|
||||
synchronous API, and a "low-level" asynchronous API. In both cases,
|
||||
incoming requests from the kernel are passed to the main program using
|
||||
callbacks. When using the high-level API, the callbacks may work with
|
||||
file names and paths instead of inodes, and processing of a request
|
||||
finishes when the callback function returns. When using the low-level
|
||||
API, the callbacks must work with inodes and responses must be sent
|
||||
explicitly using a separate set of API functions.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
./configure
|
||||
make -j8
|
||||
make install
|
||||
|
||||
You may also need to add `/usr/local/lib` to `/etc/ld.so.conf` and/or
|
||||
run *ldconfig*. If you're building from the git repository (instead of
|
||||
using a release tarball), you also need to run `./makeconf.sh` to
|
||||
create the `configure` script.
|
||||
|
||||
You'll also need a fuse kernel module (Linux kernels 2.6.14 or later
|
||||
contain FUSE support).
|
||||
|
||||
For more details see the file `INSTALL`
|
||||
|
||||
Security implications
|
||||
---------------------
|
||||
|
||||
If you run `make install`, the *fusermount* program is installed
|
||||
set-user-id to root. This is done to allow normal users to mount
|
||||
their own filesystem implementations.
|
||||
|
||||
There must however be some limitations, in order to prevent Bad User from
|
||||
doing nasty things. Currently those limitations are:
|
||||
|
||||
- The user can only mount on a mountpoint, for which it has write
|
||||
permission
|
||||
|
||||
- The mountpoint is not a sticky directory which isn't owned by the
|
||||
user (like /tmp usually is)
|
||||
|
||||
- No other user (including root) can access the contents of the
|
||||
mounted filesystem (though this can be relaxed by allowing the use
|
||||
of the `allow_other` and `allow_root` mount options in `fuse.conf`)
|
||||
|
||||
|
||||
Building your own filesystem
|
||||
------------------------------
|
||||
|
||||
FUSE comes with several example file systems in the `examples`
|
||||
directory. For example, the *fusexmp* example mirrors the contents of
|
||||
the root directory under the mountpoint. Start from there and adapt
|
||||
the code!
|
||||
|
||||
The documentation of the API functions and necessary callbacks is
|
||||
mostly contained in the files `include/fuse.h` (for the high-level
|
||||
API) and `include/fuse_lowlevel.h` (for the low-level API). An
|
||||
autogenerated html version of the API is available in the `doc/html`
|
||||
directory and at http://libfuse.github.io/doxygen.
|
||||
|
||||
|
||||
Getting Help
|
||||
------------
|
||||
|
||||
If you need help, please ask on the <fuse-devel@lists.sourceforge.net>
|
||||
mailing list (subscribe at
|
||||
https://lists.sourceforge.net/lists/listinfo/fuse-devel).
|
||||
|
||||
Please report any bugs on the GitHub issue tracker at
|
||||
https://github.com/libfuse/main/issues.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2018, 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.
|
||||
*/
|
||||
@@ -0,0 +1,16 @@
|
||||
# ecfd: embeddable C feature discovery
|
||||
|
||||
This is a simple script and set of tests to help with what would traditionally be the `configure` step of most C/C++. It is intended to be simple to be embedded into a project and only depends on the C compiler and /bin/sh.
|
||||
|
||||
|
||||
## USAGE
|
||||
|
||||
First copy `ecfd` into your project. Next simply call the build script which will run the tests and output a `config.` style file to `stdout`. Compile errors will output to `stderr` which can be used to understand why they aren't working.
|
||||
|
||||
```
|
||||
$ ./ecfd/build > include/config.h
|
||||
```
|
||||
|
||||
## TESTS
|
||||
|
||||
Each test is in a single module. The build script will try to compile each module and on success will execute the program. If anything is printed to `stdout` it will be appended to the `#define` printed to the build script's `stdout`. If the compilation fails nothing will be printed. Look at the tests directory for examples.
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
PWD=$(pwd)
|
||||
BASEPATH="${PWD}/${0%/*}"
|
||||
|
||||
CC=${CC:-cc}
|
||||
OUTPUT="/dev/null"
|
||||
|
||||
echo "#ifndef CONFIG_H_INCLUDED"
|
||||
echo "#define CONFIG_H_INCLUDED"
|
||||
echo
|
||||
|
||||
IFS=
|
||||
for file in "${BASEPATH}/tests/"*.c
|
||||
do
|
||||
binary="${file%.c}"
|
||||
basename=$(basename ${binary})
|
||||
|
||||
${CC} -o "${binary}" "${file}" 1>&2
|
||||
if [ $? -eq 0 ]; then
|
||||
STDOUT=$(${binary})
|
||||
if [ "${STDOUT}" != "" ]; then
|
||||
echo "#define ${basename} ${STDOUT}"
|
||||
else
|
||||
echo "#define ${basename}"
|
||||
fi
|
||||
rm -f "${binary}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "#endif"
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
(void)fork;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <malloc.h>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
malloc_trim(0);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char *argv[])
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
(void)st.st_atim;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char *argv[])
|
||||
{
|
||||
(void)utimensat;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define STATIC_ASSERT(condition) \
|
||||
((void)sizeof(char[1 - 2*!(condition)]))
|
||||
@@ -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
|
||||
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_H_
|
||||
#define _FUSE_H_
|
||||
|
||||
#include "extern_c.h"
|
||||
#include "fuse_common.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Basic FUSE API *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/** Handle for a FUSE filesystem */
|
||||
struct fuse;
|
||||
|
||||
/** Structure containing a raw command */
|
||||
struct fuse_cmd;
|
||||
|
||||
struct fuse_dirents_t;
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
|
||||
/**
|
||||
* The file system operations:
|
||||
*
|
||||
* Most of these should work very similarly to the well known UNIX
|
||||
* file system operations. A major exception is that instead of
|
||||
* returning an error in 'errno', the operation should return the
|
||||
* negated error value (-errno) directly.
|
||||
*
|
||||
* All methods are optional, but some are essential for a useful
|
||||
* filesystem (e.g. getattr). Open, flush, release, fsync, opendir,
|
||||
* releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock,
|
||||
* init and destroy are special purpose methods, without which a full
|
||||
* featured filesystem can still be implemented.
|
||||
*
|
||||
* Almost all operations take a path which can be of any length.
|
||||
*
|
||||
* Changed in fuse 2.8.0 (regardless of API version)
|
||||
* Previously, paths were limited to a length of PATH_MAX.
|
||||
*
|
||||
* See http://fuse.sourceforge.net/wiki/ for more information. There
|
||||
* is also a snapshot of the relevant wiki pages in the doc/ folder.
|
||||
*/
|
||||
struct fuse_operations
|
||||
{
|
||||
/** Get file attributes.
|
||||
*
|
||||
* Similar to stat(). The 'st_dev' and 'st_blksize' fields are
|
||||
* ignored. The 'st_ino' field is ignored except if the 'use_ino'
|
||||
* mount option is given.
|
||||
*/
|
||||
int (*getattr) (const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Read the target of a symbolic link
|
||||
*
|
||||
* The buffer should be filled with a null terminated string. The
|
||||
* buffer size argument includes the space for the terminating
|
||||
* null character. If the linkname is too long to fit in the
|
||||
* buffer, it should be truncated. The return value should be 0
|
||||
* for success.
|
||||
*/
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
|
||||
/** Create a file node
|
||||
*
|
||||
* This is called for creation of all non-directory, non-symlink
|
||||
* nodes. If the filesystem defines a create() method, then for
|
||||
* regular files that will be called instead.
|
||||
*/
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
|
||||
/** Create a directory
|
||||
*
|
||||
* Note that the mode argument may not have the type specification
|
||||
* bits set, i.e. S_ISDIR(mode) can be false. To obtain the
|
||||
* correct directory type bits use mode|S_IFDIR
|
||||
* */
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
|
||||
/** Hide files unlinked / renamed over
|
||||
*
|
||||
* Allows storing of a file handle when a file is unlinked
|
||||
* while open. Helps manage the fact the kernel usually does
|
||||
* not send fh with getattr requests.
|
||||
*/
|
||||
int (*prepare_hide)(const char *name_, uint64_t *fh_);
|
||||
int (*free_hide)(const uint64_t fh_);
|
||||
|
||||
/** Remove a file */
|
||||
int (*unlink) (const char *);
|
||||
|
||||
/** Remove a directory */
|
||||
int (*rmdir) (const char *);
|
||||
|
||||
/** Create a symbolic link */
|
||||
int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Rename a file */
|
||||
int (*rename) (const char *, const char *);
|
||||
|
||||
/** Create a hard link to a file */
|
||||
int (*link) (const char *, const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Change the permission bits of a file */
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*fchmod)(const fuse_file_info_t *, const mode_t);
|
||||
|
||||
/** Change the owner and group of a file */
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*fchown)(const fuse_file_info_t *, const uid_t, const gid_t);
|
||||
|
||||
/** Change the size of a file */
|
||||
int (*truncate) (const char *, off_t);
|
||||
|
||||
/** File open operation
|
||||
*
|
||||
* No creation (O_CREAT, O_EXCL) and by default also no
|
||||
* truncation (O_TRUNC) flags will be passed to open(). If an
|
||||
* application specifies O_TRUNC, fuse first calls truncate()
|
||||
* and then open(). Only if 'atomic_o_trunc' has been
|
||||
* specified and kernel version is 2.6.24 or later, O_TRUNC is
|
||||
* passed on to open.
|
||||
*
|
||||
* Unless the 'default_permissions' mount option is given,
|
||||
* open should check if the operation is permitted for the
|
||||
* given flags. Optionally open may also return an arbitrary
|
||||
* filehandle in the fuse_file_info structure, which will be
|
||||
* passed to all file operations.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*open) (const char *, fuse_file_info_t *);
|
||||
|
||||
/** Get file system statistics
|
||||
*
|
||||
* The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
|
||||
*
|
||||
* Replaced 'struct statfs' parameter with 'struct statvfs' in
|
||||
* version 2.5
|
||||
*/
|
||||
int (*statfs) (const char *, struct statvfs *);
|
||||
|
||||
/** Possibly flush cached data
|
||||
*
|
||||
* BIG NOTE: This is not equivalent to fsync(). It's not a
|
||||
* request to sync dirty data.
|
||||
*
|
||||
* Flush is called on each close() of a file descriptor. So if a
|
||||
* filesystem wants to return write errors in close() and the file
|
||||
* has cached dirty data, this is a good place to write back data
|
||||
* and return any errors. Since many applications ignore close()
|
||||
* errors this is not always useful.
|
||||
*
|
||||
* NOTE: The flush() method may be called more than once for each
|
||||
* open(). This happens if more than one file descriptor refers
|
||||
* to an opened file due to dup(), dup2() or fork() calls. It is
|
||||
* not possible to determine if a flush is final, so each flush
|
||||
* should be treated equally. Multiple write-flush sequences are
|
||||
* relatively rare, so this shouldn't be a problem.
|
||||
*
|
||||
* Filesystems shouldn't assume that flush will always be called
|
||||
* after some writes, or that if will be called at all.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*flush) (const fuse_file_info_t *);
|
||||
|
||||
/** Release an open file
|
||||
*
|
||||
* Release is called when there are no more references to an open
|
||||
* file: all file descriptors are closed and all memory mappings
|
||||
* are unmapped.
|
||||
*
|
||||
* For every open() call there will be exactly one release() call
|
||||
* with the same flags and file descriptor. It is possible to
|
||||
* have a file opened more than once, in which case only the last
|
||||
* release will mean, that no more reads/writes will happen on the
|
||||
* file. The return value of release is ignored.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*release) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize file contents
|
||||
*
|
||||
* If the datasync parameter is non-zero, then only the user data
|
||||
* should be flushed, not the meta data.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*fsync) (const fuse_file_info_t *, int);
|
||||
|
||||
/** Set extended attributes */
|
||||
int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
||||
|
||||
/** Get extended attributes */
|
||||
int (*getxattr) (const char *, const char *, char *, size_t);
|
||||
|
||||
/** List extended attributes */
|
||||
int (*listxattr) (const char *, char *, size_t);
|
||||
|
||||
/** Remove extended attributes */
|
||||
int (*removexattr) (const char *, const char *);
|
||||
|
||||
/** Open directory
|
||||
*
|
||||
* Unless the 'default_permissions' mount option is given,
|
||||
* this method should check if opendir is permitted for this
|
||||
* directory. Optionally opendir may also return an arbitrary
|
||||
* filehandle in the fuse_file_info structure, which will be
|
||||
* passed to readdir, closedir and fsyncdir.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*opendir) (const char *,
|
||||
fuse_file_info_t *);
|
||||
|
||||
/** Read directory
|
||||
*
|
||||
* This supersedes the old getdir() interface. New applications
|
||||
* should use this.
|
||||
*
|
||||
* The filesystem may choose between two modes of operation:
|
||||
*
|
||||
* 1) The readdir implementation ignores the offset parameter, and
|
||||
* passes zero to the filler function's offset. The filler
|
||||
* function will not return '1' (unless an error happens), so the
|
||||
* whole directory is read in a single readdir operation. This
|
||||
* works just like the old getdir() method.
|
||||
*
|
||||
* 2) The readdir implementation keeps track of the offsets of the
|
||||
* directory entries. It uses the offset parameter and always
|
||||
* passes non-zero offset to the filler function. When the buffer
|
||||
* is full (or an error happens) the filler function will return
|
||||
* '1'.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*readdir)(const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
int (*readdir_plus)(const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
|
||||
/** Release directory
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*releasedir) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize directory contents
|
||||
*
|
||||
* If the datasync parameter is non-zero, then only the user data
|
||||
* should be flushed, not the meta data
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*fsyncdir) (const fuse_file_info_t *, int);
|
||||
|
||||
/**
|
||||
* Initialize filesystem
|
||||
*
|
||||
* The return value will passed in the private_data field of
|
||||
* fuse_context to all file operations and as a parameter to the
|
||||
* destroy() method.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
* Changed in version 2.6
|
||||
*/
|
||||
void *(*init) (struct fuse_conn_info *conn);
|
||||
|
||||
/**
|
||||
* Clean up filesystem
|
||||
*
|
||||
* Called on filesystem exit.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
void (*destroy) (void);
|
||||
|
||||
/**
|
||||
* Check file access permissions
|
||||
*
|
||||
* This will be called for the access() system call. If the
|
||||
* 'default_permissions' mount option is given, this method is not
|
||||
* called.
|
||||
*
|
||||
* This method is not called under Linux kernel versions 2.4.x
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*access) (const char *, int);
|
||||
|
||||
/**
|
||||
* Create and open a file
|
||||
*
|
||||
* If the file does not exist, first create it with the specified
|
||||
* mode, and then open it.
|
||||
*
|
||||
* If this method is not implemented or under Linux kernel
|
||||
* versions earlier than 2.6.15, the mknod() and open() methods
|
||||
* will be called instead.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*create) (const char *, mode_t, fuse_file_info_t *);
|
||||
|
||||
/**
|
||||
* Change the size of an open file
|
||||
*
|
||||
* This method is called instead of the truncate() method if the
|
||||
* truncation was invoked from an ftruncate() system call.
|
||||
*
|
||||
* If this method is not implemented or under Linux kernel
|
||||
* versions earlier than 2.6.15, the truncate() method will be
|
||||
* called instead.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*ftruncate) (const fuse_file_info_t *, off_t);
|
||||
|
||||
/**
|
||||
* Get attributes from an open file
|
||||
*
|
||||
* This method is called instead of the getattr() method if the
|
||||
* file information is available.
|
||||
*
|
||||
* Currently this is only called after the create() method if that
|
||||
* is implemented (see above). Later it may be called for
|
||||
* invocations of fstat() too.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*fgetattr) (const fuse_file_info_t *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/**
|
||||
* Perform POSIX file locking operation
|
||||
*
|
||||
* The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
|
||||
*
|
||||
* For the meaning of fields in 'struct flock' see the man page
|
||||
* for fcntl(2). The l_whence field will always be set to
|
||||
* SEEK_SET.
|
||||
*
|
||||
* For checking lock ownership, the 'fuse_file_info->owner'
|
||||
* argument must be used.
|
||||
*
|
||||
* For F_GETLK operation, the library will first check currently
|
||||
* held locks, and if a conflicting lock is found it will return
|
||||
* information without calling this method. This ensures, that
|
||||
* for local locks the l_pid field is correctly filled in. The
|
||||
* results may not be accurate in case of race conditions and in
|
||||
* the presence of hard links, but it's unlikely that an
|
||||
* application would rely on accurate GETLK results in these
|
||||
* cases. If a conflicting lock is not found, this method will be
|
||||
* called, and the filesystem may fill out l_pid by a meaningful
|
||||
* value, or it may leave this field zero.
|
||||
*
|
||||
* For F_SETLK and F_SETLKW the l_pid field will be set to the pid
|
||||
* of the process performing the locking operation.
|
||||
*
|
||||
* Note: if this method is not implemented, the kernel will still
|
||||
* allow file locking to work locally. Hence it is only
|
||||
* interesting for network filesystems and similar.
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*lock) (const fuse_file_info_t *,
|
||||
int cmd,
|
||||
struct flock *);
|
||||
|
||||
/**
|
||||
* Change the access and modification times of a file with
|
||||
* nanosecond resolution
|
||||
*
|
||||
* This supersedes the old utime() interface. New applications
|
||||
* should use this.
|
||||
*
|
||||
* See the utimensat(2) man page for details.
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*utimens)(const char *, const struct timespec tv[2]);
|
||||
int (*futimens)(const fuse_file_info_t *ffi_, const struct timespec tv_[2]);
|
||||
|
||||
/**
|
||||
* Map block index within file to block index within device
|
||||
*
|
||||
* Note: This makes sense only for block device backed filesystems
|
||||
* mounted with the 'blkdev' option
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
|
||||
|
||||
/**
|
||||
* Ioctl
|
||||
*
|
||||
* flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
|
||||
* 64bit environment. The size and direction of data is
|
||||
* determined by _IOC_*() decoding of cmd. For _IOC_NONE,
|
||||
* data will be NULL, for _IOC_WRITE data is out area, for
|
||||
* _IOC_READ in area and if both are set in/out area. In all
|
||||
* non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
|
||||
*
|
||||
* If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a
|
||||
* directory file handle.
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*ioctl) (const fuse_file_info_t *ffi,
|
||||
unsigned long cmd,
|
||||
void *arg,
|
||||
unsigned int flags,
|
||||
void *data,
|
||||
uint32_t *out_bufsz);
|
||||
|
||||
/**
|
||||
* Poll for IO readiness events
|
||||
*
|
||||
* Note: If ph is non-NULL, the client should notify
|
||||
* when IO readiness events occur by calling
|
||||
* fuse_notify_poll() with the specified ph.
|
||||
*
|
||||
* Regardless of the number of times poll with a non-NULL ph
|
||||
* is received, single notification is enough to clear all.
|
||||
* Notifying more times incurs overhead but doesn't harm
|
||||
* correctness.
|
||||
*
|
||||
* The callee is responsible for destroying ph with
|
||||
* fuse_pollhandle_destroy() when no longer in use.
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*poll) (const fuse_file_info_t *ffi,
|
||||
fuse_pollhandle_t *ph,
|
||||
unsigned *reventsp);
|
||||
|
||||
/** Write contents of buffer to an open file
|
||||
*
|
||||
* Similar to the write() method, but data is supplied in a
|
||||
* generic buffer. Use fuse_buf_copy() to transfer data to
|
||||
* the destination.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*write) (const fuse_file_info_t *ffi,
|
||||
const char *data,
|
||||
size_t size,
|
||||
off_t off);
|
||||
|
||||
/** Store data from an open file in a buffer
|
||||
*
|
||||
* Similar to the read() method, but data is stored and
|
||||
* returned in a generic buffer.
|
||||
*
|
||||
* No actual copying of data has to take place, the source
|
||||
* file descriptor may simply be stored in the buffer for
|
||||
* later data transfer.
|
||||
*
|
||||
* The buffer must be allocated dynamically and stored at the
|
||||
* location pointed to by bufp. If the buffer contains memory
|
||||
* regions, they too must be allocated using malloc(). The
|
||||
* allocated memory will be freed by the caller.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*read)(const fuse_file_info_t *ffi,
|
||||
char *buf,
|
||||
size_t size,
|
||||
off_t off);
|
||||
/**
|
||||
* Perform BSD file locking operation
|
||||
*
|
||||
* The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
|
||||
*
|
||||
* Nonblocking requests will be indicated by ORing LOCK_NB to
|
||||
* the above operations
|
||||
*
|
||||
* For more information see the flock(2) manual page.
|
||||
*
|
||||
* Additionally fi->owner will be set to a value unique to
|
||||
* this open file. This same value will be supplied to
|
||||
* ->release() when the file is released.
|
||||
*
|
||||
* Note: if this method is not implemented, the kernel will still
|
||||
* allow file locking to work locally. Hence it is only
|
||||
* interesting for network filesystems and similar.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*flock) (const fuse_file_info_t *, int op);
|
||||
|
||||
/**
|
||||
* Allocates space for an open file
|
||||
*
|
||||
* This function ensures that required space is allocated for specified
|
||||
* file. If this function returns success then any subsequent write
|
||||
* request to specified range is guaranteed not to fail because of lack
|
||||
* of space on the file system media.
|
||||
*
|
||||
* Introduced in version 2.9.1
|
||||
*/
|
||||
int (*fallocate) (const fuse_file_info_t *, int, off_t, off_t);
|
||||
|
||||
/**
|
||||
* Copy a range of data from one file to another
|
||||
*
|
||||
* Performs an optimized copy between two file descriptors without
|
||||
* the additional cost of transferring data through the FUSE kernel
|
||||
* module to user space (glibc) and then back into the FUSE filesystem
|
||||
* again.
|
||||
*
|
||||
* In case this method is not implemented, glibc falls back to
|
||||
* reading data from the source and writing to the
|
||||
* destination. Effectively doing an inefficient copy of the
|
||||
* data.
|
||||
*/
|
||||
ssize_t (*copy_file_range)(const fuse_file_info_t *fi_in,
|
||||
off_t offset_in,
|
||||
const fuse_file_info_t *fi_out,
|
||||
off_t offset_out,
|
||||
size_t size,
|
||||
int flags);
|
||||
};
|
||||
|
||||
/** Extra context that may be needed by some filesystems
|
||||
*
|
||||
* The uid, gid and pid fields are not filled in case of a writepage
|
||||
* operation.
|
||||
*/
|
||||
struct fuse_context
|
||||
{
|
||||
/** Pointer to the fuse object */
|
||||
struct fuse *fuse;
|
||||
|
||||
/** User ID of the calling process */
|
||||
uid_t uid;
|
||||
|
||||
/** Group ID of the calling process */
|
||||
gid_t gid;
|
||||
|
||||
/** Thread ID of the calling process */
|
||||
pid_t pid;
|
||||
|
||||
/** Umask of the calling process (introduced in version 2.8) */
|
||||
mode_t umask;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function of FUSE.
|
||||
*
|
||||
* This is for the lazy. This is all that has to be called from the
|
||||
* main() function.
|
||||
*
|
||||
* This function does the following:
|
||||
* - parses command line options (-d -s and -h)
|
||||
* - passes relevant mount options to the fuse_mount()
|
||||
* - installs signal handlers for INT, HUP, TERM and PIPE
|
||||
* - registers an exit handler to unmount the filesystem on program exit
|
||||
* - creates a fuse handle
|
||||
* - registers the operations
|
||||
* - calls either the single-threaded or the multi-threaded event loop
|
||||
*
|
||||
* Note: this is currently implemented as a macro.
|
||||
*
|
||||
* @param argc the argument counter passed to the main() function
|
||||
* @param argv the argument vector passed to the main() function
|
||||
* @param op the file system operation
|
||||
* @return 0 on success, nonzero on failure
|
||||
*/
|
||||
/*
|
||||
int fuse_main(int argc, char *argv[], const struct fuse_operations *op);
|
||||
*/
|
||||
#define fuse_main(argc, argv, op) \
|
||||
fuse_main_real(argc, argv, op, sizeof(*(op)))
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* More detailed API *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create a new FUSE filesystem.
|
||||
*
|
||||
* @param ch the communication channel
|
||||
* @param args argument vector
|
||||
* @param op the filesystem operations
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @return the created FUSE handle
|
||||
*/
|
||||
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
|
||||
const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
/**
|
||||
* Destroy the FUSE handle.
|
||||
*
|
||||
* The communication channel attached to the handle is also destroyed.
|
||||
*
|
||||
* NOTE: This function does not unmount the filesystem. If this is
|
||||
* needed, call fuse_unmount() before calling this function.
|
||||
*
|
||||
* @param f the FUSE handle
|
||||
*/
|
||||
void fuse_destroy(struct fuse *f);
|
||||
|
||||
/**
|
||||
* Exit from event loop
|
||||
*
|
||||
* @param f the FUSE handle
|
||||
*/
|
||||
void fuse_exit(struct fuse *f);
|
||||
|
||||
int fuse_config_read_thread_count(const struct fuse *f);
|
||||
int fuse_config_process_thread_count(const struct fuse *f);
|
||||
const char* fuse_config_pin_threads(const struct fuse *f);
|
||||
|
||||
/**
|
||||
* FUSE event loop with multiple threads
|
||||
*
|
||||
* Requests from the kernel are processed, and the appropriate
|
||||
* operations are called. Request are processed in parallel by
|
||||
* distributing them between multiple threads.
|
||||
*
|
||||
* Calling this function requires the pthreads library to be linked to
|
||||
* the application.
|
||||
*
|
||||
* @param f the FUSE handle
|
||||
* @return 0 if no error occurred, -1 otherwise
|
||||
*/
|
||||
int fuse_loop_mt(struct fuse *f);
|
||||
|
||||
/**
|
||||
* Get the current context
|
||||
*
|
||||
* The context is only valid for the duration of a filesystem
|
||||
* operation, and thus must not be stored and used later.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
struct fuse_context *fuse_get_context(void);
|
||||
|
||||
/**
|
||||
* Check if the current request has already been interrupted
|
||||
*
|
||||
* @return 1 if the request has been interrupted, 0 otherwise
|
||||
*/
|
||||
int fuse_interrupted(void);
|
||||
|
||||
/**
|
||||
* Obsolete, doesn't do anything
|
||||
*
|
||||
* @return -EINVAL
|
||||
*/
|
||||
int fuse_invalidate(struct fuse *f, const char *path);
|
||||
|
||||
/* Deprecated, don't use */
|
||||
int fuse_is_lib_option(const char *opt);
|
||||
|
||||
/**
|
||||
* The real main function
|
||||
*
|
||||
* Do not call this directly, use fuse_main()
|
||||
*/
|
||||
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
int fuse_start_maintenance_thread(struct fuse *fuse);
|
||||
void fuse_stop_maintenance_thread(struct fuse *fuse);
|
||||
|
||||
int fuse_log_metrics_get(void);
|
||||
void fuse_log_metrics_set(int enabled);
|
||||
|
||||
|
||||
/**
|
||||
* Iterate over cache removing stale entries
|
||||
* use in conjunction with "-oremember"
|
||||
*
|
||||
* NOTE: This is already done for the standard sessions
|
||||
*
|
||||
* @param fuse struct fuse pointer for fuse instance
|
||||
* @return the number of seconds until the next cleanup
|
||||
*/
|
||||
int fuse_clean_cache(struct fuse *fuse);
|
||||
|
||||
/*
|
||||
* Stacking API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fuse filesystem object
|
||||
*
|
||||
* This is opaque object represents a filesystem layer
|
||||
*/
|
||||
struct fuse_fs;
|
||||
|
||||
/*
|
||||
* These functions call the relevant filesystem operation, and return
|
||||
* the result.
|
||||
*
|
||||
* If the operation is not defined, they return -ENOSYS, with the
|
||||
* exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
|
||||
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
|
||||
*/
|
||||
|
||||
int fuse_notify_poll(fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Create a new fuse filesystem object
|
||||
*
|
||||
* This is usually called from the factory of a fuse module to create
|
||||
* a new instance of a filesystem.
|
||||
*
|
||||
* @param op the filesystem operations
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @return a new filesystem object
|
||||
*/
|
||||
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Advanced API for event handling, don't worry about this... *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/* NOTE: the following functions are deprecated, and will be removed
|
||||
from the 3.0 API. Use the lowlevel session functions instead */
|
||||
|
||||
/** Function type used to process commands */
|
||||
typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
|
||||
|
||||
/** This is the part of fuse_main() before the event loop */
|
||||
struct fuse *fuse_setup(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
char **mountpoint);
|
||||
|
||||
/** This is the part of fuse_main() after the event loop */
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint);
|
||||
|
||||
/** Multi threaded event loop, which calls the custom command
|
||||
processor function */
|
||||
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
|
||||
|
||||
/** Return the exited flag, which indicates if fuse_exit() has been
|
||||
called */
|
||||
int fuse_exited(struct fuse *f);
|
||||
|
||||
/** This function is obsolete and implemented as a no-op */
|
||||
void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
|
||||
|
||||
/** Get session from fuse object */
|
||||
struct fuse_session *fuse_get_session(struct fuse *f);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_H_ */
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
|
||||
#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
|
||||
#endif
|
||||
|
||||
#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>
|
||||
|
||||
/** Major version of FUSE library interface */
|
||||
#define FUSE_MAJOR_VERSION 2
|
||||
|
||||
/** Minor version of FUSE library interface */
|
||||
#define FUSE_MINOR_VERSION 9
|
||||
|
||||
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
|
||||
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
|
||||
|
||||
/* This interface uses 64 bit off_t */
|
||||
#if _FILE_OFFSET_BITS != 64
|
||||
#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
|
||||
#endif
|
||||
|
||||
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
|
||||
#define FUSE_MAX_MAX_PAGES 256
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Information about open files
|
||||
*
|
||||
* Changed in version 2.5
|
||||
*/
|
||||
typedef struct fuse_file_info_t fuse_file_info_t;
|
||||
struct fuse_file_info_t
|
||||
{
|
||||
/** Open flags. Available in open() and release() */
|
||||
int flags;
|
||||
|
||||
/** In case of a write operation indicates if this was caused by a
|
||||
writepage */
|
||||
uint32_t writepage : 1;
|
||||
|
||||
/** Can be filled in by open, to use direct I/O on this file.
|
||||
Introduced in version 2.4 */
|
||||
uint32_t direct_io : 1;
|
||||
|
||||
/** Can be filled in by open, to indicate, that cached file data
|
||||
need not be invalidated. Introduced in version 2.4 */
|
||||
uint32_t keep_cache : 1;
|
||||
|
||||
/** Indicates a flush operation. Set in flush operation, also
|
||||
maybe set in highlevel lock operation and lowlevel release
|
||||
operation. Introduced in version 2.6 */
|
||||
uint32_t flush : 1;
|
||||
|
||||
/** Can be filled in by open, to indicate that the file is not
|
||||
seekable. Introduced in version 2.8 */
|
||||
uint32_t nonseekable : 1;
|
||||
|
||||
/* Indicates that flock locks for this file should be
|
||||
released. If set, lock_owner shall contain a valid value.
|
||||
May only be set in ->release(). Introduced in version
|
||||
2.9 */
|
||||
uint32_t flock_release : 1;
|
||||
|
||||
/* Requests the kernel to cache entries returned by readdir */
|
||||
uint32_t cache_readdir : 1;
|
||||
|
||||
uint32_t auto_cache : 1;
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
|
||||
*
|
||||
* FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
|
||||
* FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
|
||||
* FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
|
||||
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
|
||||
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
|
||||
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
|
||||
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
|
||||
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
|
||||
*/
|
||||
#define FUSE_CAP_ASYNC_READ (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
|
||||
*
|
||||
* FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
|
||||
* FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
|
||||
* FUSE_IOCTL_RETRY: retry with new iovecs
|
||||
* FUSE_IOCTL_DIR: is a directory
|
||||
*
|
||||
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
||||
*/
|
||||
#define FUSE_IOCTL_COMPAT (1 << 0)
|
||||
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
|
||||
#define FUSE_IOCTL_RETRY (1 << 2)
|
||||
#define FUSE_IOCTL_DIR (1 << 4)
|
||||
|
||||
#define FUSE_IOCTL_MAX_IOV 256
|
||||
|
||||
/**
|
||||
* Connection information, passed to the ->init() method
|
||||
*
|
||||
* Some of the elements are read-write, these can be changed to
|
||||
* indicate the value requested by the filesystem. The requested
|
||||
* value must usually be smaller than the indicated value.
|
||||
*/
|
||||
struct fuse_conn_info {
|
||||
/**
|
||||
* Major version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_major;
|
||||
|
||||
/**
|
||||
* Minor version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_minor;
|
||||
|
||||
/**
|
||||
* Maximum size of the write buffer
|
||||
*/
|
||||
unsigned max_write;
|
||||
|
||||
/**
|
||||
* Maximum readahead
|
||||
*/
|
||||
unsigned max_readahead;
|
||||
|
||||
/**
|
||||
* Capability flags, that the kernel supports
|
||||
*/
|
||||
unsigned capable;
|
||||
|
||||
/**
|
||||
* Capability flags, that the filesystem wants to enable
|
||||
*/
|
||||
unsigned want;
|
||||
|
||||
/**
|
||||
* Maximum number of backgrounded requests
|
||||
*/
|
||||
unsigned max_background;
|
||||
|
||||
/**
|
||||
* Kernel congestion threshold parameter
|
||||
*/
|
||||
unsigned congestion_threshold;
|
||||
|
||||
/**
|
||||
* Max pages
|
||||
*/
|
||||
uint16_t max_pages;
|
||||
|
||||
/**
|
||||
* For future use.
|
||||
*/
|
||||
unsigned reserved[22];
|
||||
};
|
||||
|
||||
struct fuse_session;
|
||||
struct fuse_chan;
|
||||
struct fuse_pollhandle_t;
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
|
||||
/**
|
||||
* Create a FUSE mountpoint
|
||||
*
|
||||
* Returns a control file descriptor suitable for passing to
|
||||
* fuse_new()
|
||||
*
|
||||
* @param mountpoint the mount point path
|
||||
* @param args argument vector
|
||||
* @return the communication channel on success, NULL on failure
|
||||
*/
|
||||
struct fuse_chan *fuse_mount(const char *mountpoint,
|
||||
struct fuse_args *args);
|
||||
|
||||
/**
|
||||
* Umount a FUSE mountpoint
|
||||
*
|
||||
* @param mountpoint the mount point path
|
||||
* @param ch the communication channel
|
||||
*/
|
||||
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
|
||||
|
||||
/**
|
||||
* Parse common options
|
||||
*
|
||||
* The following options are parsed:
|
||||
*
|
||||
* '-f' foreground
|
||||
* '-d' '-odebug' foreground, but keep the debug option
|
||||
* '-h' '--help' help
|
||||
* '-ho' help without header
|
||||
* '-ofsname=..' file system name, if not present, then set to the program
|
||||
* name
|
||||
*
|
||||
* All parameters may be NULL
|
||||
*
|
||||
* @param args argument vector
|
||||
* @param mountpoint the returned mountpoint, should be freed after use
|
||||
* @param foreground set to 1 if one of the relevant options is present
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int fuse_parse_cmdline(struct fuse_args *args,
|
||||
char **mountpoint,
|
||||
int *foreground);
|
||||
|
||||
/**
|
||||
* Go into the background
|
||||
*
|
||||
* @param foreground if true, stay in the foreground
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int fuse_daemonize(int foreground);
|
||||
|
||||
/**
|
||||
* Get the version of the library
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
int fuse_version(void);
|
||||
|
||||
/**
|
||||
* Destroy poll handle
|
||||
*
|
||||
* @param ph the poll handle
|
||||
*/
|
||||
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Data buffer *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Buffer flags
|
||||
*/
|
||||
enum fuse_buf_flags {
|
||||
/**
|
||||
* Buffer contains a file descriptor
|
||||
*
|
||||
* If this flag is set, the .fd field is valid, otherwise the
|
||||
* .mem fields is valid.
|
||||
*/
|
||||
FUSE_BUF_IS_FD = (1 << 1),
|
||||
|
||||
/**
|
||||
* Seek on the file descriptor
|
||||
*
|
||||
* If this flag is set then the .pos field is valid and is
|
||||
* used to seek to the given offset before performing
|
||||
* operation on file descriptor.
|
||||
*/
|
||||
FUSE_BUF_FD_SEEK = (1 << 2),
|
||||
|
||||
/**
|
||||
* Retry operation on file descriptor
|
||||
*
|
||||
* If this flag is set then retry operation on file descriptor
|
||||
* until .size bytes have been copied or an error or EOF is
|
||||
* detected.
|
||||
*/
|
||||
FUSE_BUF_FD_RETRY = (1 << 3),
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffer copy flags
|
||||
*/
|
||||
enum fuse_buf_copy_flags {
|
||||
/**
|
||||
* Don't use splice(2)
|
||||
*
|
||||
* Always fall back to using read and write instead of
|
||||
* splice(2) to copy data from one file descriptor to another.
|
||||
*
|
||||
* If this flag is not set, then only fall back if splice is
|
||||
* unavailable.
|
||||
*/
|
||||
FUSE_BUF_NO_SPLICE = (1 << 1),
|
||||
|
||||
/**
|
||||
* Force splice
|
||||
*
|
||||
* Always use splice(2) to copy data from one file descriptor
|
||||
* to another. If splice is not available, return -EINVAL.
|
||||
*/
|
||||
FUSE_BUF_FORCE_SPLICE = (1 << 2),
|
||||
|
||||
/**
|
||||
* Try to move data with splice.
|
||||
*
|
||||
* If splice is used, try to move pages from the source to the
|
||||
* destination instead of copying. See documentation of
|
||||
* SPLICE_F_MOVE in splice(2) man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_MOVE = (1 << 3),
|
||||
|
||||
/**
|
||||
* Don't block on the pipe when copying data with splice
|
||||
*
|
||||
* Makes the operations on the pipe non-blocking (if the pipe
|
||||
* is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
|
||||
* man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
|
||||
};
|
||||
|
||||
/**
|
||||
* Single data buffer
|
||||
*
|
||||
* Generic data buffer for I/O, extended attributes, etc... Data may
|
||||
* be supplied as a memory pointer or as a file descriptor
|
||||
*/
|
||||
struct fuse_buf {
|
||||
/**
|
||||
* Size of data in bytes
|
||||
*/
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* Buffer flags
|
||||
*/
|
||||
enum fuse_buf_flags flags;
|
||||
|
||||
/**
|
||||
* Memory pointer
|
||||
*
|
||||
* Used unless FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
void *mem;
|
||||
|
||||
/**
|
||||
* File descriptor
|
||||
*
|
||||
* Used if FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
int fd;
|
||||
|
||||
/**
|
||||
* File position
|
||||
*
|
||||
* Used if FUSE_BUF_FD_SEEK flag is set.
|
||||
*/
|
||||
off_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data buffer vector
|
||||
*
|
||||
* An array of data buffers, each containing a memory pointer or a
|
||||
* file descriptor.
|
||||
*
|
||||
* Allocate dynamically to add more than one buffer.
|
||||
*/
|
||||
struct fuse_bufvec {
|
||||
/**
|
||||
* Number of buffers in the array
|
||||
*/
|
||||
size_t count;
|
||||
|
||||
/**
|
||||
* Index of current buffer within the array
|
||||
*/
|
||||
size_t idx;
|
||||
|
||||
/**
|
||||
* Current offset within the current buffer
|
||||
*/
|
||||
size_t off;
|
||||
|
||||
/**
|
||||
* Array of buffers
|
||||
*/
|
||||
struct fuse_buf buf[1];
|
||||
};
|
||||
|
||||
/* Initialize bufvec with a single buffer of given size */
|
||||
#define FUSE_BUFVEC_INIT(size__) \
|
||||
((struct fuse_bufvec) { \
|
||||
/* .count= */ 1, \
|
||||
/* .idx = */ 0, \
|
||||
/* .off = */ 0, \
|
||||
/* .buf = */ { /* [0] = */ { \
|
||||
/* .size = */ (size__), \
|
||||
/* .flags = */ (enum fuse_buf_flags) 0, \
|
||||
/* .mem = */ NULL, \
|
||||
/* .fd = */ -1, \
|
||||
/* .pos = */ 0, \
|
||||
} } \
|
||||
} )
|
||||
|
||||
/**
|
||||
* Get total size of data in a fuse buffer vector
|
||||
*
|
||||
* @param bufv buffer vector
|
||||
* @return size of data
|
||||
*/
|
||||
size_t fuse_buf_size(const struct fuse_bufvec *bufv);
|
||||
|
||||
/**
|
||||
* Copy data from one buffer vector to another
|
||||
*
|
||||
* @param dst destination buffer vector
|
||||
* @param src source buffer vector
|
||||
* @param flags flags controlling the copy
|
||||
* @return actual number of bytes copied or -errno on error
|
||||
*/
|
||||
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
|
||||
enum fuse_buf_copy_flags flags);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Signal handling *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Exit session on HUP, TERM and INT signals and ignore PIPE signal
|
||||
*
|
||||
* Stores session in a global variable. May only be called once per
|
||||
* process until fuse_remove_signal_handlers() is called.
|
||||
*
|
||||
* @param se the session to exit
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int fuse_set_signal_handlers(struct fuse_session *se);
|
||||
|
||||
/**
|
||||
* Restore default signal handlers
|
||||
*
|
||||
* Resets global session. After this fuse_set_signal_handlers() may
|
||||
* be called again.
|
||||
*
|
||||
* @param se the same session as given in fuse_set_signal_handlers()
|
||||
*/
|
||||
void fuse_remove_signal_handlers(struct fuse_session *se);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_COMMON_H_ */
|
||||
@@ -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;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_msgbuf_t fuse_msgbuf_t;
|
||||
struct fuse_msgbuf_t
|
||||
{
|
||||
char *mem;
|
||||
uint32_t size;
|
||||
uint32_t used;
|
||||
};
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_OPT_H_
|
||||
#define _FUSE_OPT_H_
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
* This file defines the option parsing interface of FUSE
|
||||
*/
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Option description
|
||||
*
|
||||
* This structure describes a single option, and action associated
|
||||
* with it, in case it matches.
|
||||
*
|
||||
* More than one such match may occur, in which case the action for
|
||||
* each match is executed.
|
||||
*
|
||||
* There are three possible actions in case of a match:
|
||||
*
|
||||
* i) An integer (int or unsigned) variable determined by 'offset' is
|
||||
* set to 'value'
|
||||
*
|
||||
* ii) The processing function is called, with 'value' as the key
|
||||
*
|
||||
* iii) An integer (any) or string (char *) variable determined by
|
||||
* 'offset' is set to the value of an option parameter
|
||||
*
|
||||
* 'offset' should normally be either set to
|
||||
*
|
||||
* - 'offsetof(struct foo, member)' actions i) and iii)
|
||||
*
|
||||
* - -1 action ii)
|
||||
*
|
||||
* The 'offsetof()' macro is defined in the <stddef.h> header.
|
||||
*
|
||||
* The template determines which options match, and also have an
|
||||
* effect on the action. Normally the action is either i) or ii), but
|
||||
* if a format is present in the template, then action iii) is
|
||||
* performed.
|
||||
*
|
||||
* The types of templates are:
|
||||
*
|
||||
* 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
|
||||
* themselves. Invalid values are "--" and anything beginning
|
||||
* with "-o"
|
||||
*
|
||||
* 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or
|
||||
* the relevant option in a comma separated option list
|
||||
*
|
||||
* 3) "bar=", "--foo=", etc. These are variations of 1) and 2)
|
||||
* which have a parameter
|
||||
*
|
||||
* 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform
|
||||
* action iii).
|
||||
*
|
||||
* 5) "-x ", etc. Matches either "-xparam" or "-x param" as
|
||||
* two separate arguments
|
||||
*
|
||||
* 6) "-x %s", etc. Combination of 4) and 5)
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Key option. In case of a match, the processing function will be
|
||||
* called with the specified key.
|
||||
*/
|
||||
#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
|
||||
|
||||
/**
|
||||
* Last option. An array of 'struct fuse_opt' must end with a NULL
|
||||
* template value
|
||||
*/
|
||||
#define FUSE_OPT_END { NULL, 0, 0 }
|
||||
|
||||
/**
|
||||
* Argument list
|
||||
*/
|
||||
struct fuse_args
|
||||
{
|
||||
/** Argument count */
|
||||
int argc;
|
||||
|
||||
/** Argument vector. NULL terminated */
|
||||
char **argv;
|
||||
|
||||
/** Is 'argv' allocated? */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializer for 'struct fuse_args'
|
||||
*/
|
||||
#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function if an option did not
|
||||
* match any template
|
||||
*/
|
||||
#define FUSE_OPT_KEY_OPT -1
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function for all non-options
|
||||
*
|
||||
* Non-options are the arguments beginning with a character other than
|
||||
* '-' or all arguments after the special '--' option
|
||||
*/
|
||||
#define FUSE_OPT_KEY_NONOPT -2
|
||||
|
||||
/**
|
||||
* Special key value for options to keep
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned 1
|
||||
*/
|
||||
#define FUSE_OPT_KEY_KEEP -3
|
||||
|
||||
/**
|
||||
* Special key value for options to discard
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned zero
|
||||
*/
|
||||
#define FUSE_OPT_KEY_DISCARD -4
|
||||
|
||||
/**
|
||||
* Processing function
|
||||
*
|
||||
* This function is called if
|
||||
* - option did not match any 'struct fuse_opt'
|
||||
* - argument is a non-option
|
||||
* - option did match and offset was set to -1
|
||||
*
|
||||
* The 'arg' parameter will always contain the whole argument or
|
||||
* option including the parameter if exists. A two-argument option
|
||||
* ("-x foo") is always converted to single argument option of the
|
||||
* form "-xfoo" before this function is called.
|
||||
*
|
||||
* Options of the form '-ofoo' are passed to this function without the
|
||||
* '-o' prefix.
|
||||
*
|
||||
* The return value of this function determines whether this argument
|
||||
* is to be inserted into the output argument vector, or discarded.
|
||||
*
|
||||
* @param data is the user data passed to the fuse_opt_parse() function
|
||||
* @param arg is the whole argument or option
|
||||
* @param key determines why the processing function was called
|
||||
* @param outargs the current output argument list
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Option parsing function
|
||||
*
|
||||
* If 'args' was returned from a previous call to fuse_opt_parse() or
|
||||
* it was constructed from
|
||||
*
|
||||
* A NULL 'args' is equivalent to an empty argument vector
|
||||
*
|
||||
* A NULL 'opts' is equivalent to an 'opts' array containing a single
|
||||
* end marker
|
||||
*
|
||||
* A NULL 'proc' is equivalent to a processing function always
|
||||
* returning '1'
|
||||
*
|
||||
* @param args is the input and output argument list
|
||||
* @param data is the user data
|
||||
* @param opts is the option description array
|
||||
* @param proc is the processing function
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Add an option to a comma separated option list
|
||||
*
|
||||
* @param opts is a pointer to an option list, may point to a NULL value
|
||||
* @param opt is the option to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_opt(char **opts, const char *opt);
|
||||
|
||||
/**
|
||||
* Add an option, escaping commas, to a comma separated option list
|
||||
*
|
||||
* @param opts is a pointer to an option list, may point to a NULL value
|
||||
* @param opt is the option to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_opt_escaped(char **opts, const char *opt);
|
||||
|
||||
/**
|
||||
* Add an argument to a NULL terminated argument vector
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
|
||||
|
||||
/**
|
||||
* Add an argument at the specified position in a NULL terminated
|
||||
* argument vector
|
||||
*
|
||||
* Adds the argument to the N-th position. This is useful for adding
|
||||
* options at the beginning of the array which must not come after the
|
||||
* special '--' option.
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param pos is the position at which to add the argument
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
|
||||
|
||||
/**
|
||||
* Free the contents of argument list
|
||||
*
|
||||
* The structure itself is not freed
|
||||
*
|
||||
* @param args is the structure containing the argument list
|
||||
*/
|
||||
void fuse_opt_free_args(struct fuse_args *args);
|
||||
|
||||
|
||||
/**
|
||||
* Check if an option matches
|
||||
*
|
||||
* @param opts is the option description array
|
||||
* @param opt is the option to match
|
||||
* @return 1 if a match is found, 0 if not
|
||||
*/
|
||||
int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
|
||||
|
||||
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[];
|
||||
};
|
||||
@@ -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
|
||||
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
|
||||
template<typename T>
|
||||
class BoundedQueue
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
BoundedQueue(std::size_t max_size_,
|
||||
bool block_ = true)
|
||||
: _block(block_),
|
||||
_max_size(max_size_)
|
||||
{
|
||||
}
|
||||
|
||||
BoundedQueue(const BoundedQueue&) = delete;
|
||||
BoundedQueue(BoundedQueue&&) = default;
|
||||
|
||||
bool
|
||||
push(const T& item_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(_queue_lock);
|
||||
|
||||
_condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
|
||||
|
||||
if(_queue.size() == _max_size)
|
||||
return false;
|
||||
|
||||
_queue.push(item_);
|
||||
}
|
||||
|
||||
_condition_pop.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
push(T&& item_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(_queue_lock);
|
||||
|
||||
_condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
|
||||
|
||||
if(_queue.size() == _max_size)
|
||||
return false;
|
||||
|
||||
_queue.push(std::move(item_));
|
||||
}
|
||||
_condition_pop.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool
|
||||
emplace(Args&&... args_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(_queue_lock);
|
||||
|
||||
_condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
|
||||
|
||||
if(_queue.size() == _max_size)
|
||||
return false;
|
||||
|
||||
_queue.emplace(std::forward<Args>(args_)...);
|
||||
}
|
||||
|
||||
_condition_pop.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
pop(T& item_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(_queue_lock);
|
||||
|
||||
_condition_pop.wait(guard, [&]() { return !_queue.empty() || !_block; });
|
||||
if(_queue.empty())
|
||||
return false;
|
||||
|
||||
item_ = std::move(_queue.front());
|
||||
|
||||
_queue.pop();
|
||||
}
|
||||
|
||||
_condition_push.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return _max_size;
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
full() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_queue_lock);
|
||||
|
||||
return (_queue.size() == capacity());
|
||||
}
|
||||
|
||||
void
|
||||
block()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_block = true;
|
||||
}
|
||||
|
||||
void
|
||||
unblock()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_block = false;
|
||||
}
|
||||
|
||||
_condition_push.notify_all();
|
||||
_condition_pop.notify_all();
|
||||
}
|
||||
|
||||
bool
|
||||
blocking() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _block;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex _queue_lock;
|
||||
|
||||
private:
|
||||
bool _block;
|
||||
std::queue<T> _queue;
|
||||
const std::size_t _max_size;
|
||||
std::condition_variable _condition_push;
|
||||
std::condition_variable _condition_pop;
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include "bounded_queue.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
class BoundedThreadPool
|
||||
{
|
||||
private:
|
||||
using Proc = std::function<void(void)>;
|
||||
using Queue = BoundedQueue<Proc>;
|
||||
using Queues = std::vector<std::shared_ptr<Queue>>;
|
||||
|
||||
public:
|
||||
explicit
|
||||
BoundedThreadPool(const std::size_t thread_count_ = std::thread::hardware_concurrency())
|
||||
: _queues(),
|
||||
_count(thread_count_)
|
||||
{
|
||||
printf("threads: %d\n",thread_count_);
|
||||
for(std::size_t i = 0; i < thread_count_; i++)
|
||||
_queues.emplace_back(std::make_shared<Queue>(1));
|
||||
|
||||
auto worker = [this](std::size_t i)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
Proc f;
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count]->pop(f))
|
||||
break;
|
||||
}
|
||||
|
||||
if(!f && !_queues[i]->pop(f))
|
||||
break;
|
||||
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
_threads.reserve(thread_count_);
|
||||
for(std::size_t i = 0; i < thread_count_; ++i)
|
||||
_threads.emplace_back(worker, i);
|
||||
}
|
||||
|
||||
~BoundedThreadPool()
|
||||
{
|
||||
for(auto& queue : _queues)
|
||||
queue->unblock();
|
||||
for(auto& thread : _threads)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void
|
||||
enqueue_work(F&& f_)
|
||||
{
|
||||
auto i = _index++;
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count]->push(f_))
|
||||
return;
|
||||
}
|
||||
|
||||
_queues[i % _count]->push(std::move(f_));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
[[nodiscard]]
|
||||
std::future<typename std::result_of<F()>::type>
|
||||
enqueue_task(F&& f_)
|
||||
{
|
||||
using TaskReturnType = typename std::result_of<F()>::type;
|
||||
using Promise = std::promise<TaskReturnType>;
|
||||
|
||||
auto i = _index++;
|
||||
auto promise = std::make_shared<Promise>();
|
||||
auto future = promise->get_future();
|
||||
auto work = [=]() {
|
||||
auto rv = f_();
|
||||
promise->set_value(rv);
|
||||
};
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count]->push(work))
|
||||
return future;
|
||||
}
|
||||
|
||||
_queues[i % _count]->push(std::move(work));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<pthread_t>
|
||||
threads()
|
||||
{
|
||||
std::vector<pthread_t> rv;
|
||||
|
||||
for(auto &thread : _threads)
|
||||
rv.push_back(thread.native_handle());
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
Queues _queues;
|
||||
|
||||
private:
|
||||
std::vector<std::thread> _threads;
|
||||
|
||||
private:
|
||||
const std::size_t _count;
|
||||
std::atomic_uint _index;
|
||||
|
||||
static const unsigned int K = 2;
|
||||
};
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t min_size(size_t s1, size_t 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;
|
||||
|
||||
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;
|
||||
|
||||
src_off += res;
|
||||
dst_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
tmp.mem = buf;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (res < this_len)
|
||||
break;
|
||||
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SPLICE
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
len -= res;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
|
||||
enum fuse_buf_copy_flags flags)
|
||||
{
|
||||
size_t copied = 0;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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 (res < len)
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
+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
|
||||
+4044
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#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
|
||||
{
|
||||
int (*receive_buf)(struct fuse_session *se,
|
||||
fuse_msgbuf_t *msgbuf);
|
||||
|
||||
void (*process_buf)(struct fuse_session *se,
|
||||
const fuse_msgbuf_t *msgbuf);
|
||||
|
||||
void (*destroy)(void *data);
|
||||
|
||||
struct fuse_ll *f;
|
||||
volatile int exited;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
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,
|
||||
uint64_t,
|
||||
const void *);
|
||||
struct fuse_notify_req *next;
|
||||
struct fuse_notify_req *prev;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
int fuse_chan_clearfd(struct fuse_chan *ch);
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd);
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
|
||||
|
||||
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
|
||||
int count);
|
||||
void fuse_free_req(fuse_req_t req);
|
||||
|
||||
|
||||
struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *fd);
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -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);
|
||||
@@ -0,0 +1,487 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "bounded_thread_pool.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_misc.h"
|
||||
|
||||
#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>
|
||||
|
||||
|
||||
struct fuse_worker_data_t
|
||||
{
|
||||
struct fuse_session *se;
|
||||
sem_t finished;
|
||||
std::function<void(fuse_worker_data_t*,fuse_msgbuf_t*)> msgbuf_processor;
|
||||
std::shared_ptr<BoundedThreadPool> tp;
|
||||
};
|
||||
|
||||
class WorkerCleanup
|
||||
{
|
||||
public:
|
||||
WorkerCleanup(fuse_worker_data_t *wd_)
|
||||
: _wd(wd_)
|
||||
{
|
||||
}
|
||||
|
||||
~WorkerCleanup()
|
||||
{
|
||||
fuse_session_exit(_wd->se);
|
||||
sem_post(&_wd->finished);
|
||||
}
|
||||
|
||||
private:
|
||||
fuse_worker_data_t *_wd;
|
||||
};
|
||||
|
||||
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_);
|
||||
|
||||
fprintf(stderr,
|
||||
"mergerfs: error reading from /dev/fuse - %s (%d)\n",
|
||||
strerror(-rv_),
|
||||
-rv_);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_do_work(void *data)
|
||||
{
|
||||
fuse_worker_data_t *wd = (fuse_worker_data_t*)data;
|
||||
fuse_session *se = wd->se;
|
||||
auto &process_msgbuf = wd->msgbuf_processor;
|
||||
WorkerCleanup workercleanup(wd);
|
||||
|
||||
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 NULL;
|
||||
if(retriable_receive_error(rv))
|
||||
continue;
|
||||
if(fatal_receive_error(rv))
|
||||
return handle_receive_error(rv,msgbuf);
|
||||
} while(false);
|
||||
|
||||
process_msgbuf(wd,msgbuf);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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_)
|
||||
{
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
process_msgbuf_sync(fuse_worker_data_t *wd_,
|
||||
fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
wd_->se->process_buf(wd_->se,msgbuf_);
|
||||
msgbuf_free(msgbuf_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
process_msgbuf_async(fuse_worker_data_t *wd_,
|
||||
fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
const auto func = [=] {
|
||||
process_msgbuf_sync(wd_,msgbuf_);
|
||||
};
|
||||
|
||||
wd_->tp->enqueue_work(func);
|
||||
}
|
||||
|
||||
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_ == "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_);
|
||||
}
|
||||
|
||||
int
|
||||
fuse_session_loop_mt(struct fuse_session *se_,
|
||||
const int raw_read_thread_count_,
|
||||
const int raw_process_thread_count_,
|
||||
const char *pin_threads_type_)
|
||||
{
|
||||
int err;
|
||||
int read_thread_count;
|
||||
int process_thread_count;
|
||||
fuse_worker_data_t wd = {0};
|
||||
std::vector<pthread_t> read_threads;
|
||||
std::vector<pthread_t> process_threads;
|
||||
|
||||
read_thread_count = raw_read_thread_count_;
|
||||
process_thread_count = raw_process_thread_count_;
|
||||
::calculate_thread_counts(&read_thread_count,&process_thread_count);
|
||||
|
||||
if(process_thread_count > 0)
|
||||
{
|
||||
wd.tp = std::make_shared<BoundedThreadPool>(process_thread_count);
|
||||
wd.msgbuf_processor = process_msgbuf_async;
|
||||
process_threads = wd.tp->threads();
|
||||
}
|
||||
else
|
||||
{
|
||||
wd.msgbuf_processor = process_msgbuf_sync;
|
||||
}
|
||||
|
||||
wd.se = se_;
|
||||
sem_init(&wd.finished,0,0);
|
||||
|
||||
err = 0;
|
||||
for(int i = 0; i < read_thread_count; i++)
|
||||
{
|
||||
pthread_t thread_id;
|
||||
err = fuse_start_thread(&thread_id,fuse_do_work,&wd);
|
||||
assert(err == 0);
|
||||
read_threads.push_back(thread_id);
|
||||
}
|
||||
|
||||
if(pin_threads_type_ != NULL)
|
||||
::pin_threads(read_threads,process_threads,pin_threads_type_);
|
||||
|
||||
if(!err)
|
||||
{
|
||||
/* sem_wait() is interruptible */
|
||||
while(!fuse_session_exited(se_))
|
||||
sem_wait(&wd.finished);
|
||||
|
||||
for(const auto &thread_id : read_threads)
|
||||
pthread_cancel(thread_id);
|
||||
|
||||
for(const auto &thread_id : read_threads)
|
||||
pthread_join(thread_id,NULL);
|
||||
}
|
||||
|
||||
sem_destroy(&wd.finished);
|
||||
|
||||
return err;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#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)
|
||||
*/
|
||||
#if (!defined(__UCLIBC__) && !defined(__APPLE__))
|
||||
#define FUSE_SYMVER(x) __asm__(x)
|
||||
#else
|
||||
#define FUSE_SYMVER(x)
|
||||
#endif
|
||||
|
||||
#ifndef USE_UCLIBC
|
||||
#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
|
||||
#else
|
||||
/* 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);
|
||||
}
|
||||
#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_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_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_CTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
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 <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
static std::uint32_t g_PAGESIZE = 0;
|
||||
static std::uint32_t g_BUFSIZE = 0;
|
||||
static std::uint32_t g_MAX_ALLOCS = 128;
|
||||
|
||||
static std::mutex g_MUTEX;
|
||||
static std::vector<fuse_msgbuf_t*> g_MSGBUF_STACK;
|
||||
static std::unordered_set<fuse_msgbuf_t*> g_MSGBUF_ALLOCED;
|
||||
|
||||
static
|
||||
__attribute__((constructor))
|
||||
void
|
||||
msgbuf_constructor()
|
||||
{
|
||||
g_PAGESIZE = sysconf(_SC_PAGESIZE);
|
||||
// +2 because to do O_DIRECT we need to offset the buffer to align
|
||||
g_BUFSIZE = (g_PAGESIZE * (FUSE_MAX_MAX_PAGES + 2));
|
||||
}
|
||||
|
||||
static
|
||||
__attribute__((destructor))
|
||||
void
|
||||
msgbuf_destroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32_t
|
||||
msgbuf_get_bufsize()
|
||||
{
|
||||
return g_BUFSIZE;
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_set_bufsize(const uint32_t size_in_pages_)
|
||||
{
|
||||
g_BUFSIZE = (size_in_pages_ * g_PAGESIZE);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc()
|
||||
{
|
||||
fuse_msgbuf_t *msgbuf;
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
|
||||
if(g_MSGBUF_STACK.empty())
|
||||
{
|
||||
msgbuf = (fuse_msgbuf_t*)malloc(sizeof(fuse_msgbuf_t));
|
||||
if(msgbuf == NULL)
|
||||
return NULL;
|
||||
|
||||
msgbuf->mem = (char*)page_aligned_malloc(g_BUFSIZE);
|
||||
if(msgbuf->mem == NULL)
|
||||
{
|
||||
free(msgbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msgbuf->size = g_BUFSIZE;
|
||||
|
||||
g_MSGBUF_ALLOCED.emplace(msgbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgbuf = g_MSGBUF_STACK.back();
|
||||
g_MSGBUF_STACK.pop_back();
|
||||
}
|
||||
|
||||
return msgbuf;
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_free(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
|
||||
if(msgbuf_->size != g_BUFSIZE)
|
||||
{
|
||||
g_MSGBUF_ALLOCED.erase(msgbuf_);
|
||||
free(msgbuf_->mem);
|
||||
free(msgbuf_);
|
||||
return;
|
||||
}
|
||||
|
||||
g_MSGBUF_STACK.emplace_back(msgbuf_);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
msgbuf_alloc_count()
|
||||
{
|
||||
return g_MSGBUF_ALLOCED.size();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
msgbuf_avail_count()
|
||||
{
|
||||
return g_MSGBUF_STACK.size();
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_gc()
|
||||
{
|
||||
std::vector<fuse_msgbuf_t*> oldstack;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(g_MUTEX);
|
||||
oldstack.swap(g_MSGBUF_STACK);
|
||||
}
|
||||
|
||||
fprintf(stderr,"freeing %lu msgbufs\n",oldstack.size());
|
||||
for(auto msgbuf: oldstack)
|
||||
{
|
||||
g_MSGBUF_ALLOCED.erase(msgbuf);
|
||||
free(msgbuf->mem);
|
||||
free(msgbuf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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);
|
||||
uint32_t msgbuf_get_bufsize();
|
||||
|
||||
fuse_msgbuf_t* msgbuf_alloc();
|
||||
void msgbuf_free(fuse_msgbuf_t *msgbuf);
|
||||
|
||||
void msgbuf_gc();
|
||||
|
||||
uint64_t msgbuf_alloc_count();
|
||||
uint64_t msgbuf_avail_count();
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
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_read_thread_count(f),
|
||||
fuse_config_process_thread_count(f),
|
||||
fuse_config_pin_threads(f));
|
||||
|
||||
fuse_stop_maintenance_thread(f);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
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_opt.h"
|
||||
#include "fuse_misc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
alloc_failed(void)
|
||||
{
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
||||
{
|
||||
char **newargv;
|
||||
char *newarg;
|
||||
|
||||
assert(!args->argv || args->allocated);
|
||||
|
||||
newarg = strdup(arg);
|
||||
if (!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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int add_arg(struct fuse_opt_context *ctx, const char *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);
|
||||
|
||||
if (!d)
|
||||
return alloc_failed();
|
||||
|
||||
*opts = d;
|
||||
if (oldlen) {
|
||||
d += oldlen;
|
||||
*d++ = ',';
|
||||
}
|
||||
|
||||
for (; *opt; opt++) {
|
||||
if (esc && (*opt == ',' || *opt == '\\'))
|
||||
*d++ = '\\';
|
||||
*d++ = *opt;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_add_opt(char **opts, const char *opt)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
|
||||
{
|
||||
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();
|
||||
|
||||
*(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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
|
||||
{
|
||||
char *s = opts;
|
||||
char *d = s;
|
||||
int end = 0;
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_option_group(struct fuse_opt_context *ctx, const char *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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
if (!args || !args->argv || !args->argc)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
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_kernel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
|
||||
struct fuse_chan
|
||||
{
|
||||
struct fuse_session *se;
|
||||
int fd;
|
||||
size_t bufsize;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
memset(se, 0, sizeof(*se));
|
||||
se->f = data;
|
||||
se->receive_buf = receive_buf;
|
||||
se->process_buf = process_buf;
|
||||
se->destroy = destroy;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fuse_session_destroy(struct fuse_session *se)
|
||||
{
|
||||
se->destroy(se->f);
|
||||
if(se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
}
|
||||
|
||||
void fuse_session_reset(struct fuse_session *se)
|
||||
{
|
||||
se->exited = 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_session_exited(struct fuse_session *se)
|
||||
{
|
||||
return se->exited;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_session_exit(struct fuse_session *se_)
|
||||
{
|
||||
se_->exited = 1;
|
||||
}
|
||||
|
||||
void*
|
||||
fuse_session_data(struct fuse_session *se)
|
||||
{
|
||||
return se->f;
|
||||
}
|
||||
|
||||
struct fuse_chan *
|
||||
fuse_chan_new(int fd,
|
||||
size_t bufsize)
|
||||
{
|
||||
struct fuse_chan *ch;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int fuse_chan_clearfd(struct fuse_chan *ch)
|
||||
{
|
||||
int fd = ch->fd;
|
||||
ch->fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
size_t fuse_chan_bufsize(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->bufsize;
|
||||
}
|
||||
|
||||
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->se;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fuse_session_remove_chan(ch);
|
||||
|
||||
fd = fuse_chan_fd(ch);
|
||||
if(fd != -1)
|
||||
close(fd);
|
||||
|
||||
free(ch);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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 <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static struct fuse_session *fuse_instance;
|
||||
|
||||
static void exit_handler(int sig)
|
||||
{
|
||||
(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;
|
||||
|
||||
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 (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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
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 "config.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
enum {
|
||||
KEY_HELP,
|
||||
KEY_HELP_NOHEADER,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
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("-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");
|
||||
}
|
||||
|
||||
static void helper_help(void)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct helper_opts *hopts = data;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
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++;
|
||||
|
||||
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 *foreground_)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
if (setsid() == -1) {
|
||||
perror("fuse_daemonize: setsid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(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);
|
||||
}
|
||||
|
||||
/* 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_)
|
||||
{
|
||||
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);
|
||||
|
||||
fd = fuse_kern_mount(mountpoint_,args_);
|
||||
if(fd == -1)
|
||||
return NULL;
|
||||
|
||||
pagesize = sysconf(_SC_PAGESIZE);
|
||||
bufsize = ((FUSE_MAX_MAX_PAGES + 1) * pagesize);
|
||||
|
||||
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_)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void fuse_unmount(const char *mountpoint, struct fuse_chan *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 *fd)
|
||||
{
|
||||
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, &foreground);
|
||||
if (res == -1)
|
||||
return NULL;
|
||||
|
||||
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);
|
||||
fuse_opt_free_args(&args);
|
||||
if (fuse == NULL)
|
||||
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;
|
||||
|
||||
if (fd)
|
||||
*fd = fuse_chan_fd(ch);
|
||||
|
||||
return fuse;
|
||||
|
||||
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)
|
||||
{
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
static int fuse_main_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size)
|
||||
{
|
||||
struct fuse *fuse;
|
||||
char *mountpoint;
|
||||
int res;
|
||||
|
||||
fuse = fuse_setup_common(argc, argv, op, op_size,
|
||||
&mountpoint,
|
||||
NULL);
|
||||
if (fuse == NULL)
|
||||
return 1;
|
||||
|
||||
res = fuse_loop_mt(fuse);
|
||||
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
if (res == -1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#undef fuse_main
|
||||
int fuse_main(void);
|
||||
int fuse_main(void)
|
||||
{
|
||||
fprintf(stderr, "fuse_main(): This function does not exist\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_version(void)
|
||||
{
|
||||
return FUSE_VERSION;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
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
|
||||
@@ -14,17 +16,9 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __FLUSH_HPP__
|
||||
#define __FLUSH_HPP__
|
||||
|
||||
namespace mergerfs
|
||||
{
|
||||
namespace fuse
|
||||
{
|
||||
int
|
||||
flush(const char *path,
|
||||
fuse_file_info *fi);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__
|
||||
# include "mount_bsd.c"
|
||||
#else
|
||||
# include "mount_generic.c"
|
||||
# include "mount_util.c"
|
||||
#endif
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.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_opt.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <paths.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define FUSERMOUNT_PROG "mount_fusefs"
|
||||
#define FUSE_DEV_TRUNK "/dev/fuse"
|
||||
|
||||
enum {
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
KEY_KERN
|
||||
};
|
||||
|
||||
struct mount_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)
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
{ "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)
|
||||
{
|
||||
system(FUSERMOUNT_PROG " --help");
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void mount_version(void)
|
||||
{
|
||||
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;
|
||||
|
||||
switch (key) {
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
|
||||
case KEY_KERN:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
|
||||
|
||||
argv[0] = umount_cmd;
|
||||
argv[1] = "-f";
|
||||
argv[2] = device_path;
|
||||
argv[3] = NULL;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
return;
|
||||
|
||||
if (pid == 0) {
|
||||
close(fd);
|
||||
execvp(umount_cmd, (char **)argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd)
|
||||
{
|
||||
char *ep, dev[128];
|
||||
struct stat sbuf;
|
||||
|
||||
(void)mountpoint;
|
||||
|
||||
if (fstat(fd, &sbuf) == -1)
|
||||
goto out;
|
||||
|
||||
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
|
||||
|
||||
if (strncmp(dev, "fuse", 4))
|
||||
goto out;
|
||||
|
||||
strtol(dev + 4, &ep, 10);
|
||||
if (*ep != '\0')
|
||||
goto out;
|
||||
|
||||
do_unmount(dev, fd);
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Check if kernel is doing init in background */
|
||||
static int init_backgrounded(void)
|
||||
{
|
||||
unsigned ibg, len;
|
||||
|
||||
len = sizeof(ibg);
|
||||
|
||||
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
|
||||
return 0;
|
||||
|
||||
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;
|
||||
|
||||
fdnam = getenv("FUSE_DEV_FD");
|
||||
|
||||
if (fdnam) {
|
||||
char *ep;
|
||||
|
||||
fd = strtol(fdnam, &ep, 10);
|
||||
|
||||
if (*ep != '\0') {
|
||||
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
goto mount;
|
||||
}
|
||||
|
||||
dev = getenv("FUSE_DEV_NAME");
|
||||
|
||||
if (! dev)
|
||||
dev = (char *)FUSE_DEV_TRUNK;
|
||||
|
||||
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;
|
||||
|
||||
pid = fork();
|
||||
cpid = pid;
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
|
||||
perror("fuse: failed to mount file system");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
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);
|
||||
|
||||
if (args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
if (mo.ishelp)
|
||||
return 0;
|
||||
|
||||
res = fuse_mount_core(mountpoint, mo.kernel_opts);
|
||||
out:
|
||||
free(mo.kernel_opts);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
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 "config.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "mount_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#include <perfuse.h>
|
||||
|
||||
#define MS_RDONLY MNT_RDONLY
|
||||
#define MS_NOSUID MNT_NOSUID
|
||||
#define MS_NODEV MNT_NODEV
|
||||
#define MS_NOEXEC MNT_NOEXEC
|
||||
#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
|
||||
#define MS_NOATIME MNT_NOATIME
|
||||
|
||||
#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
|
||||
#endif
|
||||
|
||||
#define FUSERMOUNT_PROG "fusermount"
|
||||
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
|
||||
|
||||
#ifndef HAVE_FORK
|
||||
#define fork() vfork()
|
||||
#endif
|
||||
|
||||
#ifndef MS_DIRSYNC
|
||||
#define MS_DIRSYNC 128
|
||||
#endif
|
||||
|
||||
enum {
|
||||
KEY_KERN_FLAG,
|
||||
KEY_KERN_OPT,
|
||||
KEY_FUSERMOUNT_OPT,
|
||||
KEY_SUBTYPE_OPT,
|
||||
KEY_MTAB_OPT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
struct mount_opts {
|
||||
int ishelp;
|
||||
int flags;
|
||||
int auto_unmount;
|
||||
int blkdev;
|
||||
char *fsname;
|
||||
char *subtype;
|
||||
char *subtype_opt;
|
||||
char *mtab_opts;
|
||||
char *fusermount_opts;
|
||||
char *kernel_opts;
|
||||
};
|
||||
|
||||
#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
FUSE_MOUNT_OPT("blkdev", blkdev),
|
||||
FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
|
||||
FUSE_MOUNT_OPT("fsname=%s", fsname),
|
||||
FUSE_MOUNT_OPT("subtype=%s", subtype),
|
||||
FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("context=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
|
||||
FUSE_OPT_KEY("-r", KEY_RO),
|
||||
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o auto_unmount auto unmount on process termination\n"
|
||||
" -o default_permissions enable permission checking by kernel\n"
|
||||
" -o fsname=NAME set filesystem name\n"
|
||||
" -o subtype=NAME set filesystem type\n"
|
||||
" -o large_read issue large read requests (2.4 only)\n"
|
||||
" -o max_read=N set maximum size of read requests\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void exec_fusermount(const char *argv[])
|
||||
{
|
||||
execv(FUSERMOUNT_DIR "/mergerfs-" FUSERMOUNT_PROG, (char **) argv);
|
||||
execvp("mergerfs-" FUSERMOUNT_PROG, (char **) argv);
|
||||
execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
|
||||
execvp(FUSERMOUNT_PROG, (char **) argv);
|
||||
}
|
||||
|
||||
static void mount_version(void)
|
||||
{
|
||||
int pid = fork();
|
||||
if (!pid) {
|
||||
const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
|
||||
exec_fusermount(argv);
|
||||
_exit(1);
|
||||
} else if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
};
|
||||
|
||||
static const struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0},
|
||||
{"ro", MS_RDONLY, 1},
|
||||
{"suid", MS_NOSUID, 0},
|
||||
{"nosuid", MS_NOSUID, 1},
|
||||
{"dev", MS_NODEV, 0},
|
||||
{"nodev", MS_NODEV, 1},
|
||||
{"exec", MS_NOEXEC, 0},
|
||||
{"noexec", MS_NOEXEC, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0},
|
||||
{"sync", MS_SYNCHRONOUS, 1},
|
||||
{"atime", MS_NOATIME, 0},
|
||||
{"noatime", MS_NOATIME, 1},
|
||||
#ifndef __NetBSD__
|
||||
{"dirsync", MS_DIRSYNC, 1},
|
||||
#endif
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static void set_mount_flag(const char *s, int *flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strcmp(opt, s) == 0) {
|
||||
if (mount_flags[i].on)
|
||||
*flags |= mount_flags[i].flag;
|
||||
else
|
||||
*flags &= ~mount_flags[i].flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "fuse: internal error, can't find mount flag\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct mount_opts *mo = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
case KEY_KERN_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
|
||||
|
||||
case KEY_SUBTYPE_OPT:
|
||||
return fuse_opt_add_opt(&mo->subtype_opt, arg);
|
||||
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* return value:
|
||||
* >= 0 => fd
|
||||
* -1 => error
|
||||
*/
|
||||
static int receive_fd(int fd)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char buf[1];
|
||||
int rv;
|
||||
size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = 0;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
/* old BSD implementations should use msg_accrights instead of
|
||||
* msg_control; the interface is different. */
|
||||
msg.msg_control = ccmsg;
|
||||
msg.msg_controllen = sizeof(ccmsg);
|
||||
|
||||
while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
|
||||
if (rv == -1) {
|
||||
perror("recvmsg");
|
||||
return -1;
|
||||
}
|
||||
if(!rv) {
|
||||
/* EOF */
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr, "got control message of unknown type %d\n",
|
||||
cmsg->cmsg_type);
|
||||
return -1;
|
||||
}
|
||||
return *(int*)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd)
|
||||
{
|
||||
int res;
|
||||
int pid;
|
||||
|
||||
if (!mountpoint)
|
||||
return;
|
||||
|
||||
if (fd != -1) {
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = fd;
|
||||
pfd.events = 0;
|
||||
res = poll(&pfd, 1, 0);
|
||||
|
||||
/* Need to close file descriptor, otherwise synchronous umount
|
||||
would recurse into filesystem, and deadlock.
|
||||
|
||||
Caller expects fuse_kern_unmount to close the fd, so close it
|
||||
anyway. */
|
||||
close(fd);
|
||||
|
||||
/* If file poll returns POLLERR on the device file descriptor,
|
||||
then the filesystem is already unmounted */
|
||||
if (res == 1 && (pfd.revents & POLLERR))
|
||||
return;
|
||||
}
|
||||
|
||||
if (geteuid() == 0) {
|
||||
fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
res = umount2(mountpoint, 2);
|
||||
if (res == 0)
|
||||
return;
|
||||
|
||||
pid = fork();
|
||||
if(pid == -1)
|
||||
return;
|
||||
|
||||
if(pid == 0) {
|
||||
const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
|
||||
"--", mountpoint, NULL };
|
||||
|
||||
exec_fusermount(argv);
|
||||
_exit(1);
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
|
||||
const char *opts, int quiet)
|
||||
{
|
||||
int fds[2], pid;
|
||||
int res;
|
||||
int rv;
|
||||
|
||||
if (!mountpoint) {
|
||||
fprintf(stderr, "fuse: missing mountpoint parameter\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
|
||||
if(res == -1) {
|
||||
perror("fuse: socketpair() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if(pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(pid == 0) {
|
||||
char env[10];
|
||||
const char *argv[32];
|
||||
int a = 0;
|
||||
|
||||
if (quiet) {
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
if (fd != -1) {
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
}
|
||||
}
|
||||
|
||||
argv[a++] = FUSERMOUNT_PROG;
|
||||
if (opts) {
|
||||
argv[a++] = "-o";
|
||||
argv[a++] = opts;
|
||||
}
|
||||
argv[a++] = "--";
|
||||
argv[a++] = mountpoint;
|
||||
argv[a++] = NULL;
|
||||
|
||||
close(fds[1]);
|
||||
fcntl(fds[0], F_SETFD, 0);
|
||||
snprintf(env, sizeof(env), "%i", fds[0]);
|
||||
setenv(FUSE_COMMFD_ENV, env, 1);
|
||||
exec_fusermount(argv);
|
||||
perror("fuse: failed to exec fusermount");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
close(fds[0]);
|
||||
rv = receive_fd(fds[1]);
|
||||
|
||||
if (!mo->auto_unmount) {
|
||||
/* with auto_unmount option fusermount will not exit until
|
||||
this socket is closed */
|
||||
close(fds[1]);
|
||||
waitpid(pid, NULL, 0); /* bury zombie */
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
const char *mnt_opts)
|
||||
{
|
||||
char tmp[128];
|
||||
const char *devname = "/dev/fuse";
|
||||
char *source = NULL;
|
||||
char *type = NULL;
|
||||
struct stat stbuf;
|
||||
int fd;
|
||||
int res;
|
||||
|
||||
if (!mnt) {
|
||||
fprintf(stderr, "fuse: missing mountpoint parameter\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = stat(mnt, &stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
|
||||
mnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mo->auto_unmount) {
|
||||
/* Tell the caller to fallback to fusermount because
|
||||
auto-unmount does not work otherwise. */
|
||||
return -2;
|
||||
}
|
||||
|
||||
fd = open(devname, O_RDWR);
|
||||
if (fd == -1) {
|
||||
if (errno == ENODEV || errno == ENOENT)
|
||||
fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
|
||||
else
|
||||
fprintf(stderr, "fuse: failed to open %s: %s\n",
|
||||
devname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
|
||||
fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
|
||||
|
||||
res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
|
||||
if (res == -1)
|
||||
goto out_close;
|
||||
|
||||
source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
|
||||
(mo->subtype ? strlen(mo->subtype) : 0) +
|
||||
strlen(devname) + 32);
|
||||
|
||||
type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
|
||||
if (!type || !source) {
|
||||
fprintf(stderr, "fuse: failed to allocate memory\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
|
||||
if (mo->subtype) {
|
||||
strcat(type, ".");
|
||||
strcat(type, mo->subtype);
|
||||
}
|
||||
strcpy(source,
|
||||
mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
|
||||
|
||||
res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
|
||||
if (res == -1 && errno == ENODEV && mo->subtype) {
|
||||
/* Probably missing subtype support */
|
||||
strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
|
||||
if (mo->fsname) {
|
||||
if (!mo->blkdev)
|
||||
sprintf(source, "%s#%s", mo->subtype,
|
||||
mo->fsname);
|
||||
} else {
|
||||
strcpy(source, type);
|
||||
}
|
||||
res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
|
||||
}
|
||||
if (res == -1) {
|
||||
/*
|
||||
* Maybe kernel doesn't support unprivileged mounts, in this
|
||||
* case try falling back to fusermount
|
||||
*/
|
||||
if (errno == EPERM) {
|
||||
res = -2;
|
||||
} else {
|
||||
int errno_save = errno;
|
||||
if (mo->blkdev && errno == ENODEV &&
|
||||
!fuse_mnt_check_fuseblk())
|
||||
fprintf(stderr,
|
||||
"fuse: 'fuseblk' support missing\n");
|
||||
else
|
||||
fprintf(stderr, "fuse: mount failed: %s\n",
|
||||
strerror(errno_save));
|
||||
}
|
||||
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
#ifndef __NetBSD__
|
||||
#ifndef IGNORE_MTAB
|
||||
if (geteuid() == 0) {
|
||||
char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
|
||||
res = -1;
|
||||
if (!newmnt)
|
||||
goto out_umount;
|
||||
|
||||
res = fuse_mnt_add_mount("fuse", source, newmnt, type,
|
||||
mnt_opts);
|
||||
free(newmnt);
|
||||
if (res == -1)
|
||||
goto out_umount;
|
||||
}
|
||||
#endif /* IGNORE_MTAB */
|
||||
#endif /* __NetBSD__ */
|
||||
free(type);
|
||||
free(source);
|
||||
|
||||
return fd;
|
||||
|
||||
out_umount:
|
||||
umount2(mnt, 2); /* lazy umount */
|
||||
out_close:
|
||||
free(type);
|
||||
free(source);
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int get_mnt_flag_opts(char **mnt_optsp, int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
struct mount_opts mo;
|
||||
int res = -1;
|
||||
char *mnt_opts = NULL;
|
||||
|
||||
memset(&mo, 0, sizeof(mo));
|
||||
mo.flags = MS_NOSUID | MS_NODEV;
|
||||
|
||||
if (args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
fuse_opt_add_opt(&mo.kernel_opts,"allow_other");
|
||||
|
||||
res = 0;
|
||||
if (mo.ishelp)
|
||||
goto out;
|
||||
|
||||
res = -1;
|
||||
if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
|
||||
goto out;
|
||||
if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
|
||||
goto out;
|
||||
if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
|
||||
goto out;
|
||||
|
||||
res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
|
||||
if (res == -2) {
|
||||
if (mo.fusermount_opts &&
|
||||
fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
|
||||
goto out;
|
||||
|
||||
if (mo.subtype) {
|
||||
char *tmp_opts = NULL;
|
||||
|
||||
res = -1;
|
||||
if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
|
||||
fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
|
||||
free(tmp_opts);
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
|
||||
free(tmp_opts);
|
||||
if (res == -1)
|
||||
res = fuse_mount_fusermount(mountpoint, &mo,
|
||||
mnt_opts, 0);
|
||||
} else {
|
||||
res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
|
||||
}
|
||||
}
|
||||
out:
|
||||
free(mnt_opts);
|
||||
free(mo.fsname);
|
||||
free(mo.subtype);
|
||||
free(mo.fusermount_opts);
|
||||
free(mo.subtype_opt);
|
||||
free(mo.kernel_opts);
|
||||
free(mo.mtab_opts);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
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 "mount_util.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#ifndef __NetBSD__
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
|
||||
#define mtab_needs_update(mnt) 0
|
||||
#else
|
||||
static int mtab_needs_update(const char *mnt)
|
||||
{
|
||||
int res;
|
||||
struct stat stbuf;
|
||||
|
||||
/* If mtab is within new mount, don't touch it */
|
||||
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
||||
_PATH_MOUNTED[strlen(mnt)] == '/')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Skip mtab update if /etc/mtab:
|
||||
*
|
||||
* - doesn't exist,
|
||||
* - is a symlink,
|
||||
* - is on a read-only filesystem.
|
||||
*/
|
||||
res = lstat(_PATH_MOUNTED, &stbuf);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
} else {
|
||||
uid_t ruid;
|
||||
int err;
|
||||
|
||||
if (S_ISLNK(stbuf.st_mode))
|
||||
return 0;
|
||||
|
||||
ruid = getuid();
|
||||
if (ruid != 0)
|
||||
setreuid(0, -1);
|
||||
|
||||
res = access(_PATH_MOUNTED, W_OK);
|
||||
err = (res == -1) ? errno : 0;
|
||||
if (ruid != 0)
|
||||
setreuid(ruid, -1);
|
||||
|
||||
if (err == EROFS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
|
||||
static int add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
|
||||
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return add_mount(progname, fsname, mnt, type, opts);
|
||||
}
|
||||
|
||||
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
if (lazy) {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
"-l", NULL, &env);
|
||||
} else {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
NULL, &env);
|
||||
}
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
|
||||
const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!mtab_needs_update(abs_mnt)) {
|
||||
res = umount2(rel_mnt, lazy ? 2 : 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n",
|
||||
progname, abs_mnt, strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
return exec_umount(progname, rel_mnt, lazy);
|
||||
}
|
||||
|
||||
static int remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
|
||||
"--fake", mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return remove_mount(progname, mnt);
|
||||
}
|
||||
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char *copy;
|
||||
char *dst;
|
||||
char *end;
|
||||
char *lastcomp;
|
||||
const char *toresolv;
|
||||
|
||||
if (!orig[0]) {
|
||||
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
|
||||
orig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = strdup(orig);
|
||||
if (copy == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toresolv = copy;
|
||||
lastcomp = NULL;
|
||||
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
||||
if (end[0] != '/') {
|
||||
char *tmp;
|
||||
end[1] = '\0';
|
||||
tmp = strrchr(copy, '/');
|
||||
if (tmp == NULL) {
|
||||
lastcomp = copy;
|
||||
toresolv = ".";
|
||||
} else {
|
||||
lastcomp = tmp + 1;
|
||||
if (tmp == copy)
|
||||
toresolv = "/";
|
||||
}
|
||||
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
||||
lastcomp = NULL;
|
||||
toresolv = copy;
|
||||
}
|
||||
else if (tmp)
|
||||
tmp[0] = '\0';
|
||||
}
|
||||
if (realpath(toresolv, buf) == NULL) {
|
||||
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
||||
strerror(errno));
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
if (lastcomp == NULL)
|
||||
dst = strdup(buf);
|
||||
else {
|
||||
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
||||
if (dst) {
|
||||
unsigned buflen = strlen(buf);
|
||||
if (buflen && buf[buflen-1] == '/')
|
||||
sprintf(dst, "%s%s", buf, lastcomp);
|
||||
else
|
||||
sprintf(dst, "%s/%s", buf, lastcomp);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
if (dst == NULL)
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int fuse_mnt_check_fuseblk(void)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *f = fopen("/proc/filesystems", "r");
|
||||
if (!f)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
if (strstr(buf, "fuseblk\n")) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts);
|
||||
int fuse_mnt_remove_mount(const char *progname, const char *mnt);
|
||||
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
|
||||
const char *rel_mnt, int lazy);
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig);
|
||||
int fuse_mnt_check_fuseblk(void);
|
||||
@@ -0,0 +1,361 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/os.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
# ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <io.h>
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
# ifndef S_IRGRP
|
||||
# define S_IRGRP 0
|
||||
# endif
|
||||
# ifndef S_IWGRP
|
||||
# define S_IWGRP 0
|
||||
# endif
|
||||
# ifndef S_IROTH
|
||||
# define S_IROTH 0
|
||||
# endif
|
||||
# ifndef S_IWOTH
|
||||
# define S_IWOTH 0
|
||||
# endif
|
||||
# endif // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
using rwresult = int;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#elif FMT_USE_FCNTL
|
||||
// Return type of read and write functions.
|
||||
using rwresult = ssize_t;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef _WIN32
|
||||
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
|
||||
if (int error_code = convert(s)) {
|
||||
FMT_THROW(windows_error(error_code,
|
||||
"cannot convert string from UTF-16 to UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
|
||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
if (s_size == 0) {
|
||||
// WideCharToMultiByte does not support zero length, handle separately.
|
||||
buffer_.resize(1);
|
||||
buffer_[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
|
||||
length, nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
class system_message {
|
||||
system_message(const system_message&) = delete;
|
||||
void operator=(const system_message&) = delete;
|
||||
|
||||
unsigned long result_;
|
||||
wchar_t* message_;
|
||||
|
||||
static bool is_whitespace(wchar_t c) noexcept {
|
||||
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
|
||||
}
|
||||
|
||||
public:
|
||||
explicit system_message(unsigned long error_code)
|
||||
: result_(0), message_(nullptr) {
|
||||
result_ = FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
|
||||
if (result_ != 0) {
|
||||
while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
|
||||
--result_;
|
||||
}
|
||||
}
|
||||
}
|
||||
~system_message() { LocalFree(message_); }
|
||||
explicit operator bool() const noexcept { return result_ != 0; }
|
||||
operator basic_string_view<wchar_t>() const noexcept {
|
||||
return basic_string_view<wchar_t>(message_, result_);
|
||||
}
|
||||
};
|
||||
|
||||
class utf8_system_category final : public std::error_category {
|
||||
public:
|
||||
const char* name() const noexcept override { return "system"; }
|
||||
std::string message(int error_code) const override {
|
||||
system_message msg(error_code);
|
||||
if (msg) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||
return utf8_message.str();
|
||||
}
|
||||
}
|
||||
return "unknown error";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_API const std::error_category& system_category() noexcept {
|
||||
static const detail::utf8_system_category category;
|
||||
return category;
|
||||
}
|
||||
|
||||
std::system_error vwindows_error(int err_code, string_view format_str,
|
||||
format_args args) {
|
||||
auto ec = std::error_code(err_code, system_category());
|
||||
return std::system_error(ec, vformat(format_str, args));
|
||||
}
|
||||
|
||||
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||
const char* message) noexcept {
|
||||
FMT_TRY {
|
||||
system_message msg(error_code);
|
||||
if (msg) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||
fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
FMT_CATCH(...) {}
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
void report_windows_error(int error_code, const char* message) noexcept {
|
||||
report_error(detail::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
buffered_file::~buffered_file() noexcept {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
|
||||
nullptr);
|
||||
if (!file_)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||
}
|
||||
|
||||
void buffered_file::close() {
|
||||
if (!file_) return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = nullptr;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
int buffered_file::descriptor() const {
|
||||
int fd = FMT_POSIX_CALL(fileno(file_));
|
||||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
file::file(cstring_view path, int oflag) {
|
||||
# ifdef _WIN32
|
||||
using mode_t = int;
|
||||
# endif
|
||||
constexpr mode_t mode =
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
# else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
# endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||
}
|
||||
|
||||
file::~file() noexcept {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (fd_ == -1) return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
long long file::size() const {
|
||||
# ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
unsigned long long long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
# else
|
||||
using Stat = struct stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||
"return type of file::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
# endif
|
||||
}
|
||||
|
||||
std::size_t file::read(void* buffer, std::size_t count) {
|
||||
rwresult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return detail::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
||||
rwresult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
return detail::to_unsigned(result);
|
||||
}
|
||||
|
||||
file file::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return file(new_fd);
|
||||
}
|
||||
|
||||
void file::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
|
||||
fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void file::dup2(int fd, std::error_code& ec) noexcept {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
||||
}
|
||||
|
||||
void file::pipe(file& read_end, file& write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
# ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
# else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
# endif
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = file(fds[0]);
|
||||
write_end = file(fds[1]);
|
||||
}
|
||||
|
||||
buffered_file file::fdopen(const char* mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||
FILE* f = ::fdopen(fd_, mode);
|
||||
# else
|
||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
# endif
|
||||
if (!f)
|
||||
FMT_THROW(
|
||||
system_error(errno, "cannot associate stream with file descriptor"));
|
||||
buffered_file bf(f);
|
||||
fd_ = -1;
|
||||
return bf;
|
||||
}
|
||||
|
||||
long getpagesize() {
|
||||
# ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
# else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
# endif
|
||||
}
|
||||
|
||||
FMT_API void ostream::grow(size_t) {
|
||||
if (this->size() == this->capacity()) flush();
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
FMT_END_NAMESPACE
|
||||
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include "unbounded_queue.hpp"
|
||||
#include "bounded_queue.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ThreadPool(const std::size_t thread_count_ = std::thread::hardware_concurrency())
|
||||
: _queues(thread_count_),
|
||||
_count(thread_count_)
|
||||
{
|
||||
auto worker = [this](std::size_t i)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
Proc f;
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count].try_pop(f))
|
||||
break;
|
||||
}
|
||||
|
||||
if(!f && !_queues[i].pop(f))
|
||||
break;
|
||||
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
_threads.reserve(thread_count_);
|
||||
for(std::size_t i = 0; i < thread_count_; ++i)
|
||||
_threads.emplace_back(worker, i);
|
||||
}
|
||||
|
||||
~ThreadPool()
|
||||
{
|
||||
for(auto& queue : _queues)
|
||||
queue.unblock();
|
||||
for(auto& thread : _threads)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void
|
||||
enqueue_work(F&& f_)
|
||||
{
|
||||
auto i = _index++;
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count].try_push(f_))
|
||||
return;
|
||||
}
|
||||
|
||||
_queues[i % _count].push(std::move(f_));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
[[nodiscard]]
|
||||
std::future<typename std::result_of<F()>::type>
|
||||
enqueue_task(F&& f_)
|
||||
{
|
||||
using TaskReturnType = typename std::result_of<F()>::type;
|
||||
using Promise = std::promise<TaskReturnType>;
|
||||
|
||||
auto i = _index++;
|
||||
auto promise = std::make_shared<Promise>();
|
||||
auto future = promise->get_future();
|
||||
auto work = [=]() {
|
||||
auto rv = f_();
|
||||
promise->set_value(rv);
|
||||
};
|
||||
|
||||
for(std::size_t n = 0; n < (_count * K); ++n)
|
||||
{
|
||||
if(_queues[(i + n) % _count].try_push(work))
|
||||
return future;
|
||||
}
|
||||
|
||||
_queues[i % _count].push(std::move(work));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<pthread_t>
|
||||
threads()
|
||||
{
|
||||
std::vector<pthread_t> rv;
|
||||
|
||||
for(auto &thread : _threads)
|
||||
rv.push_back(thread.native_handle());
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
using Proc = std::function<void(void)>;
|
||||
using Queue = UnboundedQueue<Proc>;
|
||||
using Queues = std::vector<Queue>;
|
||||
Queues _queues;
|
||||
|
||||
private:
|
||||
std::vector<std::thread> _threads;
|
||||
|
||||
private:
|
||||
const std::size_t _count;
|
||||
std::atomic_uint _index;
|
||||
|
||||
static const unsigned int K = 2;
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
|
||||
template<typename T>
|
||||
class UnboundedQueue
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
UnboundedQueue(bool block_ = true)
|
||||
: _block(block_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
push(const T& item_)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_queue.push(item_);
|
||||
}
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
void
|
||||
push(T&& item_)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_queue.push(std::move(item_));
|
||||
}
|
||||
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void
|
||||
emplace(Args&&... args_)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_queue.emplace(std::forward<Args>(args_)...);
|
||||
}
|
||||
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
bool
|
||||
try_push(const T& item_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
|
||||
if(!lock)
|
||||
return false;
|
||||
_queue.push(item_);
|
||||
}
|
||||
|
||||
_condition.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
try_push(T&& item_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
|
||||
if(!lock)
|
||||
return false;
|
||||
_queue.push(std::move(item_));
|
||||
}
|
||||
|
||||
_condition.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: push multiple T at once
|
||||
|
||||
bool
|
||||
pop(T& item_)
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(_queue_lock);
|
||||
|
||||
_condition.wait(guard, [&]() { return !_queue.empty() || !_block; });
|
||||
if(_queue.empty())
|
||||
return false;
|
||||
|
||||
item_ = std::move(_queue.front());
|
||||
_queue.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
try_pop(T& item_)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
|
||||
if(!lock || _queue.empty())
|
||||
return false;
|
||||
|
||||
item_ = std::move(_queue.front());
|
||||
_queue.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
void
|
||||
block()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_block = true;
|
||||
}
|
||||
|
||||
void
|
||||
unblock()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
_block = false;
|
||||
}
|
||||
|
||||
_condition.notify_all();
|
||||
}
|
||||
|
||||
bool
|
||||
blocking() const
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_queue_lock);
|
||||
|
||||
return _block;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex _queue_lock;
|
||||
|
||||
private:
|
||||
bool _block;
|
||||
std::queue<T> _queue;
|
||||
std::condition_variable _condition;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
static char *progname;
|
||||
|
||||
static char *xstrdup(const char *s)
|
||||
{
|
||||
char *t = strdup(s);
|
||||
if (!t) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void *xrealloc(void *oldptr, size_t size)
|
||||
{
|
||||
void *ptr = realloc(oldptr, size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void add_arg(char **cmdp, const char *opt)
|
||||
{
|
||||
size_t optlen = strlen(opt);
|
||||
size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
|
||||
char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
|
||||
char *s;
|
||||
s = cmd + cmdlen;
|
||||
if (*cmdp)
|
||||
*s++ = ' ';
|
||||
|
||||
*s++ = '\'';
|
||||
for (; *opt; opt++) {
|
||||
if (*opt == '\'') {
|
||||
*s++ = '\'';
|
||||
*s++ = '\\';
|
||||
*s++ = '\'';
|
||||
*s++ = '\'';
|
||||
} else
|
||||
*s++ = *opt;
|
||||
}
|
||||
*s++ = '\'';
|
||||
*s = '\0';
|
||||
*cmdp = cmd;
|
||||
}
|
||||
|
||||
static char *add_option(const char *opt, char *options)
|
||||
{
|
||||
int oldlen = options ? strlen(options) : 0;
|
||||
|
||||
options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
|
||||
if (!oldlen)
|
||||
strcpy(options, opt);
|
||||
else {
|
||||
strcat(options, ",");
|
||||
strcat(options, opt);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *type = NULL;
|
||||
char *source;
|
||||
const char *mountpoint;
|
||||
char *basename;
|
||||
char *options = NULL;
|
||||
char *command = NULL;
|
||||
char *setuid = NULL;
|
||||
int i;
|
||||
int dev = 1;
|
||||
int suid = 1;
|
||||
|
||||
progname = argv[0];
|
||||
basename = strrchr(argv[0], '/');
|
||||
if (basename)
|
||||
basename++;
|
||||
else
|
||||
basename = argv[0];
|
||||
|
||||
type = "mergerfs";
|
||||
if (strncmp(basename, "mount.fuse.", 11) == 0)
|
||||
type = basename + 11;
|
||||
if (strncmp(basename, "mount.fuseblk.", 14) == 0)
|
||||
type = basename + 14;
|
||||
|
||||
if (type && !type[0])
|
||||
type = NULL;
|
||||
|
||||
if (argc < 3) {
|
||||
fprintf(stderr,
|
||||
"usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
|
||||
progname, type ? "source" : "type#[source]");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
source = argv[1];
|
||||
if (!source[0])
|
||||
source = NULL;
|
||||
|
||||
mountpoint = argv[2];
|
||||
|
||||
for (i = 3; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-v") == 0) {
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "-t") == 0) {
|
||||
i++;
|
||||
|
||||
if (i == argc) {
|
||||
fprintf(stderr,
|
||||
"%s: missing argument to option '-t'\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
type = argv[i];
|
||||
if (strncmp(type, "fuse.", 5) == 0)
|
||||
type += 5;
|
||||
else if (strncmp(type, "fuseblk.", 8) == 0)
|
||||
type += 8;
|
||||
|
||||
if (!type[0]) {
|
||||
fprintf(stderr,
|
||||
"%s: empty type given as argument to option '-t'\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
} else if (strcmp(argv[i], "-o") == 0) {
|
||||
char *opts;
|
||||
char *opt;
|
||||
i++;
|
||||
if (i == argc)
|
||||
break;
|
||||
|
||||
opts = xstrdup(argv[i]);
|
||||
opt = strtok(opts, ",");
|
||||
while (opt) {
|
||||
int j;
|
||||
int ignore = 0;
|
||||
const char *ignore_opts[] = { "",
|
||||
"user",
|
||||
"nouser",
|
||||
"users",
|
||||
"auto",
|
||||
"noauto",
|
||||
"_netdev",
|
||||
NULL};
|
||||
if (strncmp(opt, "setuid=", 7) == 0) {
|
||||
setuid = xstrdup(opt + 7);
|
||||
ignore = 1;
|
||||
}
|
||||
for (j = 0; ignore_opts[j]; j++)
|
||||
if (strcmp(opt, ignore_opts[j]) == 0)
|
||||
ignore = 1;
|
||||
|
||||
if (!ignore) {
|
||||
if (strcmp(opt, "nodev") == 0)
|
||||
dev = 0;
|
||||
else if (strcmp(opt, "nosuid") == 0)
|
||||
suid = 0;
|
||||
|
||||
options = add_option(opt, options);
|
||||
}
|
||||
opt = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dev)
|
||||
options = add_option("dev", options);
|
||||
if (suid)
|
||||
options = add_option("suid", options);
|
||||
|
||||
if (!type) {
|
||||
if (source) {
|
||||
type = xstrdup(source);
|
||||
source = strchr(type, '#');
|
||||
if (source)
|
||||
*source++ = '\0';
|
||||
if (!type[0]) {
|
||||
fprintf(stderr, "%s: empty filesystem type\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%s: empty source\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
add_arg(&command, type);
|
||||
if (source)
|
||||
add_arg(&command, source);
|
||||
add_arg(&command, mountpoint);
|
||||
if (options) {
|
||||
add_arg(&command, "-o");
|
||||
add_arg(&command, options);
|
||||
}
|
||||
|
||||
if (setuid && setuid[0]) {
|
||||
char *sucommand = command;
|
||||
command = NULL;
|
||||
add_arg(&command, "su");
|
||||
add_arg(&command, "-");
|
||||
add_arg(&command, setuid);
|
||||
add_arg(&command, "-c");
|
||||
add_arg(&command, sucommand);
|
||||
} else if (!getenv("HOME")) {
|
||||
/* Hack to make filesystems work in the boot environment */
|
||||
setenv("HOME", "/root", 0);
|
||||
}
|
||||
|
||||
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
|
||||
fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
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 "mount_util.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#ifndef __NetBSD__
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
|
||||
#define mtab_needs_update(mnt) 0
|
||||
#else
|
||||
static int mtab_needs_update(const char *mnt)
|
||||
{
|
||||
int res;
|
||||
struct stat stbuf;
|
||||
|
||||
/* If mtab is within new mount, don't touch it */
|
||||
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
||||
_PATH_MOUNTED[strlen(mnt)] == '/')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Skip mtab update if /etc/mtab:
|
||||
*
|
||||
* - doesn't exist,
|
||||
* - is a symlink,
|
||||
* - is on a read-only filesystem.
|
||||
*/
|
||||
res = lstat(_PATH_MOUNTED, &stbuf);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
} else {
|
||||
uid_t ruid;
|
||||
int err;
|
||||
|
||||
if (S_ISLNK(stbuf.st_mode))
|
||||
return 0;
|
||||
|
||||
ruid = getuid();
|
||||
if (ruid != 0)
|
||||
setreuid(0, -1);
|
||||
|
||||
res = access(_PATH_MOUNTED, W_OK);
|
||||
err = (res == -1) ? errno : 0;
|
||||
if (ruid != 0)
|
||||
setreuid(ruid, -1);
|
||||
|
||||
if (err == EROFS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
|
||||
static int add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
|
||||
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return add_mount(progname, fsname, mnt, type, opts);
|
||||
}
|
||||
|
||||
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
if (lazy) {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
"-l", NULL, &env);
|
||||
} else {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
NULL, &env);
|
||||
}
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
|
||||
const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!mtab_needs_update(abs_mnt)) {
|
||||
res = umount2(rel_mnt, lazy ? 2 : 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n",
|
||||
progname, abs_mnt, strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
return exec_umount(progname, rel_mnt, lazy);
|
||||
}
|
||||
|
||||
static int remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
|
||||
"--fake", mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return remove_mount(progname, mnt);
|
||||
}
|
||||
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char *copy;
|
||||
char *dst;
|
||||
char *end;
|
||||
char *lastcomp;
|
||||
const char *toresolv;
|
||||
|
||||
if (!orig[0]) {
|
||||
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
|
||||
orig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = strdup(orig);
|
||||
if (copy == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toresolv = copy;
|
||||
lastcomp = NULL;
|
||||
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
||||
if (end[0] != '/') {
|
||||
char *tmp;
|
||||
end[1] = '\0';
|
||||
tmp = strrchr(copy, '/');
|
||||
if (tmp == NULL) {
|
||||
lastcomp = copy;
|
||||
toresolv = ".";
|
||||
} else {
|
||||
lastcomp = tmp + 1;
|
||||
if (tmp == copy)
|
||||
toresolv = "/";
|
||||
}
|
||||
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
||||
lastcomp = NULL;
|
||||
toresolv = copy;
|
||||
}
|
||||
else if (tmp)
|
||||
tmp[0] = '\0';
|
||||
}
|
||||
if (realpath(toresolv, buf) == NULL) {
|
||||
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
||||
strerror(errno));
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
if (lastcomp == NULL)
|
||||
dst = strdup(buf);
|
||||
else {
|
||||
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
||||
if (dst) {
|
||||
unsigned buflen = strlen(buf);
|
||||
if (buflen && buf[buflen-1] == '/')
|
||||
sprintf(dst, "%s%s", buf, lastcomp);
|
||||
else
|
||||
sprintf(dst, "%s/%s", buf, lastcomp);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
if (dst == NULL)
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int fuse_mnt_check_fuseblk(void)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *f = fopen("/proc/filesystems", "r");
|
||||
if (!f)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
if (strstr(buf, "fuseblk\n")) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
+2378
-583
File diff suppressed because it is too large
Load Diff
+12
-7
@@ -1,7 +1,7 @@
|
||||
Name: mergerfs
|
||||
Version: __VERSION__
|
||||
Release: 1%{?dist}
|
||||
Summary: A FUSE union filesystem
|
||||
Summary: A featureful FUSE based union filesystem
|
||||
|
||||
Group: Applications/System
|
||||
License: ISC
|
||||
@@ -10,20 +10,20 @@ URL: https://github.com/trapexit/mergerfs
|
||||
Source: mergerfs-%{version}.tar.gz
|
||||
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: libattr-devel
|
||||
BuildRequires: fuse-devel
|
||||
# rpmbuild driven by the Makefile uses git to generate a version number
|
||||
BuildRequires: git
|
||||
|
||||
Requires: fuse
|
||||
|
||||
%global debug_package %{nil}
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%description
|
||||
mergerfs is similar to mhddfs, unionfs, and aufs. Like mhddfs in that it too
|
||||
uses FUSE. Like aufs in that it provides multiple policies for how to handle
|
||||
behavior.
|
||||
mergerfs is a union filesystem geared towards simplifying storage and
|
||||
management of files across numerous commodity storage devices. It is
|
||||
similar to mhddfs, unionfs, and aufs.
|
||||
|
||||
%build
|
||||
make %{?_smp_mflags}
|
||||
@@ -32,10 +32,15 @@ make %{?_smp_mflags}
|
||||
make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
|
||||
|
||||
%files
|
||||
%{_bindir}/*
|
||||
/usr/bin/mergerfs
|
||||
/usr/bin/mergerfs-fusermount
|
||||
/sbin/mount.mergerfs
|
||||
%doc %{_mandir}/*
|
||||
|
||||
%changelog
|
||||
* Fri Apr 26 2019 Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
- Update description
|
||||
|
||||
* Mon Jan 25 2016 Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
- Remove sbin files
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user