mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-25 07:15:35 -04:00
Compare commits
775 Commits
v3.2.2pre1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5a5857ce | ||
|
|
bc63ea82f2 | ||
|
|
0d4fb1bc89 | ||
|
|
52480aaac2 | ||
|
|
702a8f61b7 | ||
|
|
2928b2742e | ||
|
|
f1d5a3c815 | ||
|
|
3086dbc0fd | ||
|
|
63e599b921 | ||
|
|
340238421d | ||
|
|
31fbb17d23 | ||
|
|
edf298ace5 | ||
|
|
1d5b5ab83a | ||
|
|
b0ba699031 | ||
|
|
e57c7f5d87 | ||
|
|
05f30c05c9 | ||
|
|
922681e140 | ||
|
|
273b9f265f | ||
|
|
0d546ee3b4 | ||
|
|
d6124a82a4 | ||
|
|
1d828f35ca | ||
|
|
7bba25e675 | ||
|
|
6e3140d5ba | ||
|
|
1d8f47cc71 | ||
|
|
743d715d43 | ||
|
|
4b862306e5 | ||
|
|
70948a9dc3 | ||
|
|
951bf0a446 | ||
|
|
bea8a3a16f | ||
|
|
bf8aab51e8 | ||
|
|
1f689ec0c2 | ||
|
|
8839314025 | ||
|
|
47e087d8eb | ||
|
|
e1c5f0e93a | ||
|
|
cfdc27c613 | ||
|
|
7e7372a0c5 | ||
|
|
8cad2097e9 | ||
|
|
d039cfa829 | ||
|
|
0af88421dc | ||
|
|
9d014670df | ||
|
|
647a00a278 | ||
|
|
2c7777aaa6 | ||
|
|
6af41d2357 | ||
|
|
a0b9a8e989 | ||
|
|
ac692b199c | ||
|
|
147e9bea8c | ||
|
|
a5fc5ebe7a | ||
|
|
c79cb81a4f | ||
|
|
650643109e | ||
|
|
4cf08983e8 | ||
|
|
8112445318 | ||
|
|
c38f20c5ff | ||
|
|
0cf200ecbb | ||
|
|
e4c681fefd | ||
|
|
c44c90e946 | ||
|
|
fc592a8e25 | ||
|
|
40a6e13071 | ||
|
|
3cc6a9e8cd | ||
|
|
30656c5e35 | ||
|
|
15d2964256 | ||
|
|
862fe4eeaf | ||
|
|
859d44fa4f | ||
|
|
f1c24ab03b | ||
|
|
b9cc0c6176 | ||
|
|
c60550bff9 | ||
|
|
67f1dcf604 | ||
|
|
79fd7d5885 | ||
|
|
dfdcd8f851 | ||
|
|
04e2fc2c76 | ||
|
|
7f60ec001a | ||
|
|
4fa7156ccd | ||
|
|
dcf364dac5 | ||
|
|
d1eff8f0dc | ||
|
|
8f727166d9 | ||
|
|
5bcb3deb2f | ||
|
|
de3cc03b03 | ||
|
|
006ee327d6 | ||
|
|
9b6363fa10 | ||
|
|
9e2f0fe9ae | ||
|
|
4f6e4ea64a | ||
|
|
567c40935f | ||
|
|
8e11f0c169 | ||
|
|
e9dbc8d66d | ||
|
|
bd2dbd2f32 | ||
|
|
350e295d1c | ||
|
|
066156fcd9 | ||
|
|
a5bbe859db | ||
|
|
d046525de3 | ||
|
|
bb0a8118c2 | ||
|
|
d1df0aaf70 | ||
|
|
15d8e49a64 | ||
|
|
b905ab23af | ||
|
|
aa142f08ef | ||
|
|
236417cf35 | ||
|
|
2a97d81e99 | ||
|
|
359e539a72 | ||
|
|
9e0898460d | ||
|
|
185520a141 | ||
|
|
c98f9d1f68 | ||
|
|
1f9ce2fcbe | ||
|
|
797e17fc4a | ||
|
|
c2db921890 | ||
|
|
77be09aaed | ||
|
|
0d0f615240 | ||
|
|
b6457bbc83 | ||
|
|
1807ce485a | ||
|
|
9c175ac9ef | ||
|
|
a84b79ea58 | ||
|
|
d4c4f6754e | ||
|
|
a4b926dcdc | ||
|
|
0973d0e380 | ||
|
|
e405cfc073 | ||
|
|
b78a841bb0 | ||
|
|
f7a2b8a3fa | ||
|
|
d941807915 | ||
|
|
992e10efaf | ||
|
|
1c5ebdc4e5 | ||
|
|
9994933c8c | ||
|
|
23d9ead5af | ||
|
|
fcfdd36054 | ||
|
|
89b847393f | ||
|
|
788ecbe5ea | ||
|
|
353506bc51 | ||
|
|
7cff121ec8 | ||
|
|
14f33837dc | ||
|
|
3305a7a063 | ||
|
|
494879b819 | ||
|
|
8d6da040e5 | ||
|
|
68e9add76a | ||
|
|
dc34990b2e | ||
|
|
81ead9e70c | ||
|
|
996af4a79f | ||
|
|
dacadd53a9 | ||
|
|
a6312e60c9 | ||
|
|
e3ee0e7319 | ||
|
|
0fd29b6bcb | ||
|
|
7f79682732 | ||
|
|
870b7d96dc | ||
|
|
9dc31473ba | ||
|
|
536ae3f4ef | ||
|
|
0590b09d9a | ||
|
|
407c71c7ce | ||
|
|
344327385f | ||
|
|
688f5c379a | ||
|
|
9f86ddc965 | ||
|
|
c35e28331f | ||
|
|
b4a27ca25d | ||
|
|
8ad4b5d912 | ||
|
|
589b0691e5 | ||
|
|
36212021f0 | ||
|
|
2b38542e0d | ||
|
|
321dd78f8c | ||
|
|
6f10f12577 | ||
|
|
1a95869dfc | ||
|
|
c9fe6ca304 | ||
|
|
990fa5c1e1 | ||
|
|
07069880a2 | ||
|
|
e55b190f4a | ||
|
|
48d51a1370 | ||
|
|
f654e47691 | ||
|
|
83ad3533d4 | ||
|
|
fa28c5d693 | ||
|
|
62bb9bba02 | ||
|
|
6601510425 | ||
|
|
f7ac7ffd16 | ||
|
|
4320c25fcc | ||
|
|
4490fb8660 | ||
|
|
475ca7d43c | ||
|
|
7c3c54b132 | ||
|
|
bcf0738f98 | ||
|
|
8749ec6436 | ||
|
|
42e2b56c4e | ||
|
|
0902b52f66 | ||
|
|
9615a2492b | ||
|
|
4592aa770d | ||
|
|
8bc363cc9f | ||
|
|
a9a3155756 | ||
|
|
fcc79836b8 | ||
|
|
804411b7fd | ||
|
|
0b1b2a3ff4 | ||
|
|
50bdf9685d | ||
|
|
3f2a38b011 | ||
|
|
5510255f12 | ||
|
|
56a039b04a | ||
|
|
7bc3be2b9e | ||
|
|
411c4789df | ||
|
|
231b239f30 | ||
|
|
4c8683c875 | ||
|
|
85c906f964 | ||
|
|
35f5a21a16 | ||
|
|
99673f937f | ||
|
|
9505ac5945 | ||
|
|
0dd25d4752 | ||
|
|
ae3e13ba99 | ||
|
|
6c8ca91c73 | ||
|
|
079e74a30f | ||
|
|
abc3c74652 | ||
|
|
99ab59464b | ||
|
|
a47ae6fad9 | ||
|
|
2f9b963aba | ||
|
|
3476caea3e | ||
|
|
6f3c5eccee | ||
|
|
79fda35342 | ||
|
|
cd76993461 | ||
|
|
05a683900f | ||
|
|
86f41650fb | ||
|
|
9a06b2edb0 | ||
|
|
273dced284 | ||
|
|
b6e2321973 | ||
|
|
fe95a9369a | ||
|
|
6ae7f4085a | ||
|
|
0f599d3641 | ||
|
|
c3d3b49d72 | ||
|
|
c69dc7a5ab | ||
|
|
2c82006b1f | ||
|
|
0698ea9aeb | ||
|
|
90df93e446 | ||
|
|
5c93dedf45 | ||
|
|
f1e3434b59 | ||
|
|
48252c3c2b | ||
|
|
5b67ff2a86 | ||
|
|
8990ad96de | ||
|
|
0f44e864d4 | ||
|
|
ab0d5021ed | ||
|
|
7402896523 | ||
|
|
5374994089 | ||
|
|
526366129a | ||
|
|
556a2c5bc2 | ||
|
|
27feda0436 | ||
|
|
bf96cd314c | ||
|
|
1b2688807d | ||
|
|
08ec80ac65 | ||
|
|
6b5ae825db | ||
|
|
3b719d1d6e | ||
|
|
ebe1af749c | ||
|
|
de6848ed97 | ||
|
|
42f8386823 | ||
|
|
ad6245f394 | ||
|
|
ca980b5863 | ||
|
|
677aa0dc91 | ||
|
|
025596757c | ||
|
|
449d9bf950 | ||
|
|
35ecec972a | ||
|
|
d76cabe54f | ||
|
|
b5544a95b1 | ||
|
|
11bd2a4fd6 | ||
|
|
6ba434de5c | ||
|
|
3296351442 | ||
|
|
0088a85aeb | ||
|
|
4923c4dc0c | ||
|
|
76c4fa8b54 | ||
|
|
25efa10802 | ||
|
|
fdf5e577f5 | ||
|
|
19bd0dd340 | ||
|
|
ed4b3448be | ||
|
|
4d44bf122d | ||
|
|
6af27a538e | ||
|
|
f9e29dfb09 | ||
|
|
591de7ce5c | ||
|
|
c8c627756a | ||
|
|
46884e4ff6 | ||
|
|
97e02bf21a | ||
|
|
77d762ced8 | ||
|
|
5b27d2e6f3 | ||
|
|
7e634f5355 | ||
|
|
8fe8cfd60a | ||
|
|
7a2dbf7177 | ||
|
|
8449539a0f | ||
|
|
71c2b5d0e3 | ||
|
|
f3f5d8420f | ||
|
|
8b1b81e054 | ||
|
|
e8161304f7 | ||
|
|
b012cde1ed | ||
|
|
464555ea92 | ||
|
|
df904f590e | ||
|
|
208d6ad1cd | ||
|
|
51dae12c92 | ||
|
|
950730313d | ||
|
|
81c5c81381 | ||
|
|
a6a0d2f77c | ||
|
|
418e38a878 | ||
|
|
b2dcabdbb9 | ||
|
|
ad53a9b5a0 | ||
|
|
1750288660 | ||
|
|
087fffaa2b | ||
|
|
5c1fa2a21d | ||
|
|
0efa63f2e6 | ||
|
|
ae16850dc5 | ||
|
|
7e2711bb2b | ||
|
|
b8c2fde3a5 | ||
|
|
1f12b196fd | ||
|
|
bafe73dd5c | ||
|
|
db5bfe67a5 | ||
|
|
5447d038c6 | ||
|
|
711773631b | ||
|
|
bf3e49b453 | ||
|
|
034d5e8770 | ||
|
|
ad8917437a | ||
|
|
1b664d30e4 | ||
|
|
ea38f34d02 | ||
|
|
44d4727664 | ||
|
|
1f2f413167 | ||
|
|
0a09df2c5e | ||
|
|
cc861cf8c0 | ||
|
|
5183c0d6f0 | ||
|
|
706bff9176 | ||
|
|
2c1204032b | ||
|
|
8adc2240e0 | ||
|
|
84ad83525b | ||
|
|
9a3449a398 | ||
|
|
3258534e99 | ||
|
|
b94bba4036 | ||
|
|
a182507bef | ||
|
|
2895b65f53 | ||
|
|
def595c559 | ||
|
|
68b1ce1dc3 | ||
|
|
5a4116e553 | ||
|
|
024bf1d831 | ||
|
|
db4f919ebe | ||
|
|
6ac2c7b682 | ||
|
|
0e10163a9d | ||
|
|
5fcf20ee9d | ||
|
|
fc72d2b771 | ||
|
|
b7ea3fcd19 | ||
|
|
9cb7529ba6 | ||
|
|
55ad8757ec | ||
|
|
3e4b01173a | ||
|
|
2f1d1d5cac | ||
|
|
4c0a4067df | ||
|
|
8550142804 | ||
|
|
97f40754ba | ||
|
|
cff8f04477 | ||
|
|
db8034f12e | ||
|
|
c86763dc38 | ||
|
|
5ce575b157 | ||
|
|
fabef23bea | ||
|
|
685bf58046 | ||
|
|
9e2921fce8 | ||
|
|
80d8f7c7cb | ||
|
|
38e1b075b4 | ||
|
|
d659610afc | ||
|
|
6cafc1f8bf | ||
|
|
788f11ea6a | ||
|
|
b7fdc9ef0e | ||
|
|
0d8cc26044 | ||
|
|
2955888468 | ||
|
|
0773cecc1f | ||
|
|
8e33586359 | ||
|
|
da5c72da4b | ||
|
|
2f7c583143 | ||
|
|
51fd4993ba | ||
|
|
e37bfdb445 | ||
|
|
3d7015afa2 | ||
|
|
7e5424b806 | ||
|
|
43f70b961e | ||
|
|
b7231c7d02 | ||
|
|
15c34f0a8c | ||
|
|
d1e42ffa16 | ||
|
|
36f489c211 | ||
|
|
defe2287aa | ||
|
|
112bef11ad | ||
|
|
b38780f3fd | ||
|
|
5f33238f06 | ||
|
|
3592ac3c02 | ||
|
|
c897b16f32 | ||
|
|
4f741addbd | ||
|
|
355b81d8bc | ||
|
|
6f35553372 | ||
|
|
71090b7e2c | ||
|
|
2ab2ee166e | ||
|
|
1e858e39e6 | ||
|
|
664639e349 | ||
|
|
517b9d91fc | ||
|
|
0ac7ebceef | ||
|
|
85c56b2603 | ||
|
|
10aeb75cea | ||
|
|
d41bb98c09 | ||
|
|
2fda51692b | ||
|
|
1de71e8a78 | ||
|
|
60dd42be60 | ||
|
|
d821e4cbfb | ||
|
|
8aa465117f | ||
|
|
8977815f5d | ||
|
|
a48c20c97c | ||
|
|
601f47436f | ||
|
|
ef76d6cfa5 | ||
|
|
96ed4b47b9 | ||
|
|
13c4019e94 | ||
|
|
b7b387b1f7 | ||
|
|
7569edfaef | ||
|
|
55b2a06812 | ||
|
|
b81a509556 | ||
|
|
26f4dbe12c | ||
|
|
b3f1970f18 | ||
|
|
c51da9174f | ||
|
|
81f71f6f29 | ||
|
|
48e7005554 | ||
|
|
2b3e68814b | ||
|
|
cc83294316 | ||
|
|
08c8375acb | ||
|
|
824a057935 | ||
|
|
d91ddb97d1 | ||
|
|
5bb637ca04 | ||
|
|
142aba00d5 | ||
|
|
8687e44d10 | ||
|
|
0bd8e85185 | ||
|
|
00a5ab2364 | ||
|
|
f44e76b65c | ||
|
|
1174d97072 | ||
|
|
d9eaffe564 | ||
|
|
6197385d1f | ||
|
|
d07272d631 | ||
|
|
e2a011d9d0 | ||
|
|
76dc7d0a76 | ||
|
|
7e94e52144 | ||
|
|
5ef7e3c9c5 | ||
|
|
d2cc1149b3 | ||
|
|
c3b553a93f | ||
|
|
eb0b41587c | ||
|
|
3c0bb7ff51 | ||
|
|
995ce7198b | ||
|
|
38ffa522f6 | ||
|
|
8898aecb21 | ||
|
|
f08505e92b | ||
|
|
c1e8809a8f | ||
|
|
6130c4fa3c | ||
|
|
8c4ceb3b86 | ||
|
|
30a5909544 | ||
|
|
e841944b47 | ||
|
|
635d8c0632 | ||
|
|
6b8db0f644 | ||
|
|
3b2804c815 | ||
|
|
ff1792edf1 | ||
|
|
b985123d2e | ||
|
|
c983279020 | ||
|
|
ee9199b542 | ||
|
|
f1a6998df2 | ||
|
|
3e44bbd313 | ||
|
|
4adfdaaf12 | ||
|
|
4a7ba3cfaf | ||
|
|
ffbca80ca2 | ||
|
|
c11467af36 | ||
|
|
13cfe6406f | ||
|
|
8e77ece0ee | ||
|
|
ffec7fe109 | ||
|
|
e07f8fb863 | ||
|
|
8cf9dbb742 | ||
|
|
3008e7c226 | ||
|
|
a2b630c0bb | ||
|
|
5b1baa7a2e | ||
|
|
7f8cf771b7 | ||
|
|
b00e99c529 | ||
|
|
a76e32f949 | ||
|
|
512acd125e | ||
|
|
72adf49ba8 | ||
|
|
73ceea6ad2 | ||
|
|
39c3ae0ea3 | ||
|
|
ed19ea05fe | ||
|
|
dc1b9febf1 | ||
|
|
d9015da151 | ||
|
|
1f0e62f139 | ||
|
|
7d830ff52f | ||
|
|
8f383e8987 | ||
|
|
ca538965d8 | ||
|
|
e4669b81ae | ||
|
|
1b9308b727 | ||
|
|
80c64dc3b3 | ||
|
|
be3d6c0fbb | ||
|
|
7956070f2b | ||
|
|
d0f34b5a76 | ||
|
|
84498104bf | ||
|
|
378a0a634f | ||
|
|
ac08fa74f3 | ||
|
|
d5d4ae51ee | ||
|
|
0f87eafa2f | ||
|
|
3af00277ee | ||
|
|
b774dbc1c0 | ||
|
|
296352ecb0 | ||
|
|
11a9b62322 | ||
|
|
452ef78517 | ||
|
|
0d1b48893a | ||
|
|
ec8a05f653 | ||
|
|
78b5bc6629 | ||
|
|
f41cdc75a1 | ||
|
|
c8e7c4b352 | ||
|
|
bff084c10a | ||
|
|
16c8b05f11 | ||
|
|
15dd2058fd | ||
|
|
c27180c044 | ||
|
|
050fdd4126 | ||
|
|
ae1f002999 | ||
|
|
3911c23866 | ||
|
|
3814dbb0f4 | ||
|
|
82f023d7e3 | ||
|
|
ec57c57baf | ||
|
|
354fa581c1 | ||
|
|
d881814a35 | ||
|
|
ad048d78ac | ||
|
|
109dbc0b75 | ||
|
|
745ecf2825 | ||
|
|
265785b7b9 | ||
|
|
97f4d48a07 | ||
|
|
3cc7f0ba43 | ||
|
|
dde4695136 | ||
|
|
3337930292 | ||
|
|
44cc148907 | ||
|
|
b2e16facd4 | ||
|
|
4fd662fea9 | ||
|
|
12c058698b | ||
|
|
33095916ec | ||
|
|
5818cf8596 | ||
|
|
1fa0bd1e87 | ||
|
|
592c6bc3e5 | ||
|
|
efc81c93a9 | ||
|
|
35d4f6737a | ||
|
|
291a042b3e | ||
|
|
9dad3721a9 | ||
|
|
dbb1c2d10c | ||
|
|
e8e34ed6fb | ||
|
|
c529782a8d | ||
|
|
2dfd48492e | ||
|
|
a6bdf313f2 | ||
|
|
915685e01b | ||
|
|
05540220a9 | ||
|
|
75158e1086 | ||
|
|
676537cf29 | ||
|
|
d857fd42ac | ||
|
|
5856b71eb7 | ||
|
|
57adb2973a | ||
|
|
ead44adcd3 | ||
|
|
d3085f7add | ||
|
|
1da64c37e8 | ||
|
|
ef36b097bf | ||
|
|
ec3833c96e | ||
|
|
25dfc2c41d | ||
|
|
d5b7889d40 | ||
|
|
74561d70b5 | ||
|
|
83f7372369 | ||
|
|
8c3de35b0b | ||
|
|
ec1d5d564c | ||
|
|
26befd9c6c | ||
|
|
a28c4558c5 | ||
|
|
ed6a0dc7c2 | ||
|
|
9dd62525f3 | ||
|
|
ada588a7a8 | ||
|
|
286e164ed6 | ||
|
|
85b8dc8aba | ||
|
|
0748800118 | ||
|
|
b7fab6f285 | ||
|
|
9fc7deab0d | ||
|
|
b115bc8a5d | ||
|
|
cd018c7a4c | ||
|
|
9fce0eb5ab | ||
|
|
33e94849b1 | ||
|
|
8f1511184a | ||
|
|
acca9d43d3 | ||
|
|
58f464f4da | ||
|
|
7eb59a9152 | ||
|
|
740ed11aa8 | ||
|
|
d2a97a7ab4 | ||
|
|
15bc7ded39 | ||
|
|
f0810068a6 | ||
|
|
7aa2f36317 | ||
|
|
9cd85b8496 | ||
|
|
f8dcd7d452 | ||
|
|
69530b406e | ||
|
|
122b0fdc4f | ||
|
|
b990d97d35 | ||
|
|
fd6839b746 | ||
|
|
a79d9b22b1 | ||
|
|
2613c9d98a | ||
|
|
27aff880a9 | ||
|
|
7b53e67d64 | ||
|
|
da956469a1 | ||
|
|
9c59632d8b | ||
|
|
d1f458d383 | ||
|
|
a35a900ac0 | ||
|
|
f4c3969b63 | ||
|
|
ee75e51f2f | ||
|
|
9f9240b661 | ||
|
|
48885309c7 | ||
|
|
203b3d0143 | ||
|
|
25526eb3fe | ||
|
|
c3f7414c45 | ||
|
|
4c4fce5107 | ||
|
|
6816b31378 | ||
|
|
e94bad1c15 | ||
|
|
617d726a3d | ||
|
|
020eda887f | ||
|
|
b5f8021a12 | ||
|
|
7b6947576a | ||
|
|
9375a8c4c2 | ||
|
|
7f5c4084c7 | ||
|
|
6c89f00d1b | ||
|
|
dee0993286 | ||
|
|
47351c2b16 | ||
|
|
16b7670614 | ||
|
|
72b2a81f90 | ||
|
|
d73c26d2b7 | ||
|
|
e83bbeb673 | ||
|
|
b6aa9c5cfe | ||
|
|
dfe3b77cb5 | ||
|
|
cbe3b2bfe5 | ||
|
|
b1ae7fc941 | ||
|
|
8695bcc2b1 | ||
|
|
4ae6f708b1 | ||
|
|
14c4656fb8 | ||
|
|
13cec31f7f | ||
|
|
5db7e4b1ee | ||
|
|
54693fa992 | ||
|
|
3f83bcb4af | ||
|
|
e1e546d67e | ||
|
|
3714084f48 | ||
|
|
eb2c3a5e18 | ||
|
|
f6967eca58 | ||
|
|
8455bf66c2 | ||
|
|
00e59e01e3 | ||
|
|
91eaffe13f | ||
|
|
2066024981 | ||
|
|
d274b2096f | ||
|
|
0327a2526b | ||
|
|
f43412f1d5 | ||
|
|
b9010ec617 | ||
|
|
21ecc833ea | ||
|
|
f9bb8f76ee | ||
|
|
a5a9f268fe | ||
|
|
0a255771f4 | ||
|
|
e07d79ad50 | ||
|
|
2f74eb7584 | ||
|
|
39741c7d50 | ||
|
|
3f016888fd | ||
|
|
e5a012c959 | ||
|
|
c3cf174e5e | ||
|
|
a0a7c9f2e3 | ||
|
|
a8f61ba937 | ||
|
|
842d6edfdc | ||
|
|
92a8855ff3 | ||
|
|
def96fd7c4 | ||
|
|
f624a73bbc | ||
|
|
93a373f6ba | ||
|
|
01742c07e6 | ||
|
|
e00662f263 | ||
|
|
491ddb08a4 | ||
|
|
918cb39fed | ||
|
|
1369fe43e1 | ||
|
|
150f3416ac | ||
|
|
d8941be8cb | ||
|
|
592059c8fd | ||
|
|
37f4a23f60 | ||
|
|
27be94c889 | ||
|
|
974f49e22a | ||
|
|
9f7506ac1b | ||
|
|
8779d6c8bb | ||
|
|
96be713fd2 | ||
|
|
13d8fc9542 | ||
|
|
f74473b151 | ||
|
|
5eda68f11b | ||
|
|
f635207347 | ||
|
|
64f7e893f3 | ||
|
|
31556ec7a8 | ||
|
|
9ad3f4385f | ||
|
|
e9899dbdb4 | ||
|
|
18cffa8aa9 | ||
|
|
7e07a32504 | ||
|
|
be11a496bb | ||
|
|
c6f5f0b505 | ||
|
|
f553da1730 | ||
|
|
40753bcbf7 | ||
|
|
33df361d52 | ||
|
|
f4db970718 | ||
|
|
ab3928898f | ||
|
|
13f274fd02 | ||
|
|
b51fe50e7f | ||
|
|
1829a2ee0d | ||
|
|
65370a0f56 | ||
|
|
23213099e9 | ||
|
|
25e08110d5 | ||
|
|
95f683039d | ||
|
|
129d7195ff | ||
|
|
044339d6b4 | ||
|
|
4c4fc7462a | ||
|
|
22cb57ee20 | ||
|
|
4c0be4da13 | ||
|
|
4549855126 | ||
|
|
284c28c773 | ||
|
|
d2406ae372 | ||
|
|
1e9c34972a | ||
|
|
116bd19324 | ||
|
|
883de22c29 | ||
|
|
18f500a7a4 | ||
|
|
d14b0ca4db | ||
|
|
4156e7d464 | ||
|
|
9e48da65c1 | ||
|
|
2cdf9416ee | ||
|
|
cd0c83e485 | ||
|
|
0e814e956c | ||
|
|
f47e5a7732 | ||
|
|
91fff802b9 | ||
|
|
3c8ac20d63 | ||
|
|
38a521defd | ||
|
|
2f13049600 | ||
|
|
af531cf787 | ||
|
|
d495e343c0 | ||
|
|
de7e4d00ab | ||
|
|
374cc1be74 | ||
|
|
8b25488fe9 | ||
|
|
4f5742baa0 | ||
|
|
2b416de4ca | ||
|
|
1f41b7dca1 | ||
|
|
486e7852db | ||
|
|
a68a92793c | ||
|
|
a84e8aced7 | ||
|
|
3bc2d9aeaa | ||
|
|
6214c26bd3 | ||
|
|
da7a350667 | ||
|
|
66ca4fc97b | ||
|
|
7d63f8b249 | ||
|
|
bb1365dd77 | ||
|
|
bcc273d460 | ||
|
|
a6da3c67f8 | ||
|
|
ab110fc8fb | ||
|
|
7265d96116 | ||
|
|
560b63b051 | ||
|
|
0eb82a7c90 | ||
|
|
f92a5182fc | ||
|
|
fb6fabc116 | ||
|
|
c3269275a8 | ||
|
|
7e47855d47 | ||
|
|
d2d6ad481a | ||
|
|
5dcb49c7dd | ||
|
|
19d8550cf4 | ||
|
|
7610f76aea | ||
|
|
59cb358fda | ||
|
|
bb16db1747 | ||
|
|
d6f0342a34 | ||
|
|
6f6e5b51cc | ||
|
|
28de25a664 | ||
|
|
052b34dceb | ||
|
|
748b5c5d53 | ||
|
|
e1e4ffe057 | ||
|
|
1b53b2ff4b | ||
|
|
da45cecfc8 | ||
|
|
87b5d233e9 | ||
|
|
194cee671d | ||
|
|
b7c5520add | ||
|
|
2bee307592 | ||
|
|
85e62c330d | ||
|
|
0add026a5d | ||
|
|
f4184849c4 | ||
|
|
565cde84a7 | ||
|
|
f0e670b4c6 | ||
|
|
ef8951779d | ||
|
|
e285f8f9d2 | ||
|
|
cb383673f6 | ||
|
|
0768d620a5 | ||
|
|
d640d78f91 | ||
|
|
544b3d8b3b | ||
|
|
ce12142c45 | ||
|
|
c83a81ca38 | ||
|
|
d88db22ae8 | ||
|
|
a1cc50ba96 | ||
|
|
feb2fff894 | ||
|
|
7d30490ef4 | ||
|
|
317beebef8 | ||
|
|
7a413c9722 | ||
|
|
5be7363297 | ||
|
|
646784f0e5 | ||
|
|
18ed3f0279 | ||
|
|
00dd50a00c | ||
|
|
7039d14616 | ||
|
|
ec3c9f2f5a | ||
|
|
3b4f5fb891 | ||
|
|
76064b1bf2 | ||
|
|
8df766917e | ||
|
|
299430a6c1 |
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# The rsync-web/ subdirectory holds the project website source content
|
||||
# (mirrors what gets pushed to https://rsync.samba.org). Exclude it from
|
||||
# `git archive` output so the release source tarball produced by
|
||||
# packaging/release.py step_7_tarball does not bloat with HTML the
|
||||
# tarball doesn't need.
|
||||
/rsync-web/ export-ignore
|
||||
82
.github/workflows/almalinux-8-build.yml
vendored
Normal file
82
.github/workflows/almalinux-8-build.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Test rsync on AlmaLinux 8
|
||||
|
||||
# Older-LTS coverage on the Fedora/RHEL family to help with backporting
|
||||
# security fixes. AlmaLinux 8 is the RHEL 8 rebuild and is the oldest
|
||||
# active LTS in this family (RHEL 8 full support runs to 2029).
|
||||
# GitHub Actions has no native runner for this family, so the job runs
|
||||
# inside an almalinux:8 container hosted on ubuntu-latest.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: almalinux:8
|
||||
name: Test rsync on AlmaLinux 8
|
||||
steps:
|
||||
- name: install git
|
||||
# actions/checkout needs git in the container before the checkout step.
|
||||
run: dnf -y install git
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
# PowerTools is needed for libzstd-devel etc; xxhash and lz4 dev
|
||||
# headers live in EPEL on RHEL 8. The default python3 on RHEL 8
|
||||
# is 3.6, which is too old for runtests.py (uses capture_output=
|
||||
# / text= introduced in 3.7), so install python39 and point
|
||||
# /usr/bin/python3 at it.
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf config-manager --set-enabled powertools
|
||||
dnf -y install gcc gcc-c++ make autoconf automake m4 \
|
||||
python39 python39-pip diffutils \
|
||||
openssl openssl-devel \
|
||||
attr libattr-devel acl libacl-devel \
|
||||
zstd libzstd-devel \
|
||||
lz4 lz4-devel \
|
||||
xxhash xxhash-devel
|
||||
alternatives --set python3 /usr/bin/python3.9
|
||||
pip3 install commonmark
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: info
|
||||
run: ./rsync --version
|
||||
- name: check
|
||||
# In the container we already run as root, so no sudo. The
|
||||
# crtimes-not-supported skip matches the other Linux jobs;
|
||||
# daemon-chroot-acl and proxy-response-line-too-long skip because
|
||||
# the default (secure) transport opens no listening socket.
|
||||
run: RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run exercising the real loopback-TCP daemon path.
|
||||
run: ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: ./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: almalinux-8-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
120
.github/workflows/android-static-build.yml
vendored
Normal file
120
.github/workflows/android-static-build.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
name: Build static rsync for Android
|
||||
|
||||
# Cross-compiles statically-linked rsync binaries with the Android NDK,
|
||||
# suitable for dropping onto a phone (adb push / Termux) with no shared
|
||||
# libraries. arm64-v8a covers all modern phones; armeabi-v7a covers older
|
||||
# 32-bit devices. The binaries are uploaded as workflow artifacts.
|
||||
#
|
||||
# These are cross-compiled, so the test suite can't run here; we sanity
|
||||
# check that each binary is the right architecture, is static, and that
|
||||
# it executes (`--version`) under qemu-user.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Minimum supported API level. 24 (Android 7.0) runs on every modern
|
||||
# phone while keeping broad reach; bump if you need newer Bionic APIs.
|
||||
ANDROID_API: 24
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.abi }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- abi: arm64-v8a # modern phones
|
||||
triple: aarch64-linux-android
|
||||
qemu: qemu-aarch64-static
|
||||
- abi: armeabi-v7a # older 32-bit phones
|
||||
triple: armv7a-linux-androideabi
|
||||
qemu: qemu-arm-static
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install build prerequisites
|
||||
run: sudo apt-get update && sudo apt-get install -y autoconf automake gawk qemu-user-static
|
||||
|
||||
- name: Configure and build (${{ matrix.abi }})
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
NDK="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_ROOT}"
|
||||
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
export CC="$TC/${{ matrix.triple }}${ANDROID_API}-clang"
|
||||
export AR="$TC/llvm-ar" RANLIB="$TC/llvm-ranlib" STRIP="$TC/llvm-strip"
|
||||
export CFLAGS="-O2" LDFLAGS="-static"
|
||||
|
||||
# Bionic doesn't declare lchmod()/lutimes() until API 36, but the
|
||||
# symbols link, so configure mis-detects them -- force them off so
|
||||
# rsync uses its fallbacks. The other cache vars restore values
|
||||
# that configure can't probe when cross-compiling (Android runs a
|
||||
# normal Linux kernel, so these match the native Linux result).
|
||||
export ac_cv_func_lchmod=no ac_cv_func_lutimes=no \
|
||||
rsync_cv_HAVE_SOCKETPAIR=yes \
|
||||
rsync_cv_MKNOD_CREATES_FIFOS=yes \
|
||||
rsync_cv_MKNOD_CREATES_SOCKETS=yes
|
||||
|
||||
# Self-contained build: drop optional external libraries so the
|
||||
# static binary needs nothing at runtime. rsync keeps md5/md4
|
||||
# checksums and its bundled zlib.
|
||||
./configure --host=${{ matrix.triple }} --build=x86_64-pc-linux-gnu \
|
||||
--enable-ipv6 \
|
||||
--disable-zstd --disable-lz4 --disable-xxhash --disable-openssl \
|
||||
--disable-iconv --disable-iconv-open \
|
||||
--disable-acl-support --disable-xattr-support \
|
||||
--disable-md2man --disable-roll-simd \
|
||||
--with-included-popt --with-included-zlib
|
||||
|
||||
# Generate the awk-built headers serially first so the parallel
|
||||
# build can't race on proto.h <- daemon-parm.h.
|
||||
make proto.h
|
||||
make -j"$(nproc)" rsync
|
||||
"$STRIP" rsync
|
||||
|
||||
- name: Verify binary
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
file rsync
|
||||
# Gate: must be a statically-linked executable (no interpreter).
|
||||
file rsync | grep -q "statically linked"
|
||||
if file rsync | grep -q "dynamically linked"; then
|
||||
echo "ERROR: binary is not static" >&2; exit 1
|
||||
fi
|
||||
# Best-effort: confirm it actually runs under qemu-user.
|
||||
${{ matrix.qemu }} ./rsync --version | head -3 || \
|
||||
echo "WARNING: qemu smoke test did not run cleanly (check on a real device)"
|
||||
|
||||
- name: Package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VER=$(sed -n 's/.*RSYNC_VERSION "\([^"]*\)".*/\1/p' version.h)
|
||||
out="rsync-${VER}-android-${{ matrix.abi }}"
|
||||
mkdir -p dist
|
||||
cp rsync "dist/$out"
|
||||
( cd dist && sha256sum "$out" > "$out.sha256" )
|
||||
echo "ARTIFACT_NAME=rsync-android-${{ matrix.abi }}" >>"$GITHUB_ENV"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: dist/
|
||||
31
.github/workflows/ccpp.yml
vendored
31
.github/workflows/ccpp.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: prepare-packages
|
||||
run: sudo apt-get install fakeroot acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm
|
||||
- name: prepare-source
|
||||
run: ./prepare-source
|
||||
- name: configure
|
||||
run: ./configure --with-included-popt --with-included-zlib
|
||||
- name: make
|
||||
run: make
|
||||
- name: version-summary
|
||||
run: ./rsync --version
|
||||
- name: make check
|
||||
run: make check
|
||||
- name: make check30
|
||||
run: make check30
|
||||
- name: make check29
|
||||
run: make check29
|
||||
71
.github/workflows/coverage.yml
vendored
Normal file
71
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: Coverage (Ubuntu)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
schedule:
|
||||
- cron: '42 9 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
name: gcov coverage
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl gcovr
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --enable-coverage --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: info
|
||||
run: rsync --version
|
||||
# Two coverage runs: the default pipe transport, then a second pass over a
|
||||
# real loopback rsyncd (--use-tcp) which also exercises the require_tcp-only
|
||||
# tests. gcovr's --print-summary line/branch/decision totals go to the step
|
||||
# log (and the job summary below), so the numbers are visible in CI.
|
||||
# `make coverage` exits with the suite's status, so a regression fails CI.
|
||||
- name: coverage (pipe transport)
|
||||
run: |
|
||||
set -o pipefail
|
||||
sudo make coverage 2>&1 | tee cov-pipe.log
|
||||
- name: coverage (TCP transport)
|
||||
run: |
|
||||
set -o pipefail
|
||||
sudo make coverage-tcp 2>&1 | tee cov-tcp.log
|
||||
- name: coverage summary
|
||||
if: always()
|
||||
run: |
|
||||
{
|
||||
echo "## gcov coverage"
|
||||
echo "### Pipe transport (\`make coverage\`)"
|
||||
echo '```'
|
||||
grep -E '^(lines|functions|branches|decisions):' cov-pipe.log || echo '(no summary -- see step log)'
|
||||
echo '```'
|
||||
echo "### TCP transport (\`make coverage-tcp\`)"
|
||||
echo '```'
|
||||
grep -E '^(lines|functions|branches|decisions):' cov-tcp.log || echo '(no summary -- see step log)'
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: upload HTML reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-html
|
||||
path: |
|
||||
coverage
|
||||
coverage-tcp
|
||||
65
.github/workflows/cygwin-build.yml
vendored
Normal file
65
.github/workflows/cygwin-build.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Test rsync on Cygwin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: windows-2022
|
||||
name: Test rsync on Cygwin
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: cygwin
|
||||
run: choco install -y --no-progress cygwin cyg-get
|
||||
- name: prep
|
||||
run: |
|
||||
cyg-get make autoconf automake gcc-core attr libattr-devel python39 python39-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
|
||||
echo "C:/tools/cygwin/bin" >>$Env:GITHUB_PATH
|
||||
- name: commonmark
|
||||
run: bash -c 'python3 -mpip install --user commonmark'
|
||||
- name: configure
|
||||
run: bash -c './configure --with-rrsync'
|
||||
- name: make
|
||||
run: bash -c 'make'
|
||||
- name: install
|
||||
run: bash -c 'make install'
|
||||
- name: info
|
||||
run: bash -c '/usr/local/bin/rsync --version'
|
||||
- name: check
|
||||
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on Cygwin
|
||||
# (rsyncfns.py drives xattrs via getfattr/setfattr from the `attr`
|
||||
# package installed above), verified on a real Cygwin host. The real
|
||||
# chown/devices tests still skip (need root/mknod), as do the
|
||||
# RESOLVE_BENEATH symlink-race tests.
|
||||
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,acls,bare-do-open-symlink-race,chdir-symlink-race,chown,daemon-access-ip,daemon-chroot-acl,devices,dir-sgid,open-noatime,protected-regular,proxy-response-line-too-long,sender-flist-symlink-leak,simd-checksum,symlink-dirlink-basis make check'
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: bash -c './runtests.py --rsync-bin=`pwd`/rsync.exe --use-tcp -j 8'
|
||||
- name: ssl file list
|
||||
run: bash -c 'PATH="/usr/local/bin:$PATH" rsync-ssl --no-motd download.samba.org::rsyncftp/ || true'
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cygwin-bin
|
||||
path: |
|
||||
rsync.exe
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
51
.github/workflows/freebsd-build.yml
vendored
Normal file
51
.github/workflows/freebsd-build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on FreeBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in FreeBSD VM
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y bash autotools m4 devel/xxhash zstd liblz4 python3 archivers/liblz4 git
|
||||
run: |
|
||||
freebsd-version
|
||||
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: freebsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
65
.github/workflows/macos-build.yml
vendored
Normal file
65
.github/workflows/macos-build.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Test rsync on macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
name: Test rsync on macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
brew install automake openssl xxhash zstd lz4
|
||||
pip3 install --user --break-system-packages commonmark
|
||||
echo "$(brew --prefix)/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: |
|
||||
BREW_PREFIX=$(brew --prefix)
|
||||
OPENSSL_PREFIX=$(brew --prefix openssl)
|
||||
CPPFLAGS="-I${BREW_PREFIX}/include -I${OPENSSL_PREFIX}/include" \
|
||||
LDFLAGS="-L${BREW_PREFIX}/lib -L${OPENSSL_PREFIX}/lib" \
|
||||
./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on macOS
|
||||
# (rsyncfns.py drives xattrs via the `xattr` command), verified on a
|
||||
# real macOS host, so they're no longer in the skip set.
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,chmod-temp-dir,daemon-access-ip,daemon-chroot-acl,dir-sgid,open-noatime,preallocate,protected-regular,proxy-response-line-too-long,simd-checksum,sparse make check
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
52
.github/workflows/netbsd-build.yml
vendored
Normal file
52
.github/workflows/netbsd-build.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Test rsync on NetBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on NetBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in NetBSD VM
|
||||
id: test
|
||||
uses: vmactions/netbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
PATH=/usr/sbin:$PATH pkg_add autoconf automake python312
|
||||
ln -sf /usr/pkg/bin/python3.12 /usr/pkg/bin/python3
|
||||
run: |
|
||||
uname -a
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: netbsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
61
.github/workflows/openbsd-build.yml
vendored
Normal file
61
.github/workflows/openbsd-build.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Test rsync on OpenBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on OpenBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in OpenBSD VM
|
||||
id: test
|
||||
uses: vmactions/openbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg_add -I bash autoconf%2.71 automake%1.16
|
||||
run: |
|
||||
uname -a
|
||||
export AUTOCONF_VERSION=2.71
|
||||
export AUTOMAKE_VERSION=1.16
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
# The --use-tcp daemon tests run at -j2 here (vs -j8 elsewhere): this
|
||||
# job runs inside a nested VM, and at -j8 the many concurrent loopback
|
||||
# daemons occasionally lose a connection-handshake timing race under
|
||||
# that resource pressure, hanging one test to the 300s timeout. It is
|
||||
# an environment artifact, not an rsync bug (the handshake is
|
||||
# deadlock-free and unreproducible elsewhere, even pinned to 1 CPU at
|
||||
# -j8); -j2 keeps the VM from over-subscribing. The pipe `make check`
|
||||
# above stays at the default parallelism.
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 2
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openbsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
51
.github/workflows/solaris-build.yml
vendored
Normal file
51
.github/workflows/solaris-build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on Solaris
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on Solaris
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in Solaris VM
|
||||
id: test
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install bash automake gnu-m4 pkg://solaris/runtime/python-35 autoconf gcc git
|
||||
run: |
|
||||
uname -a
|
||||
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: solaris-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
64
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
64
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
|
||||
# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04
|
||||
# is currently the oldest GitHub Actions runner image (20.04 was retired
|
||||
# in April 2025).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check29
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-22.04-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
62
.github/workflows/ubuntu-build.yml
vendored
Normal file
62
.github/workflows/ubuntu-build.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Test rsync on Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on Ubuntu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check29
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd. The default
|
||||
# 'make check' above uses the secure stdio-pipe transport (no listening
|
||||
# sockets); this run exercises the real TCP accept/auth path. Skip-set
|
||||
# is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -15,22 +15,27 @@ config.status
|
||||
aclocal.m4
|
||||
/proto.h
|
||||
/proto.h-tstamp
|
||||
/rsync*.1
|
||||
/rsync*.5
|
||||
/rsync*.[15]
|
||||
/rrsync
|
||||
/rrsync*.1
|
||||
/rsync*.html
|
||||
/rrsync*.html
|
||||
/help-rsync*.h
|
||||
/default-cvsignore.h
|
||||
/default-dont-compress.h
|
||||
/daemon-parm.h
|
||||
/.md2man-works
|
||||
/autom4te*.cache
|
||||
/confdefs.h
|
||||
/conftest*
|
||||
/dox
|
||||
/getgroups
|
||||
/gists
|
||||
/gmon.out
|
||||
/rsync
|
||||
/stunnel-rsyncd.conf
|
||||
/shconfig
|
||||
/git-version.h
|
||||
/testdir
|
||||
/tests-dont-exist
|
||||
/testtmp
|
||||
@@ -48,6 +53,9 @@ aclocal.m4
|
||||
/testsuite/devices-fake.test
|
||||
/testsuite/xattrs-hlink.test
|
||||
/patches
|
||||
/SaVeDiR
|
||||
/.gen-stash
|
||||
/patches.gen
|
||||
/build
|
||||
/auto-build-save
|
||||
.deps
|
||||
/*.exe
|
||||
*.dSYM/
|
||||
|
||||
26
COPYING
26
COPYING
@@ -1,7 +1,16 @@
|
||||
REGARDING OPENSSL AND XXHASH
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to dynamically link rsync with the OpenSSL and xxhash
|
||||
libraries when those libraries are being distributed in compliance
|
||||
with their license terms, and to distribute a dynamically linked
|
||||
combination of rsync and these libraries. This is also considered
|
||||
to be covered under the GPL's System Libraries exception.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -645,7 +654,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -664,20 +673,11 @@ might be different; for a GUI interface, you would use an "about box".
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU 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. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
REGARDING OPENSSL AND XXHASH
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to dynamically link rsync with the OpenSSL and xxhash
|
||||
libraries when those libraries are being distributed in compliance
|
||||
with their license terms, and to distribute a dynamically linked
|
||||
combination of rsync and these libraries. This is also considered
|
||||
to be covered under the GPL's System Libraries exception.
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
247
INSTALL.md
247
INSTALL.md
@@ -1,13 +1,190 @@
|
||||
To build and install rsync:
|
||||
# How to build and install rsync
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
# make install
|
||||
When building rsync, you'll want to install various libraries in order to get
|
||||
all the features enabled. The configure script will alert you when the
|
||||
newest libraries are missing and tell you the appropriate `--disable-LIB`
|
||||
option to use if you want to just skip that feature. What follows are various
|
||||
support libraries that you may want to install to build rsync with the maximum
|
||||
features (the impatient can skip down to the package summary):
|
||||
|
||||
You may set the installation directory and other parameters by options
|
||||
to ./configure. To see them, use:
|
||||
## Ubuntu users: skip the build, use the PPA
|
||||
|
||||
$ ./configure --help
|
||||
If you are on a currently supported Ubuntu series (jammy 22.04 LTS, noble
|
||||
24.04 LTS, questing 25.10, resolute 26.04 LTS) and just want the latest
|
||||
upstream rsync, the rsync project maintains a Launchpad PPA that tracks
|
||||
stable releases:
|
||||
|
||||
> sudo add-apt-repository ppa:rsyncproject/rsync
|
||||
> sudo apt update && sudo apt install rsync
|
||||
|
||||
See [the PPA page][ppa] for current build status across architectures.
|
||||
|
||||
[ppa]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync
|
||||
|
||||
The rest of this document covers building from source.
|
||||
|
||||
## The basic setup
|
||||
|
||||
You need to have a C compiler installed and optionally a C++ compiler in order
|
||||
to try to build some hardware-accelerated checksum routines. Rsync also needs
|
||||
a modern awk, which might be provided via gawk or nawk on some OSes.
|
||||
|
||||
## Autoconf & manpages
|
||||
|
||||
If you're installing from the git repo (instead of a release tar file) you'll
|
||||
also need the GNU autotools (autoconf & automake) and your choice of 2 python3
|
||||
markdown libraries: cmarkgfm or commonmark (needed to generate the manpages).
|
||||
If your OS doesn't provide a python3-cmarkgfm or python3-commonmark package,
|
||||
you can run the following to install the commonmark python library for your
|
||||
build user (after installing python3's pip package):
|
||||
|
||||
> python3 -mpip install --user commonmark
|
||||
|
||||
You can test if you've got it fixed by running (from the rsync checkout):
|
||||
|
||||
> ./md-convert --test rsync-ssl.1.md
|
||||
|
||||
Alternately, you can avoid generating the manpages by fetching the very latest
|
||||
versions (that match the latest git source) from the [generated-files][6] dir.
|
||||
One way to do that is to run:
|
||||
|
||||
> ./prepare-source fetchgen
|
||||
|
||||
[6]: https://download.samba.org/pub/rsync/generated-files/
|
||||
|
||||
## ACL support
|
||||
|
||||
To support copying ACL file information, make sure you have an acl
|
||||
development library installed. It also helps to have the helper programs
|
||||
installed to manipulate ACLs and to run the rsync testsuite.
|
||||
|
||||
## Xattr support
|
||||
|
||||
To support copying xattr file information, make sure you have an attr
|
||||
development library installed. It also helps to have the helper programs
|
||||
installed to manipulate xattrs and to run the rsync testsuite.
|
||||
|
||||
## xxhash
|
||||
|
||||
The [xxHash library][1] provides extremely fast checksum functions that can
|
||||
make the "rsync algorithm" run much more quickly, especially when matching
|
||||
blocks in large files. Installing this development library adds xxhash
|
||||
checksums as the default checksum algorithm. You'll need at least v0.8.0
|
||||
if you want rsync to include the full range of its checksum algorithms.
|
||||
|
||||
[1]: https://cyan4973.github.io/xxHash/
|
||||
|
||||
## zstd
|
||||
|
||||
The [zstd library][2] compression algorithm that uses less CPU than
|
||||
the default zlib algorithm at the same compression level. Note that you
|
||||
need at least version 1.4, so you might need to skip the zstd compression if
|
||||
you can only install a 1.3 release. Installing this development library
|
||||
adds zstd compression as the default compression algorithm.
|
||||
|
||||
[2]: http://facebook.github.io/zstd/
|
||||
|
||||
## lz4
|
||||
|
||||
The [lz4 library][3] compression algorithm that uses very little CPU, though
|
||||
it also has the smallest compression ratio of other algorithms. Installing
|
||||
this development library adds lz4 compression as an available compression
|
||||
algorithm.
|
||||
|
||||
[3]: https://lz4.github.io/lz4/
|
||||
|
||||
## openssl crypto
|
||||
|
||||
The [openssl crypto library][4] provides some hardware accelerated checksum
|
||||
algorithms for MD4 and MD5. Installing this development library makes rsync
|
||||
use the (potentially) faster checksum routines when computing MD4 & MD5
|
||||
checksums.
|
||||
|
||||
[4]: https://www.openssl.org/docs/man1.0.2/man3/crypto.html
|
||||
|
||||
## Package summary
|
||||
|
||||
To help you get the libraries installed, here are some package install commands
|
||||
for various OSes. The commands are split up to correspond with the above
|
||||
items, but feel free to combine the package names into a single install, if you
|
||||
like.
|
||||
|
||||
- For Debian and Ubuntu (Debian Buster users may want to briefly(?) enable
|
||||
buster-backports to update zstd from 1.3 to 1.4):
|
||||
|
||||
> sudo apt install -y gcc g++ gawk autoconf automake python3-cmarkgfm
|
||||
> sudo apt install -y acl libacl1-dev
|
||||
> sudo apt install -y attr libattr1-dev
|
||||
> sudo apt install -y libxxhash-dev
|
||||
> sudo apt install -y libzstd-dev
|
||||
> sudo apt install -y liblz4-dev
|
||||
> sudo apt install -y libssl-dev
|
||||
|
||||
Or run support/install_deps_ubuntu.sh
|
||||
|
||||
- For CentOS (use EPEL for python3-pip):
|
||||
|
||||
> sudo yum -y install epel-release
|
||||
> sudo yum -y install gcc g++ gawk autoconf automake python3-pip
|
||||
> sudo yum -y install acl libacl-devel
|
||||
> sudo yum -y install attr libattr-devel
|
||||
> sudo yum -y install xxhash-devel
|
||||
> sudo yum -y install libzstd-devel
|
||||
> sudo yum -y install lz4-devel
|
||||
> sudo yum -y install openssl-devel
|
||||
> python3 -mpip install --user commonmark
|
||||
|
||||
- For Fedora 33:
|
||||
|
||||
> sudo dnf -y install acl libacl-devel
|
||||
> sudo dnf -y install attr libattr-devel
|
||||
> sudo dnf -y install xxhash-devel
|
||||
> sudo dnf -y install libzstd-devel
|
||||
> sudo dnf -y install lz4-devel
|
||||
> sudo dnf -y install openssl-devel
|
||||
|
||||
- For FreeBSD (this assumes that the python3 version is 3.7):
|
||||
|
||||
> sudo pkg install -y autotools python3 py37-CommonMark
|
||||
> sudo pkg install -y xxhash
|
||||
> sudo pkg install -y zstd
|
||||
> sudo pkg install -y liblz4
|
||||
|
||||
- For macOS:
|
||||
|
||||
> brew install automake
|
||||
> brew install xxhash
|
||||
> brew install zstd
|
||||
> brew install lz4
|
||||
> brew install openssl
|
||||
|
||||
- For Cygwin (with all cygwin programs stopped, run the appropriate setup program from a cmd shell):
|
||||
|
||||
> setup-x86_64 --quiet-mode -P make,gawk,autoconf,automake,gcc-core,python38,python38-pip
|
||||
> setup-x86_64 --quiet-mode -P attr,libattr-devel
|
||||
> setup-x86_64 --quiet-mode -P libzstd-devel
|
||||
> setup-x86_64 --quiet-mode -P liblz4-devel
|
||||
> setup-x86_64 --quiet-mode -P libssl-devel
|
||||
|
||||
Sometimes cygwin has commonmark packaged and sometimes it doesn't. Now that
|
||||
its python38 has stabilized, you could install python38-commonmark. Or just
|
||||
avoid the issue by running this from a bash shell as your build user:
|
||||
|
||||
> python3 -mpip install --user commonmark
|
||||
|
||||
## Build and install
|
||||
|
||||
After installing the various libraries, you need to configure, build, and
|
||||
install the source:
|
||||
|
||||
> ./configure
|
||||
> make
|
||||
> sudo make install
|
||||
|
||||
The default install path is /usr/local/bin, but you can set the installation
|
||||
directory and other parameters using options to ./configure. To see them, use:
|
||||
|
||||
> ./configure --help
|
||||
|
||||
Configure tries to figure out if the local system uses group "nobody" or
|
||||
"nogroup" by looking in the /etc/group file. (This is only used for the
|
||||
@@ -19,56 +196,68 @@ config.h, or just override them in your /etc/rsyncd.conf file.
|
||||
As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
|
||||
cut-down copy of a recent release is included in the rsync distribution,
|
||||
and will be used if there is no popt library on your build host, or if
|
||||
the --with-included-popt option is passed to ./configure.
|
||||
the `--with-included-popt` option is passed to ./configure.
|
||||
|
||||
If you configure using --enable-maintainer-mode, then rsync will try
|
||||
If you configure using `--enable-maintainer-mode`, then rsync will try
|
||||
to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
|
||||
useful, but it should be turned off for production builds.
|
||||
|
||||
MAKE COMPATIBILITY
|
||||
------------------
|
||||
If you want to automatically use a separate "build" directory based on
|
||||
the current git branch name, start with a pristine git checkout and run
|
||||
"mkdir auto-build-save" before you run the first ./configure command.
|
||||
That will cause a fresh build dir to spring into existence along with a
|
||||
special Makefile symlink that allows you to run "make" and "./configure"
|
||||
from the source dir (the "build" dir gets auto switched based on branch).
|
||||
This is helpful when using the branch-from-patch and patch-update scripts
|
||||
to maintain the official rsync patches. If you ever need to build from
|
||||
a "detached head" git position then you'll need to manually chdir into
|
||||
the build dir to run make. I also like to create 2 more symlinks in the
|
||||
source dir: `ln -s build/rsync . ; ln -s build/testtmp .`
|
||||
|
||||
## Make compatibility
|
||||
|
||||
Note that Makefile.in has a rule that uses a wildcard in a prerequisite. If
|
||||
your make has a problem with this rule, you will see an error like this:
|
||||
|
||||
Don't know how to make ./*.c
|
||||
|
||||
You can change the "proto.h-tstamp" target in Makefile.in to list all the *.c
|
||||
You can change the "proto.h-tstamp" target in Makefile.in to list all the \*.c
|
||||
filenames explicitly in order to avoid this issue.
|
||||
|
||||
RPM NOTES
|
||||
---------
|
||||
## RPM notes
|
||||
|
||||
Under packaging you will find .spec files for several distributions.
|
||||
The .spec file in packaging/lsb can be used for Linux systems that
|
||||
adhere to the Linux Standards Base (e.g., RedHat and others).
|
||||
|
||||
HP-UX NOTES
|
||||
-----------
|
||||
## HP-UX notes
|
||||
|
||||
The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
|
||||
ANSI C. You may see this error message in config.log if ./configure
|
||||
fails:
|
||||
|
||||
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
|
||||
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
|
||||
|
||||
Install gcc or HP's "ANSI/C Compiler".
|
||||
|
||||
MAC OSX NOTES
|
||||
-------------
|
||||
## Mac OS X notes
|
||||
|
||||
Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
|
||||
not completely implement the "New Sockets" API.
|
||||
not completely implement the "New Sockets" API.
|
||||
|
||||
<http://www.ipv6.org/impl/mac.html> says that Apple started to support
|
||||
IPv6 in 10.2 (Jaguar). If your build fails, try again after running
|
||||
configure with --disable-ipv6.
|
||||
[This site][5] says that Apple started to support IPv6 in 10.2 (Jaguar). If
|
||||
your build fails, try again after running configure with `--disable-ipv6`.
|
||||
|
||||
IBM AIX NOTES
|
||||
-------------
|
||||
Apple Silicon macs may install packages in a slightly different location and require flags.
|
||||
CFLAGS="-I /opt/homebrew/include" LDFLAGS="-L /opt/homebrew/lib"
|
||||
|
||||
[5]: http://www.ipv6.org/impl/mac.html
|
||||
|
||||
## IBM AIX notes
|
||||
|
||||
IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
|
||||
The workaround is to append the below to config.h
|
||||
#ifdef _LARGE_FILES
|
||||
#undef HAVE_SECURE_MKSTEMP
|
||||
#endif
|
||||
The workaround is to append the following to config.h:
|
||||
|
||||
> #ifdef _LARGE_FILES
|
||||
> #undef HAVE_SECURE_MKSTEMP
|
||||
> #endif
|
||||
|
||||
289
Makefile.in
289
Makefile.in
@@ -1,5 +1,4 @@
|
||||
# Makefile for rsync. This is processed by configure to produce the final
|
||||
# Makefile
|
||||
# The Makefile for rsync (configure creates it from Makefile.in).
|
||||
|
||||
prefix=@prefix@
|
||||
datarootdir=@datarootdir@
|
||||
@@ -7,6 +6,7 @@ exec_prefix=@exec_prefix@
|
||||
bindir=@bindir@
|
||||
libdir=@libdir@/rsync
|
||||
mandir=@mandir@
|
||||
with_rrsync=@with_rrsync@
|
||||
|
||||
LIBS=@LIBS@
|
||||
CC=@CC@
|
||||
@@ -27,16 +27,16 @@ MKDIR_P=@MKDIR_P@
|
||||
VPATH=$(srcdir)
|
||||
SHELL=/bin/sh
|
||||
|
||||
VERSION=@RSYNC_VERSION@
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
SIMD_x86_64=simd-checksum-x86_64.o
|
||||
ASM_x86_64=lib/md5-asm-x86_64.o
|
||||
ROLL_SIMD_x86_64=simd-checksum-x86_64.o
|
||||
ROLL_ASM_x86_64=simd-checksum-avx2.o
|
||||
MD5_ASM_x86_64=lib/md5-asm-x86_64.o
|
||||
|
||||
GENFILES=configure.sh aclocal.m4 config.h.in proto.h proto.h-tstamp rsync.1 rsync.1.html \
|
||||
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html
|
||||
GENFILES=configure.sh aclocal.m4 config.h.in rsync.1 rsync.1.html \
|
||||
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html \
|
||||
@GEN_RRSYNC@
|
||||
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
|
||||
lib/pool_alloc.h lib/mdigest.h lib/md-defines.h
|
||||
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
|
||||
@@ -44,25 +44,27 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
|
||||
zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
|
||||
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
|
||||
OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
|
||||
util.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
|
||||
util1.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
|
||||
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
|
||||
fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
|
||||
OBJS3=progress.o pipe.o @ASM@
|
||||
usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
|
||||
OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
|
||||
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
|
||||
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
|
||||
popt/popthelp.o popt/poptparse.o
|
||||
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) @SIMD@ $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
|
||||
popt_OBJS= popt/popt.o popt/poptconfig.o \
|
||||
popt/popthelp.o popt/poptparse.o popt/poptint.o
|
||||
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
|
||||
|
||||
TLS_OBJ = tls.o syscall.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
|
||||
TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
|
||||
|
||||
# Programs we must have to run the test cases
|
||||
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
|
||||
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
|
||||
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) t_chmod_secure$(EXEEXT) \
|
||||
t_secure_relpath$(EXEEXT) wildtest$(EXEEXT) simdtest$(EXEEXT)
|
||||
|
||||
CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test testsuite/xattrs-hlink.test
|
||||
CHECK_SYMLINKS = testsuite/chown-fake_test.py testsuite/devices-fake_test.py \
|
||||
testsuite/xattrs-hlink_test.py testsuite/exclude-lsh_test.py
|
||||
|
||||
# Objects for CHECK_PROGS to clean
|
||||
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
|
||||
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o t_chmod_secure.o t_secure_relpath.o trimslash.o wildtest.o
|
||||
|
||||
# note that the -I. is needed to handle config.h when using VPATH
|
||||
.c.o:
|
||||
@@ -70,28 +72,34 @@ CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.
|
||||
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
|
||||
@OBJ_RESTORE@
|
||||
|
||||
# NOTE: consider running "packaging/smart-make" instead of "make" to auto-handle
|
||||
# any changes to configure.sh and the main Makefile prior to a "make all".
|
||||
all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_RRSYNC@ @MAKE_MAN@
|
||||
.PHONY: all
|
||||
all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_MAN@
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
-${MKDIR_P} ${DESTDIR}${bindir}
|
||||
${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir}
|
||||
${INSTALLCMD} -m 755 $(srcdir)/rsync-ssl ${DESTDIR}${bindir}
|
||||
-${MKDIR_P} ${DESTDIR}${mandir}/man1
|
||||
-${MKDIR_P} ${DESTDIR}${mandir}/man5
|
||||
if test -f rsync.1; then ${INSTALLMAN} -m 644 rsync.1 ${DESTDIR}${mandir}/man1; fi
|
||||
if test -f rsync-ssl.1; then ${INSTALLMAN} -m 644 rsync-ssl.1 ${DESTDIR}${mandir}/man1; fi
|
||||
if test -f rsyncd.conf.5; then ${INSTALLMAN} -m 644 rsyncd.conf.5 ${DESTDIR}${mandir}/man5; fi
|
||||
-$(MKDIR_P) $(DESTDIR)$(bindir)
|
||||
$(INSTALLCMD) $(INSTALL_STRIP) -m 755 rsync$(EXEEXT) $(DESTDIR)$(bindir)
|
||||
$(INSTALLCMD) -m 755 $(srcdir)/rsync-ssl $(DESTDIR)$(bindir)
|
||||
-$(MKDIR_P) $(DESTDIR)$(mandir)/man1
|
||||
-$(MKDIR_P) $(DESTDIR)$(mandir)/man5
|
||||
if test -f rsync.1; then $(INSTALLMAN) -m 644 rsync.1 $(DESTDIR)$(mandir)/man1; fi
|
||||
if test -f rsync-ssl.1; then $(INSTALLMAN) -m 644 rsync-ssl.1 $(DESTDIR)$(mandir)/man1; fi
|
||||
if test -f rsyncd.conf.5; then $(INSTALLMAN) -m 644 rsyncd.conf.5 $(DESTDIR)$(mandir)/man5; fi
|
||||
if test "$(with_rrsync)" = yes; then \
|
||||
$(INSTALLCMD) -m 755 rrsync $(DESTDIR)$(bindir); \
|
||||
if test -f rrsync.1; then $(INSTALLMAN) -m 644 rrsync.1 $(DESTDIR)$(mandir)/man1; fi; \
|
||||
fi
|
||||
|
||||
install-ssl-daemon: stunnel-rsyncd.conf
|
||||
-${MKDIR_P} ${DESTDIR}/etc/stunnel
|
||||
${INSTALLCMD} -m 644 stunnel-rsyncd.conf ${DESTDIR}/etc/stunnel/rsyncd.conf
|
||||
-$(MKDIR_P) $(DESTDIR)/etc/stunnel
|
||||
$(INSTALLCMD) -m 644 stunnel-rsyncd.conf $(DESTDIR)/etc/stunnel/rsyncd.conf
|
||||
@if ! ls /etc/rsync-ssl/certs/server.* >/dev/null 2>/dev/null; then \
|
||||
echo "Note that you'll need to install the certificate used by /etc/stunnel/rsyncd.conf"; \
|
||||
fi
|
||||
|
||||
install-all: install install-ssl-client install-ssl-daemon
|
||||
install-all: install install-ssl-daemon
|
||||
|
||||
install-strip:
|
||||
$(MAKE) INSTALL_STRIP='-s' install
|
||||
@@ -99,12 +107,14 @@ install-strip:
|
||||
rsync$(EXEEXT): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
rrsync: support/rrsync
|
||||
cp -p $(srcdir)/support/rrsync rrsync
|
||||
|
||||
$(OBJS): $(HEADERS)
|
||||
$(CHECK_OBJS): $(HEADERS)
|
||||
tls.o xattrs.o: lib/sysxattrs.h
|
||||
options.o: latest-year.h help-rsync.h help-rsyncd.h
|
||||
exclude.o: default-cvsignore.h
|
||||
loadparm.o: default-dont-compress.h
|
||||
usage.o: version.h latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h
|
||||
loadparm.o: default-dont-compress.h daemon-parm.h
|
||||
|
||||
flist.o: rounding.h
|
||||
|
||||
@@ -114,6 +124,9 @@ default-cvsignore.h default-dont-compress.h: rsync.1.md define-from-md.awk
|
||||
help-rsync.h help-rsyncd.h: rsync.1.md help-from-md.awk
|
||||
$(AWK) -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
|
||||
|
||||
daemon-parm.h: daemon-parm.txt daemon-parm.awk
|
||||
$(AWK) -f $(srcdir)/daemon-parm.awk $(srcdir)/daemon-parm.txt
|
||||
|
||||
rounding.h: rounding.c rsync.h proto.h
|
||||
@for r in 0 1 3; do \
|
||||
if $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o rounding -DEXTRA_ROUNDING=$$r -I. $(srcdir)/rounding.c >rounding.out 2>&1; then \
|
||||
@@ -132,11 +145,20 @@ rounding.h: rounding.c rsync.h proto.h
|
||||
fi
|
||||
@rm -f rounding.out
|
||||
|
||||
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
|
||||
@$(srcdir)/cmdormsg disable-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
|
||||
git-version.h: ALWAYS_RUN
|
||||
$(srcdir)/mkgitver
|
||||
|
||||
lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S config.h lib/md-defines.h
|
||||
@$(srcdir)/cmdormsg disable-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
|
||||
.PHONY: ALWAYS_RUN
|
||||
ALWAYS_RUN:
|
||||
|
||||
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
|
||||
@$(srcdir)/cmd-or-msg disable-roll-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
|
||||
|
||||
simd-checksum-avx2.o: simd-checksum-avx2.S
|
||||
@$(srcdir)/cmd-or-msg disable-roll-asm $(CC) $(CFLAGS) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/simd-checksum-avx2.S
|
||||
|
||||
lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S lib/md-defines.h
|
||||
@$(srcdir)/cmd-or-msg disable-md5-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
|
||||
|
||||
tls$(EXEEXT): $(TLS_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TLS_OBJ) $(LIBS)
|
||||
@@ -150,23 +172,27 @@ getgroups$(EXEEXT): getgroups.o
|
||||
getfsdev$(EXEEXT): getfsdev.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
|
||||
|
||||
TRIMSLASH_OBJ = trimslash.o syscall.o t_stub.o lib/compat.o lib/snprintf.o
|
||||
TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
|
||||
trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
|
||||
|
||||
T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
|
||||
T_UNSAFE_OBJ = t_unsafe.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
|
||||
t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
|
||||
|
||||
T_CHMOD_SECURE_OBJ = t_chmod_secure.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
|
||||
t_chmod_secure$(EXEEXT): $(T_CHMOD_SECURE_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_CHMOD_SECURE_OBJ) $(LIBS)
|
||||
|
||||
T_SECURE_RELPATH_OBJ = t_secure_relpath.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
|
||||
t_secure_relpath$(EXEEXT): $(T_SECURE_RELPATH_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SECURE_RELPATH_OBJ) $(LIBS)
|
||||
|
||||
.PHONY: conf
|
||||
conf: configure.sh config.h.in
|
||||
|
||||
.PHONY: gen
|
||||
gen: conf proto.h man
|
||||
|
||||
.PHONY: gensend
|
||||
gensend: gen
|
||||
rsync -aic $(GENFILES) $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/
|
||||
gen: conf proto.h man git-version.h
|
||||
|
||||
aclocal.m4: $(srcdir)/m4/*.m4
|
||||
aclocal -I $(srcdir)/m4
|
||||
@@ -188,7 +214,7 @@ configure.sh config.h.in: configure.ac aclocal.m4
|
||||
else \
|
||||
echo "config.h.in has CHANGED."; \
|
||||
fi
|
||||
@if test -f configure.sh.old -o -f config.h.in.old; then \
|
||||
@if test -f configure.sh.old || test -f config.h.in.old; then \
|
||||
if test "$(MAKECMDGOALS)" = reconfigure; then \
|
||||
echo 'Continuing with "make reconfigure".'; \
|
||||
else \
|
||||
@@ -203,6 +229,10 @@ reconfigure: configure.sh
|
||||
./config.status --recheck
|
||||
./config.status
|
||||
|
||||
.PHONY: restatus
|
||||
restatus:
|
||||
./config.status
|
||||
|
||||
Makefile: Makefile.in config.status configure.sh config.h.in
|
||||
@if test -f Makefile; then cp -p Makefile Makefile.old; else touch Makefile.old; fi
|
||||
@./config.status
|
||||
@@ -227,25 +257,31 @@ proto: proto.h-tstamp
|
||||
proto.h: proto.h-tstamp
|
||||
@if test -f proto.h; then :; else cp -p $(srcdir)/proto.h .; fi
|
||||
|
||||
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c config.h
|
||||
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c
|
||||
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
|
||||
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
|
||||
|
||||
.PHONY: man
|
||||
man: rsync.1 rsync-ssl.1 rsyncd.conf.5
|
||||
man: rsync.1 rsync-ssl.1 rsyncd.conf.5 @MAKE_RRSYNC_1@
|
||||
|
||||
rsync.1: rsync.1.md md2man NEWS.md Makefile
|
||||
@$(srcdir)/maybe-make-man $(srcdir) rsync.1.md
|
||||
rsync.1: rsync.1.md md-convert version.h Makefile
|
||||
@$(srcdir)/maybe-make-man rsync.1.md
|
||||
|
||||
rsync-ssl.1: rsync-ssl.1.md md2man NEWS.md Makefile
|
||||
@$(srcdir)/maybe-make-man $(srcdir) rsync-ssl.1.md
|
||||
rsync-ssl.1: rsync-ssl.1.md md-convert version.h Makefile
|
||||
@$(srcdir)/maybe-make-man rsync-ssl.1.md
|
||||
|
||||
rsyncd.conf.5: rsyncd.conf.5.md md2man NEWS.md Makefile
|
||||
@$(srcdir)/maybe-make-man $(srcdir) rsyncd.conf.5.md
|
||||
rsyncd.conf.5: rsyncd.conf.5.md md-convert version.h Makefile
|
||||
@$(srcdir)/maybe-make-man rsyncd.conf.5.md
|
||||
|
||||
rrsync.1: support/rrsync.1.md md-convert Makefile
|
||||
@$(srcdir)/maybe-make-man support/rrsync.1.md
|
||||
|
||||
.PHONY: clean
|
||||
clean: cleantests
|
||||
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
|
||||
rounding rounding.h *.old rsync*.1 rsync*.5 rsync*.html
|
||||
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) @MAKE_RRSYNC@ \
|
||||
git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 @MAKE_RRSYNC_1@ \
|
||||
*.html daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
|
||||
rm -f *.gcno *.gcda lib/*.gcno lib/*.gcda zlib/*.gcno zlib/*.gcda popt/*.gcno popt/*.gcda
|
||||
rm -rf coverage coverage-tcp coverage-all coverage-fallback
|
||||
|
||||
.PHONY: cleantests
|
||||
cleantests:
|
||||
@@ -256,16 +292,11 @@ cleantests:
|
||||
# the source directory.
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -f Makefile config.h config.status
|
||||
rm -f stunnel-rsyncd.conf
|
||||
rm -f lib/dummy popt/dummy zlib/dummy
|
||||
rm -f $(srcdir)/Makefile $(srcdir)/config.h $(srcdir)/config.status
|
||||
rm -f $(srcdir)/lib/dummy $(srcdir)/popt/dummy $(srcdir)/zlib/dummy
|
||||
rm -f config.cache config.log
|
||||
rm -f $(srcdir)/config.cache $(srcdir)/config.log
|
||||
rm -f shconfig $(srcdir)/shconfig
|
||||
rm -f $(GENFILES)
|
||||
rm -rf autom4te.cache
|
||||
for dir in $(srcdir) . ; do \
|
||||
(cd "$$dir" && rm -rf Makefile config.h config.status stunnel-rsyncd.conf \
|
||||
lib/dummy popt/dummy zlib/dummy config.cache config.log shconfig \
|
||||
$(GENFILES) autom4te.cache) ; \
|
||||
done
|
||||
|
||||
# this target is really just for my use. It only works on a limited
|
||||
# range of machines and is used to produce a list of potentially
|
||||
@@ -275,6 +306,7 @@ finddead:
|
||||
nm *.o */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
|
||||
nm *.o */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
|
||||
comm -13 nmused.txt nmfns.txt
|
||||
@rm nmused.txt nmfns.txt
|
||||
|
||||
# 'check' is the GNU name, 'test' is the name for everybody else :-)
|
||||
.PHONY: test
|
||||
@@ -290,30 +322,133 @@ test: check
|
||||
# catch Bash-isms earlier even if we're running on GNU. Of course, we
|
||||
# might lose in the future where POSIX diverges from old sh.
|
||||
|
||||
# `make check` runs tests in parallel by default. Override with
|
||||
# `make check CHECK_J=1` (serial) or any other value.
|
||||
CHECK_J = 8
|
||||
|
||||
# Parallelism for `make coverage`. Defaults to the same as CHECK_J: the
|
||||
# coverage build sets -fprofile-update=atomic (atomic in-memory counters) and
|
||||
# gcc's libgcov serializes the per-source .gcda read-modify-write merge with a
|
||||
# file lock, so concurrent rsync processes (incl. the forked sender/generator/
|
||||
# receiver) accumulate exactly -- verified by a count-linearity check (a hot
|
||||
# line accumulates identically at -j1 and -P16). Override with
|
||||
# `make coverage COVERAGE_J=1` if your libgcov does not lock .gcda merges.
|
||||
COVERAGE_J = $(CHECK_J)
|
||||
|
||||
# Output directory and extra runtests.py flags for `make coverage`. The
|
||||
# `coverage-tcp` target reuses the coverage recipe with --use-tcp (real
|
||||
# loopback rsyncd, which exercises the TCP accept/auth path and the
|
||||
# require_tcp-only tests) and a separate output directory.
|
||||
COVERAGE_DIR = coverage
|
||||
COVERAGE_RUNFLAGS =
|
||||
|
||||
# Bundled third-party code that rsync ships but does not own; excluded from the
|
||||
# coverage report so the percentages reflect rsync's own source. zlib/ and popt/
|
||||
# are wholly vendored; the named lib/ files are PostgreSQL (getaddrinfo) and ISC
|
||||
# (inet_ntop/inet_pton) / standalone (getpass) imports. The other lib/*.c
|
||||
# (md5, mdfour, wildmatch, permstring, pool_alloc, snprintf, sysacls, sysxattrs,
|
||||
# compat) are rsync's own and stay in the report.
|
||||
COVERAGE_EXCLUDE = -e '(^|/)zlib/' -e '(^|/)popt/' \
|
||||
-e '(^|/)lib/(getaddrinfo|getpass|inet_ntop|inet_pton)\.'
|
||||
|
||||
.PHONY: check
|
||||
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J)
|
||||
|
||||
.PHONY: check29
|
||||
check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=29
|
||||
|
||||
.PHONY: check30
|
||||
check30: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=30
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=30
|
||||
|
||||
# Whole-suite gcov coverage report (HTML, with branch + decision coverage).
|
||||
# Requires a build configured with --enable-coverage and the `gcovr` tool
|
||||
# (pip install gcovr). Runs the suite in parallel (COVERAGE_J, default CHECK_J):
|
||||
# this is safe because the coverage build uses -fprofile-update=atomic and
|
||||
# libgcov locks the per-source .gcda during its merge, so concurrent rsync
|
||||
# processes accumulate exactly (see COVERAGE_J above). Use COVERAGE_J=1 if your
|
||||
# toolchain's libgcov does not lock .gcda merges.
|
||||
.PHONY: coverage
|
||||
coverage: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
@case '$(CFLAGS)' in *--coverage*) ;; \
|
||||
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
|
||||
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
|
||||
find . -name '*.gcda' -delete
|
||||
@rc=0; $(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $(COVERAGE_RUNFLAGS) || rc=$$?; \
|
||||
rm -rf $(COVERAGE_DIR) && mkdir -p $(COVERAGE_DIR); \
|
||||
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
|
||||
--html-details -o $(COVERAGE_DIR)/index.html . || exit $$?; \
|
||||
echo "Coverage report written to $(COVERAGE_DIR)/index.html"; \
|
||||
if test $$rc != 0; then \
|
||||
echo "*** test suite FAILED (status $$rc) -- coverage report still written above"; \
|
||||
fi; \
|
||||
exit $$rc
|
||||
|
||||
# Same as `make coverage` but with the daemon tests run over a real loopback
|
||||
# rsyncd (--use-tcp), into a separate report directory.
|
||||
.PHONY: coverage-tcp
|
||||
coverage-tcp:
|
||||
$(MAKE) coverage COVERAGE_RUNFLAGS=--use-tcp COVERAGE_DIR=coverage-tcp
|
||||
|
||||
# Comprehensive single report: run the suite under several configurations,
|
||||
# accumulating into the shared .gcda counters (NOT cleared between runs), then
|
||||
# emit one merged, rsync-scoped report. Covers the default (pipe) transport, the
|
||||
# protocol-29/30 compat branches, and the real-TCP daemon path (which also runs
|
||||
# the require_tcp-only tests). Run under sudo to additionally cover root-only
|
||||
# paths (devices, chown, use-chroot, protected-regular). Local target -- CI uses
|
||||
# the plain `coverage`/`coverage-tcp` targets.
|
||||
.PHONY: coverage-all
|
||||
coverage-all: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
@case '$(CFLAGS)' in *--coverage*) ;; \
|
||||
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
|
||||
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
|
||||
find . -name '*.gcda' -delete
|
||||
@rc=0; \
|
||||
for cfg in '' '--protocol=30' '--protocol=29' '--use-tcp'; do \
|
||||
echo "===== coverage-all: runtests.py $$cfg ====="; \
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $$cfg || rc=$$?; \
|
||||
done; \
|
||||
rm -rf coverage-all && mkdir -p coverage-all; \
|
||||
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
|
||||
--html-details -o coverage-all/index.html . || exit $$?; \
|
||||
echo "Merged coverage report written to coverage-all/index.html"; \
|
||||
if test $$rc != 0; then \
|
||||
echo "*** some suite runs FAILED (status $$rc) -- report still written above"; \
|
||||
fi; \
|
||||
exit $$rc
|
||||
|
||||
# Coverage for the portable (non-openat2) resolver tier. Requires a SEPARATE
|
||||
# build configured with --enable-coverage --disable-openat2: its .gcno differ
|
||||
# from the openat2 build, so this report cannot be merged with the others.
|
||||
.PHONY: coverage-fallback
|
||||
coverage-fallback:
|
||||
$(MAKE) coverage COVERAGE_DIR=coverage-fallback
|
||||
|
||||
wildtest.o: wildtest.c t_stub.o lib/wildmatch.c rsync.h config.h
|
||||
wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
|
||||
|
||||
testsuite/chown-fake.test:
|
||||
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
|
||||
simdtest$(EXEEXT): simd-checksum-x86_64.cpp $(HEADERS)
|
||||
@if test x"@ROLL_SIMD@" != x; then \
|
||||
$(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DTEST_SIMD_CHECKSUM1 \
|
||||
-o $@ $(srcdir)/simd-checksum-x86_64.cpp @ROLL_ASM@ $(LIBS); \
|
||||
else \
|
||||
touch $@; \
|
||||
fi
|
||||
|
||||
testsuite/devices-fake.test:
|
||||
ln -s devices.test $(srcdir)/testsuite/devices-fake.test
|
||||
testsuite/chown-fake_test.py:
|
||||
ln -s chown_test.py $(srcdir)/testsuite/chown-fake_test.py
|
||||
|
||||
testsuite/xattrs-hlink.test:
|
||||
ln -s xattrs.test $(srcdir)/testsuite/xattrs-hlink.test
|
||||
testsuite/devices-fake_test.py:
|
||||
ln -s devices_test.py $(srcdir)/testsuite/devices-fake_test.py
|
||||
|
||||
testsuite/xattrs-hlink_test.py:
|
||||
ln -s xattrs_test.py $(srcdir)/testsuite/xattrs-hlink_test.py
|
||||
|
||||
testsuite/exclude-lsh_test.py:
|
||||
ln -s exclude_test.py $(srcdir)/testsuite/exclude-lsh_test.py
|
||||
|
||||
# This does *not* depend on building or installing: you can use it to
|
||||
# check a version installed from a binary or some other source tree,
|
||||
@@ -321,7 +456,7 @@ testsuite/xattrs-hlink.test:
|
||||
|
||||
.PHONY: installcheck
|
||||
installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
|
||||
$(srcdir)/runtests.py --rsync-bin="$(bindir)/rsync$(EXEEXT)" --srcdir="$(srcdir)" --tooldir=`pwd` -j $(CHECK_J)
|
||||
|
||||
# TODO: Add 'dist' target; need to know which files will be included
|
||||
|
||||
@@ -338,4 +473,4 @@ doxygen:
|
||||
.PHONY: doxygen-upload
|
||||
doxygen-upload:
|
||||
rsync -avzv $(srcdir)/dox/html/ --delete \
|
||||
$${SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
|
||||
$${RSYNC_SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
|
||||
|
||||
83
README.md
83
README.md
@@ -23,8 +23,18 @@ options. To get a complete list of supported options type:
|
||||
|
||||
rsync --help
|
||||
|
||||
See the manpage for more detailed information.
|
||||
See the [manpage][0] for more detailed information.
|
||||
|
||||
[0]: https://download.samba.org/pub/rsync/rsync.1
|
||||
|
||||
BUILDING AND INSTALLING
|
||||
-----------------------
|
||||
|
||||
If you need to build rsync yourself, check out the [INSTALL][1] page for
|
||||
information on what libraries and packages you can use to get the maximum
|
||||
features in your build.
|
||||
|
||||
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
|
||||
|
||||
SETUP
|
||||
-----
|
||||
@@ -55,17 +65,17 @@ RSYNC DAEMONS
|
||||
-------------
|
||||
|
||||
Rsync can also talk to "rsync daemons" which can provide anonymous or
|
||||
authenticated rsync. See the rsyncd.conf(5) man page for details on how
|
||||
to setup an rsync daemon. See the rsync(1) man page for info on how to
|
||||
authenticated rsync. See the rsyncd.conf(5) manpage for details on how
|
||||
to setup an rsync daemon. See the rsync(1) manpage for info on how to
|
||||
connect to an rsync daemon.
|
||||
|
||||
|
||||
WEB SITE
|
||||
--------
|
||||
|
||||
The main rsync web site is here:
|
||||
For more information, visit the [main rsync web site][2].
|
||||
|
||||
> https://rsync.samba.org/
|
||||
[2]: https://rsync.samba.org/
|
||||
|
||||
You'll find a FAQ list, downloads, resources, HTML versions of the
|
||||
manpages, etc.
|
||||
@@ -77,66 +87,67 @@ MAILING LISTS
|
||||
There is a mailing list for the discussion of rsync and its applications
|
||||
that is open to anyone to join. New releases are announced on this
|
||||
list, and there is also an announcement-only mailing list for those that
|
||||
want official announcements. See the mailing-list page for full
|
||||
details:
|
||||
want official announcements. See the [mailing-list page][3] for full
|
||||
details.
|
||||
|
||||
> https://rsync.samba.org/lists.html
|
||||
[3]: https://rsync.samba.org/lists.html
|
||||
|
||||
|
||||
DISCORD
|
||||
-------
|
||||
|
||||
There is also an rsync [Discord server][d] for real-time chat about rsync
|
||||
and its development.
|
||||
|
||||
[d]: https://discord.gg/Avfvy9zhdp
|
||||
|
||||
|
||||
BUG REPORTS
|
||||
-----------
|
||||
|
||||
To visit this web page for full the details on bug reporting:
|
||||
The [bug-tracking web page][4] has full details on bug reporting.
|
||||
|
||||
> https://rsync.samba.org/bugtracking.html
|
||||
[4]: https://rsync.samba.org/bug-tracking.html
|
||||
|
||||
That page contains links to the current bug list, and information on how
|
||||
to report a bug well. You might also like to try searching the Internet
|
||||
for the error message you've received, or looking in the mailing list
|
||||
archives at:
|
||||
That page contains links to the current bug list, and information on how to
|
||||
do a good job when reporting a bug. You might also like to try searching
|
||||
the Internet for the error message you've received, or looking in the
|
||||
[mailing list archives][5].
|
||||
|
||||
> https://mail-archive.com/rsync@lists.samba.org/
|
||||
[5]: https://mail-archive.com/rsync@lists.samba.org/
|
||||
|
||||
To send a bug report, follow the instructions on the bug-tracking
|
||||
page of the web site.
|
||||
|
||||
Alternately, email your bug report to <rsync@lists.samba.org>.
|
||||
|
||||
For security issues please email details of the issue to <rsync.project@gmail.com>.
|
||||
|
||||
GIT REPOSITORY
|
||||
--------------
|
||||
|
||||
If you want to get the very latest version of rsync direct from the
|
||||
source code repository, then you will need to use git. The git repo
|
||||
is hosted on github and on samba's site. Feel free to access it here:
|
||||
is hosted [on GitHub][6] and [on Samba's site][7].
|
||||
|
||||
> https://github.com/WayneD/rsync
|
||||
[6]: https://github.com/RsyncProject/rsync
|
||||
[7]: https://git.samba.org/?p=rsync.git;a=summary
|
||||
|
||||
A backup git repo is available on the samba site:
|
||||
See [the download page][8] for full details on all the ways to grab the
|
||||
source.
|
||||
|
||||
> git clone git://git.samba.org/rsync.git
|
||||
|
||||
See the download page for full details on all the ways to grab the
|
||||
source:
|
||||
|
||||
> https://rsync.samba.org/download.html
|
||||
[8]: https://rsync.samba.org/download.html
|
||||
|
||||
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Rsync was originally written by Andrew Tridgell and is currently
|
||||
maintained by Wayne Davison. It has been improved by many developers
|
||||
from around the world.
|
||||
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
|
||||
people from around the world have helped to maintain and improve it.
|
||||
|
||||
Rsync may be used, modified and redistributed only under the terms of
|
||||
the GNU General Public License, found in the file COPYING in this
|
||||
distribution, or at:
|
||||
the GNU General Public License, found in the file [COPYING][9] in this
|
||||
distribution, or at [the Free Software Foundation][10].
|
||||
|
||||
> https://www.fsf.org/licenses/gpl.html
|
||||
|
||||
|
||||
AVAILABILITY
|
||||
------------
|
||||
|
||||
The main web site for rsync is https://rsync.samba.org/
|
||||
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
|
||||
[10]: https://www.fsf.org/licenses/gpl.html
|
||||
|
||||
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Only the current release of the software is actively supported. If you need
|
||||
help backporting fixes into an older release, feel free to ask.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email your vulnerability information to rsync's maintainer:
|
||||
|
||||
Rsync Project <rsync.project@gmail.com>
|
||||
|
||||
12
access.c
12
access.c
@@ -2,7 +2,7 @@
|
||||
* Routines to authenticate access to a daemon (hosts allow/deny).
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Tridgell
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
#include "rsync.h"
|
||||
#include "ifuncs.h"
|
||||
#ifdef HAVE_NETGROUP_H
|
||||
#include <netgroup.h>
|
||||
#endif
|
||||
|
||||
static int allow_forward_dns;
|
||||
|
||||
@@ -34,6 +37,11 @@ static int match_hostname(const char **host_ptr, const char *addr, const char *t
|
||||
if (!host || !*host)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_INNETGR
|
||||
if (*tok == '@' && tok[1])
|
||||
return innetgr(tok + 1, host, NULL, NULL);
|
||||
#endif
|
||||
|
||||
/* First check if the reverse-DNS-determined hostname matches. */
|
||||
if (iwildmatch(tok, host))
|
||||
return 1;
|
||||
@@ -91,7 +99,7 @@ static void make_mask(char *mask, int plen, int addrlen)
|
||||
return;
|
||||
}
|
||||
|
||||
static int match_address(const char *addr, const char *tok)
|
||||
static int match_address(const char *addr, char *tok)
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *resa, *rest;
|
||||
|
||||
13
acls.c
13
acls.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2006-2020 Wayne Davison
|
||||
* Copyright (C) 2006-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -28,7 +28,7 @@ extern int dry_run;
|
||||
extern int am_root;
|
||||
extern int read_only;
|
||||
extern int list_only;
|
||||
extern int orig_umask;
|
||||
extern mode_t orig_umask;
|
||||
extern int numeric_ids;
|
||||
extern int inc_recurse;
|
||||
extern int preserve_devices;
|
||||
@@ -519,6 +519,7 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
|
||||
|
||||
sys_acl_free_acl(sacl);
|
||||
if (!ok) {
|
||||
rsyserr(FERROR_XFER, errno, "get_acl: unpack_smb_acl(%s)", fname);
|
||||
return -1;
|
||||
}
|
||||
} else if (no_acl_syscall_error(errno)) {
|
||||
@@ -696,7 +697,7 @@ static uint32 recv_acl_access(int f, uchar *name_follows_ptr)
|
||||
static uchar recv_ida_entries(int f, ida_entries *ent)
|
||||
{
|
||||
uchar computed_mask_bits = 0;
|
||||
int i, count = read_varint(f);
|
||||
int i, count = read_varint_bounded(f, 0, MAX_WIRE_ACL_COUNT, "ACL count");
|
||||
|
||||
ent->idas = count ? new_array(id_access, count) : NULL;
|
||||
ent->count = count;
|
||||
@@ -712,7 +713,7 @@ static uchar recv_ida_entries(int f, ida_entries *ent)
|
||||
else
|
||||
id = recv_group_name(f, id, NULL);
|
||||
} else if (access & NAME_IS_USER) {
|
||||
if (inc_recurse && am_root && !numeric_ids)
|
||||
if (inc_recurse && !numeric_ids)
|
||||
id = match_uid(id);
|
||||
} else {
|
||||
if (inc_recurse && (!am_root || !numeric_ids))
|
||||
@@ -763,6 +764,8 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode
|
||||
#ifdef HAVE_OSX_ACLS
|
||||
/* If we received a superfluous mask, throw it away. */
|
||||
duo_item->racl.mask_obj = NO_ENTRY;
|
||||
(void)mode;
|
||||
(void)computed_mask_bits;
|
||||
#else
|
||||
if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
|
||||
/* Mask must be non-empty with lists. */
|
||||
@@ -979,7 +982,7 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item,
|
||||
&& !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
|
||||
return -1;
|
||||
#ifdef HAVE_OSX_ACLS
|
||||
mode = 0; /* eliminate compiler warning */
|
||||
(void)mode; /* eliminate compiler warning */
|
||||
#else
|
||||
if (type == SMB_ACL_TYPE_ACCESS) {
|
||||
cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, cur_mode, mode);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Support rsync daemon authentication.
|
||||
*
|
||||
* Copyright (C) 1998-2000 Andrew Tridgell
|
||||
* Copyright (C) 2002-2020 Wayne Davison
|
||||
* Copyright (C) 2002-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
extern int read_only;
|
||||
extern char *password_file;
|
||||
extern struct name_num_obj valid_auth_checksums;
|
||||
|
||||
/***************************************************************************
|
||||
encode a buffer using base64 - simple and slow algorithm. null terminates
|
||||
@@ -72,9 +73,9 @@ static void gen_challenge(const char *addr, char *challenge)
|
||||
SIVAL(input, 20, tv.tv_usec);
|
||||
SIVAL(input, 24, getpid());
|
||||
|
||||
sum_init(-1, 0);
|
||||
len = sum_init(valid_auth_checksums.negotiated_nni, 0);
|
||||
sum_update(input, sizeof input);
|
||||
len = sum_end(digest);
|
||||
sum_end(digest);
|
||||
|
||||
base64_encode(digest, len, challenge, 0);
|
||||
}
|
||||
@@ -86,10 +87,10 @@ static void generate_hash(const char *in, const char *challenge, char *out)
|
||||
char buf[MAX_DIGEST_LEN];
|
||||
int len;
|
||||
|
||||
sum_init(-1, 0);
|
||||
len = sum_init(valid_auth_checksums.negotiated_nni, 0);
|
||||
sum_update(in, strlen(in));
|
||||
sum_update(challenge, strlen(challenge));
|
||||
len = sum_end(buf);
|
||||
sum_end(buf);
|
||||
|
||||
base64_encode(buf, len, out, 0);
|
||||
}
|
||||
@@ -119,7 +120,7 @@ static const char *check_secret(int module, const char *user, const char *group,
|
||||
if ((st.st_mode & 06) != 0) {
|
||||
rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
|
||||
ok = 0;
|
||||
} else if (MY_UID() == 0 && st.st_uid != 0) {
|
||||
} else if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
|
||||
rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
|
||||
ok = 0;
|
||||
}
|
||||
@@ -196,7 +197,7 @@ static const char *getpassf(const char *filename)
|
||||
rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
if (MY_UID() == 0 && st.st_uid != 0) {
|
||||
if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
|
||||
rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
@@ -227,7 +228,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
|
||||
char *users = lp_auth_users(module);
|
||||
char challenge[MAX_DIGEST_LEN*2];
|
||||
char line[BIGPATHBUFLEN];
|
||||
char **auth_uid_groups = NULL;
|
||||
const char **auth_uid_groups = NULL;
|
||||
int auth_uid_groups_cnt = -1;
|
||||
const char *err = NULL;
|
||||
int group_match = -1;
|
||||
@@ -238,6 +239,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
|
||||
if (!users || !*users)
|
||||
return "";
|
||||
|
||||
negotiate_daemon_auth(f_out, 0);
|
||||
gen_challenge(addr, challenge);
|
||||
|
||||
io_printf(f_out, "%s%s\n", leader, challenge);
|
||||
@@ -287,7 +289,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
|
||||
else {
|
||||
gid_t *gid_array = gid_list.items;
|
||||
auth_uid_groups_cnt = gid_list.count;
|
||||
auth_uid_groups = new_array(char *, auth_uid_groups_cnt);
|
||||
auth_uid_groups = new_array(const char *, auth_uid_groups_cnt);
|
||||
for (j = 0; j < auth_uid_groups_cnt; j++)
|
||||
auth_uid_groups[j] = gid_to_group(gid_array[j]);
|
||||
}
|
||||
@@ -313,7 +315,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
|
||||
else if (opt_ch == 'd')
|
||||
err = "denied by rule";
|
||||
else {
|
||||
char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
|
||||
const char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
|
||||
err = check_secret(module, line, group, challenge, pass);
|
||||
}
|
||||
|
||||
@@ -324,7 +326,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
|
||||
int j;
|
||||
for (j = 0; j < auth_uid_groups_cnt; j++) {
|
||||
if (auth_uid_groups[j])
|
||||
free(auth_uid_groups[j]);
|
||||
free((char*)auth_uid_groups[j]);
|
||||
}
|
||||
free(auth_uid_groups);
|
||||
}
|
||||
@@ -350,6 +352,7 @@ void auth_client(int fd, const char *user, const char *challenge)
|
||||
|
||||
if (!user || !*user)
|
||||
user = "nobody";
|
||||
negotiate_daemon_auth(-1, 1);
|
||||
|
||||
if (!(pass = getpassf(password_file))
|
||||
&& !(pass = getenv("RSYNC_PASSWORD"))) {
|
||||
|
||||
19
backup.c
19
backup.c
@@ -2,7 +2,7 @@
|
||||
* Backup handling code.
|
||||
*
|
||||
* Copyright (C) 1999 Andrew Tridgell
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -39,7 +39,7 @@ static int validate_backup_dir(void)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
|
||||
if (do_lstat(backup_dir_buf, &st) < 0) {
|
||||
if (do_lstat_at(backup_dir_buf, &st) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
|
||||
@@ -98,7 +98,7 @@ static BOOL copy_valid_path(const char *fname)
|
||||
for ( ; b; name = b + 1, b = strchr(name, '/')) {
|
||||
*b = '\0';
|
||||
|
||||
while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
|
||||
while (do_mkdir_at(backup_dir_buf, ACCESSPERMS) < 0) {
|
||||
if (errno == EEXIST) {
|
||||
val = validate_backup_dir();
|
||||
if (val > 0)
|
||||
@@ -197,7 +197,7 @@ static inline int link_or_rename(const char *from, const char *to,
|
||||
if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
|
||||
return 0; /* Use copy code. */
|
||||
#endif
|
||||
if (do_link(from, to) == 0) {
|
||||
if (do_link_at(from, to) == 0) {
|
||||
if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
|
||||
return 2;
|
||||
@@ -207,7 +207,7 @@ static inline int link_or_rename(const char *from, const char *to,
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (do_rename(from, to) == 0) {
|
||||
if (do_rename_at(from, to) == 0) {
|
||||
if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
|
||||
/* If someone has hard-linked the file into the backup
|
||||
* dir, rename() might return success but do nothing! */
|
||||
@@ -246,7 +246,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
goto success;
|
||||
if (errno == EEXIST || errno == EISDIR) {
|
||||
STRUCT_STAT bakst;
|
||||
if (do_lstat(buf, &bakst) == 0) {
|
||||
if (do_lstat_at(buf, &bakst) == 0) {
|
||||
int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
|
||||
if (delete_item(buf, bakst.st_mode, flags) != 0)
|
||||
return 0;
|
||||
@@ -277,7 +277,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
/* Check to see if this is a device file, or link */
|
||||
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|
||||
|| (preserve_specials && IS_SPECIAL(file->mode))) {
|
||||
if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
|
||||
if (do_mknod_at(buf, file->mode, sx.st.st_rdev) < 0)
|
||||
rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
|
||||
else if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
|
||||
@@ -294,7 +294,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
}
|
||||
ret = 2;
|
||||
} else {
|
||||
if (do_symlink(sl, buf) < 0)
|
||||
if (do_symlink_at(sl, buf) < 0)
|
||||
rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
|
||||
else if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
|
||||
@@ -304,7 +304,8 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
#endif
|
||||
|
||||
if (!ret && !S_ISREG(file->mode)) {
|
||||
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
|
||||
if (INFO_GTE(NONREG, 1))
|
||||
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
|
||||
unmake_file(file);
|
||||
#ifdef SUPPORT_ACLS
|
||||
uncache_tmp_acls();
|
||||
|
||||
6
batch.c
6
batch.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1999 Weiss
|
||||
* Copyright (C) 2004 Chris Shoemaker
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -75,7 +75,7 @@ static int *flag_ptr[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *flag_name[] = {
|
||||
static const char *const flag_name[] = {
|
||||
"--recurse (-r)",
|
||||
"--owner (-o)",
|
||||
"--group (-g)",
|
||||
@@ -194,7 +194,7 @@ static int write_opt(const char *opt, const char *arg)
|
||||
{
|
||||
int len = strlen(opt);
|
||||
int err = write(batch_sh_fd, " ", 1) != 1;
|
||||
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
|
||||
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
|
||||
if (arg) {
|
||||
err |= write(batch_sh_fd, "=", 1) != 1;
|
||||
err |= write_arg(arg);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Simple byteorder handling.
|
||||
*
|
||||
* Copyright (C) 1992-1995 Andrew Tridgell
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -129,4 +129,3 @@ SIVAL(char *buf, int pos, uint32 val)
|
||||
{
|
||||
SIVALu((uchar*)buf, pos, val);
|
||||
}
|
||||
|
||||
|
||||
484
checksum.c
484
checksum.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2023 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "rsync.h"
|
||||
|
||||
#ifdef SUPPORT_XXHASH
|
||||
#include "xxhash.h"
|
||||
#include <xxhash.h>
|
||||
# if XXH_VERSION_NUMBER >= 800
|
||||
# define SUPPORT_XXH3 1
|
||||
# endif
|
||||
@@ -42,41 +42,94 @@ extern int protocol_version;
|
||||
extern int proper_seed_order;
|
||||
extern const char *checksum_choice;
|
||||
|
||||
struct name_num_obj valid_checksums = {
|
||||
"checksum", NULL, NULL, 0, 0, {
|
||||
#define NNI_BUILTIN (1<<0)
|
||||
#define NNI_EVP (1<<1)
|
||||
#define NNI_EVP_OK (1<<2)
|
||||
|
||||
struct name_num_item valid_checksums_items[] = {
|
||||
#ifdef SUPPORT_XXH3
|
||||
{ CSUM_XXH3_128, "xxh128", NULL },
|
||||
{ CSUM_XXH3_64, "xxh3", NULL },
|
||||
{ CSUM_XXH3_128, 0, "xxh128", NULL },
|
||||
{ CSUM_XXH3_64, 0, "xxh3", NULL },
|
||||
#endif
|
||||
#ifdef SUPPORT_XXHASH
|
||||
{ CSUM_XXH64, "xxh64", NULL },
|
||||
{ CSUM_XXH64, "xxhash", NULL },
|
||||
{ CSUM_XXH64, 0, "xxh64", NULL },
|
||||
{ CSUM_XXH64, 0, "xxhash", NULL },
|
||||
#endif
|
||||
{ CSUM_MD5, "md5", NULL },
|
||||
{ CSUM_MD4, "md4", NULL },
|
||||
{ CSUM_NONE, "none", NULL },
|
||||
{ 0, NULL, NULL }
|
||||
}
|
||||
{ CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
|
||||
{ CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
|
||||
#ifdef SHA_DIGEST_LENGTH
|
||||
{ CSUM_SHA1, NNI_EVP, "sha1", NULL },
|
||||
#endif
|
||||
{ CSUM_NONE, 0, "none", NULL },
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
int xfersum_type = 0; /* used for the file transfer checksums */
|
||||
int checksum_type = 0; /* used for the pre-transfer (--checksum) checksums */
|
||||
struct name_num_obj valid_checksums = {
|
||||
"checksum", NULL, 0, 0, valid_checksums_items
|
||||
};
|
||||
|
||||
int parse_csum_name(const char *name, int len)
|
||||
struct name_num_item valid_auth_checksums_items[] = {
|
||||
#ifdef SHA512_DIGEST_LENGTH
|
||||
{ CSUM_SHA512, NNI_EVP, "sha512", NULL },
|
||||
#endif
|
||||
#ifdef SHA256_DIGEST_LENGTH
|
||||
{ CSUM_SHA256, NNI_EVP, "sha256", NULL },
|
||||
#endif
|
||||
#ifdef SHA_DIGEST_LENGTH
|
||||
{ CSUM_SHA1, NNI_EVP, "sha1", NULL },
|
||||
#endif
|
||||
{ CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
|
||||
{ CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
struct name_num_obj valid_auth_checksums = {
|
||||
"daemon auth checksum", NULL, 0, 0, valid_auth_checksums_items
|
||||
};
|
||||
|
||||
/* These cannot make use of openssl, so they're marked just as built-in */
|
||||
struct name_num_item implied_checksum_md4 =
|
||||
{ CSUM_MD4, NNI_BUILTIN, "md4", NULL };
|
||||
struct name_num_item implied_checksum_md5 =
|
||||
{ CSUM_MD5, NNI_BUILTIN, "md5", NULL };
|
||||
|
||||
struct name_num_item *xfer_sum_nni; /* used for the transfer checksum2 computations */
|
||||
int xfer_sum_len;
|
||||
struct name_num_item *file_sum_nni; /* used for the pre-transfer --checksum computations */
|
||||
int file_sum_len, file_sum_extra_cnt;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
const EVP_MD *xfer_sum_evp_md;
|
||||
const EVP_MD *file_sum_evp_md;
|
||||
EVP_MD_CTX *ctx_evp = NULL;
|
||||
#endif
|
||||
|
||||
static int initialized_choices = 0;
|
||||
|
||||
struct name_num_item *parse_csum_name(const char *name, int len)
|
||||
{
|
||||
struct name_num_item *nni;
|
||||
|
||||
if (len < 0 && name)
|
||||
len = strlen(name);
|
||||
|
||||
init_checksum_choices();
|
||||
|
||||
if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
|
||||
if (protocol_version >= 30)
|
||||
return CSUM_MD5;
|
||||
if (protocol_version >= 27)
|
||||
return CSUM_MD4_OLD;
|
||||
if (protocol_version >= 21)
|
||||
return CSUM_MD4_BUSTED;
|
||||
return CSUM_MD4_ARCHAIC;
|
||||
if (protocol_version >= 30) {
|
||||
if (!proper_seed_order)
|
||||
return &implied_checksum_md5;
|
||||
name = "md5";
|
||||
len = 3;
|
||||
} else {
|
||||
if (protocol_version >= 27)
|
||||
implied_checksum_md4.num = CSUM_MD4_OLD;
|
||||
else if (protocol_version >= 21)
|
||||
implied_checksum_md4.num = CSUM_MD4_BUSTED;
|
||||
else
|
||||
implied_checksum_md4.num = CSUM_MD4_ARCHAIC;
|
||||
return &implied_checksum_md4;
|
||||
}
|
||||
}
|
||||
|
||||
nni = get_nni_by_name(&valid_checksums, name, len);
|
||||
@@ -86,44 +139,74 @@ int parse_csum_name(const char *name, int len)
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
return nni->num;
|
||||
return nni;
|
||||
}
|
||||
|
||||
static const char *checksum_name(int num)
|
||||
#ifdef USE_OPENSSL
|
||||
static const EVP_MD *csum_evp_md(struct name_num_item *nni)
|
||||
{
|
||||
struct name_num_item *nni = get_nni_by_num(&valid_checksums, num);
|
||||
const EVP_MD *emd;
|
||||
if (!(nni->flags & NNI_EVP))
|
||||
return NULL;
|
||||
|
||||
return nni ? nni->name : num < CSUM_MD4 ? "md4" : "UNKNOWN";
|
||||
#ifdef USE_MD5_ASM
|
||||
if (nni->num == CSUM_MD5)
|
||||
emd = NULL;
|
||||
else
|
||||
#endif
|
||||
emd = EVP_get_digestbyname(nni->name);
|
||||
if (emd && !(nni->flags & NNI_EVP_OK)) { /* Make sure it works before we advertise it */
|
||||
if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
|
||||
out_of_memory("csum_evp_md");
|
||||
/* Some routines are marked as legacy and are not enabled in the openssl.cnf file.
|
||||
* If we can't init the emd, we'll fall back to our built-in code. */
|
||||
if (EVP_DigestInit_ex(ctx_evp, emd, NULL) == 0)
|
||||
emd = NULL;
|
||||
else
|
||||
nni->flags = (nni->flags & ~NNI_BUILTIN) | NNI_EVP_OK;
|
||||
}
|
||||
if (!emd)
|
||||
nni->flags &= ~NNI_EVP;
|
||||
return emd;
|
||||
}
|
||||
#endif
|
||||
|
||||
void parse_checksum_choice(int final_call)
|
||||
{
|
||||
if (valid_checksums.negotiated_name)
|
||||
xfersum_type = checksum_type = valid_checksums.negotiated_num;
|
||||
if (valid_checksums.negotiated_nni)
|
||||
xfer_sum_nni = file_sum_nni = valid_checksums.negotiated_nni;
|
||||
else {
|
||||
char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
|
||||
const char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
|
||||
if (cp) {
|
||||
xfersum_type = parse_csum_name(checksum_choice, cp - checksum_choice);
|
||||
checksum_type = parse_csum_name(cp+1, -1);
|
||||
xfer_sum_nni = parse_csum_name(checksum_choice, cp - checksum_choice);
|
||||
file_sum_nni = parse_csum_name(cp+1, -1);
|
||||
} else
|
||||
xfersum_type = checksum_type = parse_csum_name(checksum_choice, -1);
|
||||
xfer_sum_nni = file_sum_nni = parse_csum_name(checksum_choice, -1);
|
||||
if (am_server && checksum_choice)
|
||||
validate_choice_vs_env(NSTR_CHECKSUM, xfersum_type, checksum_type);
|
||||
validate_choice_vs_env(NSTR_CHECKSUM, xfer_sum_nni->num, file_sum_nni->num);
|
||||
}
|
||||
xfer_sum_len = csum_len_for_type(xfer_sum_nni->num, 0);
|
||||
file_sum_len = csum_len_for_type(file_sum_nni->num, 0);
|
||||
#ifdef USE_OPENSSL
|
||||
xfer_sum_evp_md = csum_evp_md(xfer_sum_nni);
|
||||
file_sum_evp_md = csum_evp_md(file_sum_nni);
|
||||
#endif
|
||||
|
||||
if (xfersum_type == CSUM_NONE)
|
||||
file_sum_extra_cnt = (file_sum_len + EXTRA_LEN - 1) / EXTRA_LEN;
|
||||
|
||||
if (xfer_sum_nni->num == CSUM_NONE)
|
||||
whole_file = 1;
|
||||
|
||||
/* Snag the checksum name for both write_batch's option output & the following debug output. */
|
||||
if (valid_checksums.negotiated_name)
|
||||
checksum_choice = valid_checksums.negotiated_name;
|
||||
if (valid_checksums.negotiated_nni)
|
||||
checksum_choice = valid_checksums.negotiated_nni->name;
|
||||
else if (checksum_choice == NULL)
|
||||
checksum_choice = checksum_name(xfersum_type);
|
||||
checksum_choice = xfer_sum_nni->name;
|
||||
|
||||
if (final_call && DEBUG_GTE(NSTR, am_server ? 3 : 1)) {
|
||||
rprintf(FINFO, "%s%s checksum: %s\n",
|
||||
am_server ? "Server" : "Client",
|
||||
valid_checksums.negotiated_name ? " negotiated" : "",
|
||||
valid_checksums.negotiated_nni ? " negotiated" : "",
|
||||
checksum_choice);
|
||||
}
|
||||
}
|
||||
@@ -143,6 +226,18 @@ int csum_len_for_type(int cst, BOOL flist_csum)
|
||||
return MD4_DIGEST_LEN;
|
||||
case CSUM_MD5:
|
||||
return MD5_DIGEST_LEN;
|
||||
#ifdef SHA_DIGEST_LENGTH
|
||||
case CSUM_SHA1:
|
||||
return SHA_DIGEST_LENGTH;
|
||||
#endif
|
||||
#ifdef SHA256_DIGEST_LENGTH
|
||||
case CSUM_SHA256:
|
||||
return SHA256_DIGEST_LENGTH;
|
||||
#endif
|
||||
#ifdef SHA512_DIGEST_LENGTH
|
||||
case CSUM_SHA512:
|
||||
return SHA512_DIGEST_LENGTH;
|
||||
#endif
|
||||
case CSUM_XXH64:
|
||||
case CSUM_XXH3_64:
|
||||
return 64/8;
|
||||
@@ -168,6 +263,9 @@ int canonical_checksum(int csum_type)
|
||||
break;
|
||||
case CSUM_MD4:
|
||||
case CSUM_MD5:
|
||||
case CSUM_SHA1:
|
||||
case CSUM_SHA256:
|
||||
case CSUM_SHA512:
|
||||
return -1;
|
||||
case CSUM_XXH64:
|
||||
case CSUM_XXH3_64:
|
||||
@@ -179,7 +277,7 @@ int canonical_checksum(int csum_type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef HAVE_SIMD /* See simd-checksum-*.cpp. */
|
||||
#ifndef USE_ROLL_SIMD /* See simd-checksum-*.cpp. */
|
||||
/*
|
||||
a simple 32 bit checksum that can be updated from either end
|
||||
(inspired by Mark Adler's Adler-32 checksum)
|
||||
@@ -202,9 +300,25 @@ uint32 get_checksum1(char *buf1, int32 len)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The "sum" buffer must be at least MAX_DIGEST_LEN bytes! */
|
||||
void get_checksum2(char *buf, int32 len, char *sum)
|
||||
{
|
||||
switch (xfersum_type) {
|
||||
#ifdef USE_OPENSSL
|
||||
if (xfer_sum_evp_md) {
|
||||
static EVP_MD_CTX *evp = NULL;
|
||||
uchar seedbuf[4];
|
||||
if (!evp && !(evp = EVP_MD_CTX_create()))
|
||||
out_of_memory("get_checksum2");
|
||||
EVP_DigestInit_ex(evp, xfer_sum_evp_md, NULL);
|
||||
if (checksum_seed) {
|
||||
SIVALu(seedbuf, 0, checksum_seed);
|
||||
EVP_DigestUpdate(evp, seedbuf, 4);
|
||||
}
|
||||
EVP_DigestUpdate(evp, (uchar *)buf, len);
|
||||
EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
|
||||
} else
|
||||
#endif
|
||||
switch (xfer_sum_nni->num) {
|
||||
#ifdef SUPPORT_XXHASH
|
||||
case CSUM_XXH64:
|
||||
SIVAL64(sum, 0, XXH64(buf, len, checksum_seed));
|
||||
@@ -222,40 +336,26 @@ void get_checksum2(char *buf, int32 len, char *sum)
|
||||
}
|
||||
#endif
|
||||
case CSUM_MD5: {
|
||||
MD5_CTX m5;
|
||||
md_context m5;
|
||||
uchar seedbuf[4];
|
||||
MD5_Init(&m5);
|
||||
md5_begin(&m5);
|
||||
if (proper_seed_order) {
|
||||
if (checksum_seed) {
|
||||
SIVALu(seedbuf, 0, checksum_seed);
|
||||
MD5_Update(&m5, seedbuf, 4);
|
||||
md5_update(&m5, seedbuf, 4);
|
||||
}
|
||||
MD5_Update(&m5, (uchar *)buf, len);
|
||||
md5_update(&m5, (uchar *)buf, len);
|
||||
} else {
|
||||
MD5_Update(&m5, (uchar *)buf, len);
|
||||
md5_update(&m5, (uchar *)buf, len);
|
||||
if (checksum_seed) {
|
||||
SIVALu(seedbuf, 0, checksum_seed);
|
||||
MD5_Update(&m5, seedbuf, 4);
|
||||
md5_update(&m5, seedbuf, 4);
|
||||
}
|
||||
}
|
||||
MD5_Final((uchar *)sum, &m5);
|
||||
md5_result(&m5, (uchar *)sum);
|
||||
break;
|
||||
}
|
||||
case CSUM_MD4:
|
||||
#ifdef USE_OPENSSL
|
||||
{
|
||||
MD4_CTX m4;
|
||||
MD4_Init(&m4);
|
||||
MD4_Update(&m4, (uchar *)buf, len);
|
||||
if (checksum_seed) {
|
||||
uchar seedbuf[4];
|
||||
SIVALu(seedbuf, 0, checksum_seed);
|
||||
MD4_Update(&m4, seedbuf, 4);
|
||||
}
|
||||
MD4_Final((uchar *)sum, &m4);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case CSUM_MD4_OLD:
|
||||
case CSUM_MD4_BUSTED:
|
||||
case CSUM_MD4_ARCHAIC: {
|
||||
@@ -266,9 +366,8 @@ void get_checksum2(char *buf, int32 len, char *sum)
|
||||
|
||||
mdfour_begin(&m);
|
||||
|
||||
if (len > len1) {
|
||||
if (buf1)
|
||||
free(buf1);
|
||||
if (len > len1 || !buf1) {
|
||||
free(buf1);
|
||||
buf1 = new_array(char, len+4);
|
||||
len1 = len;
|
||||
}
|
||||
@@ -288,7 +387,7 @@ void get_checksum2(char *buf, int32 len, char *sum)
|
||||
* are multiples of 64. This is fixed by calling mdfour_update()
|
||||
* even when there are no more bytes.
|
||||
*/
|
||||
if (len - i > 0 || xfersum_type > CSUM_MD4_BUSTED)
|
||||
if (len - i > 0 || xfer_sum_nni->num > CSUM_MD4_BUSTED)
|
||||
mdfour_update(&m, (uchar *)(buf1+i), len-i);
|
||||
|
||||
mdfour_result(&m, (uchar *)sum);
|
||||
@@ -306,15 +405,33 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
int32 remainder;
|
||||
int fd;
|
||||
|
||||
memset(sum, 0, MAX_DIGEST_LEN);
|
||||
|
||||
fd = do_open(fname, O_RDONLY, 0);
|
||||
if (fd == -1)
|
||||
fd = do_open_checklinks(fname);
|
||||
if (fd == -1) {
|
||||
memset(sum, 0, file_sum_len);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = map_file(fd, len, MAX_MAP_SIZE, CHUNK_SIZE);
|
||||
|
||||
switch (checksum_type) {
|
||||
#ifdef USE_OPENSSL
|
||||
if (file_sum_evp_md) {
|
||||
static EVP_MD_CTX *evp = NULL;
|
||||
if (!evp && !(evp = EVP_MD_CTX_create()))
|
||||
out_of_memory("file_checksum");
|
||||
|
||||
EVP_DigestInit_ex(evp, file_sum_evp_md, NULL);
|
||||
|
||||
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
|
||||
EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
|
||||
|
||||
remainder = (int32)(len - i);
|
||||
if (remainder > 0)
|
||||
EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, remainder), remainder);
|
||||
|
||||
EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
|
||||
} else
|
||||
#endif
|
||||
switch (file_sum_nni->num) {
|
||||
#ifdef SUPPORT_XXHASH
|
||||
case CSUM_XXH64: {
|
||||
static XXH64_state_t* state = NULL;
|
||||
@@ -374,38 +491,21 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
}
|
||||
#endif
|
||||
case CSUM_MD5: {
|
||||
MD5_CTX m5;
|
||||
md_context m5;
|
||||
|
||||
MD5_Init(&m5);
|
||||
md5_begin(&m5);
|
||||
|
||||
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
|
||||
MD5_Update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
|
||||
md5_update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
|
||||
|
||||
remainder = (int32)(len - i);
|
||||
if (remainder > 0)
|
||||
MD5_Update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
|
||||
md5_update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
|
||||
|
||||
MD5_Final((uchar *)sum, &m5);
|
||||
md5_result(&m5, (uchar *)sum);
|
||||
break;
|
||||
}
|
||||
case CSUM_MD4:
|
||||
#ifdef USE_OPENSSL
|
||||
{
|
||||
MD4_CTX m4;
|
||||
|
||||
MD4_Init(&m4);
|
||||
|
||||
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
|
||||
MD4_Update(&m4, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
|
||||
|
||||
remainder = (int32)(len - i);
|
||||
if (remainder > 0)
|
||||
MD4_Update(&m4, (uchar *)map_ptr(buf, i, remainder), remainder);
|
||||
|
||||
MD4_Final((uchar *)sum, &m4);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case CSUM_MD4_OLD:
|
||||
case CSUM_MD4_BUSTED:
|
||||
case CSUM_MD4_ARCHAIC: {
|
||||
@@ -413,15 +513,15 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
|
||||
mdfour_begin(&m);
|
||||
|
||||
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
|
||||
mdfour_update(&m, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
|
||||
for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
|
||||
mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK), CSUM_CHUNK);
|
||||
|
||||
/* Prior to version 27 an incorrect MD4 checksum was computed
|
||||
* by failing to call mdfour_tail() for block sizes that
|
||||
* are multiples of 64. This is fixed by calling mdfour_update()
|
||||
* even when there are no more bytes. */
|
||||
remainder = (int32)(len - i);
|
||||
if (remainder > 0 || checksum_type > CSUM_MD4_BUSTED)
|
||||
if (remainder > 0 || file_sum_nni->num > CSUM_MD4_BUSTED)
|
||||
mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
|
||||
|
||||
mdfour_result(&m, (uchar *)sum);
|
||||
@@ -429,7 +529,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
}
|
||||
default:
|
||||
rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
|
||||
checksum_name(checksum_type), checksum_type);
|
||||
file_sum_nni->name, file_sum_nni->num);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
@@ -438,30 +538,43 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
}
|
||||
|
||||
static int32 sumresidue;
|
||||
static union {
|
||||
md_context md;
|
||||
#ifdef USE_OPENSSL
|
||||
MD4_CTX m4;
|
||||
#endif
|
||||
MD5_CTX m5;
|
||||
} ctx;
|
||||
static md_context ctx_md;
|
||||
#ifdef SUPPORT_XXHASH
|
||||
static XXH64_state_t* xxh64_state;
|
||||
#endif
|
||||
#ifdef SUPPORT_XXH3
|
||||
static XXH3_state_t* xxh3_state;
|
||||
#endif
|
||||
static int cursum_type;
|
||||
static struct name_num_item *cur_sum_nni;
|
||||
int cur_sum_len;
|
||||
|
||||
void sum_init(int csum_type, int seed)
|
||||
#ifdef USE_OPENSSL
|
||||
static const EVP_MD *cur_sum_evp_md;
|
||||
#endif
|
||||
|
||||
/* Initialize a hash digest accumulator. Data is supplied via
|
||||
* sum_update() and the resulting binary digest is retrieved via
|
||||
* sum_end(). This only supports one active sum at a time. */
|
||||
int sum_init(struct name_num_item *nni, int seed)
|
||||
{
|
||||
char s[4];
|
||||
|
||||
if (csum_type < 0)
|
||||
csum_type = parse_csum_name(NULL, 0);
|
||||
cursum_type = csum_type;
|
||||
if (!nni)
|
||||
nni = parse_csum_name(NULL, 0);
|
||||
cur_sum_nni = nni;
|
||||
cur_sum_len = csum_len_for_type(nni->num, 0);
|
||||
#ifdef USE_OPENSSL
|
||||
cur_sum_evp_md = csum_evp_md(nni);
|
||||
#endif
|
||||
|
||||
switch (csum_type) {
|
||||
#ifdef USE_OPENSSL
|
||||
if (cur_sum_evp_md) {
|
||||
if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
|
||||
out_of_memory("file_checksum");
|
||||
EVP_DigestInit_ex(ctx_evp, cur_sum_evp_md, NULL);
|
||||
} else
|
||||
#endif
|
||||
switch (cur_sum_nni->num) {
|
||||
#ifdef SUPPORT_XXHASH
|
||||
case CSUM_XXH64:
|
||||
if (!xxh64_state && !(xxh64_state = XXH64_createState()))
|
||||
@@ -482,20 +595,16 @@ void sum_init(int csum_type, int seed)
|
||||
break;
|
||||
#endif
|
||||
case CSUM_MD5:
|
||||
MD5_Init(&ctx.m5);
|
||||
md5_begin(&ctx_md);
|
||||
break;
|
||||
case CSUM_MD4:
|
||||
#ifdef USE_OPENSSL
|
||||
MD4_Init(&ctx.m4);
|
||||
#else
|
||||
mdfour_begin(&ctx.md);
|
||||
mdfour_begin(&ctx_md);
|
||||
sumresidue = 0;
|
||||
#endif
|
||||
break;
|
||||
case CSUM_MD4_OLD:
|
||||
case CSUM_MD4_BUSTED:
|
||||
case CSUM_MD4_ARCHAIC:
|
||||
mdfour_begin(&ctx.md);
|
||||
mdfour_begin(&ctx_md);
|
||||
sumresidue = 0;
|
||||
SIVAL(s, 0, seed);
|
||||
sum_update(s, 4);
|
||||
@@ -505,19 +614,19 @@ void sum_init(int csum_type, int seed)
|
||||
default: /* paranoia to prevent missing case values */
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
return cur_sum_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed data into an MD4 accumulator, md. The results may be
|
||||
* retrieved using sum_end(). md is used for different purposes at
|
||||
* different points during execution.
|
||||
*
|
||||
* @todo Perhaps get rid of md and just pass in the address each time.
|
||||
* Very slightly clearer and slower.
|
||||
**/
|
||||
/* Feed data into a hash digest accumulator. */
|
||||
void sum_update(const char *p, int32 len)
|
||||
{
|
||||
switch (cursum_type) {
|
||||
#ifdef USE_OPENSSL
|
||||
if (cur_sum_evp_md) {
|
||||
EVP_DigestUpdate(ctx_evp, (uchar *)p, len);
|
||||
} else
|
||||
#endif
|
||||
switch (cur_sum_nni->num) {
|
||||
#ifdef SUPPORT_XXHASH
|
||||
case CSUM_XXH64:
|
||||
XXH64_update(xxh64_state, p, len);
|
||||
@@ -532,39 +641,35 @@ void sum_update(const char *p, int32 len)
|
||||
break;
|
||||
#endif
|
||||
case CSUM_MD5:
|
||||
MD5_Update(&ctx.m5, (uchar *)p, len);
|
||||
md5_update(&ctx_md, (uchar *)p, len);
|
||||
break;
|
||||
case CSUM_MD4:
|
||||
#ifdef USE_OPENSSL
|
||||
MD4_Update(&ctx.m4, (uchar *)p, len);
|
||||
break;
|
||||
#endif
|
||||
case CSUM_MD4_OLD:
|
||||
case CSUM_MD4_BUSTED:
|
||||
case CSUM_MD4_ARCHAIC:
|
||||
if (len + sumresidue < CSUM_CHUNK) {
|
||||
memcpy(ctx.md.buffer + sumresidue, p, len);
|
||||
memcpy(ctx_md.buffer + sumresidue, p, len);
|
||||
sumresidue += len;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sumresidue) {
|
||||
int32 i = CSUM_CHUNK - sumresidue;
|
||||
memcpy(ctx.md.buffer + sumresidue, p, i);
|
||||
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, CSUM_CHUNK);
|
||||
memcpy(ctx_md.buffer + sumresidue, p, i);
|
||||
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, CSUM_CHUNK);
|
||||
len -= i;
|
||||
p += i;
|
||||
}
|
||||
|
||||
while (len >= CSUM_CHUNK) {
|
||||
mdfour_update(&ctx.md, (uchar *)p, CSUM_CHUNK);
|
||||
mdfour_update(&ctx_md, (uchar *)p, CSUM_CHUNK);
|
||||
len -= CSUM_CHUNK;
|
||||
p += CSUM_CHUNK;
|
||||
}
|
||||
|
||||
sumresidue = len;
|
||||
if (sumresidue)
|
||||
memcpy(ctx.md.buffer, p, sumresidue);
|
||||
memcpy(ctx_md.buffer, p, sumresidue);
|
||||
break;
|
||||
case CSUM_NONE:
|
||||
break;
|
||||
@@ -573,13 +678,18 @@ void sum_update(const char *p, int32 len)
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: all the callers of sum_end() pass in a pointer to a buffer that is
|
||||
* MAX_DIGEST_LEN in size, so even if the csum-len is shorter that that (i.e.
|
||||
* CSUM_MD4_ARCHAIC), we don't have to worry about limiting the data we write
|
||||
* into the "sum" buffer. */
|
||||
int sum_end(char *sum)
|
||||
/* The sum buffer only needs to be as long as the current checksum's digest
|
||||
* len, not MAX_DIGEST_LEN. Note that for CSUM_MD4_ARCHAIC that is the full
|
||||
* MD4_DIGEST_LEN even if the file-list code is going to ignore all but the
|
||||
* first 2 bytes of it. */
|
||||
void sum_end(char *sum)
|
||||
{
|
||||
switch (cursum_type) {
|
||||
#ifdef USE_OPENSSL
|
||||
if (cur_sum_evp_md) {
|
||||
EVP_DigestFinal_ex(ctx_evp, (uchar *)sum, NULL);
|
||||
} else
|
||||
#endif
|
||||
switch (cur_sum_nni->num) {
|
||||
#ifdef SUPPORT_XXHASH
|
||||
case CSUM_XXH64:
|
||||
SIVAL64(sum, 0, XXH64_digest(xxh64_state));
|
||||
@@ -597,22 +707,18 @@ int sum_end(char *sum)
|
||||
}
|
||||
#endif
|
||||
case CSUM_MD5:
|
||||
MD5_Final((uchar *)sum, &ctx.m5);
|
||||
md5_result(&ctx_md, (uchar *)sum);
|
||||
break;
|
||||
case CSUM_MD4:
|
||||
#ifdef USE_OPENSSL
|
||||
MD4_Final((uchar *)sum, &ctx.m4);
|
||||
break;
|
||||
#endif
|
||||
case CSUM_MD4_OLD:
|
||||
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
|
||||
mdfour_result(&ctx.md, (uchar *)sum);
|
||||
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
|
||||
mdfour_result(&ctx_md, (uchar *)sum);
|
||||
break;
|
||||
case CSUM_MD4_BUSTED:
|
||||
case CSUM_MD4_ARCHAIC:
|
||||
if (sumresidue)
|
||||
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
|
||||
mdfour_result(&ctx.md, (uchar *)sum);
|
||||
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
|
||||
mdfour_result(&ctx_md, (uchar *)sum);
|
||||
break;
|
||||
case CSUM_NONE:
|
||||
*sum = '\0';
|
||||
@@ -620,6 +726,78 @@ int sum_end(char *sum)
|
||||
default: /* paranoia to prevent missing case values */
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
return csum_len_for_type(cursum_type, 0);
|
||||
}
|
||||
|
||||
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
|
||||
static void verify_digest(struct name_num_item *nni, BOOL check_auth_list)
|
||||
{
|
||||
#ifdef SUPPORT_XXH3
|
||||
static int xxh3_result = 0;
|
||||
#endif
|
||||
#ifdef USE_OPENSSL
|
||||
static int prior_num = 0, prior_flags = 0, prior_result = 0;
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_XXH3
|
||||
if (nni->num == CSUM_XXH3_64 || nni->num == CSUM_XXH3_128) {
|
||||
if (!xxh3_result) {
|
||||
char buf[32816];
|
||||
int j;
|
||||
for (j = 0; j < (int)sizeof buf; j++)
|
||||
buf[j] = ' ' + (j % 96);
|
||||
sum_init(nni, 0);
|
||||
sum_update(buf, 32816);
|
||||
sum_update(buf, 31152);
|
||||
sum_update(buf, 32474);
|
||||
sum_update(buf, 9322);
|
||||
xxh3_result = XXH3_64bits_digest(xxh3_state) != 0xadbcf16d4678d1de ? -1 : 1;
|
||||
}
|
||||
if (xxh3_result < 0)
|
||||
nni->num = CSUM_gone;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
if (BITS_SETnUNSET(nni->flags, NNI_EVP, NNI_BUILTIN|NNI_EVP_OK)) {
|
||||
if (nni->num == prior_num && nni->flags == prior_flags) {
|
||||
nni->flags = prior_result;
|
||||
if (!(nni->flags & NNI_EVP))
|
||||
nni->num = CSUM_gone;
|
||||
} else {
|
||||
prior_num = nni->num;
|
||||
prior_flags = nni->flags;
|
||||
if (!csum_evp_md(nni))
|
||||
nni->num = CSUM_gone;
|
||||
prior_result = nni->flags;
|
||||
if (check_auth_list && (nni = get_nni_by_num(&valid_auth_checksums, prior_num)) != NULL)
|
||||
verify_digest(nni, False);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_checksum_choices()
|
||||
{
|
||||
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
|
||||
struct name_num_item *nni;
|
||||
#endif
|
||||
|
||||
if (initialized_choices)
|
||||
return;
|
||||
|
||||
#if defined USE_OPENSSL && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
|
||||
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
|
||||
for (nni = valid_checksums.list; nni->name; nni++)
|
||||
verify_digest(nni, True);
|
||||
|
||||
for (nni = valid_auth_checksums.list; nni->name; nni++)
|
||||
verify_digest(nni, False);
|
||||
#endif
|
||||
|
||||
initialized_choices = 1;
|
||||
}
|
||||
|
||||
18
cleanup.c
18
cleanup.c
@@ -137,7 +137,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
if (DEBUG_GTE(EXIT, 2)) {
|
||||
rprintf(FINFO,
|
||||
"[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n",
|
||||
who_am_i(), code, file, line);
|
||||
who_am_i(), code, src_file(file), line);
|
||||
}
|
||||
|
||||
#include "case_N.h"
|
||||
@@ -198,7 +198,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
switch_step++;
|
||||
|
||||
if (cleanup_fname)
|
||||
do_unlink(cleanup_fname);
|
||||
do_unlink_at(cleanup_fname);
|
||||
if (exit_code)
|
||||
kill_all(SIGUSR1);
|
||||
if (cleanup_pid && cleanup_pid == getpid()) {
|
||||
@@ -222,10 +222,6 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
* we don't want to output a duplicate error. */
|
||||
if ((exit_code && line > 0)
|
||||
|| am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1)))) {
|
||||
#ifdef HAVE_USLEEP /* Try for a teeny delay if both sender & receiver are sending a msg at the same time. */
|
||||
if (am_server && exit_code)
|
||||
usleep(50);
|
||||
#endif
|
||||
log_exit(exit_code, exit_file, exit_line);
|
||||
}
|
||||
|
||||
@@ -273,8 +269,16 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
break;
|
||||
}
|
||||
|
||||
if (called_from_signal_handler)
|
||||
if (called_from_signal_handler) {
|
||||
#ifdef GCOV_COVERAGE
|
||||
/* _exit() bypasses the gcov atexit flush; rsync's generator (and
|
||||
* other processes) normally finish via the signal handler, so
|
||||
* without this they would write no .gcda. Harmless otherwise. */
|
||||
extern void __gcov_dump(void);
|
||||
__gcov_dump();
|
||||
#endif
|
||||
_exit(exit_code);
|
||||
}
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
|
||||
36
clientname.c
36
clientname.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-2020 Wayne Davison
|
||||
* Copyright (C) 2002-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -49,7 +49,7 @@ static char ipaddr_buf[100];
|
||||
|
||||
static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len);
|
||||
static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size);
|
||||
static int valid_ipaddr(const char *s);
|
||||
static int valid_ipaddr(const char *s, int allow_scope);
|
||||
|
||||
/* Return the IP addr of the client as a string. */
|
||||
char *client_addr(int fd)
|
||||
@@ -73,7 +73,7 @@ char *client_addr(int fd)
|
||||
if ((p = strchr(ipaddr_buf, ' ')) != NULL)
|
||||
*p = '\0';
|
||||
}
|
||||
if (valid_ipaddr(ipaddr_buf))
|
||||
if (valid_ipaddr(ipaddr_buf, True))
|
||||
return ipaddr_buf;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ char *client_name(const char *ipaddr)
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
NOISY_DEATH("Unknown ai_family value");
|
||||
}
|
||||
freeaddrinfo(answer);
|
||||
|
||||
@@ -156,7 +156,7 @@ char *client_name(const char *ipaddr)
|
||||
}
|
||||
|
||||
|
||||
/* Try to read an proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
|
||||
/* Try to read a proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
|
||||
int read_proxy_protocol_header(int fd)
|
||||
{
|
||||
union {
|
||||
@@ -167,7 +167,7 @@ int read_proxy_protocol_header(int fd)
|
||||
char sig[PROXY_V2_SIG_SIZE];
|
||||
char ver_cmd;
|
||||
char fam;
|
||||
char len[2];
|
||||
unsigned char len[2];
|
||||
union {
|
||||
struct {
|
||||
char src_addr[4];
|
||||
@@ -213,12 +213,14 @@ int read_proxy_protocol_header(int fd)
|
||||
if (size != sizeof hdr.v2.addr.ip4)
|
||||
return 0;
|
||||
inet_ntop(AF_INET, hdr.v2.addr.ip4.src_addr, ipaddr_buf, sizeof ipaddr_buf);
|
||||
return valid_ipaddr(ipaddr_buf);
|
||||
return valid_ipaddr(ipaddr_buf, False);
|
||||
#ifdef INET6
|
||||
case PROXY_FAM_TCPv6:
|
||||
if (size != sizeof hdr.v2.addr.ip6)
|
||||
return 0;
|
||||
inet_ntop(AF_INET6, hdr.v2.addr.ip6.src_addr, ipaddr_buf, sizeof ipaddr_buf);
|
||||
return valid_ipaddr(ipaddr_buf);
|
||||
return valid_ipaddr(ipaddr_buf, False);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -274,7 +276,7 @@ int read_proxy_protocol_header(int fd)
|
||||
if ((sp = strchr(p, ' ')) == NULL)
|
||||
return 0;
|
||||
*sp = '\0';
|
||||
if (!valid_ipaddr(p))
|
||||
if (!valid_ipaddr(p, False))
|
||||
return 0;
|
||||
strlcpy(ipaddr_buf, p, sizeof ipaddr_buf); /* It will always fit when valid. */
|
||||
|
||||
@@ -282,7 +284,7 @@ int read_proxy_protocol_header(int fd)
|
||||
if ((sp = strchr(p, ' ')) == NULL)
|
||||
return 0;
|
||||
*sp = '\0';
|
||||
if (!valid_ipaddr(p))
|
||||
if (!valid_ipaddr(p, False))
|
||||
return 0;
|
||||
/* Ignore destination address. */
|
||||
|
||||
@@ -464,7 +466,7 @@ static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, cha
|
||||
}
|
||||
|
||||
/* Returns 1 for a valid IPv4 or IPv6 addr, or 0 for a bad one. */
|
||||
static int valid_ipaddr(const char *s)
|
||||
static int valid_ipaddr(const char *s, int allow_scope)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -482,6 +484,11 @@ static int valid_ipaddr(const char *s)
|
||||
for (count = 0; count < 8; count++) {
|
||||
if (!*s)
|
||||
return saw_double_colon;
|
||||
if (allow_scope && *s == '%') {
|
||||
if (saw_double_colon)
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strchr(s, ':') == NULL && strchr(s, '.') != NULL) {
|
||||
if ((!saw_double_colon && count != 6) || (saw_double_colon && count > 6))
|
||||
@@ -507,8 +514,11 @@ static int valid_ipaddr(const char *s)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipv4_at_end)
|
||||
return !*s;
|
||||
if (!ipv4_at_end) {
|
||||
if (allow_scope && *s == '%')
|
||||
for (s++; isAlNum(s); s++) { }
|
||||
return !*s && s[-1] != '%';
|
||||
}
|
||||
}
|
||||
|
||||
/* IPv4 */
|
||||
|
||||
342
clientserver.c
342
clientserver.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-2020 Wayne Davison
|
||||
* Copyright (C) 2002-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rsync.h"
|
||||
#include "itypes.h"
|
||||
#include "ifuncs.h"
|
||||
|
||||
extern int quiet;
|
||||
extern int dry_run;
|
||||
@@ -29,6 +30,7 @@ extern int list_only;
|
||||
extern int am_sender;
|
||||
extern int am_server;
|
||||
extern int am_daemon;
|
||||
extern int am_chrooted;
|
||||
extern int am_root;
|
||||
extern int msgs2stderr;
|
||||
extern int rsync_port;
|
||||
@@ -36,8 +38,9 @@ extern int protect_args;
|
||||
extern int ignore_errors;
|
||||
extern int preserve_xattrs;
|
||||
extern int kluge_around_eof;
|
||||
extern int daemon_over_rsh;
|
||||
extern int munge_symlinks;
|
||||
extern int use_secure_symlinks;
|
||||
extern int open_noatime;
|
||||
extern int sanitize_paths;
|
||||
extern int numeric_ids;
|
||||
extern int filesfrom_fd;
|
||||
@@ -46,6 +49,7 @@ extern int protocol_version;
|
||||
extern int io_timeout;
|
||||
extern int no_detach;
|
||||
extern int write_batch;
|
||||
extern int old_style_args;
|
||||
extern int default_af_hint;
|
||||
extern int logfile_format_has_i;
|
||||
extern int logfile_format_has_o_or_i;
|
||||
@@ -65,11 +69,13 @@ extern uid_t our_uid;
|
||||
extern gid_t our_gid;
|
||||
|
||||
char *auth_user;
|
||||
char *daemon_auth_choices;
|
||||
int read_only = 0;
|
||||
int module_id = -1;
|
||||
int pid_file_fd = -1;
|
||||
int early_input_len = 0;
|
||||
char *early_input = NULL;
|
||||
pid_t namecvt_pid = 0;
|
||||
struct chmod_mode_struct *daemon_chmod_modes;
|
||||
|
||||
#define EARLY_INPUT_CMD "#early_input="
|
||||
@@ -84,6 +90,7 @@ unsigned int module_dirlen = 0;
|
||||
char *full_module_path;
|
||||
|
||||
static int rl_nulls = 0;
|
||||
static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
static struct sigaction sigact;
|
||||
@@ -145,13 +152,9 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
|
||||
static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
|
||||
{
|
||||
int remote_sub = -1;
|
||||
#if SUBPROTOCOL_VERSION != 0
|
||||
int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
|
||||
#else
|
||||
int our_sub = 0;
|
||||
#endif
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub);
|
||||
output_daemon_greeting(f_out, am_client);
|
||||
if (!am_client) {
|
||||
char *motd = lp_motd_file();
|
||||
if (motd && *motd) {
|
||||
@@ -183,16 +186,30 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
|
||||
}
|
||||
|
||||
if (remote_sub < 0) {
|
||||
if (remote_protocol == 30) {
|
||||
if (remote_protocol >= 30) {
|
||||
if (am_client)
|
||||
rprintf(FERROR, "rsync: server is speaking an incompatible beta of protocol 30\n");
|
||||
rprintf(FERROR, "rsync: the server omitted the subprotocol value: %s\n", buf);
|
||||
else
|
||||
io_printf(f_out, "@ERROR: your client is speaking an incompatible beta of protocol 30\n");
|
||||
io_printf(f_out, "@ERROR: your client omitted the subprotocol value: %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
remote_sub = 0;
|
||||
}
|
||||
|
||||
daemon_auth_choices = strchr(buf + 9, ' ');
|
||||
if (daemon_auth_choices) {
|
||||
char *cp;
|
||||
daemon_auth_choices = strdup(daemon_auth_choices + 1);
|
||||
if ((cp = strchr(daemon_auth_choices, '\n')) != NULL)
|
||||
*cp = '\0';
|
||||
} else if (remote_protocol > 31) {
|
||||
if (am_client)
|
||||
rprintf(FERROR, "rsync: the server omitted the digest name list: %s\n", buf);
|
||||
else
|
||||
io_printf(f_out, "@ERROR: your client omitted the digest name list: %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (protocol_version > remote_protocol) {
|
||||
protocol_version = remote_protocol;
|
||||
if (remote_sub)
|
||||
@@ -278,10 +295,6 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* set daemon_over_rsh to false since we need to build the
|
||||
* true set of args passed through the rsh/ssh connection;
|
||||
* this is a no-op for direct-socket-connection mode */
|
||||
daemon_over_rsh = 0;
|
||||
server_options(sargs, &sargc);
|
||||
|
||||
if (sargc >= MAX_ARGS - 2)
|
||||
@@ -289,20 +302,45 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
|
||||
|
||||
sargs[sargc++] = ".";
|
||||
|
||||
if (!old_style_args)
|
||||
snprintf(line, sizeof line, " %.*s/", modlen, modname);
|
||||
|
||||
while (argc > 0) {
|
||||
if (sargc >= MAX_ARGS - 1) {
|
||||
arg_overflow:
|
||||
rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
if (strncmp(*argv, modname, modlen) == 0
|
||||
&& argv[0][modlen] == '\0')
|
||||
if (strncmp(*argv, modname, modlen) == 0 && argv[0][modlen] == '\0')
|
||||
sargs[sargc++] = modname; /* we send "modname/" */
|
||||
else if (**argv == '-') {
|
||||
if (asprintf(sargs + sargc++, "./%s", *argv) < 0)
|
||||
out_of_memory("start_inband_exchange");
|
||||
} else
|
||||
sargs[sargc++] = *argv;
|
||||
else {
|
||||
char *arg = *argv;
|
||||
int extra_chars = *arg == '-' ? 2 : 0; /* a leading dash needs a "./" prefix. */
|
||||
/* If --old-args was not specified, make sure that the arg won't split at a mod name! */
|
||||
if (!old_style_args && (p = strstr(arg, line)) != NULL) {
|
||||
do {
|
||||
extra_chars += 2;
|
||||
} while ((p = strstr(p+1, line)) != NULL);
|
||||
}
|
||||
if (extra_chars) {
|
||||
char *f = arg;
|
||||
char *t = arg = new_array(char, strlen(arg) + extra_chars + 1);
|
||||
if (*f == '-') {
|
||||
*t++ = '.';
|
||||
*t++ = '/';
|
||||
}
|
||||
while (*f) {
|
||||
if (*f == ' ' && strncmp(f, line, modlen+2) == 0) {
|
||||
*t++ = '[';
|
||||
*t++ = *f++;
|
||||
*t++ = ']';
|
||||
} else
|
||||
*t++ = *f++;
|
||||
}
|
||||
*t = '\0';
|
||||
}
|
||||
sargs[sargc++] = arg;
|
||||
}
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
@@ -356,7 +394,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
|
||||
|
||||
if (rl_nulls) {
|
||||
for (i = 0; i < sargc; i++) {
|
||||
if (!sargs[i]) /* stop at --protect-args NULL */
|
||||
if (!sargs[i]) /* stop at --secluded-args NULL */
|
||||
break;
|
||||
write_sbuf(f_out, sargs[i]);
|
||||
write_byte(f_out, 0);
|
||||
@@ -381,7 +419,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PUTENV
|
||||
#if defined HAVE_SETENV || defined HAVE_PUTENV
|
||||
static int read_arg_from_pipe(int fd, char *buf, int limit)
|
||||
{
|
||||
char *bp = buf, *eob = buf + limit - 1;
|
||||
@@ -404,27 +442,61 @@ static int read_arg_from_pipe(int fd, char *buf, int limit)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void set_env_str(const char *var, const char *str)
|
||||
void set_env_str(const char *var, const char *str)
|
||||
{
|
||||
#ifdef HAVE_SETENV
|
||||
if (setenv(var, str, 1) < 0)
|
||||
out_of_memory("set_env_str");
|
||||
#else
|
||||
#ifdef HAVE_PUTENV
|
||||
char *mem;
|
||||
if (asprintf(&mem, "%s=%s", var, str) < 0)
|
||||
out_of_memory("set_env_str");
|
||||
putenv(mem);
|
||||
#else
|
||||
(void)var;
|
||||
(void)str;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined HAVE_SETENV || defined HAVE_PUTENV
|
||||
|
||||
static void set_envN_str(const char *var, int num, const char *str)
|
||||
{
|
||||
#ifdef HAVE_SETENV
|
||||
char buf[128];
|
||||
(void)snprintf(buf, sizeof buf, "%s%d", var, num);
|
||||
if (setenv(buf, str, 1) < 0)
|
||||
out_of_memory("set_env_str");
|
||||
#else
|
||||
#ifdef HAVE_PUTENV
|
||||
char *mem;
|
||||
if (asprintf(&mem, "%s%d=%s", var, num, str) < 0)
|
||||
out_of_memory("set_envN_str");
|
||||
putenv(mem);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_env_num(const char *var, long num)
|
||||
{
|
||||
#ifdef HAVE_SETENV
|
||||
char val[64];
|
||||
(void)snprintf(val, sizeof val, "%ld", num);
|
||||
if (setenv(var, val, 1) < 0)
|
||||
out_of_memory("set_env_str");
|
||||
#else
|
||||
#ifdef HAVE_PUTENV
|
||||
char *mem;
|
||||
if (asprintf(&mem, "%s=%ld", var, num) < 0)
|
||||
out_of_memory("set_env_num");
|
||||
putenv(mem);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Used for both early exec & pre-xfer exec */
|
||||
/* Used for "early exec", "pre-xfer exec", and the "name converter" script. */
|
||||
static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
{
|
||||
int arg_fds[2], error_fds[2], arg_fd;
|
||||
@@ -452,15 +524,13 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
set_env_str("RSYNC_REQUEST", buf);
|
||||
|
||||
for (j = 0; ; j++) {
|
||||
char *p;
|
||||
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
|
||||
if (len <= 0) {
|
||||
if (!len)
|
||||
break;
|
||||
_exit(1);
|
||||
}
|
||||
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
|
||||
putenv(p);
|
||||
set_envN_str("RSYNC_ARG", j, buf);
|
||||
}
|
||||
|
||||
dup2(arg_fd, STDIN_FILENO);
|
||||
@@ -491,7 +561,9 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int am_early)
|
||||
#endif
|
||||
|
||||
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
@@ -510,10 +582,11 @@ static void write_pre_exec_args(int write_fd, char *request, char **early_argv,
|
||||
}
|
||||
write_byte(write_fd, 0);
|
||||
|
||||
if (am_early && early_input_len)
|
||||
if (exec_type == 1 && early_input_len)
|
||||
write_buf(write_fd, early_input, early_input_len);
|
||||
|
||||
close(write_fd);
|
||||
if (exec_type != 2) /* the name converter needs this left open */
|
||||
close(write_fd);
|
||||
}
|
||||
|
||||
static char *finish_pre_exec(const char *desc, pid_t pid, int read_fd)
|
||||
@@ -630,7 +703,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
int set_uid;
|
||||
char *p, *err_msg = NULL;
|
||||
char *name = lp_name(i);
|
||||
int use_chroot = lp_use_chroot(i);
|
||||
int use_chroot = lp_use_chroot(i); /* might be 1 (yes), 0 (no), or -1 (unset) */
|
||||
int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1;
|
||||
int save_munge_symlinks;
|
||||
pid_t pre_exec_pid = 0;
|
||||
@@ -695,17 +768,17 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
|
||||
module_id = i;
|
||||
|
||||
if (lp_transfer_logging(i) && !logfile_format)
|
||||
logfile_format = lp_log_format(i);
|
||||
if (lp_transfer_logging(module_id) && !logfile_format)
|
||||
logfile_format = lp_log_format(module_id);
|
||||
if (log_format_has(logfile_format, 'i'))
|
||||
logfile_format_has_i = 1;
|
||||
if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
|
||||
logfile_format_has_o_or_i = 1;
|
||||
|
||||
uid = MY_UID();
|
||||
am_root = (uid == 0);
|
||||
am_root = (uid == ROOT_UID);
|
||||
|
||||
p = *lp_uid(i) ? lp_uid(i) : am_root ? NOBODY_USER : NULL;
|
||||
p = *lp_uid(module_id) ? lp_uid(module_id) : am_root ? NOBODY_USER : NULL;
|
||||
if (p) {
|
||||
if (!user_to_uid(p, &uid, True)) {
|
||||
rprintf(FLOG, "Invalid uid %s\n", p);
|
||||
@@ -716,7 +789,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
} else
|
||||
set_uid = 0;
|
||||
|
||||
p = *lp_gid(i) ? conf_strtok(lp_gid(i)) : NULL;
|
||||
p = *lp_gid(module_id) ? conf_strtok(lp_gid(module_id)) : NULL;
|
||||
if (p) {
|
||||
/* The "*" gid must be the first item in the list. */
|
||||
if (strcmp(p, "*") == 0) {
|
||||
@@ -749,12 +822,26 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
return -1;
|
||||
}
|
||||
|
||||
module_dir = lp_path(i);
|
||||
module_dir = lp_path(module_id);
|
||||
if (*module_dir == '\0') {
|
||||
rprintf(FLOG, "No path specified for module %s\n", name);
|
||||
io_printf(f_out, "@ERROR: no path setting.\n");
|
||||
return -1;
|
||||
}
|
||||
if (use_chroot < 0) {
|
||||
if (strstr(module_dir, "/./") != NULL)
|
||||
use_chroot = 1; /* The module is expecting a chroot inner & outer path. */
|
||||
else if (chroot("/") < 0) {
|
||||
rprintf(FLOG, "chroot test failed: %s. "
|
||||
"Switching 'use chroot' from unset to false.\n",
|
||||
strerror(errno));
|
||||
use_chroot = 0;
|
||||
} else {
|
||||
if (chdir("/") < 0)
|
||||
rsyserr(FLOG, errno, "chdir(\"/\") failed");
|
||||
use_chroot = 1;
|
||||
}
|
||||
}
|
||||
if (use_chroot) {
|
||||
if ((p = strstr(module_dir, "/./")) != NULL) {
|
||||
*p = '\0'; /* Temporary... */
|
||||
@@ -786,38 +873,39 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
} else
|
||||
set_filter_dir(module_dir, module_dirlen);
|
||||
|
||||
p = lp_filter(i);
|
||||
p = lp_filter(module_id);
|
||||
parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
|
||||
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3);
|
||||
|
||||
p = lp_include_from(i);
|
||||
p = lp_include_from(module_id);
|
||||
parse_filter_file(&daemon_filter_list, p, rule_template(FILTRULE_INCLUDE),
|
||||
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
|
||||
|
||||
p = lp_include(i);
|
||||
p = lp_include(module_id);
|
||||
parse_filter_str(&daemon_filter_list, p,
|
||||
rule_template(FILTRULE_INCLUDE | FILTRULE_WORD_SPLIT),
|
||||
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
|
||||
|
||||
p = lp_exclude_from(i);
|
||||
p = lp_exclude_from(module_id);
|
||||
parse_filter_file(&daemon_filter_list, p, rule_template(0),
|
||||
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
|
||||
|
||||
p = lp_exclude(i);
|
||||
p = lp_exclude(module_id);
|
||||
parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
|
||||
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
|
||||
|
||||
log_init(1);
|
||||
|
||||
#ifdef HAVE_PUTENV
|
||||
if ((*lp_early_exec(i) || *lp_prexfer_exec(i) || *lp_postxfer_exec(i))
|
||||
#if defined HAVE_SETENV || defined HAVE_PUTENV
|
||||
if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id)
|
||||
|| *lp_postxfer_exec(module_id) || *lp_name_converter(module_id))
|
||||
&& !getenv("RSYNC_NO_XFER_EXEC")) {
|
||||
set_env_num("RSYNC_PID", (long)getpid());
|
||||
|
||||
/* For post-xfer exec, fork a new process to run the rsync
|
||||
* daemon while this process waits for the exit status and
|
||||
* runs the indicated command at that point. */
|
||||
if (*lp_postxfer_exec(i)) {
|
||||
if (*lp_postxfer_exec(module_id)) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
rsyserr(FLOG, errno, "fork failed");
|
||||
@@ -837,7 +925,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
else
|
||||
status = -1;
|
||||
set_env_num("RSYNC_EXIT_STATUS", status);
|
||||
if (shell_exec(lp_postxfer_exec(i)) < 0)
|
||||
if (shell_exec(lp_postxfer_exec(module_id)) < 0)
|
||||
status = -1;
|
||||
_exit(status);
|
||||
}
|
||||
@@ -845,9 +933,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
|
||||
/* For early exec, fork a child process to run the indicated
|
||||
* command and wait for it to exit. */
|
||||
if (*lp_early_exec(i)) {
|
||||
if (*lp_early_exec(module_id)) {
|
||||
int arg_fd;
|
||||
pid_t pid = start_pre_exec(lp_early_exec(i), &arg_fd, NULL);
|
||||
pid_t pid = start_pre_exec(lp_early_exec(module_id), &arg_fd, NULL);
|
||||
if (pid == (pid_t)-1) {
|
||||
rsyserr(FLOG, errno, "early exec preparation failed");
|
||||
io_printf(f_out, "@ERROR: early exec preparation failed\n");
|
||||
@@ -864,14 +952,23 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
/* For pre-xfer exec, fork a child process to run the indicated
|
||||
* command, though it first waits for the parent process to
|
||||
* send us the user's request via a pipe. */
|
||||
if (*lp_prexfer_exec(i)) {
|
||||
pre_exec_pid = start_pre_exec(lp_prexfer_exec(i), &pre_exec_arg_fd, &pre_exec_error_fd);
|
||||
if (*lp_prexfer_exec(module_id)) {
|
||||
pre_exec_pid = start_pre_exec(lp_prexfer_exec(module_id), &pre_exec_arg_fd, &pre_exec_error_fd);
|
||||
if (pre_exec_pid == (pid_t)-1) {
|
||||
rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
|
||||
io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*lp_name_converter(module_id)) {
|
||||
namecvt_pid = start_pre_exec(lp_name_converter(module_id), &namecvt_fd_req, &namecvt_fd_ans);
|
||||
if (namecvt_pid == (pid_t)-1) {
|
||||
rsyserr(FLOG, errno, "name-converter exec preparation failed");
|
||||
io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -881,32 +978,23 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
|
||||
if (use_chroot) {
|
||||
/*
|
||||
* XXX: The 'use chroot' flag is a fairly reliable
|
||||
* source of confusion, because it fails under two
|
||||
* important circumstances: running as non-root,
|
||||
* running on Win32 (or possibly others). On the
|
||||
* other hand, if you are running as root, then it
|
||||
* might be better to always use chroot.
|
||||
*
|
||||
* So, perhaps if we can't chroot we should just issue
|
||||
* a warning, unless a "require chroot" flag is set,
|
||||
* in which case we fail.
|
||||
*/
|
||||
/* Cache timezone data before chroot makes /etc/localtime inaccessible */
|
||||
tzset();
|
||||
if (chroot(module_chdir)) {
|
||||
rsyserr(FLOG, errno, "chroot %s failed", module_chdir);
|
||||
rsyserr(FLOG, errno, "chroot(\"%s\") failed", module_chdir);
|
||||
io_printf(f_out, "@ERROR: chroot failed\n");
|
||||
return -1;
|
||||
}
|
||||
am_chrooted = 1;
|
||||
module_chdir = module_dir;
|
||||
}
|
||||
|
||||
if (!change_dir(module_chdir, CD_NORMAL))
|
||||
return path_failure(f_out, module_chdir, True);
|
||||
if (module_dirlen || (!use_chroot && !*lp_daemon_chroot()))
|
||||
if (module_dirlen)
|
||||
sanitize_paths = 1;
|
||||
|
||||
if ((munge_symlinks = lp_munge_symlinks(i)) < 0)
|
||||
if ((munge_symlinks = lp_munge_symlinks(module_id)) < 0)
|
||||
munge_symlinks = !use_chroot || module_dirlen;
|
||||
if (munge_symlinks) {
|
||||
STRUCT_STAT st;
|
||||
@@ -920,6 +1008,15 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable secure symlink handling for any non-chrooted daemon module.
|
||||
* This prevents TOCTOU race attacks where an attacker could switch a
|
||||
* directory to a symlink between path validation and file open.
|
||||
* Match the gate used by the do_*_at() wrappers in syscall.c
|
||||
* (am_daemon && !am_chrooted) -- the protection has nothing to do
|
||||
* with symlink munging, so a module configured with
|
||||
* "munge symlinks = false" must still get the secure-open path. */
|
||||
use_secure_symlinks = am_daemon && !am_chrooted;
|
||||
|
||||
if (gid_list.count) {
|
||||
gid_t *gid_array = gid_list.items;
|
||||
if (setgid(gid_array[0])) {
|
||||
@@ -958,11 +1055,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
|
||||
our_uid = MY_UID();
|
||||
am_root = (our_uid == 0);
|
||||
am_root = (our_uid == ROOT_UID);
|
||||
}
|
||||
|
||||
if (lp_temp_dir(i) && *lp_temp_dir(i)) {
|
||||
tmpdir = lp_temp_dir(i);
|
||||
if (lp_temp_dir(module_id) && *lp_temp_dir(module_id)) {
|
||||
tmpdir = lp_temp_dir(module_id);
|
||||
if (strlen(tmpdir) >= MAXPATHLEN - 10) {
|
||||
rprintf(FLOG,
|
||||
"the 'temp dir' value for %s is WAY too long -- ignoring.\n",
|
||||
@@ -989,7 +1086,12 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
} else
|
||||
orig_early_argv = NULL;
|
||||
|
||||
/* The default is to use the user's setting unless the module sets True or False. */
|
||||
if (lp_open_noatime(module_id) >= 0)
|
||||
open_noatime = lp_open_noatime(module_id);
|
||||
|
||||
munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */
|
||||
|
||||
if (am_daemon > 0)
|
||||
msgs2stderr = 0; /* A non-rsh-run daemon doesn't have stderr for msgs. */
|
||||
|
||||
@@ -998,6 +1100,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
err_msg = finish_pre_exec("pre-xfer exec", pre_exec_pid, pre_exec_error_fd);
|
||||
}
|
||||
|
||||
if (namecvt_pid)
|
||||
write_pre_exec_args(namecvt_fd_req, request, orig_early_argv, orig_argv, 2);
|
||||
|
||||
if (orig_early_argv)
|
||||
free(orig_early_argv);
|
||||
|
||||
@@ -1008,7 +1113,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
if (write_batch < 0)
|
||||
dry_run = 1;
|
||||
|
||||
if (lp_fake_super(i)) {
|
||||
if (lp_fake_super(module_id)) {
|
||||
if (preserve_xattrs > 1)
|
||||
preserve_xattrs = 1;
|
||||
am_root = -1;
|
||||
@@ -1033,7 +1138,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
|
||||
#ifndef DEBUG
|
||||
/* don't allow the logs to be flooded too fast */
|
||||
limit_output_verbosity(lp_max_verbosity(i));
|
||||
limit_output_verbosity(lp_max_verbosity(module_id));
|
||||
#endif
|
||||
|
||||
if (protocol_version < 23 && (protocol_version == 22 || am_sender))
|
||||
@@ -1094,20 +1199,21 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
#endif
|
||||
|
||||
if (!numeric_ids
|
||||
&& (use_chroot ? lp_numeric_ids(i) != False : lp_numeric_ids(i) == True))
|
||||
&& (use_chroot ? lp_numeric_ids(module_id) != False && !*lp_name_converter(module_id)
|
||||
: lp_numeric_ids(module_id) == True))
|
||||
numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */
|
||||
|
||||
if (lp_timeout(i) && (!io_timeout || lp_timeout(i) < io_timeout))
|
||||
set_io_timeout(lp_timeout(i));
|
||||
if (lp_timeout(module_id) && (!io_timeout || lp_timeout(module_id) < io_timeout))
|
||||
set_io_timeout(lp_timeout(module_id));
|
||||
|
||||
/* If we have some incoming/outgoing chmod changes, append them to
|
||||
* any user-specified changes (making our changes have priority).
|
||||
* We also get a pointer to just our changes so that a receiver
|
||||
* process can use them separately if --perms wasn't specified. */
|
||||
if (am_sender)
|
||||
p = lp_outgoing_chmod(i);
|
||||
p = lp_outgoing_chmod(module_id);
|
||||
else
|
||||
p = lp_incoming_chmod(i);
|
||||
p = lp_incoming_chmod(module_id);
|
||||
if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) {
|
||||
rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n",
|
||||
am_sender ? "outgo" : "incom", p);
|
||||
@@ -1118,6 +1224,38 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL namecvt_call(const char *cmd, const char **name_p, id_t *id_p)
|
||||
{
|
||||
char buf[1024];
|
||||
int got, len;
|
||||
|
||||
if (*name_p)
|
||||
len = snprintf(buf, sizeof buf, "%s %s\n", cmd, *name_p);
|
||||
else
|
||||
len = snprintf(buf, sizeof buf, "%s %ld\n", cmd, (long)*id_p);
|
||||
if (len >= (int)sizeof buf) {
|
||||
rprintf(FERROR, "namecvt_call() request was too large.\n");
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
while ((got = write(namecvt_fd_req, buf, len)) != len) {
|
||||
if (got < 0 && errno == EINTR)
|
||||
continue;
|
||||
rprintf(FERROR, "Connection to name-converter failed.\n");
|
||||
exit_cleanup(RERR_SOCKETIO);
|
||||
}
|
||||
|
||||
if (!read_line_old(namecvt_fd_ans, buf, sizeof buf, 0))
|
||||
return False;
|
||||
|
||||
if (*name_p)
|
||||
*id_p = (id_t)atol(buf);
|
||||
else
|
||||
*name_p = strdup(buf);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/* send a list of available modules to the client. Don't list those
|
||||
with "list = False". */
|
||||
static void send_listing(int fd)
|
||||
@@ -1174,11 +1312,51 @@ int start_daemon(int f_in, int f_out)
|
||||
if (lp_proxy_protocol() && !read_proxy_protocol_header(f_in))
|
||||
return -1;
|
||||
|
||||
/* Do reverse DNS lookup before chroot/setuid. The result is cached,
|
||||
* so the later client_name() call will use this cached value. This
|
||||
* ensures hostname-based ACLs work even when DNS is unavailable
|
||||
* after chroot.
|
||||
*
|
||||
* "reverse lookup" can be set globally OR per-module, so we also
|
||||
* scan each module: a deployment with "reverse lookup = no" in the
|
||||
* global section but "reverse lookup = yes" in a specific module
|
||||
* still triggers a post-chroot lookup at access-check time
|
||||
* (rsync_module() in this file), which would also fail in the
|
||||
* chroot and turn hostname-based deny rules into silent bypasses. */
|
||||
{
|
||||
int need_reverse = lp_reverse_lookup(-1);
|
||||
int j, num_modules = lp_num_modules();
|
||||
for (j = 0; !need_reverse && j < num_modules; j++) {
|
||||
if (lp_reverse_lookup(j))
|
||||
need_reverse = 1;
|
||||
}
|
||||
if (need_reverse)
|
||||
(void)client_name(client_addr(f_in));
|
||||
}
|
||||
|
||||
p = lp_daemon_chroot();
|
||||
if (*p) {
|
||||
log_init(0); /* Make use we've initialized syslog before chrooting. */
|
||||
if (chroot(p) < 0 || chdir("/") < 0) {
|
||||
rsyserr(FLOG, errno, "daemon chroot %s failed", p);
|
||||
tzset();
|
||||
if (chroot(p) < 0) {
|
||||
rsyserr(FLOG, errno, "daemon chroot(\"%s\") failed", p);
|
||||
return -1;
|
||||
}
|
||||
/* Deliberately do NOT set am_chrooted here. am_chrooted
|
||||
* gates the per-module symlink-race defenses
|
||||
* (secure_relative_open() and the do_*_at() wrappers in
|
||||
* syscall.c) and means "the kernel is enforcing path
|
||||
* confinement at the module boundary". The daemon chroot
|
||||
* confines path resolution to the daemon-chroot directory,
|
||||
* not to any individual module path -- modules sharing the
|
||||
* daemon chroot are still distinguishable filesystem
|
||||
* subtrees and a sender-controlled symlink in module A
|
||||
* could redirect a syscall to module B (or to other files
|
||||
* inside the daemon chroot) without the per-module
|
||||
* defenses. Leave am_chrooted=0 here so secure_relative_open()
|
||||
* still fires for "use chroot = no" modules. */
|
||||
if (chdir("/") < 0) {
|
||||
rsyserr(FLOG, errno, "daemon chdir(\"/\") failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1207,7 +1385,7 @@ int start_daemon(int f_in, int f_out)
|
||||
return -1;
|
||||
}
|
||||
our_uid = MY_UID();
|
||||
am_root = (our_uid == 0);
|
||||
am_root = (our_uid == ROOT_UID);
|
||||
}
|
||||
|
||||
addr = client_addr(f_in);
|
||||
@@ -1400,7 +1578,7 @@ int daemon_main(void)
|
||||
log_init(0);
|
||||
|
||||
rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n",
|
||||
RSYNC_VERSION, rsync_port);
|
||||
rsync_version(), rsync_port);
|
||||
/* TODO: If listening on a particular address, then show that
|
||||
* address too. In fact, why not just do getnameinfo on the
|
||||
* local address??? */
|
||||
|
||||
264
compat.c
264
compat.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) Andrew Tridgell 1996
|
||||
* Copyright (C) Paul Mackerras 1996
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rsync.h"
|
||||
#include "itypes.h"
|
||||
#include "ifuncs.h"
|
||||
|
||||
extern int am_server;
|
||||
extern int am_sender;
|
||||
@@ -43,6 +44,7 @@ extern int protect_args;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_atimes;
|
||||
extern int preserve_crtimes;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_xattrs;
|
||||
extern int xfer_flags_as_varint;
|
||||
@@ -50,19 +52,25 @@ extern int need_messages_from_generator;
|
||||
extern int delete_mode, delete_before, delete_during, delete_after;
|
||||
extern int do_compression;
|
||||
extern int do_compression_level;
|
||||
extern int do_compression_threads;
|
||||
extern int saw_stderr_opt;
|
||||
extern int msgs2stderr;
|
||||
extern char *shell_cmd;
|
||||
extern char *partial_dir;
|
||||
extern char *files_from;
|
||||
extern char *filesfrom_host;
|
||||
extern const char *checksum_choice;
|
||||
extern const char *compress_choice;
|
||||
extern char *daemon_auth_choices;
|
||||
extern filter_rule_list filter_list;
|
||||
extern int need_unsorted_flist;
|
||||
#ifdef ICONV_OPTION
|
||||
extern iconv_t ic_send, ic_recv;
|
||||
extern char *iconv_opt;
|
||||
#endif
|
||||
extern struct name_num_obj valid_checksums;
|
||||
extern struct name_num_obj valid_checksums, valid_auth_checksums;
|
||||
|
||||
extern struct name_num_item *xfer_sum_nni;
|
||||
|
||||
int remote_protocol = 0;
|
||||
int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
|
||||
@@ -73,9 +81,13 @@ int want_xattr_optim = 0;
|
||||
int proper_seed_order = 0;
|
||||
int inplace_partial = 0;
|
||||
int do_negotiated_strings = 0;
|
||||
int xmit_id0_names = 0;
|
||||
|
||||
struct name_num_item *xattr_sum_nni;
|
||||
int xattr_sum_len = 0;
|
||||
|
||||
/* These index values are for the file-list's extra-attribute array. */
|
||||
int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
|
||||
int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
|
||||
|
||||
int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
|
||||
int sender_symlink_iconv = 0; /* sender should convert symlink content */
|
||||
@@ -86,19 +98,21 @@ int filesfrom_convert = 0;
|
||||
|
||||
#define MAX_NSTR_STRLEN 256
|
||||
|
||||
struct name_num_obj valid_compressions = {
|
||||
"compress", NULL, NULL, 0, 0, {
|
||||
struct name_num_item valid_compressions_items[] = {
|
||||
#ifdef SUPPORT_ZSTD
|
||||
{ CPRES_ZSTD, "zstd", NULL },
|
||||
{ CPRES_ZSTD, 0, "zstd", NULL },
|
||||
#endif
|
||||
#ifdef SUPPORT_LZ4
|
||||
{ CPRES_LZ4, "lz4", NULL },
|
||||
{ CPRES_LZ4, 0, "lz4", NULL },
|
||||
#endif
|
||||
{ CPRES_ZLIBX, "zlibx", NULL },
|
||||
{ CPRES_ZLIB, "zlib", NULL },
|
||||
{ CPRES_NONE, "none", NULL },
|
||||
{ 0, NULL, NULL }
|
||||
}
|
||||
{ CPRES_ZLIBX, 0, "zlibx", NULL },
|
||||
{ CPRES_ZLIB, 0, "zlib", NULL },
|
||||
{ CPRES_NONE, 0, "none", NULL },
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
struct name_num_obj valid_compressions = {
|
||||
"compress", NULL, 0, 0, valid_compressions_items
|
||||
};
|
||||
|
||||
#define CF_INC_RECURSE (1<<0)
|
||||
@@ -109,6 +123,7 @@ struct name_num_obj valid_compressions = {
|
||||
#define CF_CHKSUM_SEED_FIX (1<<5)
|
||||
#define CF_INPLACE_PARTIAL_DIR (1<<6)
|
||||
#define CF_VARINT_FLIST_FLAGS (1<<7)
|
||||
#define CF_ID0_NAMES (1<<8)
|
||||
|
||||
static const char *client_info;
|
||||
|
||||
@@ -117,13 +132,9 @@ static const char *client_info;
|
||||
* of that protocol for it to be advertised as available. */
|
||||
static void check_sub_protocol(void)
|
||||
{
|
||||
char *dot;
|
||||
const char *dot;
|
||||
int their_protocol, their_sub;
|
||||
#if SUBPROTOCOL_VERSION != 0
|
||||
int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
|
||||
#else
|
||||
int our_sub = 0;
|
||||
#endif
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
/* client_info starts with VER.SUB string if client is a pre-release. */
|
||||
if (!(their_protocol = atoi(client_info))
|
||||
@@ -150,7 +161,13 @@ static void check_sub_protocol(void)
|
||||
|
||||
void set_allow_inc_recurse(void)
|
||||
{
|
||||
client_info = shell_cmd ? shell_cmd : "";
|
||||
if (!local_server)
|
||||
client_info = shell_cmd ? shell_cmd : "";
|
||||
else if (am_server) {
|
||||
char buf[64];
|
||||
maybe_add_e_option(buf, sizeof buf);
|
||||
client_info = *buf ? strdup(buf+1) : ""; /* The +1 skips the leading "e". */
|
||||
}
|
||||
|
||||
if (!recurse || use_qsort)
|
||||
allow_inc_recurse = 0;
|
||||
@@ -158,15 +175,14 @@ void set_allow_inc_recurse(void)
|
||||
&& (delete_before || delete_after
|
||||
|| delay_updates || prune_empty_dirs))
|
||||
allow_inc_recurse = 0;
|
||||
else if (am_server && !local_server
|
||||
&& (strchr(client_info, 'i') == NULL))
|
||||
else if (am_server && strchr(client_info, 'i') == NULL)
|
||||
allow_inc_recurse = 0;
|
||||
}
|
||||
|
||||
void parse_compress_choice(int final_call)
|
||||
{
|
||||
if (valid_compressions.negotiated_name)
|
||||
do_compression = valid_compressions.negotiated_num;
|
||||
if (valid_compressions.negotiated_nni)
|
||||
do_compression = valid_compressions.negotiated_nni->num;
|
||||
else if (compress_choice) {
|
||||
struct name_num_item *nni = get_nni_by_name(&valid_compressions, compress_choice, -1);
|
||||
if (!nni) {
|
||||
@@ -188,8 +204,8 @@ void parse_compress_choice(int final_call)
|
||||
compress_choice = NULL;
|
||||
|
||||
/* Snag the compression name for both write_batch's option output & the following debug output. */
|
||||
if (valid_compressions.negotiated_name)
|
||||
compress_choice = valid_compressions.negotiated_name;
|
||||
if (valid_compressions.negotiated_nni)
|
||||
compress_choice = valid_compressions.negotiated_nni->name;
|
||||
else if (compress_choice == NULL) {
|
||||
struct name_num_item *nni = get_nni_by_num(&valid_compressions, do_compression);
|
||||
compress_choice = nni ? nni->name : "UNKNOWN";
|
||||
@@ -199,7 +215,7 @@ void parse_compress_choice(int final_call)
|
||||
&& (do_compression != CPRES_NONE || do_compression_level != CLVL_NOT_SPECIFIED)) {
|
||||
rprintf(FINFO, "%s%s compress: %s (level %d)\n",
|
||||
am_server ? "Server" : "Client",
|
||||
valid_compressions.negotiated_name ? " negotiated" : "",
|
||||
valid_compressions.negotiated_nni ? " negotiated" : "",
|
||||
compress_choice, do_compression_level);
|
||||
}
|
||||
}
|
||||
@@ -212,6 +228,8 @@ struct name_num_item *get_nni_by_name(struct name_num_obj *nno, const char *name
|
||||
len = strlen(name);
|
||||
|
||||
for (nni = nno->list; nni->name; nni++) {
|
||||
if (nni->num == CSUM_gone)
|
||||
continue;
|
||||
if (strncasecmp(name, nni->name, len) == 0 && nni->name[len] == '\0')
|
||||
return nni;
|
||||
}
|
||||
@@ -246,10 +264,12 @@ static void init_nno_saw(struct name_num_obj *nno, int val)
|
||||
if (!nno->saw) {
|
||||
nno->saw = new_array0(uchar, nno->saw_len);
|
||||
|
||||
/* We'll take this opportunity to make sure that the main_name values are set right. */
|
||||
/* We'll take this opportunity to set the main_nni values for duplicates. */
|
||||
for (cnt = 1, nni = nno->list; nni->name; nni++, cnt++) {
|
||||
if (nni->num == CSUM_gone)
|
||||
continue;
|
||||
if (nno->saw[nni->num])
|
||||
nni->main_name = nno->list[nno->saw[nni->num]-1].name;
|
||||
nni->main_nni = &nno->list[nno->saw[nni->num]-1];
|
||||
else
|
||||
nno->saw[nni->num] = cnt;
|
||||
}
|
||||
@@ -275,8 +295,8 @@ static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf
|
||||
struct name_num_item *nni = get_nni_by_name(nno, tok, to - tok);
|
||||
if (nni && !nno->saw[nni->num]) {
|
||||
nno->saw[nni->num] = ++cnt;
|
||||
if (nni->main_name) {
|
||||
to = tok + strlcpy(tok, nni->main_name, tobuf_len - (tok - tobuf));
|
||||
if (nni->main_nni) {
|
||||
to = tok + strlcpy(tok, nni->main_nni->name, tobuf_len - (tok - tobuf));
|
||||
if (to - tobuf >= tobuf_len) {
|
||||
to = tok - 1;
|
||||
break;
|
||||
@@ -310,13 +330,44 @@ static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf
|
||||
return to - tobuf;
|
||||
}
|
||||
|
||||
static int parse_negotiate_str(struct name_num_obj *nno, char *tmpbuf)
|
||||
{
|
||||
struct name_num_item *nni, *ret = NULL;
|
||||
int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
|
||||
char *space, *tok = tmpbuf;
|
||||
while (tok) {
|
||||
while (*tok == ' ') tok++; /* Should be unneeded... */
|
||||
if (!*tok)
|
||||
break;
|
||||
if ((space = strchr(tok, ' ')) != NULL)
|
||||
*space = '\0';
|
||||
nni = get_nni_by_name(nno, tok, -1);
|
||||
if (space) {
|
||||
*space = ' ';
|
||||
tok = space + 1;
|
||||
} else
|
||||
tok = NULL;
|
||||
if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
|
||||
continue;
|
||||
ret = nni;
|
||||
best = nno->saw[nni->num];
|
||||
if (best == 1 || am_server) /* The server side stops at the first acceptable client choice */
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
free(nno->saw);
|
||||
nno->saw = NULL;
|
||||
nno->negotiated_nni = ret->main_nni ? ret->main_nni : ret;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This routine is always called with a tmpbuf of MAX_NSTR_STRLEN length, but the
|
||||
* buffer may be pre-populated with a "len" length string to use OR a len of -1
|
||||
* to tell us to read a string from the fd. */
|
||||
static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf, int len)
|
||||
{
|
||||
struct name_num_item *ret = NULL;
|
||||
|
||||
if (len < 0)
|
||||
len = read_vstring(f_in, tmpbuf, MAX_NSTR_STRLEN);
|
||||
|
||||
@@ -327,37 +378,8 @@ static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf,
|
||||
rprintf(FINFO, "Server %s list (on client): %s\n", nno->type, tmpbuf);
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
struct name_num_item *nni;
|
||||
int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
|
||||
char *space, *tok = tmpbuf;
|
||||
while (tok) {
|
||||
while (*tok == ' ') tok++; /* Should be unneeded... */
|
||||
if (!*tok)
|
||||
break;
|
||||
if ((space = strchr(tok, ' ')) != NULL)
|
||||
*space = '\0';
|
||||
nni = get_nni_by_name(nno, tok, -1);
|
||||
if (space) {
|
||||
*space = ' ';
|
||||
tok = space + 1;
|
||||
} else
|
||||
tok = NULL;
|
||||
if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
|
||||
continue;
|
||||
ret = nni;
|
||||
best = nno->saw[nni->num];
|
||||
if (best == 1 || am_server) /* The server side stops at the first acceptable client choice */
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
free(nno->saw);
|
||||
nno->saw = NULL;
|
||||
nno->negotiated_name = ret->main_name ? ret->main_name : ret->name;
|
||||
nno->negotiated_num = ret->num;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (len > 0 && parse_negotiate_str(nno, tmpbuf))
|
||||
return;
|
||||
|
||||
if (!am_server || !do_negotiated_strings) {
|
||||
char *cp = tmpbuf;
|
||||
@@ -366,7 +388,7 @@ static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf,
|
||||
rprintf(FERROR, "%s list: %s\n", am_server ? "Client" : "Server", tmpbuf);
|
||||
/* Recreate our original list from the saw values. This can't overflow our huge
|
||||
* buffer because we don't have enough valid entries to get anywhere close. */
|
||||
for (j = 1; j <= nno->saw_len; j++) {
|
||||
for (j = 1, *cp = '\0'; j <= nno->saw_len; j++) {
|
||||
struct name_num_item *nni;
|
||||
for (nni = nno->list; nni->name; nni++) {
|
||||
if (nno->saw[nni->num] == j) {
|
||||
@@ -376,6 +398,8 @@ static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!*tmpbuf)
|
||||
strlcpy(cp, " INVALID", MAX_NSTR_STRLEN);
|
||||
rprintf(FERROR, "%s list:%s\n", am_server ? "Server" : "Client", tmpbuf);
|
||||
}
|
||||
|
||||
@@ -387,11 +411,11 @@ static const char *getenv_nstr(int ntype)
|
||||
const char *env_str = getenv(ntype == NSTR_COMPRESS ? "RSYNC_COMPRESS_LIST" : "RSYNC_CHECKSUM_LIST");
|
||||
|
||||
/* When writing a batch file, we always negotiate an old-style choice. */
|
||||
if (write_batch)
|
||||
if (write_batch)
|
||||
env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
|
||||
|
||||
if (am_server && env_str) {
|
||||
char *cp = strchr(env_str, '&');
|
||||
const char *cp = strchr(env_str, '&');
|
||||
if (cp)
|
||||
env_str = cp + 1;
|
||||
}
|
||||
@@ -420,7 +444,7 @@ void validate_choice_vs_env(int ntype, int num1, int num2)
|
||||
nno->saw[CSUM_MD4_ARCHAIC] = nno->saw[CSUM_MD4_BUSTED] = nno->saw[CSUM_MD4_OLD] = nno->saw[CSUM_MD4];
|
||||
|
||||
if (!nno->saw[num1] || (num2 >= 0 && !nno->saw[num2])) {
|
||||
rprintf(FERROR, "Your --%s-choice value (%s) was refused by the server.\n",
|
||||
rprintf(FERROR, "Your --%s-choice value (%s) was refused by the server.\n",
|
||||
ntype == NSTR_COMPRESS ? "compress" : "checksum",
|
||||
ntype == NSTR_COMPRESS ? compress_choice : checksum_choice);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
@@ -451,8 +475,10 @@ int get_default_nno_list(struct name_num_obj *nno, char *to_buf, int to_buf_len,
|
||||
init_nno_saw(nno, 0);
|
||||
|
||||
for (nni = nno->list, len = 0; nni->name; nni++) {
|
||||
if (nni->main_name) {
|
||||
if (!dup_markup)
|
||||
if (nni->num == CSUM_gone)
|
||||
continue;
|
||||
if (nni->main_nni) {
|
||||
if (!dup_markup || nni->main_nni->num == CSUM_gone)
|
||||
continue;
|
||||
delim = dup_markup;
|
||||
}
|
||||
@@ -510,6 +536,8 @@ static void negotiate_the_strings(int f_in, int f_out)
|
||||
{
|
||||
/* We send all the negotiation strings before we start to read them to help avoid a slow startup. */
|
||||
|
||||
init_checksum_choices();
|
||||
|
||||
if (!checksum_choice)
|
||||
send_negotiate_str(f_out, &valid_checksums, NSTR_CHECKSUM);
|
||||
|
||||
@@ -539,7 +567,7 @@ static void negotiate_the_strings(int f_in, int f_out)
|
||||
/* If the other side is too old to negotiate, the above steps just made sure that
|
||||
* the env didn't disallow the old algorithm. Mark things as non-negotiated. */
|
||||
if (!do_negotiated_strings)
|
||||
valid_checksums.negotiated_name = valid_compressions.negotiated_name = NULL;
|
||||
valid_checksums.negotiated_nni = valid_compressions.negotiated_nni = NULL;
|
||||
}
|
||||
|
||||
void setup_protocol(int f_out,int f_in)
|
||||
@@ -551,7 +579,9 @@ void setup_protocol(int f_out,int f_in)
|
||||
* aligned for direct int64-pointer memory access. */
|
||||
if (preserve_atimes)
|
||||
atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
|
||||
if (am_sender) /* This is most likely in the in64 union as well. */
|
||||
if (preserve_crtimes)
|
||||
crtimes_ndx = (file_extra_cnt += EXTRA64_CNT);
|
||||
if (am_sender) /* This is most likely in the file_extras64 union as well. */
|
||||
pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT);
|
||||
else
|
||||
depth_ndx = ++file_extra_cnt;
|
||||
@@ -589,7 +619,7 @@ void setup_protocol(int f_out,int f_in)
|
||||
if (remote_protocol < MIN_PROTOCOL_VERSION
|
||||
|| remote_protocol > MAX_PROTOCOL_VERSION) {
|
||||
rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n");
|
||||
rprintf(FERROR,"(see the rsync man page for an explanation)\n");
|
||||
rprintf(FERROR,"(see the rsync manpage for an explanation)\n");
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (remote_protocol < OLD_PROTOCOL_VERSION) {
|
||||
@@ -609,6 +639,9 @@ void setup_protocol(int f_out,int f_in)
|
||||
if (read_batch)
|
||||
check_batch_flags();
|
||||
|
||||
if (!saw_stderr_opt && protocol_version <= 28 && am_server)
|
||||
msgs2stderr = 0; /* The client side may not have stderr setup for us. */
|
||||
|
||||
#ifndef SUPPORT_PREALLOCATION
|
||||
if (preallocate_files && !am_sender) {
|
||||
rprintf(FERROR, "preallocation is not supported on this %s\n",
|
||||
@@ -684,15 +717,17 @@ void setup_protocol(int f_out,int f_in)
|
||||
#ifdef ICONV_OPTION
|
||||
compat_flags |= CF_SYMLINK_ICONV;
|
||||
#endif
|
||||
if (local_server || strchr(client_info, 'f') != NULL)
|
||||
if (strchr(client_info, 'f') != NULL)
|
||||
compat_flags |= CF_SAFE_FLIST;
|
||||
if (local_server || strchr(client_info, 'x') != NULL)
|
||||
if (strchr(client_info, 'x') != NULL)
|
||||
compat_flags |= CF_AVOID_XATTR_OPTIM;
|
||||
if (local_server || strchr(client_info, 'C') != NULL)
|
||||
if (strchr(client_info, 'C') != NULL)
|
||||
compat_flags |= CF_CHKSUM_SEED_FIX;
|
||||
if (local_server || strchr(client_info, 'I') != NULL)
|
||||
if (strchr(client_info, 'I') != NULL)
|
||||
compat_flags |= CF_INPLACE_PARTIAL_DIR;
|
||||
if (local_server || strchr(client_info, 'v') != NULL) {
|
||||
if (strchr(client_info, 'u') != NULL)
|
||||
compat_flags |= CF_ID0_NAMES;
|
||||
if (strchr(client_info, 'v') != NULL) {
|
||||
do_negotiated_strings = 1;
|
||||
compat_flags |= CF_VARINT_FLIST_FLAGS;
|
||||
}
|
||||
@@ -712,6 +747,11 @@ void setup_protocol(int f_out,int f_in)
|
||||
want_xattr_optim = protocol_version >= 31 && !(compat_flags & CF_AVOID_XATTR_OPTIM);
|
||||
proper_seed_order = compat_flags & CF_CHKSUM_SEED_FIX ? 1 : 0;
|
||||
xfer_flags_as_varint = compat_flags & CF_VARINT_FLIST_FLAGS ? 1 : 0;
|
||||
xmit_id0_names = compat_flags & CF_ID0_NAMES ? 1 : 0;
|
||||
if (!xfer_flags_as_varint && preserve_crtimes) {
|
||||
fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (am_sender) {
|
||||
receiver_symlink_times = am_server
|
||||
? strchr(client_info, 'L') != NULL
|
||||
@@ -723,7 +763,7 @@ void setup_protocol(int f_out,int f_in)
|
||||
#endif
|
||||
#ifdef ICONV_OPTION
|
||||
sender_symlink_iconv = iconv_opt && (am_server
|
||||
? local_server || strchr(client_info, 's') != NULL
|
||||
? strchr(client_info, 's') != NULL
|
||||
: !!(compat_flags & CF_SYMLINK_ICONV));
|
||||
#endif
|
||||
if (inc_recurse && !allow_inc_recurse) {
|
||||
@@ -776,11 +816,77 @@ void setup_protocol(int f_out,int f_in)
|
||||
checksum_seed = read_int(f_in);
|
||||
}
|
||||
|
||||
parse_checksum_choice(1); /* Sets checksum_type & xfersum_type */
|
||||
parse_checksum_choice(1); /* Sets file_sum_nni & xfer_sum_nni */
|
||||
parse_compress_choice(1); /* Sets do_compression */
|
||||
|
||||
/* TODO in the future allow this algorithm to be chosen somehow, but it can't get too
|
||||
* long or the size starts to cause a problem in the xattr abbrev/non-abbrev code. */
|
||||
xattr_sum_nni = parse_csum_name(NULL, 0);
|
||||
xattr_sum_len = csum_len_for_type(xattr_sum_nni->num, 0);
|
||||
|
||||
if (write_batch && !am_server)
|
||||
write_batch_shell_file();
|
||||
|
||||
init_flist();
|
||||
}
|
||||
|
||||
void output_daemon_greeting(int f_out, int am_client)
|
||||
{
|
||||
char tmpbuf[MAX_NSTR_STRLEN];
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
init_checksum_choices();
|
||||
|
||||
get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
|
||||
|
||||
io_printf(f_out, "@RSYNCD: %d.%d %s\n", protocol_version, our_sub, tmpbuf);
|
||||
|
||||
if (am_client && DEBUG_GTE(NSTR, 2))
|
||||
rprintf(FINFO, "Client %s list (on client): %s\n", valid_auth_checksums.type, tmpbuf);
|
||||
}
|
||||
|
||||
void negotiate_daemon_auth(int f_out, int am_client)
|
||||
{
|
||||
char tmpbuf[MAX_NSTR_STRLEN];
|
||||
int save_am_server = am_server;
|
||||
int md4_is_old = 0;
|
||||
|
||||
if (!am_client)
|
||||
am_server = 1;
|
||||
|
||||
if (daemon_auth_choices)
|
||||
strlcpy(tmpbuf, daemon_auth_choices, MAX_NSTR_STRLEN);
|
||||
else {
|
||||
strlcpy(tmpbuf, protocol_version >= 30 ? "md5" : "md4", MAX_NSTR_STRLEN);
|
||||
md4_is_old = 1;
|
||||
}
|
||||
|
||||
if (am_client) {
|
||||
recv_negotiate_str(-1, &valid_auth_checksums, tmpbuf, strlen(tmpbuf));
|
||||
if (DEBUG_GTE(NSTR, 1)) {
|
||||
rprintf(FINFO, "Client negotiated %s: %s\n", valid_auth_checksums.type,
|
||||
valid_auth_checksums.negotiated_nni->name);
|
||||
}
|
||||
} else {
|
||||
if (!parse_negotiate_str(&valid_auth_checksums, tmpbuf)) {
|
||||
get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
|
||||
io_printf(f_out, "@ERROR: your client does not support one of our daemon-auth checksums: %s\n",
|
||||
tmpbuf);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
am_server = save_am_server;
|
||||
if (md4_is_old && valid_auth_checksums.negotiated_nni->num == CSUM_MD4) {
|
||||
valid_auth_checksums.negotiated_nni->num = CSUM_MD4_OLD;
|
||||
valid_auth_checksums.negotiated_nni->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int get_subprotocol_version()
|
||||
{
|
||||
#if SUBPROTOCOL_VERSION != 0
|
||||
return protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
1210
config.guess
vendored
1210
config.guess
vendored
File diff suppressed because it is too large
Load Diff
676
config.sub
vendored
676
config.sub
vendored
File diff suppressed because it is too large
Load Diff
28
configure
vendored
28
configure
vendored
@@ -4,24 +4,24 @@
|
||||
# then transfer control to the configure.sh script to do the real work.
|
||||
|
||||
dir=`dirname $0`
|
||||
realconfigure="$dir/configure.sh"
|
||||
if test x"$dir" = x; then
|
||||
dir=.
|
||||
fi
|
||||
|
||||
if test ! -f "$realconfigure"; then
|
||||
if test -f "$HOME/build_farm/build_test.fns"; then
|
||||
# Test the included popt
|
||||
set -- --with-included-popt "${@}"
|
||||
# Allow the build farm to grab latest files via rsync.
|
||||
actions='build fetch'
|
||||
else
|
||||
actions='build'
|
||||
if test "$dir" = '.'; then
|
||||
branch=`packaging/prep-auto-dir` || exit 1
|
||||
if test x"$branch" != x; then
|
||||
cd build || exit 1
|
||||
dir=..
|
||||
fi
|
||||
if "$dir/prepare-source" $actions; then
|
||||
:
|
||||
else
|
||||
fi
|
||||
|
||||
if test ! -f configure.sh; then
|
||||
if ! "$dir/prepare-source" build; then
|
||||
echo 'Failed to build configure.sh and/or config.h.in -- giving up.' >&2
|
||||
rm -f "$realconfigure"
|
||||
rm -f configure.sh
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$realconfigure" "${@}"
|
||||
exec ./configure.sh --srcdir="$dir" "${@}"
|
||||
|
||||
693
configure.ac
693
configure.ac
File diff suppressed because it is too large
Load Diff
@@ -7,39 +7,54 @@ basically a summary of clientserver.c and authenticate.c.
|
||||
This is the protocol used for rsync --daemon; i.e. connections to port
|
||||
873 rather than invocations over a remote shell.
|
||||
|
||||
When the server accepts a connection, it prints a greeting
|
||||
When the server accepts a connection, it prints a newline-terminated
|
||||
greeting line:
|
||||
|
||||
@RSYNCD: <version>.<subprotocol>
|
||||
@RSYNCD: <version>.<subprotocol> <digest1> <digestN>
|
||||
|
||||
where <version> is the numeric version (see PROTOCOL_VERSION in rsync.h)
|
||||
'.' is a literal period, and <subprotocol> is the numeric subprotocol
|
||||
version (see SUBPROTOCOL_VERSION -- it will be 0 for final releases).
|
||||
Protocols prior to 30 only output <version> alone. The daemon expects
|
||||
to see a similar greeting back from the client. For protocols prior to
|
||||
30, an absent ".<subprotocol>" value is assumed to be 0. For protocol
|
||||
30, an absent value is a fatal error. The daemon then follows this line
|
||||
with a free-format text message-of-the-day (if any is defined).
|
||||
The <version> is the numeric version (see PROTOCOL_VERSION in rsync.h)
|
||||
The <subprotocol> is the numeric subprotocol version (which is 0 for a
|
||||
final protocol version, as the SUBPROTOCOL_VERSION define discusses).
|
||||
The <digestN> names are the authentication digest algorithms that the
|
||||
daemon supports, listed in order of preference.
|
||||
|
||||
An rsync prior to 3.2.7 omits the digest names. An rsync prior to 3.0.0
|
||||
also omits the period and the <subprotocol> value. Since a final
|
||||
protocol has a subprotocol value of 0, a missing subprotocol value is
|
||||
assumed to be 0 for any protocol prior to 30. It is considered a fatal
|
||||
error for protocol 30 and above to omit it. It is considered a fatal
|
||||
error for protocol 32 and above to omit the digest name list (currently
|
||||
31 is the newest protocol).
|
||||
|
||||
The daemon expects to see a similar greeting line back from the client.
|
||||
Once received, the daemon follows the opening line with a free-format
|
||||
text message-of-the-day (if any is defined).
|
||||
|
||||
The server is now in the connected state. The client can either send
|
||||
the command
|
||||
the command:
|
||||
|
||||
#list
|
||||
|
||||
to get a listing of modules, or the name of a module. After this, the
|
||||
(to get a listing of modules) or the name of a module. After this, the
|
||||
connection is now bound to a particular module. Access per host for
|
||||
this module is now checked, as is per-module connection limits.
|
||||
|
||||
If authentication is required to use this module, the server will say
|
||||
If authentication is required to use this module, the server will say:
|
||||
|
||||
@RSYNCD: AUTHREQD <challenge>
|
||||
|
||||
where <challenge> is a random string of base64 characters. The client
|
||||
must respond with
|
||||
must respond with:
|
||||
|
||||
<user> <response>
|
||||
|
||||
where <user> is the username they claim to be, and <response> is the
|
||||
base64 form of the MD4 hash of challenge+password.
|
||||
The <user> is the username they claim to be. The <response> is the
|
||||
base64 form of the digest hash of the challenge+password string. The
|
||||
chosen digest method is the most preferred client method that is also in
|
||||
the server's list. If no digest list was explicitly provided, the side
|
||||
expecting a list assumes the other side provided either the single name
|
||||
"md5" (for a negotiated protocol 30 or 31), or the single name "md4"
|
||||
(for an older protocol).
|
||||
|
||||
At this point the server applies all remaining constraints before
|
||||
handing control to the client, including switching uid/gid, setting up
|
||||
@@ -76,6 +91,13 @@ stay tuned (or write it yourself!).
|
||||
------------
|
||||
Protocol version changes
|
||||
|
||||
31 (2013-09-28, 3.1.0)
|
||||
|
||||
Initial release of protocol 31 had no changes. Rsync 3.2.7
|
||||
introduced the suffixed list of digest names on the greeting
|
||||
line. The presence of the list is allowed even if the greeting
|
||||
indicates an older protocol version number.
|
||||
|
||||
30 (2007-10-04, 3.0.0pre1)
|
||||
|
||||
The use of a ".<subprotocol>" number was added to
|
||||
|
||||
114
daemon-parm.awk
Executable file
114
daemon-parm.awk
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/awk -f
|
||||
|
||||
# The caller must pass arg: daemon-parm.txt
|
||||
# The resulting code is output into daemon-parm.h
|
||||
|
||||
BEGIN {
|
||||
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */\n\n"
|
||||
sect = psect = defines = accessors = prior_ptype = ""
|
||||
parms = "\nstatic const struct parm_struct parm_table[] = {"
|
||||
comment_fmt = "\n/********** %s **********/\n"
|
||||
tdstruct = "typedef struct {"
|
||||
}
|
||||
|
||||
/^\s*$/ { next }
|
||||
/^#/ { next }
|
||||
|
||||
/^Globals:/ {
|
||||
if (defines != "") {
|
||||
print "The Globals section must come first!"
|
||||
defines = ""
|
||||
exit
|
||||
}
|
||||
defines = tdstruct
|
||||
values = "\nstatic const all_vars Defaults = {\n { /* Globals: */\n"
|
||||
exps = exp_values = sprintf(comment_fmt, "EXP")
|
||||
sect = "GLOBAL"
|
||||
psect = ", P_GLOBAL, &Vars.g."
|
||||
next
|
||||
}
|
||||
|
||||
/^Locals:/ {
|
||||
if (sect == "") {
|
||||
print "The Locals section must come after the Globals!"
|
||||
exit
|
||||
}
|
||||
defines = defines exps "} global_vars;\n\n" tdstruct
|
||||
values = values exp_values "\n }, { /* Locals: */\n"
|
||||
exps = exp_values = sprintf(comment_fmt, "EXP")
|
||||
sect = "LOCAL"
|
||||
psect = ", P_LOCAL, &Vars.l."
|
||||
next
|
||||
}
|
||||
|
||||
/^(STRING|CHAR|PATH|INTEGER|ENUM|OCTAL|BOOL|BOOLREV|BOOL3)[ \t]/ {
|
||||
ptype = $1
|
||||
name = $2
|
||||
$1 = $2 = ""
|
||||
sub(/^[ \t]+/, "")
|
||||
|
||||
if (ptype != prior_ptype) {
|
||||
comment = sprintf(comment_fmt, ptype)
|
||||
defines = defines comment
|
||||
values = values comment
|
||||
parms = parms "\n"
|
||||
accessors = accessors "\n"
|
||||
prior_ptype = ptype
|
||||
}
|
||||
|
||||
if (ptype == "STRING" || ptype == "PATH") {
|
||||
atype = "STRING"
|
||||
vtype = "char*"
|
||||
} else if (ptype ~ /BOOL/) {
|
||||
atype = vtype = "BOOL"
|
||||
} else if (ptype == "CHAR") {
|
||||
atype = "CHAR"
|
||||
vtype = "char"
|
||||
} else {
|
||||
atype = "INTEGER"
|
||||
vtype = "int"
|
||||
}
|
||||
|
||||
# The name might be var_name|public_name
|
||||
pubname = name
|
||||
sub(/\|.*/, "", name)
|
||||
sub(/.*\|/, "", pubname)
|
||||
gsub(/_/, " ", pubname)
|
||||
gsub(/-/, "", name)
|
||||
|
||||
if (ptype == "ENUM")
|
||||
enum = "enum_" name
|
||||
else
|
||||
enum = "NULL"
|
||||
|
||||
defines = defines "\t" vtype " " name ";\n"
|
||||
values = values "\t" $0 ", /* " name " */\n"
|
||||
parms = parms " {\"" pubname "\", P_" ptype psect name ", " enum ", 0},\n"
|
||||
accessors = accessors "FN_" sect "_" atype "(lp_" name ", " name ")\n"
|
||||
|
||||
if (vtype == "char*") {
|
||||
exps = exps "\tBOOL " name "_EXP;\n"
|
||||
exp_values = exp_values "\tFalse, /* " name "_EXP */\n"
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
|
||||
/./ {
|
||||
print "Extraneous line:" $0
|
||||
defines = ""
|
||||
exit
|
||||
}
|
||||
|
||||
END {
|
||||
if (sect != "" && defines != "") {
|
||||
defines = defines exps "} local_vars;\n\n"
|
||||
defines = defines tdstruct "\n\tglobal_vars g;\n\tlocal_vars l;\n} all_vars;\n"
|
||||
values = values exp_values "\n }\n};\n\nstatic all_vars Vars;\n"
|
||||
parms = parms "\n {NULL, P_BOOL, P_NONE, NULL, NULL, 0}\n};\n"
|
||||
print heading defines values parms accessors > "daemon-parm.h"
|
||||
} else {
|
||||
print "Failed to parse the data in " ARGV[1]
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
68
daemon-parm.txt
Normal file
68
daemon-parm.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
Globals: ================================================================
|
||||
|
||||
STRING bind_address|address NULL
|
||||
STRING daemon_chroot NULL
|
||||
STRING daemon_gid NULL
|
||||
STRING daemon_uid NULL
|
||||
STRING motd_file NULL
|
||||
STRING pid_file NULL
|
||||
STRING socket_options NULL
|
||||
|
||||
INTEGER listen_backlog 5
|
||||
INTEGER rsync_port|port 0
|
||||
|
||||
BOOL proxy_protocol False
|
||||
|
||||
Locals: =================================================================
|
||||
|
||||
STRING auth_users NULL
|
||||
STRING charset NULL
|
||||
STRING comment NULL
|
||||
STRING dont_compress DEFAULT_DONT_COMPRESS
|
||||
STRING early_exec NULL
|
||||
STRING exclude NULL
|
||||
STRING exclude_from NULL
|
||||
STRING filter NULL
|
||||
STRING gid NULL
|
||||
STRING hosts_allow NULL
|
||||
STRING hosts_deny NULL
|
||||
STRING include NULL
|
||||
STRING include_from NULL
|
||||
STRING incoming_chmod NULL
|
||||
STRING lock_file DEFAULT_LOCK_FILE
|
||||
STRING log_file NULL
|
||||
STRING log_format "%o %h [%a] %m (%u) %f %l"
|
||||
STRING name NULL
|
||||
STRING name_converter NULL
|
||||
STRING outgoing_chmod NULL
|
||||
STRING post-xfer_exec NULL
|
||||
STRING pre-xfer_exec NULL
|
||||
STRING refuse_options NULL
|
||||
STRING secrets_file NULL
|
||||
STRING syslog_tag "rsyncd"
|
||||
STRING uid NULL
|
||||
|
||||
PATH path NULL
|
||||
PATH temp_dir NULL
|
||||
|
||||
INTEGER max_connections 0
|
||||
INTEGER max_verbosity 1
|
||||
INTEGER timeout 0
|
||||
|
||||
ENUM syslog_facility LOG_DAEMON
|
||||
|
||||
BOOL fake_super False
|
||||
BOOL forward_lookup True
|
||||
BOOL ignore_errors False
|
||||
BOOL ignore_nonreadable False
|
||||
BOOL list True
|
||||
BOOL read_only True
|
||||
BOOL reverse_lookup True
|
||||
BOOL strict_modes True
|
||||
BOOL transfer_logging False
|
||||
BOOL write_only False
|
||||
|
||||
BOOL3 munge_symlinks Unset
|
||||
BOOL3 numeric_ids Unset
|
||||
BOOL3 open_noatime Unset
|
||||
BOOL3 use_chroot Unset
|
||||
10
delete.c
10
delete.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2024 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -98,7 +98,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
|
||||
|
||||
strlcpy(p, fp->basename, remainder);
|
||||
if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
|
||||
do_chmod(fname, fp->mode | S_IWUSR);
|
||||
do_chmod_at(fname, fp->mode | S_IWUSR);
|
||||
/* Save stack by recursing to ourself directly. */
|
||||
if (S_ISDIR(fp->mode)) {
|
||||
if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
|
||||
@@ -139,7 +139,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
|
||||
}
|
||||
|
||||
if (flags & DEL_NO_UID_WRITE)
|
||||
do_chmod(fbuf, mode | S_IWUSR);
|
||||
do_chmod_at(fbuf, mode | S_IWUSR);
|
||||
|
||||
if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
|
||||
/* This only happens on the first call to delete_item() since
|
||||
@@ -160,7 +160,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
what = "rmdir";
|
||||
ok = do_rmdir(fbuf) == 0;
|
||||
ok = do_rmdir_at(fbuf) == 0;
|
||||
} else {
|
||||
if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
|
||||
what = "make_backup";
|
||||
@@ -188,7 +188,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
|
||||
stats.deleted_symlinks++;
|
||||
#endif
|
||||
else if (IS_DEVICE(mode))
|
||||
stats.deleted_symlinks++;
|
||||
stats.deleted_devices++;
|
||||
else
|
||||
stats.deleted_specials++;
|
||||
}
|
||||
|
||||
344
exclude.c
344
exclude.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2024 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -21,21 +21,25 @@
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
#include "default-cvsignore.h"
|
||||
#include "ifuncs.h"
|
||||
|
||||
extern int am_server;
|
||||
extern int am_sender;
|
||||
extern int am_generator;
|
||||
extern int eol_nulls;
|
||||
extern int io_error;
|
||||
extern int xfer_dirs;
|
||||
extern int recurse;
|
||||
extern int local_server;
|
||||
extern int prune_empty_dirs;
|
||||
extern int ignore_perishable;
|
||||
extern int relative_paths;
|
||||
extern int delete_mode;
|
||||
extern int delete_excluded;
|
||||
extern int cvs_exclude;
|
||||
extern int sanitize_paths;
|
||||
extern int protocol_version;
|
||||
extern int trust_sender_args;
|
||||
extern int module_id;
|
||||
|
||||
extern char curr_dir[MAXPATHLEN];
|
||||
@@ -45,8 +49,11 @@ extern unsigned int module_dirlen;
|
||||
filter_rule_list filter_list = { .debug_type = "" };
|
||||
filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
|
||||
filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
|
||||
filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
|
||||
|
||||
int saw_xattr_filter = 0;
|
||||
int trust_sender_args = 0;
|
||||
int trust_sender_filter = 0;
|
||||
|
||||
/* Need room enough for ":MODS " prefix plus some room to grow. */
|
||||
#define MAX_RULE_PREFIX (16)
|
||||
@@ -71,6 +78,10 @@ static filter_rule **mergelist_parents;
|
||||
static int mergelist_cnt = 0;
|
||||
static int mergelist_size = 0;
|
||||
|
||||
#define LOCAL_RULE 1
|
||||
#define REMOTE_RULE 2
|
||||
static uchar cur_elide_value = REMOTE_RULE;
|
||||
|
||||
/* Each filter_list_struct describes a singly-linked list by keeping track
|
||||
* of both the head and tail pointers. The list is slightly unusual in that
|
||||
* a parent-dir's content can be appended to the end of the local list in a
|
||||
@@ -153,13 +164,17 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
|
||||
{
|
||||
const char *cp;
|
||||
unsigned int pre_len, suf_len, slash_cnt = 0;
|
||||
char *mention_rule_suffix;
|
||||
|
||||
if (DEBUG_GTE(FILTER, 2)) {
|
||||
rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s\n",
|
||||
if (DEBUG_GTE(FILTER, 1) && pat_len && (pat[pat_len-1] == ' ' || pat[pat_len-1] == '\t'))
|
||||
mention_rule_suffix = " -- CAUTION: trailing whitespace!";
|
||||
else
|
||||
mention_rule_suffix = DEBUG_GTE(FILTER, 2) ? "" : NULL;
|
||||
if (mention_rule_suffix) {
|
||||
rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s%s\n",
|
||||
who_am_i(), get_rule_prefix(rule, pat, 0, NULL),
|
||||
(int)pat_len, pat,
|
||||
(rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
|
||||
listp->debug_type);
|
||||
(int)pat_len, pat, (rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
|
||||
listp->debug_type, mention_rule_suffix);
|
||||
}
|
||||
|
||||
/* These flags also indicate that we're reading a list that
|
||||
@@ -209,6 +224,7 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
|
||||
slash_cnt++;
|
||||
}
|
||||
}
|
||||
rule->elide = 0;
|
||||
strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
|
||||
pat_len += pre_len;
|
||||
if (suf_len) {
|
||||
@@ -289,6 +305,271 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
|
||||
}
|
||||
}
|
||||
|
||||
/* If the wildcards failed, the remote shell might give us a file matching the literal
|
||||
* wildcards. Since "*" & "?" already match themselves, this just needs to deal with
|
||||
* failed "[foo]" idioms.
|
||||
*/
|
||||
static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len)
|
||||
{
|
||||
filter_rule *rule;
|
||||
const char *arg = based_on->pattern, *cp;
|
||||
char *p;
|
||||
int cnt = 0;
|
||||
|
||||
if (arg_len < 0)
|
||||
arg_len = strlen(arg);
|
||||
|
||||
for (cp = arg; *cp; cp++) {
|
||||
if (*cp == '\\' && cp[1]) {
|
||||
cp++;
|
||||
} else if (*cp == '[')
|
||||
cnt++;
|
||||
}
|
||||
if (!cnt)
|
||||
return;
|
||||
|
||||
rule = new0(filter_rule);
|
||||
rule->rflags = based_on->rflags;
|
||||
rule->u.slash_cnt = based_on->u.slash_cnt;
|
||||
p = rule->pattern = new_array(char, arg_len + cnt + 1);
|
||||
for (cp = arg; *cp; ) {
|
||||
if (*cp == '\\' && cp[1]) {
|
||||
*p++ = *cp++;
|
||||
} else if (*cp == '[')
|
||||
*p++ = '\\';
|
||||
*p++ = *cp++;
|
||||
}
|
||||
*p++ = '\0';
|
||||
|
||||
rule->next = implied_filter_list.head;
|
||||
implied_filter_list.head = rule;
|
||||
if (DEBUG_GTE(FILTER, 3)) {
|
||||
rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,
|
||||
rule->rflags & FILTRULE_DIRECTORY ? "/" : "");
|
||||
}
|
||||
}
|
||||
|
||||
static char *partial_string_buf = NULL;
|
||||
static int partial_string_len = 0;
|
||||
void implied_include_partial_string(const char *s_start, const char *s_end)
|
||||
{
|
||||
partial_string_len = s_end - s_start;
|
||||
if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */
|
||||
partial_string_len = 0;
|
||||
return;
|
||||
}
|
||||
if (!partial_string_buf)
|
||||
partial_string_buf = new_array(char, MAXPATHLEN);
|
||||
memcpy(partial_string_buf, s_start, partial_string_len);
|
||||
}
|
||||
|
||||
void free_implied_include_partial_string()
|
||||
{
|
||||
if (partial_string_buf) {
|
||||
if (partial_string_len)
|
||||
add_implied_include("", 0);
|
||||
free(partial_string_buf);
|
||||
partial_string_buf = NULL;
|
||||
}
|
||||
partial_string_len = 0; /* paranoia */
|
||||
}
|
||||
|
||||
/* Each arg the client sends to the remote sender turns into an implied include
|
||||
* that the receiver uses to validate the file list from the sender. */
|
||||
void add_implied_include(const char *arg, int skip_daemon_module)
|
||||
{
|
||||
int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
|
||||
int slash_cnt = 0;
|
||||
const char *cp;
|
||||
char *p;
|
||||
if (trust_sender_args)
|
||||
return;
|
||||
if (partial_string_len) {
|
||||
arg_len = strlen(arg);
|
||||
if (partial_string_len + arg_len >= MAXPATHLEN) {
|
||||
partial_string_len = 0;
|
||||
return; /* Should be impossible... */
|
||||
}
|
||||
memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);
|
||||
partial_string_len = 0;
|
||||
arg = partial_string_buf;
|
||||
}
|
||||
if (skip_daemon_module) {
|
||||
if ((cp = strchr(arg, '/')) != NULL)
|
||||
arg = cp + 1;
|
||||
else
|
||||
arg = "";
|
||||
}
|
||||
if (relative_paths) {
|
||||
if ((cp = strstr(arg, "/./")) != NULL)
|
||||
arg = cp + 3;
|
||||
} else if ((cp = strrchr(arg, '/')) != NULL) {
|
||||
arg = cp + 1;
|
||||
}
|
||||
if (*arg == '.' && arg[1] == '\0')
|
||||
arg++;
|
||||
arg_len = strlen(arg);
|
||||
if (arg_len) {
|
||||
char *new_pat;
|
||||
if (strpbrk(arg, "*[?")) {
|
||||
/* We need to add room to escape backslashes if wildcard chars are present. */
|
||||
for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
|
||||
arg_len++;
|
||||
saw_wild = 1;
|
||||
}
|
||||
arg_len++; /* Leave room for the prefixed slash */
|
||||
p = new_pat = new_array(char, arg_len + 1);
|
||||
*p++ = '/';
|
||||
slash_cnt++;
|
||||
for (cp = arg; *cp; ) {
|
||||
switch (*cp) {
|
||||
case '\\':
|
||||
if (cp[1] == ']') {
|
||||
if (!saw_wild)
|
||||
cp++; /* A \] in a non-wild filter causes a problem, so drop the \ . */
|
||||
} else if (!strchr("*[?", cp[1])) {
|
||||
backslash_cnt++;
|
||||
if (saw_wild)
|
||||
*p++ = '\\';
|
||||
}
|
||||
*p++ = *cp++;
|
||||
break;
|
||||
case '/':
|
||||
if (p[-1] == '/') { /* This is safe because of the initial slash. */
|
||||
if (*++cp == '\0') {
|
||||
slash_cnt--;
|
||||
p--;
|
||||
}
|
||||
} else if (cp[1] == '\0') {
|
||||
cp++;
|
||||
} else {
|
||||
slash_cnt++;
|
||||
*p++ = *cp++;
|
||||
}
|
||||
break;
|
||||
case '.':
|
||||
if (p[-1] == '/') {
|
||||
if (cp[1] == '/') {
|
||||
cp += 2;
|
||||
if (!*cp) {
|
||||
slash_cnt--;
|
||||
p--;
|
||||
}
|
||||
} else if (cp[1] == '\0') {
|
||||
cp++;
|
||||
slash_cnt--;
|
||||
p--;
|
||||
} else
|
||||
*p++ = *cp++;
|
||||
} else
|
||||
*p++ = *cp++;
|
||||
break;
|
||||
case '[':
|
||||
saw_live_open_brkt = 1;
|
||||
*p++ = *cp++;
|
||||
break;
|
||||
default:
|
||||
*p++ = *cp++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
arg_len = p - new_pat;
|
||||
if (!arg_len)
|
||||
free(new_pat);
|
||||
else {
|
||||
filter_rule *rule = new0(filter_rule);
|
||||
rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
|
||||
rule->u.slash_cnt = slash_cnt;
|
||||
arg = rule->pattern = new_pat;
|
||||
if (!implied_filter_list.head)
|
||||
implied_filter_list.head = implied_filter_list.tail = rule;
|
||||
else {
|
||||
rule->next = implied_filter_list.head;
|
||||
implied_filter_list.head = rule;
|
||||
}
|
||||
if (DEBUG_GTE(FILTER, 3))
|
||||
rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), arg);
|
||||
if (saw_live_open_brkt)
|
||||
maybe_add_literal_brackets_rule(rule, arg_len);
|
||||
if (relative_paths && slash_cnt) {
|
||||
int sub_slash_cnt = slash_cnt;
|
||||
while ((p = strrchr(new_pat, '/')) != NULL && p != new_pat) {
|
||||
filter_rule const *ent;
|
||||
filter_rule *R_rule;
|
||||
int found = 0;
|
||||
*p = '\0';
|
||||
for (ent = implied_filter_list.head; ent; ent = ent->next) {
|
||||
if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
*p = '/';
|
||||
break; /* We added all parent dirs already */
|
||||
}
|
||||
R_rule = new0(filter_rule);
|
||||
R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
|
||||
/* Check if our sub-path has wildcards or escaped backslashes */
|
||||
if (saw_wild && strpbrk(new_pat, "*[?\\"))
|
||||
R_rule->rflags |= FILTRULE_WILD;
|
||||
R_rule->pattern = strdup(new_pat);
|
||||
R_rule->u.slash_cnt = --sub_slash_cnt;
|
||||
R_rule->next = implied_filter_list.head;
|
||||
implied_filter_list.head = R_rule;
|
||||
if (DEBUG_GTE(FILTER, 3)) {
|
||||
rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
|
||||
who_am_i(), R_rule->pattern);
|
||||
}
|
||||
if (saw_live_open_brkt)
|
||||
maybe_add_literal_brackets_rule(R_rule, -1);
|
||||
}
|
||||
for (p = new_pat; sub_slash_cnt < slash_cnt; sub_slash_cnt++) {
|
||||
p += strlen(p);
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recurse || xfer_dirs) {
|
||||
/* Now create a rule with an added "/" & "**" or "*" at the end */
|
||||
filter_rule *rule = new0(filter_rule);
|
||||
rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
|
||||
if (recurse)
|
||||
rule->rflags |= FILTRULE_WILD2;
|
||||
/* We must leave enough room for / * * \0. */
|
||||
if (!saw_wild && backslash_cnt) {
|
||||
/* We are appending a wildcard, so now the backslashes need to be escaped. */
|
||||
p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
|
||||
for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
|
||||
if (*cp == '\\')
|
||||
*p++ = '\\';
|
||||
*p++ = *cp++;
|
||||
}
|
||||
} else {
|
||||
p = rule->pattern = new_array(char, arg_len + 3 + 1);
|
||||
if (arg_len) {
|
||||
memcpy(p, arg, arg_len);
|
||||
p += arg_len;
|
||||
}
|
||||
}
|
||||
*p++ = '/';
|
||||
*p++ = '*';
|
||||
if (recurse)
|
||||
*p++ = '*';
|
||||
*p = '\0';
|
||||
rule->u.slash_cnt = slash_cnt + 1;
|
||||
rule->next = implied_filter_list.head;
|
||||
implied_filter_list.head = rule;
|
||||
if (DEBUG_GTE(FILTER, 3))
|
||||
rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
|
||||
if (saw_live_open_brkt)
|
||||
maybe_add_literal_brackets_rule(rule, p - rule->pattern);
|
||||
}
|
||||
}
|
||||
|
||||
/* This frees any non-inherited items, leaving just inherited items on the list. */
|
||||
static void pop_filter_list(filter_rule_list *listp)
|
||||
{
|
||||
@@ -439,7 +720,8 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
|
||||
parent_dirscan = True;
|
||||
while (*y) {
|
||||
char save[MAXPATHLEN];
|
||||
strlcpy(save, y, MAXPATHLEN);
|
||||
/* copylen is strlen(y) which is < MAXPATHLEN. +1 for \0 */
|
||||
size_t copylen = strlcpy(save, y, MAXPATHLEN) + 1;
|
||||
*y = '\0';
|
||||
dirbuf_len = y - dirbuf;
|
||||
strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
|
||||
@@ -453,7 +735,7 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
|
||||
lp->head = NULL;
|
||||
}
|
||||
lp->tail = NULL;
|
||||
strlcpy(y, save, MAXPATHLEN);
|
||||
strlcpy(y, save, copylen);
|
||||
while ((*x++ = *y++) != '/') {}
|
||||
}
|
||||
parent_dirscan = False;
|
||||
@@ -622,11 +904,11 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
|
||||
{
|
||||
int slash_handling, str_cnt = 0, anchored_match = 0;
|
||||
int ret_match = ex->rflags & FILTRULE_NEGATE ? 0 : 1;
|
||||
char *p, *pattern = ex->pattern;
|
||||
const char *p, *pattern = ex->pattern;
|
||||
const char *strings[16]; /* more than enough */
|
||||
const char *name = fname + (*fname == '/');
|
||||
|
||||
if (!*name)
|
||||
if (!*name || ex->elide == cur_elide_value)
|
||||
return 0;
|
||||
|
||||
if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR))
|
||||
@@ -703,11 +985,12 @@ static void report_filter_result(enum logcode code, char const *name,
|
||||
filter_rule const *ent,
|
||||
int name_flags, const char *type)
|
||||
{
|
||||
int log_level = am_sender || am_generator ? 1 : 3;
|
||||
|
||||
/* If a trailing slash is present to match only directories,
|
||||
* then it is stripped out by add_rule(). So as a special
|
||||
* case we add it back in here. */
|
||||
|
||||
if (DEBUG_GTE(FILTER, 1)) {
|
||||
* case we add it back in the log output. */
|
||||
if (DEBUG_GTE(FILTER, log_level)) {
|
||||
static char *actions[2][2]
|
||||
= { {"show", "hid"}, {"risk", "protect"} };
|
||||
const char *w = who_am_i();
|
||||
@@ -715,7 +998,7 @@ static void report_filter_result(enum logcode code, char const *name,
|
||||
: name_flags & NAME_IS_DIR ? "directory"
|
||||
: "file";
|
||||
rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
|
||||
w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)],
|
||||
w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)],
|
||||
t, name, ent->pattern,
|
||||
ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
|
||||
}
|
||||
@@ -741,6 +1024,15 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_server_filter(filter_rule_list *listp, enum logcode code, const char *name, int name_flags)
|
||||
{
|
||||
int ret;
|
||||
cur_elide_value = LOCAL_RULE;
|
||||
ret = check_filter(listp, code, name, name_flags);
|
||||
cur_elide_value = REMOTE_RULE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return -1 if file "name" is defined to be excluded by the specified
|
||||
* exclude list, 1 if it is included, and 0 if it was not matched. */
|
||||
int check_filter(filter_rule_list *listp, enum logcode code,
|
||||
@@ -887,6 +1179,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
|
||||
}
|
||||
switch (ch) {
|
||||
case ':':
|
||||
trust_sender_filter = 1;
|
||||
rule->rflags |= FILTRULE_PERDIR_MERGE
|
||||
| FILTRULE_FINISH_SETUP;
|
||||
/* FALL THROUGH */
|
||||
@@ -1053,7 +1346,7 @@ static void get_cvs_excludes(uint32 rflags)
|
||||
return;
|
||||
initialized = 1;
|
||||
|
||||
parse_filter_str(&cvs_filter_list, DEFAULT_CVSIGNORE,
|
||||
parse_filter_str(&cvs_filter_list, default_cvsignore(),
|
||||
rule_template(rflags | (protocol_version >= 30 ? FILTRULE_PERISHABLE : 0)),
|
||||
0);
|
||||
|
||||
@@ -1295,7 +1588,7 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
|
||||
|
||||
static void send_rules(int f_out, filter_rule_list *flp)
|
||||
{
|
||||
filter_rule *ent, *prev = NULL;
|
||||
filter_rule *ent;
|
||||
|
||||
for (ent = flp->head; ent; ent = ent->next) {
|
||||
unsigned int len, plen, dlen;
|
||||
@@ -1310,21 +1603,15 @@ static void send_rules(int f_out, filter_rule_list *flp)
|
||||
* merge files as an optimization (since they can only have
|
||||
* include/exclude rules). */
|
||||
if (ent->rflags & FILTRULE_SENDER_SIDE)
|
||||
elide = am_sender ? 1 : -1;
|
||||
elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
|
||||
if (ent->rflags & FILTRULE_RECEIVER_SIDE)
|
||||
elide = elide ? 0 : am_sender ? -1 : 1;
|
||||
elide = elide ? 0 : am_sender ? REMOTE_RULE : LOCAL_RULE;
|
||||
else if (delete_excluded && !elide
|
||||
&& (!(ent->rflags & FILTRULE_PERDIR_MERGE)
|
||||
|| ent->rflags & FILTRULE_NO_PREFIXES))
|
||||
elide = am_sender ? 1 : -1;
|
||||
if (elide < 0) {
|
||||
if (prev)
|
||||
prev->next = ent->next;
|
||||
else
|
||||
flp->head = ent->next;
|
||||
} else
|
||||
prev = ent;
|
||||
if (elide > 0)
|
||||
elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
|
||||
ent->elide = elide;
|
||||
if (elide == LOCAL_RULE)
|
||||
continue;
|
||||
if (ent->rflags & FILTRULE_CVS_IGNORE
|
||||
&& !(ent->rflags & FILTRULE_MERGE_FILE)) {
|
||||
@@ -1352,7 +1639,6 @@ static void send_rules(int f_out, filter_rule_list *flp)
|
||||
if (dlen)
|
||||
write_byte(f_out, '/');
|
||||
}
|
||||
flp->tail = prev;
|
||||
}
|
||||
|
||||
/* This is only called by the client. */
|
||||
|
||||
46
fileio.c
46
fileio.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Tridgell
|
||||
* Copyright (C) 2002 Martin Pool
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2023 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -40,30 +40,34 @@ OFF_T preallocated_len = 0;
|
||||
static OFF_T sparse_seek = 0;
|
||||
static OFF_T sparse_past_write = 0;
|
||||
|
||||
int sparse_end(int f, OFF_T size)
|
||||
int sparse_end(int f, OFF_T size, int updating_basis_or_equiv)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
sparse_past_write = 0;
|
||||
|
||||
if (!sparse_seek)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_FTRUNCATE
|
||||
ret = do_ftruncate(f, size);
|
||||
#else
|
||||
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
|
||||
ret = -1;
|
||||
else {
|
||||
do {
|
||||
ret = write(f, "", 1);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
ret = ret <= 0 ? -1 : 0;
|
||||
}
|
||||
if (updating_basis_or_equiv) {
|
||||
if (sparse_seek && do_punch_hole(f, sparse_past_write, sparse_seek) < 0)
|
||||
ret = -1;
|
||||
#ifdef HAVE_FTRUNCATE /* A compilation formality -- in-place requires ftruncate() */
|
||||
else /* Just in case the original file was longer */
|
||||
ret = do_ftruncate(f, size);
|
||||
#endif
|
||||
} else if (sparse_seek) {
|
||||
#ifdef HAVE_FTRUNCATE
|
||||
ret = do_ftruncate(f, size);
|
||||
#else
|
||||
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
|
||||
ret = -1;
|
||||
else {
|
||||
do {
|
||||
ret = write(f, "", 1);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
sparse_seek = 0;
|
||||
ret = ret <= 0 ? -1 : 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
sparse_past_write = sparse_seek = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
216
flist.c
216
flist.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-2020 Wayne Davison
|
||||
* Copyright (C) 2002-2023 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -33,7 +33,6 @@ extern int am_sender;
|
||||
extern int am_generator;
|
||||
extern int inc_recurse;
|
||||
extern int always_checksum;
|
||||
extern int checksum_type;
|
||||
extern int module_id;
|
||||
extern int ignore_errors;
|
||||
extern int numeric_ids;
|
||||
@@ -43,6 +42,7 @@ extern int use_qsort;
|
||||
extern int xfer_dirs;
|
||||
extern int filesfrom_fd;
|
||||
extern int one_file_system;
|
||||
extern int copy_devices;
|
||||
extern int copy_dirlinks;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
@@ -56,6 +56,7 @@ extern int delete_during;
|
||||
extern int missing_args;
|
||||
extern int eol_nulls;
|
||||
extern int atimes_ndx;
|
||||
extern int crtimes_ndx;
|
||||
extern int relative_paths;
|
||||
extern int implied_dirs;
|
||||
extern int ignore_perishable;
|
||||
@@ -71,18 +72,20 @@ extern int need_unsorted_flist;
|
||||
extern int sender_symlink_iconv;
|
||||
extern int output_needs_newline;
|
||||
extern int sender_keeps_checksum;
|
||||
extern int trust_sender_filter;
|
||||
extern int unsort_ndx;
|
||||
extern uid_t our_uid;
|
||||
extern struct stats stats;
|
||||
extern char *filesfrom_host;
|
||||
extern char *usermap, *groupmap;
|
||||
|
||||
extern struct name_num_item *file_sum_nni;
|
||||
|
||||
extern char curr_dir[MAXPATHLEN];
|
||||
|
||||
extern struct chmod_mode_struct *chmod_modes;
|
||||
|
||||
extern filter_rule_list filter_list;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list;
|
||||
|
||||
#ifdef ICONV_OPTION
|
||||
extern int filesfrom_convert;
|
||||
@@ -133,6 +136,7 @@ static char empty_sum[MAX_DIGEST_LEN];
|
||||
static int flist_count_offset; /* for --delete --progress */
|
||||
static int show_filelist_progress;
|
||||
|
||||
static struct file_list *flist_new(int flags, const char *msg);
|
||||
static void flist_sort_and_clean(struct file_list *flist, int strip_root);
|
||||
static void output_flist(struct file_list *flist);
|
||||
|
||||
@@ -142,7 +146,8 @@ void init_flist(void)
|
||||
rprintf(FINFO, "FILE_STRUCT_LEN=%d, EXTRA_LEN=%d\n",
|
||||
(int)FILE_STRUCT_LEN, (int)EXTRA_LEN);
|
||||
}
|
||||
flist_csum_len = csum_len_for_type(checksum_type, 1);
|
||||
/* Note that this isn't identical to file_sum_len in the case of CSUM_MD4_ARCHAIC: */
|
||||
flist_csum_len = csum_len_for_type(file_sum_nni->num, 1);
|
||||
|
||||
show_filelist_progress = INFO_GTE(FLIST, 1) && xfer_dirs && !am_server && !inc_recurse;
|
||||
}
|
||||
@@ -293,6 +298,8 @@ static void flist_expand(struct file_list *flist, int extra)
|
||||
flist->malloced = FLIST_START;
|
||||
else if (flist->malloced >= FLIST_LINEAR)
|
||||
flist->malloced += FLIST_LINEAR;
|
||||
else if (flist->malloced < FLIST_START_LARGE/16)
|
||||
flist->malloced *= 4;
|
||||
else
|
||||
flist->malloced *= 2;
|
||||
|
||||
@@ -303,7 +310,7 @@ static void flist_expand(struct file_list *flist, int extra)
|
||||
|
||||
new_ptr = realloc_array(flist->files, struct file_struct *, flist->malloced);
|
||||
|
||||
if (DEBUG_GTE(FLIST, 1) && flist->malloced != FLIST_START) {
|
||||
if (DEBUG_GTE(FLIST, 1) && flist->files) {
|
||||
rprintf(FCLIENT, "[%s] expand file_list pointer array to %s bytes, did%s move\n",
|
||||
who_am_i(),
|
||||
big_num(sizeof flist->files[0] * flist->malloced),
|
||||
@@ -377,6 +384,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
|
||||
int ndx, int first_ndx)
|
||||
{
|
||||
static time_t modtime, atime;
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
static time_t crtime;
|
||||
#endif
|
||||
static mode_t mode;
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
static int64 dev;
|
||||
@@ -443,7 +453,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
|
||||
if (protocol_version < 28)
|
||||
xflags |= XMIT_SAME_RDEV_pre28;
|
||||
else {
|
||||
rdev = MAKEDEV(major(rdev), 0);
|
||||
rdev = MAKEDEV(rdev_major, 0);
|
||||
xflags |= XMIT_SAME_RDEV_MAJOR;
|
||||
if (protocol_version < 30)
|
||||
xflags |= XMIT_RDEV_MINOR_8_pre30;
|
||||
@@ -482,6 +492,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
|
||||
else
|
||||
atime = F_ATIME(file);
|
||||
}
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx) {
|
||||
crtime = F_CRTIME(file);
|
||||
if (crtime == modtime)
|
||||
xflags |= XMIT_CRTIME_EQ_MTIME;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (tmp_dev != -1) {
|
||||
@@ -569,6 +586,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
|
||||
}
|
||||
if (xflags & XMIT_MOD_NSEC)
|
||||
write_varint(f, F_MOD_NSEC(file));
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
|
||||
write_varlong(f, crtime, 4);
|
||||
#endif
|
||||
if (!(xflags & XMIT_SAME_MODE))
|
||||
write_int(f, to_wire_mode(mode));
|
||||
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
|
||||
@@ -661,6 +682,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
|
||||
static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
|
||||
{
|
||||
static int64 modtime, atime;
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
static time_t crtime;
|
||||
#endif
|
||||
static mode_t mode;
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
static int64 dev;
|
||||
@@ -679,8 +703,11 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
int alloc_len, basename_len, linkname_len;
|
||||
int extra_len = file_extra_cnt * EXTRA_LEN;
|
||||
int first_hlink_ndx = -1;
|
||||
char real_ISREG_entry;
|
||||
int64 file_length;
|
||||
#ifdef CAN_SET_NSEC
|
||||
uint32 modtime_nsec;
|
||||
#endif
|
||||
const char *basename;
|
||||
struct file_struct *file;
|
||||
alloc_pool_t *pool;
|
||||
@@ -729,7 +756,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
if (*thisname
|
||||
&& (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
|
||||
rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (sanitize_paths)
|
||||
@@ -767,23 +794,31 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
|
||||
file_length = F_LENGTH(first);
|
||||
modtime = first->modtime;
|
||||
#ifdef CAN_SET_NSEC
|
||||
modtime_nsec = F_MOD_NSEC_or_0(first);
|
||||
#endif
|
||||
mode = first->mode;
|
||||
if (atimes_ndx && !S_ISDIR(mode))
|
||||
atime = F_ATIME(first);
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx)
|
||||
crtime = F_CRTIME(first);
|
||||
#endif
|
||||
if (preserve_uid)
|
||||
uid = F_OWNER(first);
|
||||
if (preserve_gid)
|
||||
gid = F_GROUP(first);
|
||||
if (preserve_devices && IS_DEVICE(mode)) {
|
||||
uint32 *devp = F_RDEV_P(first);
|
||||
rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
|
||||
rdev_major = DEV_MAJOR(devp);
|
||||
rdev = MAKEDEV(rdev_major, DEV_MINOR(devp));
|
||||
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
|
||||
}
|
||||
if (preserve_links && S_ISLNK(mode))
|
||||
linkname_len = strlen(F_SYMLINK(first)) + 1;
|
||||
else
|
||||
linkname_len = 0;
|
||||
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
|
||||
goto create_object;
|
||||
}
|
||||
}
|
||||
@@ -801,14 +836,44 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
modtime = read_int(f);
|
||||
modtime = read_uint(f);
|
||||
}
|
||||
if (xflags & XMIT_MOD_NSEC)
|
||||
modtime_nsec = read_varint(f);
|
||||
#ifndef CAN_SET_NSEC
|
||||
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
#else
|
||||
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
else
|
||||
modtime_nsec = 0;
|
||||
if (!(xflags & XMIT_SAME_MODE))
|
||||
#endif
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx) {
|
||||
if (xflags & XMIT_CRTIME_EQ_MTIME)
|
||||
crtime = modtime;
|
||||
else
|
||||
crtime = read_varlong(f, 4);
|
||||
#if SIZEOF_TIME_T < SIZEOF_INT64
|
||||
if (!am_generator && (int64)(time_t)crtime != crtime) {
|
||||
rprintf(FERROR_XFER,
|
||||
"Create time value of %s truncated on receiver.\n",
|
||||
lastname);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!(xflags & XMIT_SAME_MODE)) {
|
||||
mode = from_wire_mode(read_int(f));
|
||||
/* Reject modes whose type bits are not one of the standard
|
||||
* file types; otherwise garbage mode values propagate through
|
||||
* the file-type checks below unpredictably. */
|
||||
if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)
|
||||
&& !S_ISCHR(mode) && !S_ISBLK(mode)
|
||||
&& !S_ISFIFO(mode) && !S_ISSOCK(mode)) {
|
||||
rprintf(FERROR, "invalid file mode 0%o for %s [%s]\n",
|
||||
(unsigned)mode, lastname, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
}
|
||||
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
|
||||
atime = read_varlong(f, 4);
|
||||
#if SIZEOF_TIME_T < SIZEOF_INT64
|
||||
@@ -892,10 +957,20 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
#endif
|
||||
linkname_len = 0;
|
||||
|
||||
if (copy_devices && IS_DEVICE(mode)) {
|
||||
/* This is impossible in the official release, but some pre-release patches
|
||||
* didn't convert the device into a file before sending, so we'll do it here
|
||||
* (even though the length is typically 0 and any checksum data is zeros). */
|
||||
mode = S_IFREG | (mode & ACCESSPERMS);
|
||||
modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
|
||||
real_ISREG_entry = 0;
|
||||
} else
|
||||
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
|
||||
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
create_object:
|
||||
if (preserve_hard_links) {
|
||||
if (protocol_version < 28 && S_ISREG(mode))
|
||||
if (protocol_version < 28 && real_ISREG_entry)
|
||||
xflags |= XMIT_HLINKED;
|
||||
if (xflags & XMIT_HLINKED)
|
||||
extra_len += (inc_recurse+1) * EXTRA_LEN;
|
||||
@@ -924,6 +999,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (*thisname == '/' ? thisname[1] != '.' || thisname[2] != '\0' : *thisname != '.' || thisname[1] != '\0') {
|
||||
int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
|
||||
if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
|
||||
&& filter_list.head && check_server_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
|
||||
rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
|
||||
rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
if (inc_recurse && S_ISDIR(mode)) {
|
||||
if (one_file_system) {
|
||||
/* Room to save the dir's device for -x */
|
||||
@@ -988,6 +1076,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
}
|
||||
if (atimes_ndx && !S_ISDIR(mode))
|
||||
F_ATIME(file) = atime;
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx)
|
||||
F_CRTIME(file) = crtime;
|
||||
#endif
|
||||
if (unsort_ndx)
|
||||
F_NDX(file) = flist->used + flist->ndx_start;
|
||||
|
||||
@@ -1107,8 +1199,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
}
|
||||
#endif
|
||||
|
||||
if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
|
||||
if (S_ISREG(mode))
|
||||
if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
|
||||
if (real_ISREG_entry)
|
||||
bp = F_SUM(file);
|
||||
else {
|
||||
/* Prior to 28, we get a useless set of nulls. */
|
||||
@@ -1307,6 +1399,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
linkname_len = 0;
|
||||
#endif
|
||||
|
||||
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
|
||||
if (st.st_size == 0) {
|
||||
int fd = do_open_checklinks(fname);
|
||||
if (fd >= 0) {
|
||||
st.st_size = get_device_size(fd, fname);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
st.st_mode = S_IFREG | (st.st_mode & ACCESSPERMS);
|
||||
st.st_mtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
|
||||
}
|
||||
|
||||
#ifdef ST_MTIME_NSEC
|
||||
if (st.ST_MTIME_NSEC && protocol_version >= 31)
|
||||
extra_len += EXTRA_LEN;
|
||||
@@ -1385,6 +1489,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
file->flags |= FLAG_OWNED_BY_US;
|
||||
if (atimes_ndx && !S_ISDIR(file->mode))
|
||||
F_ATIME(file) = st.st_atime;
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx)
|
||||
F_CRTIME(file) = get_create_time(fname, &st);
|
||||
#endif
|
||||
|
||||
if (basename != thisname)
|
||||
file->dirname = lastdir;
|
||||
@@ -1892,7 +2000,7 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname,
|
||||
memcpy(F_DIR_RELNAMES_P(lastpath_struct), &relname_list, sizeof relname_list);
|
||||
}
|
||||
rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32);
|
||||
*rnpp = (relnamecache*)new_array(char, sizeof (relnamecache) + len);
|
||||
*rnpp = (relnamecache*)new_array(char, RELNAMECACHE_LEN + len + 1);
|
||||
(*rnpp)->name_type = name_type;
|
||||
strlcpy((*rnpp)->fname, limit+1, len + 1);
|
||||
|
||||
@@ -2051,8 +2159,7 @@ void send_extra_file_list(int f, int at_least)
|
||||
|
||||
if (need_unsorted_flist) {
|
||||
flist->sorted = new_array(struct file_struct *, flist->used);
|
||||
memcpy(flist->sorted, flist->files,
|
||||
flist->used * sizeof (struct file_struct*));
|
||||
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
|
||||
} else
|
||||
flist->sorted = flist->files;
|
||||
|
||||
@@ -2132,8 +2239,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
flist = cur_flist = flist_new(0, "send_file_list");
|
||||
flist_expand(flist, FLIST_START_LARGE);
|
||||
if (inc_recurse) {
|
||||
dir_flist = flist_new(FLIST_TEMP, "send_file_list");
|
||||
flist_expand(dir_flist, FLIST_START_LARGE);
|
||||
flags |= FLAG_DIVERT_DIRS;
|
||||
} else
|
||||
dir_flist = cur_flist;
|
||||
@@ -2269,7 +2378,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
}
|
||||
|
||||
dirlen = dir ? strlen(dir) : 0;
|
||||
if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
|
||||
if (dirlen != lastdir_len || (dirlen && memcmp(lastdir, dir, dirlen) != 0)) {
|
||||
if (!change_pathname(NULL, dir, -dirlen))
|
||||
goto bad_path;
|
||||
lastdir = pathname;
|
||||
@@ -2405,8 +2514,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
* send them together in a single file-list. */
|
||||
if (need_unsorted_flist) {
|
||||
flist->sorted = new_array(struct file_struct *, flist->used);
|
||||
memcpy(flist->sorted, flist->files,
|
||||
flist->used * sizeof (struct file_struct*));
|
||||
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
|
||||
} else
|
||||
flist->sorted = flist->files;
|
||||
flist_sort_and_clean(flist, 0);
|
||||
@@ -2414,7 +2522,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
file_old_total += flist->used;
|
||||
|
||||
if (numeric_ids <= 0 && !inc_recurse)
|
||||
send_id_list(f);
|
||||
send_id_lists(f);
|
||||
|
||||
/* send the io_error flag */
|
||||
if (protocol_version < 30)
|
||||
@@ -2487,11 +2595,27 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
init_hard_links();
|
||||
#endif
|
||||
|
||||
if (inc_recurse && dir_ndx >= 0) {
|
||||
if (dir_ndx >= dir_flist->used) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing invalid dir_ndx %u >= %u\n", dir_ndx, dir_flist->used);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
struct file_struct *file = dir_flist->files[dir_ndx];
|
||||
if (file->flags & FLAG_GOT_DIR_FLIST) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing malicious duplicate flist for dir %d\n", dir_ndx);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
file->flags |= FLAG_GOT_DIR_FLIST;
|
||||
}
|
||||
|
||||
flist = flist_new(0, "recv_file_list");
|
||||
flist_expand(flist, FLIST_START_LARGE);
|
||||
|
||||
if (inc_recurse) {
|
||||
if (flist->ndx_start == 1)
|
||||
if (flist->ndx_start == 1) {
|
||||
dir_flist = flist_new(FLIST_TEMP, "recv_file_list");
|
||||
flist_expand(dir_flist, FLIST_START_LARGE);
|
||||
}
|
||||
dstart = dir_flist->used;
|
||||
} else {
|
||||
dir_flist = flist;
|
||||
@@ -2542,7 +2666,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
rprintf(FERROR,
|
||||
"ABORTING due to invalid path from sender: %s/%s\n",
|
||||
cur_dir, file->basename);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
good_dirname = cur_dir;
|
||||
}
|
||||
@@ -2559,7 +2683,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
} else if (S_ISLNK(file->mode))
|
||||
stats.num_symlinks++;
|
||||
else if (IS_DEVICE(file->mode))
|
||||
stats.num_symlinks++;
|
||||
stats.num_devices++;
|
||||
else
|
||||
stats.num_specials++;
|
||||
|
||||
@@ -2587,8 +2711,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
* list unsorted for our exchange of index numbers with the
|
||||
* other side (since their names may not sort the same). */
|
||||
flist->sorted = new_array(struct file_struct *, flist->used);
|
||||
memcpy(flist->sorted, flist->files,
|
||||
flist->used * sizeof (struct file_struct*));
|
||||
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
|
||||
if (inc_recurse && dir_flist->used > dstart) {
|
||||
static int dir_flist_malloced = 0;
|
||||
if (dir_flist_malloced < dir_flist->malloced) {
|
||||
@@ -2598,7 +2721,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
dir_flist_malloced = dir_flist->malloced;
|
||||
}
|
||||
memcpy(dir_flist->sorted + dstart, dir_flist->files + dstart,
|
||||
(dir_flist->used - dstart) * sizeof (struct file_struct*));
|
||||
(dir_flist->used - dstart) * PTR_SIZE);
|
||||
fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
|
||||
}
|
||||
} else {
|
||||
@@ -2730,28 +2853,28 @@ int flist_find(struct file_list *flist, struct file_struct *f)
|
||||
* 1=match directories, 0=match non-directories, or -1=match either. */
|
||||
int flist_find_name(struct file_list *flist, const char *fname, int want_dir_match)
|
||||
{
|
||||
struct { /* We have to create a temporary file_struct for the search. */
|
||||
struct file_struct f;
|
||||
char name_space[MAXPATHLEN];
|
||||
} t;
|
||||
static struct file_struct *f;
|
||||
char fbuf[MAXPATHLEN];
|
||||
const char *slash = strrchr(fname, '/');
|
||||
const char *basename = slash ? slash+1 : fname;
|
||||
|
||||
memset(&t.f, 0, FILE_STRUCT_LEN);
|
||||
memcpy((void *)t.f.basename, basename, strlen(basename)+1);
|
||||
if (!f)
|
||||
f = (struct file_struct*)new_array(char, FILE_STRUCT_LEN + MAXPATHLEN + 1);
|
||||
|
||||
memset(f, 0, FILE_STRUCT_LEN);
|
||||
memcpy((void*)f->basename, basename, strlen(basename)+1);
|
||||
|
||||
if (slash) {
|
||||
strlcpy(fbuf, fname, slash - fname + 1);
|
||||
t.f.dirname = fbuf;
|
||||
f->dirname = fbuf;
|
||||
} else
|
||||
t.f.dirname = NULL;
|
||||
f->dirname = NULL;
|
||||
|
||||
t.f.mode = want_dir_match > 0 ? S_IFDIR : S_IFREG;
|
||||
f->mode = want_dir_match > 0 ? S_IFDIR : S_IFREG;
|
||||
|
||||
if (want_dir_match < 0)
|
||||
return flist_find_ignore_dirness(flist, &t.f);
|
||||
return flist_find(flist, &t.f);
|
||||
return flist_find_ignore_dirness(flist, f);
|
||||
return flist_find(flist, f);
|
||||
}
|
||||
|
||||
/* Search for an identically-named item in the file list. Differs from
|
||||
@@ -2792,25 +2915,20 @@ void clear_file(struct file_struct *file)
|
||||
}
|
||||
|
||||
/* Allocate a new file list. */
|
||||
struct file_list *flist_new(int flags, char *msg)
|
||||
static struct file_list *flist_new(int flags, const char *msg)
|
||||
{
|
||||
struct file_list *flist;
|
||||
|
||||
flist = new0(struct file_list);
|
||||
|
||||
if (flags & FLIST_TEMP) {
|
||||
if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0,
|
||||
out_of_memory,
|
||||
POOL_INTERN)))
|
||||
if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
|
||||
out_of_memory(msg);
|
||||
} else {
|
||||
/* This is a doubly linked list with prev looping back to
|
||||
* the end of the list, but the last next pointer is NULL. */
|
||||
if (!first_flist) {
|
||||
flist->file_pool = pool_create(NORMAL_EXTENT, 0,
|
||||
out_of_memory,
|
||||
POOL_INTERN);
|
||||
if (!flist->file_pool)
|
||||
if (!(flist->file_pool = pool_create(NORMAL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
|
||||
out_of_memory(msg);
|
||||
|
||||
flist->ndx_start = flist->flist_num = inc_recurse ? 1 : 0;
|
||||
@@ -3060,8 +3178,8 @@ static void output_flist(struct file_list *flist)
|
||||
} else
|
||||
*uidbuf = '\0';
|
||||
if (gid_ndx) {
|
||||
static char parens[] = "(\0)\0\0\0";
|
||||
char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
|
||||
static const char parens[] = "(\0)\0\0\0";
|
||||
const char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
|
||||
snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
|
||||
pp, F_GROUP(file), pp + 2);
|
||||
} else
|
||||
|
||||
432
generator.c
432
generator.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2023 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -35,16 +35,18 @@ extern int inc_recurse;
|
||||
extern int relative_paths;
|
||||
extern int implied_dirs;
|
||||
extern int keep_dirlinks;
|
||||
extern int write_devices;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_xattrs;
|
||||
extern int preserve_links;
|
||||
extern int preserve_devices;
|
||||
extern int write_devices;
|
||||
extern int preserve_specials;
|
||||
extern int preserve_hard_links;
|
||||
extern int preserve_executability;
|
||||
extern int preserve_perms;
|
||||
extern int preserve_times;
|
||||
extern int preserve_mtimes;
|
||||
extern int omit_dir_times;
|
||||
extern int omit_link_times;
|
||||
extern int delete_mode;
|
||||
extern int delete_before;
|
||||
extern int delete_during;
|
||||
@@ -84,7 +86,7 @@ extern int list_only;
|
||||
extern int read_batch;
|
||||
extern int write_batch;
|
||||
extern int safe_symlinks;
|
||||
extern long block_size; /* "long" because popt can't set an int32. */
|
||||
extern int32 block_size;
|
||||
extern int unsort_ndx;
|
||||
extern int max_delete;
|
||||
extern int force_delete;
|
||||
@@ -112,10 +114,6 @@ static int need_retouch_dir_times;
|
||||
static int need_retouch_dir_perms;
|
||||
static const char *solo_file = NULL;
|
||||
|
||||
enum nonregtype {
|
||||
TYPE_DIR, TYPE_SPECIAL, TYPE_DEVICE, TYPE_SYMLINK
|
||||
};
|
||||
|
||||
/* Forward declarations. */
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
static void handle_skipped_hlink(struct file_struct *file, int itemizing,
|
||||
@@ -186,7 +184,8 @@ static int remember_delete(struct file_struct *file, const char *fname, int flag
|
||||
static int read_delay_line(char *buf, int *flags_p)
|
||||
{
|
||||
static int read_pos = 0;
|
||||
int j, len, mode;
|
||||
unsigned int mode;
|
||||
int j, len;
|
||||
char *bp, *past_space;
|
||||
|
||||
while (1) {
|
||||
@@ -230,11 +229,13 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
*flags_p = 0;
|
||||
|
||||
if (sscanf(bp, "%x ", &mode) != 1) {
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space = strchr(bp, ' ') + 1;
|
||||
past_space = strchr(bp, ' ');
|
||||
if (!past_space) {
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space++;
|
||||
len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
|
||||
read_pos = j + 1;
|
||||
|
||||
@@ -248,6 +249,10 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
memcpy(buf, past_space, len);
|
||||
|
||||
return mode;
|
||||
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_delayed_deletions(char *delbuf)
|
||||
@@ -270,7 +275,7 @@ static void do_delayed_deletions(char *delbuf)
|
||||
* MAXPATHLEN buffer with the name of the directory in it (the functions we
|
||||
* call will append names onto the end, but the old dir value will be restored
|
||||
* on exit). */
|
||||
static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
|
||||
static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t fs_dev)
|
||||
{
|
||||
static int already_warned = 0;
|
||||
static struct hashtable *dev_tbl;
|
||||
@@ -305,12 +310,12 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
|
||||
if (!dev_tbl)
|
||||
dev_tbl = hashtable_create(16, HT_KEY64);
|
||||
if (file->flags & FLAG_TOP_DIR) {
|
||||
hashtable_find(dev_tbl, *fs_dev+1, "");
|
||||
filesystem_dev = *fs_dev;
|
||||
} else if (filesystem_dev != *fs_dev) {
|
||||
if (!hashtable_find(dev_tbl, *fs_dev+1, NULL))
|
||||
hashtable_find(dev_tbl, fs_dev+1, "");
|
||||
filesystem_dev = fs_dev;
|
||||
} else if (filesystem_dev != fs_dev) {
|
||||
if (!hashtable_find(dev_tbl, fs_dev+1, NULL))
|
||||
return;
|
||||
filesystem_dev = *fs_dev; /* it's a prior top-dir dev */
|
||||
filesystem_dev = fs_dev; /* it's a prior top-dir dev */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,9 +384,9 @@ static void do_delete_pass(void)
|
||||
|| !S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
delete_in_dir(fbuf, file, &st.st_dev);
|
||||
delete_in_dir(fbuf, file, st.st_dev);
|
||||
}
|
||||
delete_in_dir(NULL, NULL, &dev_zero);
|
||||
delete_in_dir(NULL, NULL, dev_zero);
|
||||
|
||||
if (INFO_GTE(FLIST, 2) && !am_server)
|
||||
rprintf(FINFO, " \r");
|
||||
@@ -396,6 +401,19 @@ static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname))
|
||||
{
|
||||
int differs = mtime_differs(&sxp->st, file);
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (!differs && crtimes_ndx) {
|
||||
if (sxp->crtime == 0)
|
||||
sxp->crtime = get_create_time(fname, &sxp->st);
|
||||
differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0);
|
||||
}
|
||||
#endif
|
||||
return differs;
|
||||
}
|
||||
|
||||
static inline int perms_differ(struct file_struct *file, stat_x *sxp)
|
||||
{
|
||||
if (preserve_perms)
|
||||
@@ -450,7 +468,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
|
||||
{
|
||||
if (S_ISLNK(file->mode)) {
|
||||
#ifdef CAN_SET_SYMLINK_TIMES
|
||||
if (preserve_times & PRESERVE_LINK_TIMES && mtime_differs(&sxp->st, file))
|
||||
if (preserve_mtimes && !omit_link_times && any_time_differs(sxp, file, fname))
|
||||
return 0;
|
||||
#endif
|
||||
#ifdef CAN_CHMOD_SYMLINK
|
||||
@@ -470,7 +488,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
if (preserve_times && mtime_differs(&sxp->st, file))
|
||||
if (preserve_mtimes && any_time_differs(sxp, file, fname))
|
||||
return 0;
|
||||
if (perms_differ(file, sxp))
|
||||
return 0;
|
||||
@@ -494,9 +512,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
|
||||
const char *xname)
|
||||
{
|
||||
if (statret >= 0) { /* A from-dest-dir statret can == 1! */
|
||||
int keep_time = !preserve_times ? 0
|
||||
: S_ISDIR(file->mode) ? preserve_times & PRESERVE_DIR_TIMES
|
||||
: S_ISLNK(file->mode) ? preserve_times & PRESERVE_LINK_TIMES
|
||||
int keep_time = !preserve_mtimes ? 0
|
||||
: S_ISDIR(file->mode) ? !omit_dir_times
|
||||
: S_ISLNK(file->mode) ? !omit_link_times
|
||||
: 1;
|
||||
|
||||
if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
|
||||
@@ -512,7 +530,15 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
|
||||
if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
|
||||
&& !same_time(F_ATIME(file), 0, sxp->st.st_atime, 0))
|
||||
iflags |= ITEM_REPORT_ATIME;
|
||||
#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
|
||||
#ifdef SUPPORT_CRTIMES
|
||||
if (crtimes_ndx) {
|
||||
if (sxp->crtime == 0)
|
||||
sxp->crtime = get_create_time(fnamecmp, &sxp->st);
|
||||
if (!same_time(sxp->crtime, 0, F_CRTIME(file), 0))
|
||||
iflags |= ITEM_REPORT_CRTIME;
|
||||
}
|
||||
#endif
|
||||
#ifndef CAN_CHMOD_SYMLINK
|
||||
if (S_ISLNK(file->mode)) {
|
||||
;
|
||||
} else
|
||||
@@ -578,30 +604,77 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Perform our quick-check heuristic for determining if a file is unchanged. */
|
||||
int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
|
||||
static enum filetype get_file_type(mode_t mode)
|
||||
{
|
||||
if (st->st_size != F_LENGTH(file))
|
||||
return 0;
|
||||
|
||||
/* if always checksum is set then we use the checksum instead
|
||||
of the file time to determine whether to sync */
|
||||
if (always_checksum > 0 && S_ISREG(st->st_mode)) {
|
||||
char sum[MAX_DIGEST_LEN];
|
||||
file_checksum(fn, st, sum);
|
||||
return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
|
||||
}
|
||||
|
||||
if (size_only > 0)
|
||||
return 1;
|
||||
|
||||
if (ignore_times)
|
||||
return 0;
|
||||
|
||||
return !mtime_differs(st, file);
|
||||
if (S_ISREG(mode))
|
||||
return FT_REG;
|
||||
if (S_ISLNK(mode))
|
||||
return FT_SYMLINK;
|
||||
if (S_ISDIR(mode))
|
||||
return FT_DIR;
|
||||
if (IS_SPECIAL(mode))
|
||||
return FT_SPECIAL;
|
||||
if (IS_DEVICE(mode))
|
||||
return FT_DEVICE;
|
||||
return FT_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/* Perform our quick-check heuristic for determining if a file is unchanged. */
|
||||
int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file, STRUCT_STAT *st)
|
||||
{
|
||||
switch (ftype) {
|
||||
case FT_REG:
|
||||
if (st->st_size != F_LENGTH(file))
|
||||
return 0;
|
||||
|
||||
/* If always_checksum is set then we use the checksum instead
|
||||
* of the file mtime to determine whether to sync. */
|
||||
if (always_checksum > 0) {
|
||||
char sum[MAX_DIGEST_LEN];
|
||||
file_checksum(fn, st, sum);
|
||||
return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
|
||||
}
|
||||
|
||||
if (size_only > 0)
|
||||
return 1;
|
||||
|
||||
if (ignore_times)
|
||||
return 0;
|
||||
|
||||
if (mtime_differs(st, file))
|
||||
return 0;
|
||||
break;
|
||||
case FT_DIR:
|
||||
break;
|
||||
case FT_SYMLINK: {
|
||||
#ifdef SUPPORT_LINKS
|
||||
char lnk[MAXPATHLEN];
|
||||
int len = do_readlink(fn, lnk, MAXPATHLEN-1);
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
lnk[len] = '\0';
|
||||
if (strcmp(lnk, F_SYMLINK(file)) != 0)
|
||||
return 0;
|
||||
break;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
case FT_SPECIAL:
|
||||
if (!BITS_EQUAL(file->mode, st->st_mode, _S_IFMT))
|
||||
return 0;
|
||||
break;
|
||||
case FT_DEVICE: {
|
||||
uint32 *devp = F_RDEV_P(file);
|
||||
if (st->st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
case FT_UNSUPPORTED:
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* set (initialize) the size entries in the per-file sum_struct
|
||||
@@ -716,7 +789,7 @@ static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy)
|
||||
for (i = 0; i < sum.count; i++) {
|
||||
int32 n1 = (int32)MIN(len, (OFF_T)sum.blength);
|
||||
char *map = map_ptr(mapbuf, offset, n1);
|
||||
char sum2[SUM_LENGTH];
|
||||
char sum2[MAX_DIGEST_LEN];
|
||||
uint32 sum1;
|
||||
|
||||
len -= n1;
|
||||
@@ -808,9 +881,12 @@ static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list
|
||||
len = strlen(name);
|
||||
suf = find_filename_suffix(name, len, &suf_len);
|
||||
|
||||
dist = fuzzy_distance(name, len, fname, fname_len);
|
||||
/* Add some extra weight to how well the suffixes match. */
|
||||
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len) * 10;
|
||||
dist = fuzzy_distance(name, len, fname, fname_len, lowest_dist);
|
||||
/* Add some extra weight to how well the suffixes match unless we've already disqualified
|
||||
* this file based on a heuristic. */
|
||||
if (dist < 0xFFFF0000U) {
|
||||
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len, 0xFFFF0000U) * 10;
|
||||
}
|
||||
if (DEBUG_GTE(FUZZY, 2)) {
|
||||
rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
|
||||
f_name(fp, NULL), (int)(dist>>16), (int)(dist&0xFFFF));
|
||||
@@ -886,7 +962,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
best_match = j;
|
||||
match_level = 1;
|
||||
}
|
||||
if (!unchanged_file(cmpbuf, file, &sxp->st))
|
||||
if (!quick_check_ok(FT_REG, cmpbuf, file, &sxp->st))
|
||||
continue;
|
||||
if (match_level == 1) {
|
||||
best_match = j;
|
||||
@@ -914,7 +990,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
if (find_exact_for_existing) {
|
||||
if (alt_dest_type == LINK_DEST && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
|
||||
return -1;
|
||||
if (do_unlink(fname) < 0 && errno != ENOENT)
|
||||
if (do_unlink_at(fname) < 0 && errno != ENOENT)
|
||||
goto got_nothing_for_ya;
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -985,29 +1061,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
{
|
||||
int best_match = -1;
|
||||
int match_level = 0;
|
||||
enum nonregtype type;
|
||||
uint32 *devp;
|
||||
#ifdef SUPPORT_LINKS
|
||||
char lnk[MAXPATHLEN];
|
||||
int len;
|
||||
#endif
|
||||
enum filetype ftype = get_file_type(file->mode);
|
||||
int j = 0;
|
||||
|
||||
#ifndef SUPPORT_LINKS
|
||||
if (S_ISLNK(file->mode))
|
||||
if (ftype == FT_SYMLINK)
|
||||
return -1;
|
||||
#endif
|
||||
if (S_ISDIR(file->mode)) {
|
||||
type = TYPE_DIR;
|
||||
} else if (IS_SPECIAL(file->mode))
|
||||
type = TYPE_SPECIAL;
|
||||
else if (IS_DEVICE(file->mode))
|
||||
type = TYPE_DEVICE;
|
||||
#ifdef SUPPORT_LINKS
|
||||
else if (S_ISLNK(file->mode))
|
||||
type = TYPE_SYMLINK;
|
||||
#endif
|
||||
else {
|
||||
if (ftype == FT_REG || ftype == FT_UNSUPPORTED) {
|
||||
rprintf(FERROR,
|
||||
"internal: try_dests_non() called with invalid mode (%o)\n",
|
||||
(int)file->mode);
|
||||
@@ -1018,53 +1079,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, &sxp->st, 0) < 0)
|
||||
continue;
|
||||
switch (type) {
|
||||
case TYPE_DIR:
|
||||
if (!S_ISDIR(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_SPECIAL:
|
||||
if (!IS_SPECIAL(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_DEVICE:
|
||||
if (!IS_DEVICE(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_SYMLINK:
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (!S_ISLNK(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
if (ftype != get_file_type(sxp->st.st_mode))
|
||||
continue;
|
||||
if (match_level < 1) {
|
||||
match_level = 1;
|
||||
best_match = j;
|
||||
}
|
||||
switch (type) {
|
||||
case TYPE_DIR:
|
||||
case TYPE_SPECIAL:
|
||||
break;
|
||||
case TYPE_DEVICE:
|
||||
devp = F_RDEV_P(file);
|
||||
if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_SYMLINK:
|
||||
#ifdef SUPPORT_LINKS
|
||||
if ((len = do_readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0)
|
||||
continue;
|
||||
lnk[len] = '\0';
|
||||
if (strcmp(lnk, F_SYMLINK(file)) != 0)
|
||||
continue;
|
||||
break;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
if (!quick_check_ok(ftype, cmpbuf, file, &sxp->st))
|
||||
continue;
|
||||
if (match_level < 2) {
|
||||
match_level = 2;
|
||||
best_match = j;
|
||||
@@ -1096,7 +1118,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
&& !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
|
||||
#endif
|
||||
&& !S_ISDIR(file->mode)) {
|
||||
if (do_link(cmpbuf, fname) < 0) {
|
||||
if (do_link_at(cmpbuf, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to hard-link %s with %s",
|
||||
cmpbuf, fname);
|
||||
@@ -1109,14 +1131,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
match_level = 2;
|
||||
if (itemizing && stdout_format_has_i
|
||||
&& (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
|
||||
int chg = alt_dest_type == COMPARE_DEST && type != TYPE_DIR ? 0
|
||||
int chg = alt_dest_type == COMPARE_DEST && ftype != FT_DIR ? 0
|
||||
: ITEM_LOCAL_CHANGE + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
|
||||
char *lp = match_level == 3 ? "" : NULL;
|
||||
itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
|
||||
}
|
||||
if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT) {
|
||||
rprintf(FCLIENT, "%s%s is uptodate\n",
|
||||
fname, type == TYPE_DIR ? "/" : "");
|
||||
fname, ftype == FT_DIR ? "/" : "");
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
@@ -1131,6 +1153,7 @@ static void list_file_entry(struct file_struct *f)
|
||||
int size_width = human_readable ? 14 : 11;
|
||||
int mtime_width = 1 + strlen(mtime_str);
|
||||
int atime_width = atimes_ndx ? mtime_width : 0;
|
||||
int crtime_width = crtimes_ndx ? mtime_width : 0;
|
||||
|
||||
if (!F_IS_ACTIVE(f)) {
|
||||
/* this can happen if duplicate names were removed */
|
||||
@@ -1141,10 +1164,11 @@ static void list_file_entry(struct file_struct *f)
|
||||
|
||||
if (missing_args == 2 && f->mode == 0) {
|
||||
rprintf(FINFO, "%-*s %s\n",
|
||||
10 + 1 + size_width + mtime_width + atime_width, "*missing",
|
||||
10 + 1 + size_width + mtime_width + atime_width + crtime_width, "*missing",
|
||||
f_name(f, NULL));
|
||||
} else {
|
||||
const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : "";
|
||||
const char *crtime_str = crtimes_ndx ? timestring(F_CRTIME(f)) : "";
|
||||
const char *arrow, *lnk;
|
||||
|
||||
permstring(permbuf, f->mode);
|
||||
@@ -1157,9 +1181,9 @@ static void list_file_entry(struct file_struct *f)
|
||||
#endif
|
||||
arrow = lnk = "";
|
||||
|
||||
rprintf(FINFO, "%s %*s %s%*s %s%s%s\n",
|
||||
rprintf(FINFO, "%s %*s %s%*s%*s %s%s%s\n",
|
||||
permbuf, size_width, human_num(F_LENGTH(f)),
|
||||
timestring(f->modtime), atime_width, atime_str,
|
||||
timestring(f->modtime), atime_width, atime_str, crtime_width, crtime_str,
|
||||
f_name(f, NULL), arrow, lnk);
|
||||
}
|
||||
}
|
||||
@@ -1208,7 +1232,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
char fnamecmpbuf[MAXPATHLEN];
|
||||
uchar fnamecmp_type;
|
||||
int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
|
||||
int is_dir = !S_ISDIR(file->mode) ? 0
|
||||
enum filetype stype, ftype = get_file_type(file->mode);
|
||||
int is_dir = ftype != FT_DIR ? 0
|
||||
: inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
|
||||
: 1;
|
||||
|
||||
@@ -1277,21 +1302,26 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
* this function was asked to process in the file list. */
|
||||
if (!inc_recurse
|
||||
&& (*dn != '.' || dn[1]) /* Avoid an issue with --relative and the "." dir. */
|
||||
&& (!prior_dir_file || strcmp(dn, f_name(prior_dir_file, NULL)) != 0)
|
||||
&& flist_find_name(cur_flist, dn, 1) < 0) {
|
||||
&& (!prior_dir_file || strcmp(dn, f_name(prior_dir_file, NULL)) != 0)) {
|
||||
int ok = 0, j = flist_find_name(cur_flist, dn, -1);
|
||||
if (j >= 0) {
|
||||
struct file_struct *f = cur_flist->sorted[j];
|
||||
if (S_ISDIR(f->mode) || (missing_args == 2 && !file->mode && !f->mode))
|
||||
ok = 1;
|
||||
}
|
||||
/* The --delete-missing-args option can actually put invalid entries into
|
||||
* the file list, so if that option was specified, we'll just complain about
|
||||
* it and allow it. */
|
||||
if (missing_args == 2 && file->mode == 0)
|
||||
if (!ok && missing_args == 2 && file->mode == 0 && j < 0)
|
||||
rprintf(FERROR, "WARNING: parent dir is absent in the file list: %s\n", dn);
|
||||
else {
|
||||
else if (!ok) {
|
||||
rprintf(FERROR, "ABORTING due to invalid path from sender: %s/%s\n",
|
||||
dn, file->basename);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
}
|
||||
if (relative_paths && !implied_dirs
|
||||
&& do_stat(dn, &sx.st) < 0) {
|
||||
if (relative_paths && !implied_dirs && file->mode != 0
|
||||
&& do_stat_at(dn, &sx.st) < 0) {
|
||||
if (dry_run)
|
||||
goto parent_is_dry_missing;
|
||||
if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
|
||||
@@ -1351,10 +1381,27 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
&& !am_root && sx.st.st_uid == our_uid)
|
||||
del_opts |= DEL_NO_UID_WRITE;
|
||||
|
||||
if (statret == 0)
|
||||
stype = get_file_type(sx.st.st_mode);
|
||||
else
|
||||
stype = FT_UNSUPPORTED;
|
||||
|
||||
if (ignore_existing > 0 && statret == 0
|
||||
&& (!is_dir || !S_ISDIR(sx.st.st_mode))) {
|
||||
if (INFO_GTE(SKIP, 1) && is_dir >= 0)
|
||||
rprintf(FINFO, "%s exists\n", fname);
|
||||
&& (!is_dir || stype != FT_DIR)) {
|
||||
if (INFO_GTE(SKIP, 1) && is_dir >= 0) {
|
||||
const char *suf = "";
|
||||
if (INFO_GTE(SKIP, 2)) {
|
||||
if (ftype != stype)
|
||||
suf = " (type change)";
|
||||
else if (!quick_check_ok(ftype, fname, file, &sx.st))
|
||||
suf = always_checksum ? " (sum change)" : " (file change)";
|
||||
else if (!unchanged_attrs(fname, file, &sx))
|
||||
suf = " (attr change)";
|
||||
else
|
||||
suf = " (uptodate)";
|
||||
}
|
||||
rprintf(FINFO, "%s exists%s\n", fname, suf);
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (F_IS_HLINKED(file))
|
||||
handle_skipped_hlink(file, itemizing, code, f_out);
|
||||
@@ -1375,7 +1422,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
} else
|
||||
added_perms = 0;
|
||||
if (is_dir < 0) {
|
||||
if (!(preserve_times & PRESERVE_DIR_TIMES))
|
||||
if (!preserve_mtimes || omit_dir_times)
|
||||
goto cleanup;
|
||||
/* In inc_recurse mode we want to make sure any missing
|
||||
* directories get created while we're still processing
|
||||
@@ -1383,10 +1430,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
* dir's mtime right away). We will handle the dir in
|
||||
* full later (right before we handle its contents). */
|
||||
if (statret == 0
|
||||
&& (S_ISDIR(sx.st.st_mode)
|
||||
&& (stype == FT_DIR
|
||||
|| delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
|
||||
goto cleanup; /* Any errors get reported later. */
|
||||
if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
if (do_mkdir_at(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
file->flags |= FLAG_DIR_CREATED;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1395,7 +1442,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
* file of that name and it is *not* a directory, then
|
||||
* we need to delete it. If it doesn't exist, then
|
||||
* (perhaps recursively) create it. */
|
||||
if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
|
||||
if (statret == 0 && stype != FT_DIR) {
|
||||
if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)
|
||||
goto skipping_dir_contents;
|
||||
statret = -1;
|
||||
@@ -1428,10 +1475,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
itemize(fnamecmp, file, ndx, statret, &sx,
|
||||
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
|
||||
}
|
||||
if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (real_ret != 0 && do_mkdir_at(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (!relative_paths || errno != ENOENT
|
||||
|| make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
|
||||
|| (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
|| (do_mkdir_at(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"recv_generator: mkdir %s failed",
|
||||
full_fname(fname));
|
||||
@@ -1458,7 +1505,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
#ifdef HAVE_CHMOD
|
||||
if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
|
||||
mode_t mode = file->mode | S_IRWXU;
|
||||
if (do_chmod(fname, mode) < 0) {
|
||||
if (do_chmod_at(fname, mode) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to modify permissions on %s",
|
||||
full_fname(fname));
|
||||
@@ -1479,7 +1526,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
else if (delete_during && f_out != -1 && !phase
|
||||
&& !(file->flags & FLAG_MISSING_DIR)) {
|
||||
if (file->flags & FLAG_CONTENT_DIR)
|
||||
delete_in_dir(fname, file, &real_sx.st.st_dev);
|
||||
delete_in_dir(fname, file, real_sx.st.st_dev);
|
||||
else
|
||||
change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
|
||||
}
|
||||
@@ -1490,7 +1537,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
/* If we're not preserving permissions, change the file-list's
|
||||
* mode based on the local permissions and some heuristics. */
|
||||
if (!preserve_perms) {
|
||||
int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
|
||||
int exists = statret == 0 && stype != FT_DIR;
|
||||
file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, exists);
|
||||
}
|
||||
|
||||
@@ -1500,7 +1547,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
#endif
|
||||
|
||||
if (preserve_links && S_ISLNK(file->mode)) {
|
||||
if (preserve_links && ftype == FT_SYMLINK) {
|
||||
#ifdef SUPPORT_LINKS
|
||||
const char *sl = F_SYMLINK(file);
|
||||
if (safe_symlinks && unsafe_symlink(sl, fname)) {
|
||||
@@ -1517,12 +1564,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
if (statret == 0) {
|
||||
char lnk[MAXPATHLEN];
|
||||
int len;
|
||||
|
||||
if (S_ISLNK(sx.st.st_mode)
|
||||
&& (len = do_readlink(fname, lnk, MAXPATHLEN-1)) > 0
|
||||
&& strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
|
||||
if (stype == FT_SYMLINK && quick_check_ok(stype, fname, file, &sx.st)) {
|
||||
/* The link is pointing to the right place. */
|
||||
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
|
||||
if (itemizing)
|
||||
@@ -1555,7 +1597,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
if (atomic_create(file, fname, sl, NULL, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) {
|
||||
set_file_attrs(fname, file, NULL, NULL, 0);
|
||||
if (itemizing) {
|
||||
if (statret == 0 && !S_ISLNK(sx.st.st_mode))
|
||||
if (statret == 0 && stype != FT_SYMLINK)
|
||||
statret = -1;
|
||||
itemize(fnamecmp, file, ndx, statret, &sx,
|
||||
ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
|
||||
@@ -1576,28 +1618,22 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|
||||
|| (preserve_specials && IS_SPECIAL(file->mode))) {
|
||||
if ((am_root && preserve_devices && ftype == FT_DEVICE)
|
||||
|| (preserve_specials && ftype == FT_SPECIAL)) {
|
||||
dev_t rdev;
|
||||
int del_for_flag = 0;
|
||||
if (IS_DEVICE(file->mode)) {
|
||||
int del_for_flag;
|
||||
if (ftype == FT_DEVICE) {
|
||||
uint32 *devp = F_RDEV_P(file);
|
||||
rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
|
||||
} else
|
||||
del_for_flag = DEL_FOR_DEVICE;
|
||||
} else {
|
||||
rdev = 0;
|
||||
del_for_flag = DEL_FOR_SPECIAL;
|
||||
}
|
||||
if (statret == 0) {
|
||||
if (IS_DEVICE(file->mode)) {
|
||||
if (!IS_DEVICE(sx.st.st_mode))
|
||||
statret = -1;
|
||||
del_for_flag = DEL_FOR_DEVICE;
|
||||
} else {
|
||||
if (!IS_SPECIAL(sx.st.st_mode))
|
||||
statret = -1;
|
||||
del_for_flag = DEL_FOR_SPECIAL;
|
||||
}
|
||||
if (statret == 0
|
||||
&& BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
|
||||
&& (IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev == rdev)) {
|
||||
if (ftype != stype)
|
||||
statret = -1;
|
||||
else if (quick_check_ok(ftype, fname, file, &sx.st)) {
|
||||
/* The device or special file is identical. */
|
||||
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
|
||||
if (itemizing)
|
||||
@@ -1650,10 +1686,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!S_ISREG(file->mode)) {
|
||||
if (solo_file)
|
||||
fname = f_name(file, NULL);
|
||||
rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
|
||||
if (ftype != FT_REG) {
|
||||
if (INFO_GTE(NONREG, 1)) {
|
||||
if (solo_file)
|
||||
fname = f_name(file, NULL);
|
||||
rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -1674,7 +1712,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (update_only > 0 && statret == 0 && file->modtime - sx.st.st_mtime <= modify_window) {
|
||||
if (update_only > 0 && statret == 0 && file->modtime - sx.st.st_mtime < modify_window) {
|
||||
if (INFO_GTE(SKIP, 1))
|
||||
rprintf(FINFO, "%s is newer\n", fname);
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1686,7 +1724,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
|
||||
fnamecmp_type = FNAMECMP_FNAME;
|
||||
|
||||
if (statret == 0 && !(S_ISREG(sx.st.st_mode) || (write_devices && IS_DEVICE(sx.st.st_mode)))) {
|
||||
if (statret == 0 && !(stype == FT_REG || (write_devices && stype == FT_DEVICE))) {
|
||||
if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
|
||||
goto cleanup;
|
||||
statret = -1;
|
||||
@@ -1720,7 +1758,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
partialptr = NULL;
|
||||
|
||||
if (statret != 0 && fuzzy_basis) {
|
||||
if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
|
||||
if (need_fuzzy_dirlist) {
|
||||
const char *dn = file->dirname ? file->dirname : ".";
|
||||
int i;
|
||||
strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
|
||||
@@ -1764,13 +1802,19 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
|
||||
/* This early open into fd skips the regular open below. */
|
||||
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
|
||||
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
|
||||
}
|
||||
|
||||
if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
|
||||
;
|
||||
else if (fnamecmp_type >= FNAMECMP_FUZZY)
|
||||
;
|
||||
else if (unchanged_file(fnamecmp, file, &sx.st)) {
|
||||
else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st)) {
|
||||
if (partialptr) {
|
||||
do_unlink(partialptr);
|
||||
do_unlink_at(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT | maybe_ATTRS_ACCURATE_TIME);
|
||||
@@ -1784,7 +1828,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
return_with_success:
|
||||
if (!dry_run)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
send_msg_success(fname, ndx);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -1829,7 +1873,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
|
||||
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
|
||||
rsyserr(FERROR, errno, "failed to open %s, continuing",
|
||||
full_fname(fnamecmp));
|
||||
pretend_missing:
|
||||
@@ -1846,11 +1890,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
|
||||
if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
|
||||
if (!(backupptr = get_backup_name(fname))) {
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
|
||||
close(fd);
|
||||
goto pretend_missing;
|
||||
}
|
||||
if (robust_unlink(backupptr) && errno != ENOENT) {
|
||||
@@ -1858,14 +1900,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
back_file = NULL;
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
if ((f_copy = do_open_at(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
back_file = NULL;
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
fnamecmp_type = FNAMECMP_BACKUP;
|
||||
@@ -1916,7 +1956,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
write_sum_head(f_out, NULL);
|
||||
else if (sx.st.st_size <= 0) {
|
||||
write_sum_head(f_out, NULL);
|
||||
close(fd);
|
||||
} else {
|
||||
if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
|
||||
rprintf(FWARNING,
|
||||
@@ -1924,10 +1963,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
fnamecmp);
|
||||
write_sum_head(f_out, NULL);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (back_file) {
|
||||
int save_preserve_xattrs = preserve_xattrs;
|
||||
if (f_copy >= 0)
|
||||
@@ -1982,7 +2022,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
|
||||
if (slnk) {
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (do_symlink(slnk, create_name) < 0) {
|
||||
if (do_symlink_at(slnk, create_name) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
|
||||
full_fname(create_name), slnk);
|
||||
return 0;
|
||||
@@ -1998,7 +2038,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
if (do_mknod(create_name, file->mode, rdev) < 0) {
|
||||
if (do_mknod_at(create_name, file->mode, rdev) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "mknod %s failed",
|
||||
full_fname(create_name));
|
||||
return 0;
|
||||
@@ -2006,10 +2046,14 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
}
|
||||
|
||||
if (!skip_atomic) {
|
||||
if (do_rename(tmpname, fname) < 0) {
|
||||
if (do_rename_at(tmpname, fname) < 0) {
|
||||
char *full_tmpname = strdup(full_fname(tmpname));
|
||||
if (full_tmpname == NULL)
|
||||
out_of_memory("atomic_create");
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
|
||||
full_fname(tmpname), full_fname(fname));
|
||||
do_unlink(tmpname);
|
||||
full_tmpname, full_fname(fname));
|
||||
free(full_tmpname);
|
||||
do_unlink_at(tmpname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2073,7 +2117,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
|
||||
continue;
|
||||
fname = f_name(file, NULL);
|
||||
if (fix_dir_perms)
|
||||
do_chmod(fname, file->mode);
|
||||
do_chmod_at(fname, file->mode);
|
||||
if (need_retouch_dir_times) {
|
||||
STRUCT_STAT st;
|
||||
if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
|
||||
@@ -2108,6 +2152,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
if (send_failed)
|
||||
ndx = get_hlink_num();
|
||||
flist = flist_for_ndx(ndx, "check_for_finished_files.1");
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
assert(file->flags & FLAG_HLINKED);
|
||||
if (send_failed)
|
||||
@@ -2136,6 +2182,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
|
||||
flist = cur_flist;
|
||||
cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
|
||||
if (ndx < cur_flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
file = cur_flist->files[ndx - cur_flist->ndx_start];
|
||||
if (solo_file)
|
||||
@@ -2213,7 +2261,7 @@ void generate_files(int f_out, const char *local_name)
|
||||
}
|
||||
solo_file = local_name;
|
||||
dir_tweaking = !(list_only || solo_file || dry_run);
|
||||
need_retouch_dir_times = preserve_times & PRESERVE_DIR_TIMES;
|
||||
need_retouch_dir_times = preserve_mtimes && !omit_dir_times;
|
||||
loopchk_limit = allowed_lull ? allowed_lull * 5 : 200;
|
||||
symlink_timeset_failed_flags = ITEM_REPORT_TIME
|
||||
| (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
|
||||
@@ -2266,7 +2314,7 @@ void generate_files(int f_out, const char *local_name)
|
||||
dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
|
||||
} else
|
||||
dirdev = MAKEDEV(0, 0);
|
||||
delete_in_dir(fbuf, fp, &dirdev);
|
||||
delete_in_dir(fbuf, fp, dirdev);
|
||||
} else
|
||||
change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
|
||||
}
|
||||
@@ -2313,7 +2361,7 @@ void generate_files(int f_out, const char *local_name)
|
||||
} while ((cur_flist = cur_flist->next) != NULL);
|
||||
|
||||
if (delete_during)
|
||||
delete_in_dir(NULL, NULL, &dev_zero);
|
||||
delete_in_dir(NULL, NULL, dev_zero);
|
||||
phase++;
|
||||
if (DEBUG_GTE(GENR, 1))
|
||||
rprintf(FINFO, "generate_files phase=%d\n", phase);
|
||||
|
||||
174
hashtable.c
174
hashtable.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Routines to provide a memory-efficient hashtable.
|
||||
*
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -350,6 +350,9 @@ void *hashtable_find(struct hashtable *tbl, int64 key, void *data_when_new)
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define NON_ZERO_32(x) ((x) ? (x) : (uint32_t)1)
|
||||
#define NON_ZERO_64(x, y) ((x) || (y) ? (y) | (int64)(x) << 32 | (y) : (int64)1)
|
||||
|
||||
uint32_t hashlittle(const void *key, size_t length)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
@@ -390,7 +393,7 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return c;
|
||||
case 0 : return NON_ZERO_32(c);
|
||||
}
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
@@ -436,7 +439,7 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return c; /* zero length requires no mixing */
|
||||
case 0 : return NON_ZERO_32(c); /* zero length requires no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
@@ -489,10 +492,171 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
/* FALLTHROUGH */
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return c;
|
||||
case 0 : return NON_ZERO_32(c);
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
return NON_ZERO_32(c);
|
||||
}
|
||||
|
||||
#if SIZEOF_INT64 >= 8
|
||||
/*
|
||||
* hashlittle2: return 2 32-bit hash values joined into an int64.
|
||||
*
|
||||
* This is identical to hashlittle(), except it returns two 32-bit hash
|
||||
* values instead of just one. This is good enough for hash table
|
||||
* lookup with 2^^64 buckets, or if you want a second hash if you're not
|
||||
* happy with the first, or if you want a probably-unique 64-bit ID for
|
||||
* the key. *pc is better mixed than *pb, so use *pc first. If you want
|
||||
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
|
||||
*/
|
||||
int64 hashlittle2(const void *key, size_t length)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + ((uint32_t)length);
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return NON_ZERO_64(b, c);
|
||||
}
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*--------------- all but last block: aligned reads and different mixing */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=k[4];
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=k[2];
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=k[0];
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return NON_ZERO_64(b, c); /* zero length strings require no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 12: c+=((uint32_t)k[11])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 11: c+=((uint32_t)k[10])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 10: c+=((uint32_t)k[9])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 9 : c+=k[8];
|
||||
/* FALLTHROUGH */
|
||||
case 8 : b+=((uint32_t)k[7])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 7 : b+=((uint32_t)k[6])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 6 : b+=((uint32_t)k[5])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 5 : b+=k[4];
|
||||
/* FALLTHROUGH */
|
||||
case 4 : a+=((uint32_t)k[3])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 3 : a+=((uint32_t)k[2])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 2 : a+=((uint32_t)k[1])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return NON_ZERO_64(b, c);
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return NON_ZERO_64(b, c);
|
||||
}
|
||||
#else
|
||||
#define hashlittle2(key, len) hashlittle(key, len)
|
||||
#endif
|
||||
|
||||
12
hlink.c
12
hlink.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -117,7 +117,7 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
|
||||
struct ht_int32_node *node = NULL;
|
||||
int32 gnum, gnum_next;
|
||||
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)()) hlink_compare_gnum);
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)(const void*, const void*))hlink_compare_gnum);
|
||||
|
||||
for (from = 0; from < ndx_count; from++) {
|
||||
file = hlink_flist->sorted[ndx_list[from]];
|
||||
@@ -406,7 +406,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!unchanged_file(cmpbuf, file, &alt_sx.st))
|
||||
if (!quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st))
|
||||
continue;
|
||||
statret = 1;
|
||||
if (unchanged_attrs(cmpbuf, file, &alt_sx))
|
||||
@@ -446,7 +446,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
|
||||
return -1;
|
||||
|
||||
if (remove_source_files == 1 && do_xfers)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
send_msg_success(fname, ndx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -454,7 +454,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
|
||||
int hard_link_one(struct file_struct *file, const char *fname,
|
||||
const char *oldname, int terse)
|
||||
{
|
||||
if (do_link(oldname, fname) < 0) {
|
||||
if (do_link_at(oldname, fname) < 0) {
|
||||
enum logcode code;
|
||||
if (terse) {
|
||||
if (!INFO_GTE(NAME, 1))
|
||||
@@ -519,7 +519,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
|
||||
if (val < 0)
|
||||
continue;
|
||||
if (remove_source_files == 1 && do_xfers)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
send_msg_success(fname, ndx);
|
||||
}
|
||||
|
||||
if (inc_recurse) {
|
||||
|
||||
5
ifuncs.h
5
ifuncs.h
@@ -1,6 +1,6 @@
|
||||
/* Inline functions for rsync.
|
||||
*
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -75,6 +75,7 @@ d_name(struct dirent *di)
|
||||
static inline void
|
||||
init_stat_x(stat_x *sx_p)
|
||||
{
|
||||
sx_p->crtime = 0;
|
||||
#ifdef SUPPORT_ACLS
|
||||
sx_p->acc_acl = sx_p->def_acl = NULL;
|
||||
#endif
|
||||
@@ -105,7 +106,7 @@ free_stat_x(stat_x *sx_p)
|
||||
static inline char *my_strdup(const char *str, const char *file, int line)
|
||||
{
|
||||
int len = strlen(str)+1;
|
||||
char *buf = my_alloc(do_malloc, len, 1, file, line);
|
||||
char *buf = my_alloc(NULL, len, 1, file, line);
|
||||
memcpy(buf, str, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ else
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
if [ -f $src ] || [ -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
|
||||
274
io.c
274
io.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2001 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -41,6 +41,7 @@ extern int am_server;
|
||||
extern int am_sender;
|
||||
extern int am_receiver;
|
||||
extern int am_generator;
|
||||
extern int local_server;
|
||||
extern int msgs2stderr;
|
||||
extern int inc_recurse;
|
||||
extern int io_error;
|
||||
@@ -54,12 +55,15 @@ extern int read_batch;
|
||||
extern int compat_flags;
|
||||
extern int protect_args;
|
||||
extern int checksum_seed;
|
||||
extern int xfer_sum_len;
|
||||
extern int daemon_connection;
|
||||
extern int protocol_version;
|
||||
extern int remove_source_files;
|
||||
extern int preserve_hard_links;
|
||||
extern BOOL extra_flist_sending_enabled;
|
||||
extern BOOL flush_ok_after_signal;
|
||||
extern struct stats stats;
|
||||
extern time_t stop_at_utime;
|
||||
extern struct file_list *cur_flist;
|
||||
#ifdef ICONV_OPTION
|
||||
extern int filesfrom_convert;
|
||||
@@ -82,6 +86,8 @@ int sock_f_out = -1;
|
||||
int64 total_data_read = 0;
|
||||
int64 total_data_written = 0;
|
||||
|
||||
char num_dev_ino_buf[4 + 8 + 8];
|
||||
|
||||
static struct {
|
||||
xbuf in, out, msg;
|
||||
int in_fd;
|
||||
@@ -111,7 +117,7 @@ static int active_filecnt = 0;
|
||||
static OFF_T active_bytecnt = 0;
|
||||
static int first_message = 1;
|
||||
|
||||
static char int_byte_extra[64] = {
|
||||
static const char int_byte_extra[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
|
||||
@@ -262,15 +268,18 @@ static size_t safe_read(int fd, char *buf, size_t len)
|
||||
rprintf(FINFO, "select exception on fd %d\n", fd); */
|
||||
|
||||
if (FD_ISSET(fd, &r_fds)) {
|
||||
int n = read(fd, buf + got, len - got);
|
||||
if (DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] safe_read(%d)=%ld\n", who_am_i(), fd, (long)n);
|
||||
ssize_t n = read(fd, buf + got, len - got);
|
||||
if (DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] safe_read(%d)=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), fd, (SIZE_T_FMT_CAST)n);
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
rsyserr(FERROR, errno, "safe_read failed to read %ld bytes", (long)len);
|
||||
rsyserr(FERROR, errno, "safe_read failed to read %" SIZE_T_FMT_MOD "d bytes",
|
||||
(SIZE_T_FMT_CAST)len);
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
if ((got += (size_t)n) == len)
|
||||
@@ -302,7 +311,7 @@ static const char *what_fd_is(int fd)
|
||||
* is not used on the socket except very early in the transfer. */
|
||||
static void safe_write(int fd, const char *buf, size_t len)
|
||||
{
|
||||
int n;
|
||||
ssize_t n;
|
||||
|
||||
assert(fd != iobuf.out_fd);
|
||||
|
||||
@@ -313,8 +322,8 @@ static void safe_write(int fd, const char *buf, size_t len)
|
||||
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
|
||||
write_failed:
|
||||
rsyserr(FERROR, errno,
|
||||
"safe_write failed to write %ld bytes to %s",
|
||||
(long)len, what_fd_is(fd));
|
||||
"safe_write failed to write %" SIZE_T_FMT_MOD "d bytes to %s",
|
||||
(SIZE_T_FMT_CAST)len, what_fd_is(fd));
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
} else {
|
||||
@@ -360,7 +369,7 @@ static void safe_write(int fd, const char *buf, size_t len)
|
||||
* a chunk of data and put it into the output buffer. */
|
||||
static void forward_filesfrom_data(void)
|
||||
{
|
||||
int len;
|
||||
ssize_t len;
|
||||
|
||||
len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
|
||||
if (len <= 0) {
|
||||
@@ -371,12 +380,15 @@ static void forward_filesfrom_data(void)
|
||||
free_xbuf(&ff_xb);
|
||||
if (ff_reenable_multiplex >= 0)
|
||||
io_start_multiplex_out(ff_reenable_multiplex);
|
||||
free_implied_include_partial_string();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] files-from read=%ld\n", who_am_i(), (long)len);
|
||||
if (DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] files-from read=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)len);
|
||||
}
|
||||
|
||||
#ifdef ICONV_OPTION
|
||||
len += ff_xb.len;
|
||||
@@ -412,6 +424,7 @@ static void forward_filesfrom_data(void)
|
||||
while (s != eob) {
|
||||
if (*s++ == '\0') {
|
||||
ff_xb.len = s - sob - 1;
|
||||
add_implied_include(sob, 0);
|
||||
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
|
||||
exit_cleanup(RERR_PROTOCOL); /* impossible? */
|
||||
write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
|
||||
@@ -427,6 +440,7 @@ static void forward_filesfrom_data(void)
|
||||
ff_lastchar = '\0';
|
||||
else {
|
||||
/* Handle a partial string specially, saving any incomplete chars. */
|
||||
implied_include_partial_string(sob, s);
|
||||
flags &= ~ICB_INCLUDE_INCOMPLETE;
|
||||
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
|
||||
if (errno == E2BIG)
|
||||
@@ -443,13 +457,17 @@ static void forward_filesfrom_data(void)
|
||||
char *f = ff_xb.buf + ff_xb.pos;
|
||||
char *t = ff_xb.buf;
|
||||
char *eob = f + len;
|
||||
char *cur = t;
|
||||
/* Eliminate any multi-'\0' runs. */
|
||||
while (f != eob) {
|
||||
if (!(*t++ = *f++)) {
|
||||
add_implied_include(cur, 0);
|
||||
cur = t;
|
||||
while (f != eob && *f == '\0')
|
||||
f++;
|
||||
}
|
||||
}
|
||||
implied_include_partial_string(cur, t);
|
||||
ff_lastchar = f[-1];
|
||||
if ((len = t - ff_xb.buf) != 0) {
|
||||
/* This will not circle back to perform_io() because we only get
|
||||
@@ -463,7 +481,7 @@ void reduce_iobuf_size(xbuf *out, size_t new_size)
|
||||
{
|
||||
if (new_size < out->size) {
|
||||
/* Avoid weird buffer interactions by only outputting this to stderr. */
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 4)) {
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
|
||||
const char *name = out == &iobuf.out ? "iobuf.out"
|
||||
: out == &iobuf.msg ? "iobuf.msg"
|
||||
: NULL;
|
||||
@@ -481,7 +499,7 @@ void restore_iobuf_size(xbuf *out)
|
||||
if (IOBUF_WAS_REDUCED(out->size)) {
|
||||
size_t new_size = IOBUF_RESTORE_SIZE(out->size);
|
||||
/* Avoid weird buffer interactions by only outputting this to stderr. */
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 4)) {
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
|
||||
const char *name = out == &iobuf.out ? "iobuf.out"
|
||||
: out == &iobuf.msg ? "iobuf.msg"
|
||||
: NULL;
|
||||
@@ -560,52 +578,59 @@ static char *perform_io(size_t needed, int flags)
|
||||
case PIO_NEED_INPUT:
|
||||
/* We never resize the circular input buffer. */
|
||||
if (iobuf.in.size < needed) {
|
||||
rprintf(FERROR, "need to read %ld bytes, iobuf.in.buf is only %ld bytes.\n",
|
||||
(long)needed, (long)iobuf.in.size);
|
||||
rprintf(FERROR, "need to read %" SIZE_T_FMT_MOD "d bytes,"
|
||||
" iobuf.in.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
|
||||
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.in.size);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%ld, %sinput)\n",
|
||||
who_am_i(), (long)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %sinput)\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
|
||||
}
|
||||
break;
|
||||
|
||||
case PIO_NEED_OUTROOM:
|
||||
/* We never resize the circular output buffer. */
|
||||
if (iobuf.out.size - iobuf.out_empty_len < needed) {
|
||||
fprintf(stderr, "need to write %ld bytes, iobuf.out.buf is only %ld bytes.\n",
|
||||
(long)needed, (long)(iobuf.out.size - iobuf.out_empty_len));
|
||||
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
|
||||
" iobuf.out.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
|
||||
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)(iobuf.out.size - iobuf.out_empty_len));
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%ld, outroom) needs to flush %ld\n",
|
||||
who_am_i(), (long)needed,
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
|
||||
" outroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)needed,
|
||||
iobuf.out.len + needed > iobuf.out.size
|
||||
? (long)(iobuf.out.len + needed - iobuf.out.size) : 0L);
|
||||
? (SIZE_T_FMT_CAST)(iobuf.out.len + needed - iobuf.out.size) : (SIZE_T_FMT_CAST)0);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIO_NEED_MSGROOM:
|
||||
/* We never resize the circular message buffer. */
|
||||
if (iobuf.msg.size < needed) {
|
||||
fprintf(stderr, "need to write %ld bytes, iobuf.msg.buf is only %ld bytes.\n",
|
||||
(long)needed, (long)iobuf.msg.size);
|
||||
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
|
||||
" iobuf.msg.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
|
||||
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.msg.size);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%ld, msgroom) needs to flush %ld\n",
|
||||
who_am_i(), (long)needed,
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
|
||||
" msgroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)needed,
|
||||
iobuf.msg.len + needed > iobuf.msg.size
|
||||
? (long)(iobuf.msg.len + needed - iobuf.msg.size) : 0L);
|
||||
? (SIZE_T_FMT_CAST)(iobuf.msg.len + needed - iobuf.msg.size) : (SIZE_T_FMT_CAST)0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 3))
|
||||
rprintf(FINFO, "[%s] perform_io(%ld, %d)\n", who_am_i(), (long)needed, flags);
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
|
||||
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %d)\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)needed, flags);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -662,9 +687,9 @@ static char *perform_io(size_t needed, int flags)
|
||||
SIVAL(iobuf.out.buf + iobuf.raw_data_header_pos, 0,
|
||||
((MPLEX_BASE + (int)MSG_DATA)<<24) + iobuf.out.len - 4);
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 1)) {
|
||||
rprintf(FINFO, "[%s] send_msg(%d, %ld)\n",
|
||||
who_am_i(), (int)MSG_DATA, (long)iobuf.out.len - 4);
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
|
||||
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
|
||||
who_am_i(), (int)MSG_DATA, (SIZE_T_FMT_CAST)iobuf.out.len - 4);
|
||||
}
|
||||
|
||||
/* reserve room for the next MSG_DATA header */
|
||||
@@ -755,7 +780,7 @@ static char *perform_io(size_t needed, int flags)
|
||||
|
||||
if (iobuf.in_fd >= 0 && FD_ISSET(iobuf.in_fd, &r_fds)) {
|
||||
size_t len, pos = iobuf.in.pos + iobuf.in.len;
|
||||
int n;
|
||||
ssize_t n;
|
||||
if (pos >= iobuf.in.size) {
|
||||
pos -= iobuf.in.size;
|
||||
len = iobuf.in.size - iobuf.in.len;
|
||||
@@ -782,12 +807,14 @@ static char *perform_io(size_t needed, int flags)
|
||||
exit_cleanup(RERR_SOCKETIO);
|
||||
}
|
||||
}
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] recv=%ld\n", who_am_i(), (long)n);
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] recv=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)n);
|
||||
}
|
||||
|
||||
if (io_timeout) {
|
||||
last_io_in = time(NULL);
|
||||
if (flags & PIO_NEED_INPUT)
|
||||
if (io_timeout && flags & PIO_NEED_INPUT)
|
||||
maybe_send_keepalive(last_io_in, 0);
|
||||
}
|
||||
stats.total_read += n;
|
||||
@@ -795,9 +822,14 @@ static char *perform_io(size_t needed, int flags)
|
||||
iobuf.in.len += n;
|
||||
}
|
||||
|
||||
if (stop_at_utime && time(NULL) >= stop_at_utime) {
|
||||
rprintf(FERROR, "stopping at requested limit\n");
|
||||
exit_cleanup(RERR_TIMEOUT);
|
||||
}
|
||||
|
||||
if (out && FD_ISSET(iobuf.out_fd, &w_fds)) {
|
||||
size_t len = iobuf.raw_flushing_ends_before ? iobuf.raw_flushing_ends_before - out->pos : out->len;
|
||||
int n;
|
||||
ssize_t n;
|
||||
|
||||
if (bwlimit_writemax && len > bwlimit_writemax)
|
||||
len = bwlimit_writemax;
|
||||
@@ -817,9 +849,9 @@ static char *perform_io(size_t needed, int flags)
|
||||
exit_cleanup(RERR_SOCKETIO);
|
||||
}
|
||||
}
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] %s sent=%ld\n",
|
||||
who_am_i(), out == &iobuf.out ? "out" : "msg", (long)n);
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] %s sent=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), out == &iobuf.out ? "out" : "msg", (SIZE_T_FMT_CAST)n);
|
||||
}
|
||||
|
||||
if (io_timeout)
|
||||
@@ -912,7 +944,11 @@ void noop_io_until_death(void)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof || msgs2stderr)
|
||||
if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof)
|
||||
return;
|
||||
|
||||
/* If we're talking to a daemon over a socket, don't short-circuit this logic */
|
||||
if (msgs2stderr && daemon_connection >= 0)
|
||||
return;
|
||||
|
||||
kluge_around_eof = 2;
|
||||
@@ -930,13 +966,15 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
|
||||
{
|
||||
char *hdr;
|
||||
size_t needed, pos;
|
||||
BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr || code != MSG_INFO);
|
||||
BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr == 1 || code != MSG_INFO);
|
||||
|
||||
if (!OUT_MULTIPLEXED)
|
||||
return 0;
|
||||
|
||||
if (want_debug)
|
||||
rprintf(FINFO, "[%s] send_msg(%d, %ld)\n", who_am_i(), (int)code, (long)len);
|
||||
if (want_debug) {
|
||||
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
|
||||
who_am_i(), (int)code, (SIZE_T_FMT_CAST)len);
|
||||
}
|
||||
|
||||
/* When checking for enough free space for this message, we need to
|
||||
* make sure that there is space for the 4-byte header, plus we'll
|
||||
@@ -952,9 +990,9 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
|
||||
#endif
|
||||
needed = len + 4 + 3;
|
||||
if (iobuf.msg.len + needed > iobuf.msg.size) {
|
||||
if (!am_receiver)
|
||||
if (am_sender)
|
||||
perform_io(needed, PIO_NEED_MSGROOM);
|
||||
else { /* We allow the receiver to increase their iobuf.msg size to avoid a deadlock. */
|
||||
else { /* We sometimes allow the iobuf.msg size to increase to avoid a deadlock. */
|
||||
size_t old_size = iobuf.msg.size;
|
||||
restore_iobuf_size(&iobuf.msg);
|
||||
realloc_xbuf(&iobuf.msg, iobuf.msg.size * 2);
|
||||
@@ -1011,8 +1049,10 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
|
||||
|
||||
SIVAL(hdr, 0, ((MPLEX_BASE + (int)code)<<24) + len);
|
||||
|
||||
if (want_debug && convert > 0)
|
||||
rprintf(FINFO, "[%s] converted msg len=%ld\n", who_am_i(), (long)len);
|
||||
if (want_debug && convert > 0) {
|
||||
rprintf(FINFO, "[%s] converted msg len=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1028,10 +1068,31 @@ void send_msg_int(enum msgcode code, int num)
|
||||
send_msg(code, numbuf, 4, -1);
|
||||
}
|
||||
|
||||
void send_msg_success(const char *fname, int num)
|
||||
{
|
||||
if (local_server) {
|
||||
STRUCT_STAT st;
|
||||
|
||||
if (DEBUG_GTE(IO, 1))
|
||||
rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
|
||||
|
||||
if (stat(fname, &st) < 0)
|
||||
memset(&st, 0, sizeof (STRUCT_STAT));
|
||||
SIVAL(num_dev_ino_buf, 0, num);
|
||||
SIVAL64(num_dev_ino_buf, 4, st.st_dev);
|
||||
SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
|
||||
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
|
||||
} else
|
||||
send_msg_int(MSG_SUCCESS, num);
|
||||
}
|
||||
|
||||
static void got_flist_entry_status(enum festatus status, int ndx)
|
||||
{
|
||||
struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
|
||||
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
if (remove_source_files) {
|
||||
active_filecnt--;
|
||||
active_bytecnt -= F_LENGTH(flist->files[ndx - flist->ndx_start]);
|
||||
@@ -1042,8 +1103,12 @@ static void got_flist_entry_status(enum festatus status, int ndx)
|
||||
|
||||
switch (status) {
|
||||
case FES_SUCCESS:
|
||||
if (remove_source_files)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
if (remove_source_files) {
|
||||
if (local_server)
|
||||
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
|
||||
else
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case FES_NO_SEND:
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1096,8 +1161,8 @@ void set_io_timeout(int secs)
|
||||
|
||||
static void check_for_d_option_error(const char *msg)
|
||||
{
|
||||
static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
char *colon;
|
||||
static const char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
const char *colon;
|
||||
int saw_d = 0;
|
||||
|
||||
if (*msg != 'r'
|
||||
@@ -1285,7 +1350,7 @@ void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
|
||||
|
||||
BOOL io_start_buffering_out(int f_out)
|
||||
{
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_start_buffering_out(%d)\n", who_am_i(), f_out);
|
||||
|
||||
if (iobuf.out.buf) {
|
||||
@@ -1304,7 +1369,7 @@ BOOL io_start_buffering_out(int f_out)
|
||||
|
||||
BOOL io_start_buffering_in(int f_in)
|
||||
{
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_start_buffering_in(%d)\n", who_am_i(), f_in);
|
||||
|
||||
if (iobuf.in.buf) {
|
||||
@@ -1323,7 +1388,7 @@ BOOL io_start_buffering_in(int f_in)
|
||||
|
||||
void io_end_buffering_in(BOOL free_buffers)
|
||||
{
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] io_end_buffering_in(IOBUF_%s_BUFS)\n",
|
||||
who_am_i(), free_buffers ? "FREE" : "KEEP");
|
||||
}
|
||||
@@ -1338,7 +1403,7 @@ void io_end_buffering_in(BOOL free_buffers)
|
||||
|
||||
void io_end_buffering_out(BOOL free_buffers)
|
||||
{
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
|
||||
rprintf(FINFO, "[%s] io_end_buffering_out(IOBUF_%s_BUFS)\n",
|
||||
who_am_i(), free_buffers ? "FREE" : "KEEP");
|
||||
}
|
||||
@@ -1426,8 +1491,10 @@ static void read_a_msg(void)
|
||||
msg_bytes = tag & 0xFFFFFF;
|
||||
tag = (tag >> 24) - MPLEX_BASE;
|
||||
|
||||
if (DEBUG_GTE(IO, 1) && msgs2stderr)
|
||||
rprintf(FINFO, "[%s] got msg=%d, len=%ld\n", who_am_i(), (int)tag, (long)msg_bytes);
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
|
||||
rprintf(FINFO, "[%s] got msg=%d, len=%" SIZE_T_FMT_MOD "d\n",
|
||||
who_am_i(), (int)tag, (SIZE_T_FMT_CAST)msg_bytes);
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case MSG_DATA:
|
||||
@@ -1536,14 +1603,15 @@ static void read_a_msg(void)
|
||||
}
|
||||
break;
|
||||
case MSG_SUCCESS:
|
||||
if (msg_bytes != 4) {
|
||||
if (msg_bytes != (local_server ? 4+8+8 : 4)) {
|
||||
invalid_msg:
|
||||
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
|
||||
tag, (unsigned long)msg_bytes, who_am_i(),
|
||||
inc_recurse ? "/inc" : "");
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
val = raw_read_int();
|
||||
raw_read_buf(num_dev_ino_buf, msg_bytes);
|
||||
val = IVAL(num_dev_ino_buf, 0);
|
||||
iobuf.in_multiplexed = 1;
|
||||
if (am_generator)
|
||||
got_flist_entry_status(FES_SUCCESS, val);
|
||||
@@ -1603,8 +1671,10 @@ static void read_a_msg(void)
|
||||
else
|
||||
goto invalid_msg;
|
||||
iobuf.in_multiplexed = 1;
|
||||
if (DEBUG_GTE(EXIT, 3))
|
||||
rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %ld bytes\n", who_am_i(), (long)msg_bytes);
|
||||
if (DEBUG_GTE(EXIT, 3)) {
|
||||
rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %" SIZE_T_FMT_MOD "d bytes\n",
|
||||
who_am_i(), (SIZE_T_FMT_CAST)msg_bytes);
|
||||
}
|
||||
if (msg_bytes == 0) {
|
||||
if (!am_sender && !am_generator) {
|
||||
if (DEBUG_GTE(EXIT, 3)) {
|
||||
@@ -1718,6 +1788,13 @@ int32 read_int(int f)
|
||||
return num;
|
||||
}
|
||||
|
||||
uint32 read_uint(int f)
|
||||
{
|
||||
char b[4];
|
||||
read_buf(f, b, 4);
|
||||
return IVAL(b, 0);
|
||||
}
|
||||
|
||||
int32 read_varint(int f)
|
||||
{
|
||||
union {
|
||||
@@ -1791,6 +1868,45 @@ int64 read_varlong(int f, uchar min_bytes)
|
||||
return u.x;
|
||||
}
|
||||
|
||||
/* Read an int32 and verify lo <= v <= hi. On out-of-range, abort with a
|
||||
* protocol error naming "what". The bound is co-located with the read so it
|
||||
* cannot be forgotten by a downstream user. */
|
||||
int32 read_int_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_int(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* As read_int_bounded but for varint-encoded values. */
|
||||
int32 read_varint_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a varint that will be used as a size_t. Rejects negative values
|
||||
* (which would wrap to ~SIZE_MAX) and values exceeding the supplied max. */
|
||||
size_t read_varint_size(int f, size_t max, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < 0 || (size_t)v > max) {
|
||||
rprintf(FERROR, "wire size %s out of range: %ld > %lu [%s]\n",
|
||||
what, (long)v, (unsigned long)max, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return (size_t)v;
|
||||
}
|
||||
|
||||
int64 read_longint(int f)
|
||||
{
|
||||
#if SIZEOF_INT64 >= 8
|
||||
@@ -1810,6 +1926,7 @@ int64 read_longint(int f)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Debugging note: this will be named read_buf_() when using an external zlib. */
|
||||
void read_buf(int f, char *buf, size_t len)
|
||||
{
|
||||
if (f != iobuf.in_fd) {
|
||||
@@ -1896,6 +2013,21 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
/* Guard against integer overflow in downstream allocations sized by
|
||||
* count*element_size. my_alloc uses divide-not-multiply so it is
|
||||
* already wraparound-safe, but checking here gives a clearer error
|
||||
* and also covers the (size_t)count * xfer_sum_len arithmetic that
|
||||
* is performed *before* reaching my_alloc. */
|
||||
if (xfer_sum_len > 0 && (size_t)sum->count > SIZE_MAX / (size_t)xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (too large) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if ((size_t)sum->count > SIZE_MAX / sizeof(struct sum_buf)) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (sum_buf overflow) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->blength = read_int(f);
|
||||
if (sum->blength < 0 || sum->blength > max_blength) {
|
||||
rprintf(FERROR, "Invalid block length %ld [%s]\n",
|
||||
@@ -1903,7 +2035,7 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
|
||||
if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
|
||||
if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
|
||||
sum->s2length, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
@@ -2298,7 +2430,7 @@ void io_start_multiplex_out(int fd)
|
||||
{
|
||||
io_flush(FULL_FLUSH);
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_start_multiplex_out(%d)\n", who_am_i(), fd);
|
||||
|
||||
if (!iobuf.msg.buf)
|
||||
@@ -2315,7 +2447,7 @@ void io_start_multiplex_out(int fd)
|
||||
/* Setup for multiplexing a MSG_* stream with the data stream. */
|
||||
void io_start_multiplex_in(int fd)
|
||||
{
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_start_multiplex_in(%d)\n", who_am_i(), fd);
|
||||
|
||||
iobuf.in_multiplexed = 1; /* See also IN_MULTIPLEXED */
|
||||
@@ -2326,7 +2458,7 @@ int io_end_multiplex_in(int mode)
|
||||
{
|
||||
int ret = iobuf.in_multiplexed ? iobuf.in_fd : -1;
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_end_multiplex_in(mode=%d)\n", who_am_i(), mode);
|
||||
|
||||
iobuf.in_multiplexed = 0;
|
||||
@@ -2344,7 +2476,7 @@ int io_end_multiplex_out(int mode)
|
||||
{
|
||||
int ret = iobuf.out_empty_len ? iobuf.out_fd : -1;
|
||||
|
||||
if (msgs2stderr && DEBUG_GTE(IO, 2))
|
||||
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
|
||||
rprintf(FINFO, "[%s] io_end_multiplex_out(mode=%d)\n", who_am_i(), mode);
|
||||
|
||||
if (mode != MPLX_TO_BUFFERED)
|
||||
|
||||
8
itypes.h
8
itypes.h
@@ -1,6 +1,6 @@
|
||||
/* Inline functions for rsync.
|
||||
*
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -40,6 +40,12 @@ isSpace(const char *ptr)
|
||||
return isspace(*(unsigned char *)ptr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
isAlNum(const char *ptr)
|
||||
{
|
||||
return isalnum(*(unsigned char *)ptr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
isLower(const char *ptr)
|
||||
{
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define LATEST_YEAR "2020"
|
||||
#define LATEST_YEAR "2026"
|
||||
|
||||
85
lib/compat.c
85
lib/compat.c
@@ -24,16 +24,24 @@
|
||||
|
||||
static char number_separator;
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *strdup(char *s)
|
||||
char get_number_separator(void)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
char *ret = (char *)malloc(len);
|
||||
if (ret)
|
||||
memcpy(ret, s, len);
|
||||
return ret;
|
||||
if (!number_separator) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%f", 3.14);
|
||||
if (strchr(buf, '.') != NULL)
|
||||
number_separator = ',';
|
||||
else
|
||||
number_separator = '.';
|
||||
}
|
||||
|
||||
return number_separator;
|
||||
}
|
||||
|
||||
char get_decimal_point(void)
|
||||
{
|
||||
return get_number_separator() == ',' ? '.' : ',';
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETCWD
|
||||
char *getcwd(char *buf, int size)
|
||||
@@ -155,30 +163,6 @@ int sys_gettimeofday(struct timeval *tv)
|
||||
#endif
|
||||
}
|
||||
|
||||
#define HUMANIFY(mult) \
|
||||
do { \
|
||||
if (num >= mult || num <= -mult) { \
|
||||
double dnum = (double)num / mult; \
|
||||
char units; \
|
||||
if (num < 0) \
|
||||
dnum = -dnum; \
|
||||
if (dnum < mult) \
|
||||
units = 'K'; \
|
||||
else if ((dnum /= mult) < mult) \
|
||||
units = 'M'; \
|
||||
else if ((dnum /= mult) < mult) \
|
||||
units = 'G'; \
|
||||
else { \
|
||||
dnum /= mult; \
|
||||
units = 'T'; \
|
||||
} \
|
||||
if (num < 0) \
|
||||
dnum = -dnum; \
|
||||
snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \
|
||||
return bufs[n]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Return the int64 number as a string. If the human_flag arg is non-zero,
|
||||
* we may output the number in K, M, G, or T units. If we don't add a unit
|
||||
* suffix, we will append the fract string, if it is non-NULL. We can
|
||||
@@ -190,22 +174,35 @@ char *do_big_num(int64 num, int human_flag, const char *fract)
|
||||
char *s;
|
||||
int len, negated;
|
||||
|
||||
if (human_flag && !number_separator) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%f", 3.14);
|
||||
if (strchr(buf, '.') != NULL)
|
||||
number_separator = ',';
|
||||
else
|
||||
number_separator = '.';
|
||||
}
|
||||
if (human_flag && !number_separator)
|
||||
(void)get_number_separator();
|
||||
|
||||
n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
|
||||
|
||||
if (human_flag > 1) {
|
||||
if (human_flag == 2)
|
||||
HUMANIFY(1000);
|
||||
else
|
||||
HUMANIFY(1024);
|
||||
int mult = human_flag == 2 ? 1000 : 1024;
|
||||
if (num >= mult || num <= -mult) {
|
||||
double dnum = (double)num / mult;
|
||||
char units;
|
||||
if (num < 0)
|
||||
dnum = -dnum;
|
||||
if (dnum < mult)
|
||||
units = 'K';
|
||||
else if ((dnum /= mult) < mult)
|
||||
units = 'M';
|
||||
else if ((dnum /= mult) < mult)
|
||||
units = 'G';
|
||||
else if ((dnum /= mult) < mult)
|
||||
units = 'T';
|
||||
else {
|
||||
dnum /= mult;
|
||||
units = 'P';
|
||||
}
|
||||
if (num < 0)
|
||||
dnum = -dnum;
|
||||
snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
|
||||
return bufs[n];
|
||||
}
|
||||
}
|
||||
|
||||
s = bufs[n] + sizeof bufs[0] - 1;
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
/* Keep this simple so both C and ASM can use it */
|
||||
|
||||
/* These allow something like CFLAGS=-DDISABLE_SHA512_DIGEST */
|
||||
#ifdef DISABLE_SHA256_DIGEST
|
||||
#undef SHA256_DIGEST_LENGTH
|
||||
#endif
|
||||
#ifdef DISABLE_SHA512_DIGEST
|
||||
#undef SHA512_DIGEST_LENGTH
|
||||
#endif
|
||||
|
||||
#define MD4_DIGEST_LEN 16
|
||||
#define MD5_DIGEST_LEN 16
|
||||
#if defined SHA512_DIGEST_LENGTH
|
||||
#define MAX_DIGEST_LEN SHA512_DIGEST_LENGTH
|
||||
#elif defined SHA256_DIGEST_LENGTH
|
||||
#define MAX_DIGEST_LEN SHA256_DIGEST_LENGTH
|
||||
#elif defined SHA_DIGEST_LENGTH
|
||||
#define MAX_DIGEST_LEN SHA_DIGEST_LENGTH
|
||||
#else
|
||||
#define MAX_DIGEST_LEN MD5_DIGEST_LEN
|
||||
#endif
|
||||
|
||||
#define CSUM_CHUNK 64
|
||||
|
||||
#define CSUM_gone -1
|
||||
#define CSUM_NONE 0
|
||||
#define CSUM_MD4_ARCHAIC 1
|
||||
#define CSUM_MD4_BUSTED 2
|
||||
@@ -15,3 +32,6 @@
|
||||
#define CSUM_XXH64 6
|
||||
#define CSUM_XXH3_64 7
|
||||
#define CSUM_XXH3_128 8
|
||||
#define CSUM_SHA1 9
|
||||
#define CSUM_SHA256 10
|
||||
#define CSUM_SHA512 11
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "config.h"
|
||||
#include "md-defines.h"
|
||||
|
||||
#if !defined USE_OPENSSL && CSUM_CHUNK == 64
|
||||
#ifdef USE_MD5_ASM /* { */
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define md5_process_asm _md5_process_asm
|
||||
@@ -698,4 +698,4 @@ md5_process_asm:
|
||||
pop %rbp
|
||||
ret
|
||||
|
||||
#endif /* !USE_OPENSSL ... */
|
||||
#endif /* } USE_MD5_ASM */
|
||||
|
||||
21
lib/md5.c
21
lib/md5.c
@@ -2,7 +2,7 @@
|
||||
* RFC 1321 compliant MD5 implementation
|
||||
*
|
||||
* Copyright (C) 2001-2003 Christophe Devine
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#ifndef USE_OPENSSL
|
||||
void md5_begin(md_context *ctx)
|
||||
{
|
||||
ctx->A = 0x67452301;
|
||||
@@ -148,7 +147,10 @@ static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
|
||||
ctx->D += D;
|
||||
}
|
||||
|
||||
#if defined HAVE_ASM && CSUM_CHUNK == 64
|
||||
#ifdef USE_MD5_ASM
|
||||
#if CSUM_CHUNK != 64
|
||||
#error The MD5 ASM code does not support CSUM_CHUNK != 64
|
||||
#endif
|
||||
extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
|
||||
#endif
|
||||
|
||||
@@ -176,26 +178,26 @@ void md5_update(md_context *ctx, const uchar *input, uint32 length)
|
||||
left = 0;
|
||||
}
|
||||
|
||||
#if defined HAVE_ASM && CSUM_CHUNK == 64
|
||||
#ifdef USE_MD5_ASM /* { */
|
||||
if (length >= CSUM_CHUNK) {
|
||||
uint32 chunks = length / CSUM_CHUNK;
|
||||
md5_process_asm(ctx, input, chunks);
|
||||
length -= chunks * CSUM_CHUNK;
|
||||
input += chunks * CSUM_CHUNK;
|
||||
}
|
||||
#else
|
||||
#else /* } { */
|
||||
while (length >= CSUM_CHUNK) {
|
||||
md5_process(ctx, input);
|
||||
length -= CSUM_CHUNK;
|
||||
input += CSUM_CHUNK;
|
||||
}
|
||||
#endif
|
||||
#endif /* } */
|
||||
|
||||
if (length)
|
||||
memcpy(ctx->buffer + left, input, length);
|
||||
}
|
||||
|
||||
static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
|
||||
static const uchar md5_padding[CSUM_CHUNK] = { 0x80 };
|
||||
|
||||
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
|
||||
{
|
||||
@@ -221,9 +223,8 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
|
||||
SIVALu(digest, 8, ctx->C);
|
||||
SIVALu(digest, 12, ctx->D);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_MD5
|
||||
#ifdef TEST_MD5 /* { */
|
||||
|
||||
void get_md5(uchar *out, const uchar *input, int n)
|
||||
{
|
||||
@@ -317,4 +318,4 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* } */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* The include file for both the MD4 and MD5 routines. */
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include "openssl/md4.h"
|
||||
#include "openssl/md5.h"
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
#include "md-defines.h"
|
||||
|
||||
@@ -17,13 +17,6 @@ void mdfour_begin(md_context *md);
|
||||
void mdfour_update(md_context *md, const uchar *in, uint32 length);
|
||||
void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
|
||||
|
||||
#ifndef USE_OPENSSL
|
||||
#define MD5_CTX md_context
|
||||
#define MD5_Init md5_begin
|
||||
#define MD5_Update md5_update
|
||||
#define MD5_Final(digest, cptr) md5_result(cptr, digest)
|
||||
|
||||
void md5_begin(md_context *ctx);
|
||||
void md5_update(md_context *ctx, const uchar *input, uint32 length);
|
||||
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
|
||||
#endif
|
||||
|
||||
@@ -33,7 +33,7 @@ pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool
|
||||
.SH SYNOPSIS
|
||||
.B #include "pool_alloc.h"
|
||||
|
||||
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB);
|
||||
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char*,char*,int), int \fIflags\fB);
|
||||
|
||||
\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ struct alloc_pool
|
||||
size_t size; /* extent size */
|
||||
size_t quantum; /* allocation quantum */
|
||||
struct pool_extent *extents; /* top extent is "live" */
|
||||
void (*bomb)(); /* function to call if
|
||||
* malloc fails */
|
||||
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
|
||||
int flags;
|
||||
|
||||
/* statistical data */
|
||||
@@ -43,15 +42,16 @@ struct align_test {
|
||||
/* Temporarily cast a void* var into a char* var when adding an offset (to
|
||||
* keep some compilers from complaining about the pointer arithmetic). */
|
||||
#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
|
||||
#define PTR_SUB(b,o) ( (void*) ((char*)(b) - (o)) )
|
||||
|
||||
alloc_pool_t
|
||||
pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags)
|
||||
pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
|
||||
{
|
||||
struct alloc_pool *pool;
|
||||
|
||||
if ((MINALIGN & (MINALIGN - 1)) != 0) {
|
||||
if ((MINALIGN & (MINALIGN - 1)) != (0)) {
|
||||
if (bomb)
|
||||
(*bomb)("Compiler error: MINALIGN is not a power of 2\n");
|
||||
(*bomb)("Compiler error: MINALIGN is not a power of 2", __FILE__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ pool_destroy(alloc_pool_t p)
|
||||
for (cur = pool->extents; cur; cur = next) {
|
||||
next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
@@ -169,7 +169,7 @@ pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
|
||||
|
||||
bomb_out:
|
||||
if (pool->bomb)
|
||||
(*pool->bomb)(bomb_msg);
|
||||
(*pool->bomb)(bomb_msg, __FILE__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
|
||||
if (cur->free + cur->bound >= pool->size) {
|
||||
prev->next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
@@ -293,7 +293,7 @@ pool_free_old(alloc_pool_t p, void *addr)
|
||||
while ((cur = next) != NULL) {
|
||||
next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
typedef void *alloc_pool_t;
|
||||
|
||||
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags);
|
||||
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags);
|
||||
void pool_destroy(alloc_pool_t pool);
|
||||
void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
|
||||
void pool_free(alloc_pool_t pool, size_t size, void *addr);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* for string length. This covers a nasty loophole.
|
||||
*
|
||||
* The other functions are there to prevent NULL pointers from
|
||||
* causing nast effects.
|
||||
* causing nasty effects.
|
||||
*
|
||||
* More Recently:
|
||||
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
|
||||
|
||||
792
lib/sysacls.c
792
lib/sysacls.c
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
* Version 2.2.x
|
||||
* Portable SMB ACL interface
|
||||
* Copyright (C) Jeremy Allison 2000
|
||||
* Copyright (C) 2007-2019 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -139,7 +139,9 @@ typedef struct acl *SMB_ACL_ENTRY_T;
|
||||
|
||||
/* Based on the Solaris & UnixWare code. */
|
||||
|
||||
#ifndef __TANDEM
|
||||
#undef GROUP
|
||||
#endif
|
||||
#include <sys/aclv.h>
|
||||
|
||||
/* SVR4.2 ES/MP ACLs */
|
||||
@@ -230,7 +232,7 @@ struct new_acl_entry{
|
||||
|
||||
#define SMB_ACL_ENTRY_T struct new_acl_entry*
|
||||
#define SMB_ACL_T struct acl_entry_link*
|
||||
|
||||
|
||||
#define SMB_ACL_TAG_T unsigned short
|
||||
#define SMB_ACL_TYPE_T int
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Extended attribute support for rsync.
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc.
|
||||
* Copyright (C) 2003-2019 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
* Written by Jay Fenlason.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -67,7 +67,7 @@ ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t si
|
||||
u_int32_t offset = len;
|
||||
size_t data_retrieved = len;
|
||||
while (data_retrieved < size) {
|
||||
len = getxattr(path, name, value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
|
||||
len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
|
||||
if (len <= 0)
|
||||
break;
|
||||
data_retrieved += len;
|
||||
@@ -126,9 +126,18 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
|
||||
unsigned char keylen;
|
||||
ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
|
||||
|
||||
if (len <= 0 || (size_t)len > size)
|
||||
if (len <= 0 || size == 0)
|
||||
return len;
|
||||
|
||||
if ((size_t)len >= size) {
|
||||
/* FreeBSD extattr_list_xx() returns 'size' as 'len' in case there are
|
||||
more data available, truncating the output, we solve this by signalling
|
||||
ERANGE in case len == size so that the code in xattrs.c will retry with
|
||||
a bigger buffer */
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FreeBSD puts a single-byte length before each string, with no '\0'
|
||||
* terminator. We need to change this into a series of null-terminted
|
||||
* strings. Since the size is the same, we can simply transform the
|
||||
@@ -136,7 +145,7 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
|
||||
for (off = 0; off < len; off += keylen + 1) {
|
||||
keylen = ((unsigned char*)list)[off];
|
||||
if (off + keylen >= len) {
|
||||
/* Should be impossible, but kernel bugs happen! */
|
||||
/* Should be impossible, but bugs happen! */
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
@@ -167,7 +176,7 @@ static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
|
||||
} else {
|
||||
size_t bufpos;
|
||||
for (bufpos = 0; bufpos < sb.st_size; ) {
|
||||
ssize_t cnt = read(attrfd, buf + bufpos, sb.st_size - bufpos);
|
||||
ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
|
||||
if (cnt <= 0) {
|
||||
if (cnt < 0 && errno == EINTR)
|
||||
continue;
|
||||
@@ -218,7 +227,7 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
|
||||
return -1;
|
||||
|
||||
for (bufpos = 0; bufpos < size; ) {
|
||||
ssize_t cnt = write(attrfd, value+bufpos, size);
|
||||
ssize_t cnt = write(attrfd, (char*)value + bufpos, size);
|
||||
if (cnt <= 0) {
|
||||
if (cnt < 0 && errno == EINTR)
|
||||
continue;
|
||||
@@ -274,7 +283,8 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
|
||||
&& (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
|
||||
continue;
|
||||
|
||||
if ((ret += len+1) > size) {
|
||||
ret += len + 1;
|
||||
if ((size_t)ret > size) {
|
||||
if (size == 0)
|
||||
continue;
|
||||
ret = -1;
|
||||
|
||||
450
loadparm.c
450
loadparm.c
@@ -48,7 +48,6 @@
|
||||
extern item_list dparam_list;
|
||||
|
||||
#define strequal(a, b) (strcasecmp(a, b)==0)
|
||||
#define BOOLSTR(b) ((b) ? "Yes" : "No")
|
||||
|
||||
#ifndef LOG_DAEMON
|
||||
#define LOG_DAEMON 0
|
||||
@@ -56,7 +55,7 @@ extern item_list dparam_list;
|
||||
|
||||
/* the following are used by loadparm for option lists */
|
||||
typedef enum {
|
||||
P_BOOL, P_BOOLREV, P_CHAR, P_INTEGER,
|
||||
P_BOOL, P_BOOLREV, P_BOOL3, P_CHAR, P_INTEGER,
|
||||
P_OCTAL, P_PATH, P_STRING, P_ENUM
|
||||
} parm_type;
|
||||
|
||||
@@ -66,7 +65,7 @@ typedef enum {
|
||||
|
||||
struct enum_list {
|
||||
int value;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct parm_struct {
|
||||
@@ -74,7 +73,7 @@ struct parm_struct {
|
||||
parm_type type;
|
||||
parm_class class;
|
||||
void *ptr;
|
||||
struct enum_list *enum_list;
|
||||
const struct enum_list *enum_list;
|
||||
unsigned flags;
|
||||
};
|
||||
|
||||
@@ -87,234 +86,6 @@ struct parm_struct {
|
||||
#define LP_SNUM_OK(i) ((i) >= 0 && (i) < (int)section_list.count)
|
||||
#define SECTION_PTR(s, p) (((char*)(s)) + (ptrdiff_t)(((char*)(p))-(char*)&Vars.l))
|
||||
|
||||
/* This structure describes global (ie., server-wide) parameters. */
|
||||
typedef struct {
|
||||
char *bind_address;
|
||||
char *daemon_chroot;
|
||||
char *daemon_gid;
|
||||
char *daemon_uid;
|
||||
char *motd_file;
|
||||
char *pid_file;
|
||||
char *socket_options;
|
||||
|
||||
/* Each _EXP var tracks if the associated char* var has been expanded yet or not. */
|
||||
BOOL bind_address_EXP;
|
||||
BOOL daemon_chroot_EXP;
|
||||
BOOL daemon_gid_EXP;
|
||||
BOOL daemon_uid_EXP;
|
||||
BOOL motd_file_EXP;
|
||||
BOOL pid_file_EXP;
|
||||
BOOL socket_options_EXP;
|
||||
|
||||
int listen_backlog;
|
||||
int rsync_port;
|
||||
|
||||
BOOL proxy_protocol;
|
||||
} global_vars;
|
||||
|
||||
/* This structure describes a single section. Their order must match the
|
||||
* initializers below, which you can accomplish by keeping each sub-section
|
||||
* sorted. (e.g. in vim, just visually select each subsection and use !sort.)
|
||||
* NOTE: the char* variables MUST all remain at the start of the struct! */
|
||||
typedef struct {
|
||||
char *auth_users;
|
||||
char *charset;
|
||||
char *comment;
|
||||
char *dont_compress;
|
||||
char *early_exec;
|
||||
char *exclude;
|
||||
char *exclude_from;
|
||||
char *filter;
|
||||
char *gid;
|
||||
char *hosts_allow;
|
||||
char *hosts_deny;
|
||||
char *include;
|
||||
char *include_from;
|
||||
char *incoming_chmod;
|
||||
char *lock_file;
|
||||
char *log_file;
|
||||
char *log_format;
|
||||
char *name;
|
||||
char *outgoing_chmod;
|
||||
char *path;
|
||||
char *postxfer_exec;
|
||||
char *prexfer_exec;
|
||||
char *refuse_options;
|
||||
char *secrets_file;
|
||||
char *syslog_tag;
|
||||
char *temp_dir;
|
||||
char *uid;
|
||||
|
||||
/* Each _EXP var tracks if the associated char* var has been expanded yet or not. */
|
||||
BOOL auth_users_EXP;
|
||||
BOOL charset_EXP;
|
||||
BOOL comment_EXP;
|
||||
BOOL dont_compress_EXP;
|
||||
BOOL early_exec_EXP;
|
||||
BOOL exclude_EXP;
|
||||
BOOL exclude_from_EXP;
|
||||
BOOL filter_EXP;
|
||||
BOOL gid_EXP;
|
||||
BOOL hosts_allow_EXP;
|
||||
BOOL hosts_deny_EXP;
|
||||
BOOL include_EXP;
|
||||
BOOL include_from_EXP;
|
||||
BOOL incoming_chmod_EXP;
|
||||
BOOL lock_file_EXP;
|
||||
BOOL log_file_EXP;
|
||||
BOOL log_format_EXP;
|
||||
BOOL name_EXP;
|
||||
BOOL outgoing_chmod_EXP;
|
||||
BOOL path_EXP;
|
||||
BOOL postxfer_exec_EXP;
|
||||
BOOL prexfer_exec_EXP;
|
||||
BOOL refuse_options_EXP;
|
||||
BOOL secrets_file_EXP;
|
||||
BOOL syslog_tag_EXP;
|
||||
BOOL temp_dir_EXP;
|
||||
BOOL uid_EXP;
|
||||
|
||||
int max_connections;
|
||||
int max_verbosity;
|
||||
int syslog_facility;
|
||||
int timeout;
|
||||
|
||||
BOOL fake_super;
|
||||
BOOL forward_lookup;
|
||||
BOOL ignore_errors;
|
||||
BOOL ignore_nonreadable;
|
||||
BOOL list;
|
||||
BOOL munge_symlinks;
|
||||
BOOL numeric_ids;
|
||||
BOOL read_only;
|
||||
BOOL reverse_lookup;
|
||||
BOOL strict_modes;
|
||||
BOOL transfer_logging;
|
||||
BOOL use_chroot;
|
||||
BOOL write_only;
|
||||
} local_vars;
|
||||
|
||||
/* This structure describes the global variables (g) as well as the globally
|
||||
* specified values of the local variables (l), which are used when modules
|
||||
* don't specify their own values. */
|
||||
typedef struct {
|
||||
global_vars g;
|
||||
local_vars l;
|
||||
} all_vars;
|
||||
|
||||
/* The application defaults for all the variables. "Defaults" is
|
||||
* used to re-initialize "Vars" before each config-file read.
|
||||
*
|
||||
* In order to keep these sorted in the same way as the structure
|
||||
* above, use the variable name in the leading comment, including a
|
||||
* trailing ';' (to avoid a sorting problem with trailing digits). */
|
||||
static const all_vars Defaults = {
|
||||
/* ==== global_vars ==== */
|
||||
{
|
||||
/* bind_address; */ NULL,
|
||||
/* daemon_chroot; */ NULL,
|
||||
/* daemon_gid; */ NULL,
|
||||
/* daemon_uid; */ NULL,
|
||||
/* motd_file; */ NULL,
|
||||
/* pid_file; */ NULL,
|
||||
/* socket_options; */ NULL,
|
||||
|
||||
/* bind_address_EXP; */ False,
|
||||
/* daemon_chroot_EXP; */ False,
|
||||
/* daemon_gid_EXP; */ False,
|
||||
/* daemon_uid_EXP; */ False,
|
||||
/* motd_file_EXP; */ False,
|
||||
/* pid_file_EXP; */ False,
|
||||
/* socket_options_EXP; */ False,
|
||||
|
||||
/* listen_backlog; */ 5,
|
||||
/* rsync_port; */ 0,
|
||||
|
||||
/* proxy_protocol; */ False,
|
||||
},
|
||||
|
||||
/* ==== local_vars ==== */
|
||||
{
|
||||
/* auth_users; */ NULL,
|
||||
/* charset; */ NULL,
|
||||
/* comment; */ NULL,
|
||||
/* dont_compress; */ DEFAULT_DONT_COMPRESS,
|
||||
/* early_exec; */ NULL,
|
||||
/* exclude; */ NULL,
|
||||
/* exclude_from; */ NULL,
|
||||
/* filter; */ NULL,
|
||||
/* gid; */ NULL,
|
||||
/* hosts_allow; */ NULL,
|
||||
/* hosts_deny; */ NULL,
|
||||
/* include; */ NULL,
|
||||
/* include_from; */ NULL,
|
||||
/* incoming_chmod; */ NULL,
|
||||
/* lock_file; */ DEFAULT_LOCK_FILE,
|
||||
/* log_file; */ NULL,
|
||||
/* log_format; */ "%o %h [%a] %m (%u) %f %l",
|
||||
/* name; */ NULL,
|
||||
/* outgoing_chmod; */ NULL,
|
||||
/* path; */ NULL,
|
||||
/* postxfer_exec; */ NULL,
|
||||
/* prexfer_exec; */ NULL,
|
||||
/* refuse_options; */ NULL,
|
||||
/* secrets_file; */ NULL,
|
||||
/* syslog_tag; */ "rsyncd",
|
||||
/* temp_dir; */ NULL,
|
||||
/* uid; */ NULL,
|
||||
|
||||
/* auth_users_EXP; */ False,
|
||||
/* charset_EXP; */ False,
|
||||
/* comment_EXP; */ False,
|
||||
/* dont_compress_EXP; */ False,
|
||||
/* early_exec_EXP; */ False,
|
||||
/* exclude_EXP; */ False,
|
||||
/* exclude_from_EXP; */ False,
|
||||
/* filter_EXP; */ False,
|
||||
/* gid_EXP; */ False,
|
||||
/* hosts_allow_EXP; */ False,
|
||||
/* hosts_deny_EXP; */ False,
|
||||
/* include_EXP; */ False,
|
||||
/* include_from_EXP; */ False,
|
||||
/* incoming_chmod_EXP; */ False,
|
||||
/* lock_file_EXP; */ False,
|
||||
/* log_file_EXP; */ False,
|
||||
/* log_format_EXP; */ False,
|
||||
/* name_EXP; */ False,
|
||||
/* outgoing_chmod_EXP; */ False,
|
||||
/* path_EXP; */ False,
|
||||
/* postxfer_exec_EXP; */ False,
|
||||
/* prexfer_exec_EXP; */ False,
|
||||
/* refuse_options_EXP; */ False,
|
||||
/* secrets_file_EXP; */ False,
|
||||
/* syslog_tag_EXP; */ False,
|
||||
/* temp_dir_EXP; */ False,
|
||||
/* uid_EXP; */ False,
|
||||
|
||||
/* max_connections; */ 0,
|
||||
/* max_verbosity; */ 1,
|
||||
/* syslog_facility; */ LOG_DAEMON,
|
||||
/* timeout; */ 0,
|
||||
|
||||
/* fake_super; */ False,
|
||||
/* forward_lookup; */ True,
|
||||
/* ignore_errors; */ False,
|
||||
/* ignore_nonreadable; */ False,
|
||||
/* list; */ True,
|
||||
/* munge_symlinks; */ (BOOL)-1,
|
||||
/* numeric_ids; */ (BOOL)-1,
|
||||
/* read_only; */ True,
|
||||
/* reverse_lookup; */ True,
|
||||
/* strict_modes; */ True,
|
||||
/* transfer_logging; */ False,
|
||||
/* use_chroot; */ True,
|
||||
/* write_only; */ False,
|
||||
}
|
||||
};
|
||||
|
||||
/* The currently configured values for all the variables. */
|
||||
static all_vars Vars;
|
||||
|
||||
/* Stack of "Vars" values used by the &include directive. */
|
||||
static item_list Vars_stack = EMPTY_ITEM_LIST;
|
||||
|
||||
@@ -324,9 +95,7 @@ static item_list section_list = EMPTY_ITEM_LIST;
|
||||
static int iSectionIndex = -1;
|
||||
static BOOL bInGlobalSection = True;
|
||||
|
||||
#define NUMPARAMETERS (sizeof (parm_table) / sizeof (struct parm_struct))
|
||||
|
||||
static struct enum_list enum_facilities[] = {
|
||||
static const struct enum_list enum_syslog_facility[] = {
|
||||
#ifdef LOG_AUTH
|
||||
{ LOG_AUTH, "auth" },
|
||||
#endif
|
||||
@@ -393,95 +162,27 @@ static struct enum_list enum_facilities[] = {
|
||||
{ -1, NULL }
|
||||
};
|
||||
|
||||
static struct parm_struct parm_table[] =
|
||||
{
|
||||
{"address", P_STRING, P_GLOBAL,&Vars.g.bind_address, NULL,0},
|
||||
{"daemon chroot", P_STRING, P_GLOBAL,&Vars.g.daemon_chroot, NULL,0},
|
||||
{"daemon gid", P_STRING, P_GLOBAL,&Vars.g.daemon_gid, NULL,0},
|
||||
{"daemon uid", P_STRING, P_GLOBAL,&Vars.g.daemon_uid, NULL,0},
|
||||
{"listen backlog", P_INTEGER,P_GLOBAL,&Vars.g.listen_backlog, NULL,0},
|
||||
{"motd file", P_STRING, P_GLOBAL,&Vars.g.motd_file, NULL,0},
|
||||
{"pid file", P_STRING, P_GLOBAL,&Vars.g.pid_file, NULL,0},
|
||||
{"port", P_INTEGER,P_GLOBAL,&Vars.g.rsync_port, NULL,0},
|
||||
{"proxy protocol", P_BOOL, P_LOCAL, &Vars.g.proxy_protocol, NULL,0},
|
||||
{"socket options", P_STRING, P_GLOBAL,&Vars.g.socket_options, NULL,0},
|
||||
|
||||
{"auth users", P_STRING, P_LOCAL, &Vars.l.auth_users, NULL,0},
|
||||
{"charset", P_STRING, P_LOCAL, &Vars.l.charset, NULL,0},
|
||||
{"comment", P_STRING, P_LOCAL, &Vars.l.comment, NULL,0},
|
||||
{"dont compress", P_STRING, P_LOCAL, &Vars.l.dont_compress, NULL,0},
|
||||
{"early exec", P_STRING, P_LOCAL, &Vars.l.early_exec, NULL,0},
|
||||
{"exclude from", P_STRING, P_LOCAL, &Vars.l.exclude_from, NULL,0},
|
||||
{"exclude", P_STRING, P_LOCAL, &Vars.l.exclude, NULL,0},
|
||||
{"fake super", P_BOOL, P_LOCAL, &Vars.l.fake_super, NULL,0},
|
||||
{"filter", P_STRING, P_LOCAL, &Vars.l.filter, NULL,0},
|
||||
{"forward lookup", P_BOOL, P_LOCAL, &Vars.l.forward_lookup, NULL,0},
|
||||
{"gid", P_STRING, P_LOCAL, &Vars.l.gid, NULL,0},
|
||||
{"hosts allow", P_STRING, P_LOCAL, &Vars.l.hosts_allow, NULL,0},
|
||||
{"hosts deny", P_STRING, P_LOCAL, &Vars.l.hosts_deny, NULL,0},
|
||||
{"ignore errors", P_BOOL, P_LOCAL, &Vars.l.ignore_errors, NULL,0},
|
||||
{"ignore nonreadable",P_BOOL, P_LOCAL, &Vars.l.ignore_nonreadable, NULL,0},
|
||||
{"include from", P_STRING, P_LOCAL, &Vars.l.include_from, NULL,0},
|
||||
{"include", P_STRING, P_LOCAL, &Vars.l.include, NULL,0},
|
||||
{"incoming chmod", P_STRING, P_LOCAL, &Vars.l.incoming_chmod, NULL,0},
|
||||
{"list", P_BOOL, P_LOCAL, &Vars.l.list, NULL,0},
|
||||
{"lock file", P_STRING, P_LOCAL, &Vars.l.lock_file, NULL,0},
|
||||
{"log file", P_STRING, P_LOCAL, &Vars.l.log_file, NULL,0},
|
||||
{"log format", P_STRING, P_LOCAL, &Vars.l.log_format, NULL,0},
|
||||
{"max connections", P_INTEGER,P_LOCAL, &Vars.l.max_connections, NULL,0},
|
||||
{"max verbosity", P_INTEGER,P_LOCAL, &Vars.l.max_verbosity, NULL,0},
|
||||
{"munge symlinks", P_BOOL, P_LOCAL, &Vars.l.munge_symlinks, NULL,0},
|
||||
{"name", P_STRING, P_LOCAL, &Vars.l.name, NULL,0},
|
||||
{"numeric ids", P_BOOL, P_LOCAL, &Vars.l.numeric_ids, NULL,0},
|
||||
{"outgoing chmod", P_STRING, P_LOCAL, &Vars.l.outgoing_chmod, NULL,0},
|
||||
{"path", P_PATH, P_LOCAL, &Vars.l.path, NULL,0},
|
||||
#ifdef HAVE_PUTENV
|
||||
{"post-xfer exec", P_STRING, P_LOCAL, &Vars.l.postxfer_exec, NULL,0},
|
||||
{"pre-xfer exec", P_STRING, P_LOCAL, &Vars.l.prexfer_exec, NULL,0},
|
||||
#endif
|
||||
{"read only", P_BOOL, P_LOCAL, &Vars.l.read_only, NULL,0},
|
||||
{"refuse options", P_STRING, P_LOCAL, &Vars.l.refuse_options, NULL,0},
|
||||
{"reverse lookup", P_BOOL, P_LOCAL, &Vars.l.reverse_lookup, NULL,0},
|
||||
{"secrets file", P_STRING, P_LOCAL, &Vars.l.secrets_file, NULL,0},
|
||||
{"strict modes", P_BOOL, P_LOCAL, &Vars.l.strict_modes, NULL,0},
|
||||
{"syslog facility", P_ENUM, P_LOCAL, &Vars.l.syslog_facility, enum_facilities,0},
|
||||
{"syslog tag", P_STRING, P_LOCAL, &Vars.l.syslog_tag, NULL,0},
|
||||
{"temp dir", P_PATH, P_LOCAL, &Vars.l.temp_dir, NULL,0},
|
||||
{"timeout", P_INTEGER,P_LOCAL, &Vars.l.timeout, NULL,0},
|
||||
{"transfer logging", P_BOOL, P_LOCAL, &Vars.l.transfer_logging, NULL,0},
|
||||
{"uid", P_STRING, P_LOCAL, &Vars.l.uid, NULL,0},
|
||||
{"use chroot", P_BOOL, P_LOCAL, &Vars.l.use_chroot, NULL,0},
|
||||
{"write only", P_BOOL, P_LOCAL, &Vars.l.write_only, NULL,0},
|
||||
{NULL, P_BOOL, P_NONE, NULL, NULL,0}
|
||||
};
|
||||
|
||||
/* Initialise the Default all_vars structure. */
|
||||
void reset_daemon_vars(void)
|
||||
{
|
||||
memcpy(&Vars, &Defaults, sizeof Vars);
|
||||
}
|
||||
|
||||
/* Expand %VAR% references. Any unknown vars or unrecognized
|
||||
* syntax leaves the raw chars unchanged. */
|
||||
static char *expand_vars(char *str)
|
||||
static char *expand_vars(const char *str)
|
||||
{
|
||||
char *buf, *t, *f;
|
||||
char *buf, *t;
|
||||
const char *f;
|
||||
int bufsize;
|
||||
|
||||
if (!str || !strchr(str, '%'))
|
||||
return str;
|
||||
return (char *)str; /* TODO change return value to const char* at some point. */
|
||||
|
||||
bufsize = strlen(str) + 2048;
|
||||
buf = new_array(char, bufsize+1); /* +1 for trailing '\0' */
|
||||
|
||||
for (t = buf, f = str; bufsize && *f; ) {
|
||||
if (*f == '%' && *++f != '%') {
|
||||
char *percent = strchr(f, '%');
|
||||
if (percent) {
|
||||
if (*f == '%' && isUpper(f+1)) {
|
||||
const char *percent = strchr(f+1, '%');
|
||||
if (percent && percent - f < bufsize) {
|
||||
char *val;
|
||||
*percent = '\0';
|
||||
val = getenv(f);
|
||||
*percent = '%';
|
||||
strlcpy(t, f+1, percent - f);
|
||||
val = getenv(t);
|
||||
if (val) {
|
||||
int len = strlcpy(t, val, bufsize+1);
|
||||
if (len > bufsize)
|
||||
@@ -492,7 +193,6 @@ static char *expand_vars(char *str)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
f--;
|
||||
}
|
||||
*t++ = *f++;
|
||||
bufsize--;
|
||||
@@ -510,6 +210,8 @@ static char *expand_vars(char *str)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Each "char* foo" has an associated "BOOL foo_EXP" that tracks if the string has been expanded yet or not. */
|
||||
|
||||
/* NOTE: use this function and all the FN_{GLOBAL,LOCAL} ones WITHOUT a trailing semicolon! */
|
||||
#define RETURN_EXPANDED(val) {if (!val ## _EXP) {val = expand_vars(val); val ## _EXP = True;} return val ? val : "";}
|
||||
|
||||
@@ -534,65 +236,24 @@ static char *expand_vars(char *str)
|
||||
#define FN_LOCAL_INTEGER(fn_name, val) \
|
||||
int fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
|
||||
|
||||
FN_GLOBAL_STRING(lp_bind_address, bind_address)
|
||||
FN_GLOBAL_STRING(lp_daemon_chroot, daemon_chroot)
|
||||
FN_GLOBAL_STRING(lp_daemon_gid, daemon_gid)
|
||||
FN_GLOBAL_STRING(lp_daemon_uid, daemon_uid)
|
||||
FN_GLOBAL_STRING(lp_motd_file, motd_file)
|
||||
FN_GLOBAL_STRING(lp_pid_file, pid_file)
|
||||
FN_GLOBAL_STRING(lp_socket_options, socket_options)
|
||||
/* The following include file contains:
|
||||
*
|
||||
* typedef global_vars - describes global (ie., server-wide) parameters.
|
||||
* typedef local_vars - describes a single section.
|
||||
* typedef all_vars - a combination of global_vars & local_vars.
|
||||
* all_vars Defaults - the default values for all the variables.
|
||||
* all_vars Vars - the currently configured values for all the variables.
|
||||
* struct parm_struct parm_table - the strings & variables for the parser.
|
||||
* FN_{LOCAL,GLOBAL}_{TYPE}() definition for all the lp_var_name() accessors.
|
||||
*/
|
||||
|
||||
FN_GLOBAL_INTEGER(lp_listen_backlog, listen_backlog)
|
||||
FN_GLOBAL_INTEGER(lp_rsync_port, rsync_port)
|
||||
#include "daemon-parm.h"
|
||||
|
||||
FN_GLOBAL_BOOL(lp_proxy_protocol, proxy_protocol)
|
||||
|
||||
FN_LOCAL_STRING(lp_auth_users, auth_users)
|
||||
FN_LOCAL_STRING(lp_charset, charset)
|
||||
FN_LOCAL_STRING(lp_comment, comment)
|
||||
FN_LOCAL_STRING(lp_dont_compress, dont_compress)
|
||||
FN_LOCAL_STRING(lp_early_exec, early_exec)
|
||||
FN_LOCAL_STRING(lp_exclude, exclude)
|
||||
FN_LOCAL_STRING(lp_exclude_from, exclude_from)
|
||||
FN_LOCAL_STRING(lp_filter, filter)
|
||||
FN_LOCAL_STRING(lp_gid, gid)
|
||||
FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
|
||||
FN_LOCAL_STRING(lp_hosts_deny, hosts_deny)
|
||||
FN_LOCAL_STRING(lp_include, include)
|
||||
FN_LOCAL_STRING(lp_include_from, include_from)
|
||||
FN_LOCAL_STRING(lp_incoming_chmod, incoming_chmod)
|
||||
FN_LOCAL_STRING(lp_lock_file, lock_file)
|
||||
FN_LOCAL_STRING(lp_log_file, log_file)
|
||||
FN_LOCAL_STRING(lp_log_format, log_format)
|
||||
FN_LOCAL_STRING(lp_name, name)
|
||||
FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
|
||||
FN_LOCAL_STRING(lp_path, path)
|
||||
FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
|
||||
FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
|
||||
FN_LOCAL_STRING(lp_refuse_options, refuse_options)
|
||||
FN_LOCAL_STRING(lp_secrets_file, secrets_file)
|
||||
FN_LOCAL_STRING(lp_syslog_tag, syslog_tag)
|
||||
FN_LOCAL_STRING(lp_temp_dir, temp_dir)
|
||||
FN_LOCAL_STRING(lp_uid, uid)
|
||||
|
||||
FN_LOCAL_INTEGER(lp_max_connections, max_connections)
|
||||
FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
|
||||
FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
|
||||
FN_LOCAL_INTEGER(lp_timeout, timeout)
|
||||
|
||||
FN_LOCAL_BOOL(lp_fake_super, fake_super)
|
||||
FN_LOCAL_BOOL(lp_forward_lookup, forward_lookup)
|
||||
FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
|
||||
FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
|
||||
FN_LOCAL_BOOL(lp_list, list)
|
||||
FN_LOCAL_BOOL(lp_munge_symlinks, munge_symlinks)
|
||||
FN_LOCAL_BOOL(lp_numeric_ids, numeric_ids)
|
||||
FN_LOCAL_BOOL(lp_read_only, read_only)
|
||||
FN_LOCAL_BOOL(lp_reverse_lookup, reverse_lookup)
|
||||
FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
|
||||
FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
|
||||
FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
|
||||
FN_LOCAL_BOOL(lp_write_only, write_only)
|
||||
/* Initialise the Default all_vars structure. */
|
||||
void reset_daemon_vars(void)
|
||||
{
|
||||
memcpy(&Vars, &Defaults, sizeof Vars);
|
||||
}
|
||||
|
||||
/* Assign a copy of v to *s. Handles NULL strings. We don't worry
|
||||
* about overwriting a malloc'd string because the long-running
|
||||
@@ -617,19 +278,14 @@ static void init_section(local_vars *psection)
|
||||
copy_section(psection, &Vars.l);
|
||||
}
|
||||
|
||||
/* Do a case-insensitive, whitespace-ignoring string compare. */
|
||||
static int strwicmp(char *psz1, char *psz2)
|
||||
/* Do a case-insensitive, whitespace-ignoring string equality check. */
|
||||
static int strwiEQ(char *psz1, char *psz2)
|
||||
{
|
||||
/* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
|
||||
/* appropriate value. */
|
||||
/* If one or both strings are NULL, we return equality right away. */
|
||||
if (psz1 == psz2)
|
||||
return 0;
|
||||
|
||||
if (psz1 == NULL)
|
||||
return -1;
|
||||
|
||||
if (psz2 == NULL)
|
||||
return 1;
|
||||
if (psz1 == NULL || psz2 == NULL)
|
||||
return 0;
|
||||
|
||||
/* sync the strings on first non-whitespace */
|
||||
while (1) {
|
||||
@@ -637,12 +293,14 @@ static int strwicmp(char *psz1, char *psz2)
|
||||
psz1++;
|
||||
while (isSpace(psz2))
|
||||
psz2++;
|
||||
if (toUpper(psz1) != toUpper(psz2) || *psz1 == '\0' || *psz2 == '\0')
|
||||
if (*psz1 == '\0' || *psz2 == '\0')
|
||||
break;
|
||||
if (toUpper(psz1) != toUpper(psz2))
|
||||
break;
|
||||
psz1++;
|
||||
psz2++;
|
||||
}
|
||||
return *psz1 - *psz2;
|
||||
return *psz1 == *psz2;
|
||||
}
|
||||
|
||||
/* Find a section by name. Otherwise works like get_section. */
|
||||
@@ -651,7 +309,7 @@ static int getsectionbyname(char *name)
|
||||
int i;
|
||||
|
||||
for (i = section_list.count - 1; i >= 0; i--) {
|
||||
if (strwicmp(iSECTION(i).name, name) == 0)
|
||||
if (strwiEQ(iSECTION(i).name, name))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -691,7 +349,7 @@ static int map_parameter(char *parmname)
|
||||
return -1;
|
||||
|
||||
for (iIndex = 0; parm_table[iIndex].label; iIndex++) {
|
||||
if (strwicmp(parm_table[iIndex].label, parmname) == 0)
|
||||
if (strwiEQ(parm_table[iIndex].label, parmname))
|
||||
return iIndex;
|
||||
}
|
||||
|
||||
@@ -702,16 +360,14 @@ static int map_parameter(char *parmname)
|
||||
/* Set a boolean variable from the text value stored in the passed string.
|
||||
* Returns True in success, False if the passed string does not correctly
|
||||
* represent a boolean. */
|
||||
static BOOL set_boolean(BOOL *pb, char *parmvalue)
|
||||
static BOOL set_boolean(BOOL *pb, char *parmvalue, int allow_unset)
|
||||
{
|
||||
if (strwicmp(parmvalue, "yes") == 0
|
||||
|| strwicmp(parmvalue, "true") == 0
|
||||
|| strwicmp(parmvalue, "1") == 0)
|
||||
if (strwiEQ(parmvalue, "yes") || strwiEQ(parmvalue, "true") || strwiEQ(parmvalue, "1"))
|
||||
*pb = True;
|
||||
else if (strwicmp(parmvalue, "no") == 0
|
||||
|| strwicmp(parmvalue, "False") == 0
|
||||
|| strwicmp(parmvalue, "0") == 0)
|
||||
else if (strwiEQ(parmvalue, "no") || strwiEQ(parmvalue, "false") || strwiEQ(parmvalue, "0"))
|
||||
*pb = False;
|
||||
else if (allow_unset && (strwiEQ(parmvalue, "unset") || strwiEQ(parmvalue, "-1")))
|
||||
*pb = Unset;
|
||||
else {
|
||||
rprintf(FLOG, "Badly formed boolean in configuration file: \"%s\".\n", parmvalue);
|
||||
return False;
|
||||
@@ -760,11 +416,15 @@ static BOOL do_parameter(char *parmname, char *parmvalue)
|
||||
|
||||
switch (parm_table[parmnum].type) {
|
||||
case P_BOOL:
|
||||
set_boolean(parm_ptr, parmvalue);
|
||||
set_boolean(parm_ptr, parmvalue, False);
|
||||
break;
|
||||
|
||||
case P_BOOL3:
|
||||
set_boolean(parm_ptr, parmvalue, True);
|
||||
break;
|
||||
|
||||
case P_BOOLREV:
|
||||
set_boolean(parm_ptr, parmvalue);
|
||||
set_boolean(parm_ptr, parmvalue, False);
|
||||
*(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
|
||||
break;
|
||||
|
||||
@@ -777,7 +437,7 @@ static BOOL do_parameter(char *parmname, char *parmvalue)
|
||||
break;
|
||||
|
||||
case P_OCTAL:
|
||||
sscanf(parmvalue, "%o", (int *)parm_ptr);
|
||||
sscanf(parmvalue, "%o", (unsigned int *)parm_ptr);
|
||||
break;
|
||||
|
||||
case P_PATH:
|
||||
@@ -834,7 +494,7 @@ static BOOL do_section(char *sectionname)
|
||||
return True;
|
||||
}
|
||||
|
||||
isglobal = strwicmp(sectionname, GLOBAL_NAME) == 0;
|
||||
isglobal = strwiEQ(sectionname, GLOBAL_NAME);
|
||||
|
||||
/* At the end of the global section, add any --dparam items. */
|
||||
if (bInGlobalSection && !isglobal) {
|
||||
|
||||
79
log.c
79
log.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2000-2001 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -34,10 +34,8 @@ extern int module_id;
|
||||
extern int allow_8bit_chars;
|
||||
extern int protocol_version;
|
||||
extern int always_checksum;
|
||||
extern int preserve_times;
|
||||
extern int preserve_mtimes;
|
||||
extern int msgs2stderr;
|
||||
extern int xfersum_type;
|
||||
extern int checksum_type;
|
||||
extern int stdout_format_has_i;
|
||||
extern int stdout_format_has_o_or_i;
|
||||
extern int logfile_format_has_i;
|
||||
@@ -62,6 +60,8 @@ extern unsigned int module_dirlen;
|
||||
extern char sender_file_sum[MAX_DIGEST_LEN];
|
||||
extern const char undetermined_hostname[];
|
||||
|
||||
extern struct name_num_item *xfer_sum_nni, *file_sum_nni;
|
||||
|
||||
static int log_initialised;
|
||||
static int logfile_was_closed;
|
||||
static FILE *logfile_fp;
|
||||
@@ -251,7 +251,7 @@ static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isp
|
||||
void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
|
||||
{
|
||||
char trailing_CR_or_NL;
|
||||
FILE *f = msgs2stderr ? stderr : stdout;
|
||||
FILE *f = msgs2stderr == 1 ? stderr : stdout;
|
||||
#ifdef ICONV_OPTION
|
||||
iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
|
||||
#else
|
||||
@@ -263,7 +263,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
|
||||
if (len < 0)
|
||||
exit_cleanup(RERR_MESSAGEIO);
|
||||
|
||||
if (msgs2stderr) {
|
||||
if (msgs2stderr == 1) {
|
||||
/* A normal daemon can get msgs2stderr set if the socket is busted, so we
|
||||
* change the message destination into an FLOG message in order to try to
|
||||
* get some info about an abnormal-exit into the log file. An rsh daemon
|
||||
@@ -327,7 +327,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
|
||||
exit_cleanup(RERR_MESSAGEIO);
|
||||
}
|
||||
|
||||
if (am_server && !msgs2stderr) {
|
||||
if (am_server && msgs2stderr != 1 && (msgs2stderr != 2 || f != stderr)) {
|
||||
enum msgcode msg = (enum msgcode)code;
|
||||
if (protocol_version < 30) {
|
||||
if (msg == MSG_ERROR)
|
||||
@@ -350,8 +350,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
|
||||
output_needs_newline = 0;
|
||||
}
|
||||
|
||||
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
|
||||
? buf[--len] : 0;
|
||||
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
|
||||
|
||||
if (len && buf[0] == '\r') {
|
||||
fputc('\r', f);
|
||||
@@ -372,7 +371,12 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
|
||||
iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
|
||||
ierrno = errno;
|
||||
if (outbuf.len) {
|
||||
filtered_fwrite(f, convbuf, outbuf.len, 0, 0);
|
||||
char trailing = inbuf.len ? '\0' : trailing_CR_or_NL;
|
||||
filtered_fwrite(f, convbuf, outbuf.len, 0, trailing);
|
||||
if (trailing) {
|
||||
trailing_CR_or_NL = '\0';
|
||||
fflush(f);
|
||||
}
|
||||
outbuf.len = 0;
|
||||
}
|
||||
/* Log one byte of illegal/incomplete sequence and continue with
|
||||
@@ -452,11 +456,17 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
|
||||
char buf[BIGPATHBUFLEN];
|
||||
size_t len;
|
||||
|
||||
/* snprintf returns the would-have-been length on truncation, so
|
||||
* each cumulative call must be guarded; if not, sizeof buf - len
|
||||
* can underflow when promoted to size_t and the next call writes
|
||||
* past the buffer. */
|
||||
len = snprintf(buf, sizeof buf, RSYNC_NAME ": [%s] ", who_am_i());
|
||||
|
||||
va_start(ap, format);
|
||||
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
|
||||
va_end(ap);
|
||||
if (len < sizeof buf) {
|
||||
va_start(ap, format);
|
||||
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (len < sizeof buf) {
|
||||
len += snprintf(buf + len, sizeof buf - len,
|
||||
@@ -676,12 +686,12 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
|
||||
n = NULL;
|
||||
if (S_ISREG(file->mode)) {
|
||||
if (always_checksum)
|
||||
n = sum_as_hex(checksum_type, F_SUM(file), 1);
|
||||
n = sum_as_hex(file_sum_nni->num, F_SUM(file), 1);
|
||||
else if (iflags & ITEM_TRANSFER)
|
||||
n = sum_as_hex(xfersum_type, sender_file_sum, 0);
|
||||
n = sum_as_hex(xfer_sum_nni->num, sender_file_sum, 0);
|
||||
}
|
||||
if (!n) {
|
||||
int sum_len = csum_len_for_type(always_checksum ? checksum_type : xfersum_type,
|
||||
int sum_len = csum_len_for_type(always_checksum ? file_sum_nni->num : xfer_sum_nni->num,
|
||||
always_checksum);
|
||||
memset(buf2, ' ', sum_len*2);
|
||||
buf2[sum_len*2] = '\0';
|
||||
@@ -702,7 +712,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
|
||||
c[1] = 'L';
|
||||
c[3] = '.';
|
||||
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
|
||||
: !preserve_times || !receiver_symlink_times
|
||||
: !preserve_mtimes || !receiver_symlink_times
|
||||
|| (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't';
|
||||
} else {
|
||||
c[1] = S_ISDIR(file->mode) ? 'd'
|
||||
@@ -710,14 +720,15 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
|
||||
: IS_DEVICE(file->mode) ? 'D' : 'f';
|
||||
c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
|
||||
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
|
||||
: !preserve_times ? 'T' : 't';
|
||||
: !preserve_mtimes ? 'T' : 't';
|
||||
}
|
||||
c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c';
|
||||
c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
|
||||
c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
|
||||
c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
|
||||
c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
|
||||
: S_ISLNK(file->mode) ? 'U' : 'u';
|
||||
c[8] = !(iflags & (ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME)) ? '.'
|
||||
: BITS_SET(iflags, ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME) ? 'b'
|
||||
: iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
|
||||
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
|
||||
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
|
||||
c[11] = '\0';
|
||||
@@ -833,14 +844,24 @@ void maybe_log_item(struct file_struct *file, int iflags, int itemizing, const c
|
||||
|
||||
void log_delete(const char *fname, int mode)
|
||||
{
|
||||
static struct {
|
||||
union file_extras ex[4]; /* just in case... */
|
||||
struct file_struct file;
|
||||
} x; /* Zero-initialized due to static declaration. */
|
||||
static struct file_struct *file = NULL;
|
||||
int len = strlen(fname);
|
||||
const char *fmt;
|
||||
|
||||
x.file.mode = mode;
|
||||
if (!file) {
|
||||
int extra_len = (file_extra_cnt + 2) * EXTRA_LEN;
|
||||
char *bp;
|
||||
#if EXTRA_ROUNDING > 0
|
||||
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
|
||||
extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
|
||||
#endif
|
||||
|
||||
bp = new_array0(char, FILE_STRUCT_LEN + extra_len + 1);
|
||||
bp += extra_len;
|
||||
file = (struct file_struct *)bp;
|
||||
}
|
||||
|
||||
file->mode = mode;
|
||||
|
||||
if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
|
||||
if (S_ISDIR(mode))
|
||||
@@ -850,14 +871,14 @@ void log_delete(const char *fname, int mode)
|
||||
;
|
||||
else {
|
||||
fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
|
||||
log_formatted(FCLIENT, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
|
||||
log_formatted(FCLIENT, fmt, "del.", file, fname, ITEM_DELETED, NULL);
|
||||
}
|
||||
|
||||
if (!logfile_name || dry_run || !logfile_format)
|
||||
return;
|
||||
|
||||
fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n";
|
||||
log_formatted(FLOG, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
|
||||
log_formatted(FLOG, fmt, "del.", file, fname, ITEM_DELETED, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -886,10 +907,10 @@ void log_exit(int code, const char *file, int line)
|
||||
/* VANISHED is not an error, only a warning */
|
||||
if (code == RERR_VANISHED) {
|
||||
rprintf(FWARNING, "rsync warning: %s (code %d) at %s(%d) [%s=%s]\n",
|
||||
name, code, file, line, who_am_i(), RSYNC_VERSION);
|
||||
name, code, src_file(file), line, who_am_i(), rsync_version());
|
||||
} else {
|
||||
rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s=%s]\n",
|
||||
name, code, file, line, who_am_i(), RSYNC_VERSION);
|
||||
name, code, src_file(file), line, who_am_i(), rsync_version());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
dnl AC_HAVE_TYPE(TYPE,INCLUDES)
|
||||
AC_DEFUN([AC_HAVE_TYPE], [
|
||||
AC_REQUIRE([AC_HEADER_STDC])
|
||||
cv=`echo "$1" | sed 'y%./+- %__p__%'`
|
||||
AC_MSG_CHECKING(for $1)
|
||||
AC_CACHE_VAL([ac_cv_type_$cv],
|
||||
|
||||
272
main.c
272
main.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
#include <popt.h>
|
||||
#ifdef __TANDEM
|
||||
#include <floss.h(floss_execlp)>
|
||||
#endif
|
||||
|
||||
extern int dry_run;
|
||||
extern int list_only;
|
||||
@@ -45,6 +48,7 @@ extern int called_from_signal_handler;
|
||||
extern int need_messages_from_generator;
|
||||
extern int kluge_around_eof;
|
||||
extern int got_xfer_error;
|
||||
extern int old_style_args;
|
||||
extern int msgs2stderr;
|
||||
extern int module_id;
|
||||
extern int read_only;
|
||||
@@ -54,6 +58,7 @@ extern int copy_unsafe_links;
|
||||
extern int keep_dirlinks;
|
||||
extern int preserve_hard_links;
|
||||
extern int protocol_version;
|
||||
extern int mkpath_dest_arg;
|
||||
extern int file_total;
|
||||
extern int recurse;
|
||||
extern int xfer_dirs;
|
||||
@@ -61,7 +66,7 @@ extern int protect_args;
|
||||
extern int relative_paths;
|
||||
extern int sanitize_paths;
|
||||
extern int curr_dir_depth;
|
||||
extern int curr_dir_len;
|
||||
extern unsigned int curr_dir_len;
|
||||
extern int module_id;
|
||||
extern int rsync_port;
|
||||
extern int whole_file;
|
||||
@@ -83,6 +88,9 @@ extern BOOL shutting_down;
|
||||
extern int backup_dir_len;
|
||||
extern int basis_dir_cnt;
|
||||
extern int default_af_hint;
|
||||
extern int stdout_format_has_i;
|
||||
extern int trust_sender_filter;
|
||||
extern int trust_sender_args;
|
||||
extern struct stats stats;
|
||||
extern char *stdout_format;
|
||||
extern char *logfile_format;
|
||||
@@ -93,18 +101,19 @@ extern char *shell_cmd;
|
||||
extern char *password_file;
|
||||
extern char *backup_dir;
|
||||
extern char *copy_as;
|
||||
extern char *tmpdir;
|
||||
extern char curr_dir[MAXPATHLEN];
|
||||
extern char backup_dir_buf[MAXPATHLEN];
|
||||
extern char *basis_dir[MAX_BASIS_DIRS+1];
|
||||
extern struct file_list *first_flist;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
extern filter_rule_list daemon_filter_list, implied_filter_list;
|
||||
|
||||
uid_t our_uid;
|
||||
gid_t our_gid;
|
||||
int am_receiver = 0; /* Only set to 1 after the receiver/generator fork. */
|
||||
int am_generator = 0; /* Only set to 1 after the receiver/generator fork. */
|
||||
int local_server = 0;
|
||||
int daemon_over_rsh = 0;
|
||||
int daemon_connection = 0; /* 0 = no daemon, 1 = daemon via remote shell, -1 = daemon via socket */
|
||||
mode_t orig_umask = 0;
|
||||
int batch_gen_fd = -1;
|
||||
int sender_keeps_checksum = 0;
|
||||
@@ -230,11 +239,11 @@ void write_del_stats(int f)
|
||||
|
||||
void read_del_stats(int f)
|
||||
{
|
||||
stats.deleted_files = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_dirs = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_symlinks = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_devices = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_specials = read_varint(f);
|
||||
stats.deleted_files = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_files");
|
||||
stats.deleted_files += stats.deleted_dirs = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_dirs");
|
||||
stats.deleted_files += stats.deleted_symlinks = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_symlinks");
|
||||
stats.deleted_files += stats.deleted_devices = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_devices");
|
||||
stats.deleted_files += stats.deleted_specials = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_specials");
|
||||
}
|
||||
|
||||
static void become_copy_as_user()
|
||||
@@ -299,7 +308,7 @@ static void become_copy_as_user()
|
||||
|
||||
our_uid = MY_UID();
|
||||
our_gid = MY_GID();
|
||||
am_root = (our_uid == 0);
|
||||
am_root = (our_uid == ROOT_UID);
|
||||
|
||||
if (gname)
|
||||
gname[-1] = ':';
|
||||
@@ -377,7 +386,7 @@ static void handle_stats(int f)
|
||||
|
||||
static void output_itemized_counts(const char *prefix, int *counts)
|
||||
{
|
||||
static char *labels[] = { "reg", "dir", "link", "dev", "special" };
|
||||
static char *const labels[] = { "reg", "dir", "link", "dev", "special" };
|
||||
char buf[1024], *pre = " (";
|
||||
int j, len = 0;
|
||||
int total = counts[0];
|
||||
@@ -385,9 +394,18 @@ static void output_itemized_counts(const char *prefix, int *counts)
|
||||
counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
|
||||
for (j = 0; j < 5; j++) {
|
||||
if (counts[j]) {
|
||||
/* snprintf can return more than its size arg
|
||||
* on truncation; keep len <= sizeof buf - 2 so
|
||||
* the closing ')' and trailing NUL always
|
||||
* have room and the next iteration's
|
||||
* sizeof buf - len - 2 cannot underflow. */
|
||||
if (len >= (int)sizeof buf - 2)
|
||||
break;
|
||||
len += snprintf(buf+len, sizeof buf - len - 2,
|
||||
"%s%s: %s",
|
||||
pre, labels[j], comma_num(counts[j]));
|
||||
if (len > (int)sizeof buf - 2)
|
||||
len = (int)sizeof buf - 2;
|
||||
pre = ", ";
|
||||
}
|
||||
}
|
||||
@@ -461,38 +479,33 @@ static void output_summary(void)
|
||||
**/
|
||||
static void show_malloc_stats(void)
|
||||
{
|
||||
#ifdef HAVE_MALLINFO
|
||||
struct mallinfo mi;
|
||||
|
||||
mi = mallinfo();
|
||||
#ifdef MEM_ALLOC_INFO
|
||||
struct MEM_ALLOC_INFO mi = MEM_ALLOC_INFO(); /* mallinfo or mallinfo2 */
|
||||
|
||||
rprintf(FCLIENT, "\n");
|
||||
rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
|
||||
(int)getpid(), am_server ? "server " : "",
|
||||
am_daemon ? "daemon " : "", who_am_i());
|
||||
rprintf(FINFO, " arena: %10ld (bytes from sbrk)\n",
|
||||
(long)mi.arena);
|
||||
rprintf(FINFO, " ordblks: %10ld (chunks not in use)\n",
|
||||
(long)mi.ordblks);
|
||||
rprintf(FINFO, " smblks: %10ld\n",
|
||||
(long)mi.smblks);
|
||||
rprintf(FINFO, " hblks: %10ld (chunks from mmap)\n",
|
||||
(long)mi.hblks);
|
||||
rprintf(FINFO, " hblkhd: %10ld (bytes from mmap)\n",
|
||||
(long)mi.hblkhd);
|
||||
rprintf(FINFO, " allmem: %10ld (bytes from sbrk + mmap)\n",
|
||||
(long)mi.arena + mi.hblkhd);
|
||||
rprintf(FINFO, " usmblks: %10ld\n",
|
||||
(long)mi.usmblks);
|
||||
rprintf(FINFO, " fsmblks: %10ld\n",
|
||||
(long)mi.fsmblks);
|
||||
rprintf(FINFO, " uordblks: %10ld (bytes used)\n",
|
||||
(long)mi.uordblks);
|
||||
rprintf(FINFO, " fordblks: %10ld (bytes free)\n",
|
||||
(long)mi.fordblks);
|
||||
rprintf(FINFO, " keepcost: %10ld (bytes in releasable chunk)\n",
|
||||
(long)mi.keepcost);
|
||||
#endif /* HAVE_MALLINFO */
|
||||
|
||||
#define PRINT_ALLOC_NUM(title, descr, num) \
|
||||
rprintf(FINFO, " %-11s%10" SIZE_T_FMT_MOD "d (" descr ")\n", \
|
||||
title ":", (SIZE_T_FMT_CAST)(num));
|
||||
|
||||
PRINT_ALLOC_NUM("arena", "bytes from sbrk", mi.arena);
|
||||
PRINT_ALLOC_NUM("ordblks", "chunks not in use", mi.ordblks);
|
||||
PRINT_ALLOC_NUM("smblks", "free fastbin blocks", mi.smblks);
|
||||
PRINT_ALLOC_NUM("hblks", "chunks from mmap", mi.hblks);
|
||||
PRINT_ALLOC_NUM("hblkhd", "bytes from mmap", mi.hblkhd);
|
||||
PRINT_ALLOC_NUM("allmem", "bytes from sbrk + mmap", mi.arena + mi.hblkhd);
|
||||
PRINT_ALLOC_NUM("usmblks", "always 0", mi.usmblks);
|
||||
PRINT_ALLOC_NUM("fsmblks", "bytes in freed fastbin blocks", mi.fsmblks);
|
||||
PRINT_ALLOC_NUM("uordblks", "bytes used", mi.uordblks);
|
||||
PRINT_ALLOC_NUM("fordblks", "bytes free", mi.fordblks);
|
||||
PRINT_ALLOC_NUM("keepcost", "bytes in releasable chunk", mi.keepcost);
|
||||
|
||||
#undef PRINT_ALLOC_NUM
|
||||
|
||||
#endif /* MEM_ALLOC_INFO */
|
||||
}
|
||||
|
||||
|
||||
@@ -562,12 +575,12 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
#ifdef HAVE_REMSH
|
||||
/* remsh (on HPUX) takes the arguments the other way around */
|
||||
args[argc++] = machine;
|
||||
if (user && !(daemon_over_rsh && dash_l_set)) {
|
||||
if (user && !(daemon_connection && dash_l_set)) {
|
||||
args[argc++] = "-l";
|
||||
args[argc++] = user;
|
||||
}
|
||||
#else
|
||||
if (user && !(daemon_over_rsh && dash_l_set)) {
|
||||
if (user && !(daemon_connection && dash_l_set)) {
|
||||
args[argc++] = "-l";
|
||||
args[argc++] = user;
|
||||
}
|
||||
@@ -587,7 +600,11 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
if (blocking_io < 0 && (strcmp(t, "rsh") == 0 || strcmp(t, "remsh") == 0))
|
||||
blocking_io = 1;
|
||||
|
||||
server_options(args, &argc);
|
||||
if (daemon_connection > 0) {
|
||||
args[argc++] = "--server";
|
||||
args[argc++] = "--daemon";
|
||||
} else
|
||||
server_options(args, &argc);
|
||||
|
||||
if (argc >= MAX_ARGS - 2)
|
||||
goto arg_overflow;
|
||||
@@ -595,18 +612,14 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
|
||||
args[argc++] = ".";
|
||||
|
||||
if (!daemon_over_rsh) {
|
||||
if (!daemon_connection) {
|
||||
while (remote_argc > 0) {
|
||||
if (argc >= MAX_ARGS - 1) {
|
||||
arg_overflow:
|
||||
rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
if (**remote_argv == '-') {
|
||||
if (asprintf(args + argc++, "./%s", *remote_argv++) < 0)
|
||||
out_of_memory("do_cmd");
|
||||
} else
|
||||
args[argc++] = *remote_argv++;
|
||||
args[argc++] = safe_arg(NULL, *remote_argv++);
|
||||
remote_argc--;
|
||||
}
|
||||
}
|
||||
@@ -648,7 +661,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
#ifdef ICONV_CONST
|
||||
setup_iconv();
|
||||
#endif
|
||||
if (protect_args && !daemon_over_rsh)
|
||||
if (protect_args && !daemon_connection)
|
||||
send_protected_args(*f_out_p, args);
|
||||
}
|
||||
|
||||
@@ -658,6 +671,16 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* Older versions turn an empty string as a reference to the current directory.
|
||||
* We now treat this as an error unless --old-args was used. */
|
||||
static char *dot_dir_or_error()
|
||||
{
|
||||
if (old_style_args || am_server)
|
||||
return ".";
|
||||
rprintf(FERROR, "Empty destination arg specified (use \".\" or see --old-args).\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
|
||||
/* The receiving side operates in one of two modes:
|
||||
*
|
||||
* 1. it receives any number of files into a destination directory,
|
||||
@@ -674,7 +697,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
int statret;
|
||||
int statret, trailing_slash;
|
||||
char *cp;
|
||||
|
||||
if (DEBUG_GTE(RECV, 1)) {
|
||||
@@ -685,9 +708,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
if (!dest_path || list_only)
|
||||
return NULL;
|
||||
|
||||
/* Treat an empty string as a copy into the current directory. */
|
||||
if (!*dest_path)
|
||||
dest_path = ".";
|
||||
dest_path = dot_dir_or_error();
|
||||
|
||||
if (daemon_filter_list.head) {
|
||||
char *slash = strrchr(dest_path, '/');
|
||||
@@ -707,7 +729,29 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
}
|
||||
|
||||
/* See what currently exists at the destination. */
|
||||
if ((statret = do_stat(dest_path, &st)) == 0) {
|
||||
statret = do_stat(dest_path, &st);
|
||||
cp = strrchr(dest_path, '/');
|
||||
trailing_slash = cp && !cp[1];
|
||||
|
||||
if (mkpath_dest_arg && statret < 0 && (cp || file_total > 1)) {
|
||||
int save_errno = errno;
|
||||
int ret = make_path(dest_path, file_total > 1 && !trailing_slash ? 0 : MKP_DROP_NAME);
|
||||
if (ret < 0)
|
||||
goto mkdir_error;
|
||||
if (ret && (INFO_GTE(NAME, 1) || stdout_format_has_i)) {
|
||||
if (file_total == 1 || trailing_slash)
|
||||
*cp = '\0';
|
||||
rprintf(FINFO, "created %d director%s for %s\n", ret, ret == 1 ? "y" : "ies", dest_path);
|
||||
if (file_total == 1 || trailing_slash)
|
||||
*cp = '/';
|
||||
}
|
||||
if (ret)
|
||||
statret = do_stat(dest_path, &st);
|
||||
else
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
if (statret == 0) {
|
||||
/* If the destination is a dir, enter it and use mode 1. */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (!change_dir(dest_path, CD_NORMAL)) {
|
||||
@@ -737,15 +781,12 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
exit_cleanup(RERR_FILESELECT);
|
||||
}
|
||||
|
||||
cp = strrchr(dest_path, '/');
|
||||
|
||||
/* If we need a destination directory because the transfer is not
|
||||
* of a single non-directory or the user has requested one via a
|
||||
* destination path ending in a slash, create one and use mode 1. */
|
||||
if (file_total > 1 || (cp && !cp[1])) {
|
||||
/* Lop off the final slash (if any). */
|
||||
if (cp && !cp[1])
|
||||
*cp = '\0';
|
||||
if (file_total > 1 || trailing_slash) {
|
||||
if (trailing_slash)
|
||||
*cp = '\0'; /* Lop off the final slash (if any). */
|
||||
|
||||
if (statret == 0) {
|
||||
rprintf(FERROR, "ERROR: destination path is not a directory\n");
|
||||
@@ -753,6 +794,7 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
}
|
||||
|
||||
if (do_mkdir(dest_path, ACCESSPERMS) != 0) {
|
||||
mkdir_error:
|
||||
rsyserr(FERROR, errno, "mkdir %s failed",
|
||||
full_fname(dest_path));
|
||||
exit_cleanup(RERR_FILEIO);
|
||||
@@ -762,7 +804,7 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
&& strcmp(flist->files[flist->low]->basename, ".") == 0)
|
||||
flist->files[0]->flags |= FLAG_DIR_CREATED;
|
||||
|
||||
if (INFO_GTE(NAME, 1))
|
||||
if (INFO_GTE(NAME, 1) || stdout_format_has_i)
|
||||
rprintf(FINFO, "created directory %s\n", dest_path);
|
||||
|
||||
if (dry_run) {
|
||||
@@ -977,6 +1019,23 @@ static int do_recv(int f_in, int f_out, char *local_name)
|
||||
backup_dir_buf[backup_dir_len-1] = '/';
|
||||
}
|
||||
|
||||
if (tmpdir) {
|
||||
STRUCT_STAT st;
|
||||
int ret = do_stat(tmpdir, &st);
|
||||
if (ret < 0 || !S_ISDIR(st.st_mode)) {
|
||||
if (ret == 0) {
|
||||
rprintf(FERROR, "The temp-dir is not a directory: %s\n", tmpdir);
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
if (errno == ENOENT) {
|
||||
rprintf(FERROR, "The temp-dir does not exist: %s\n", tmpdir);
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
rprintf(FERROR, "Failed to stat temp-dir %s: %s\n", tmpdir, strerror(errno));
|
||||
exit_cleanup(RERR_FILEIO);
|
||||
}
|
||||
}
|
||||
|
||||
io_flush(FULL_FLUSH);
|
||||
|
||||
if ((pid = do_fork()) == -1) {
|
||||
@@ -1037,6 +1096,7 @@ static int do_recv(int f_in, int f_out, char *local_name)
|
||||
}
|
||||
|
||||
am_generator = 1;
|
||||
implied_filter_list.head = implied_filter_list.tail = NULL;
|
||||
flist_receiving_enabled = True;
|
||||
|
||||
io_end_multiplex_in(MPLX_SWITCHING);
|
||||
@@ -1083,7 +1143,7 @@ static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
|
||||
char *local_name = NULL;
|
||||
int negated_levels;
|
||||
|
||||
if (filesfrom_fd >= 0 && !msgs2stderr && protocol_version < 31) {
|
||||
if (filesfrom_fd >= 0 && msgs2stderr != 1 && protocol_version < 31) {
|
||||
/* We can't mix messages with files-from data on the socket,
|
||||
* so temporarily turn off info/debug messages. */
|
||||
negate_output_levels();
|
||||
@@ -1332,15 +1392,6 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
|
||||
return MAX(exit_code, exit_code2);
|
||||
}
|
||||
|
||||
static void dup_argv(char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; argv[i]; i++)
|
||||
argv[i] = strdup(argv[i]);
|
||||
}
|
||||
|
||||
|
||||
/* Start a client for either type of remote connection. Work out
|
||||
* whether the arguments request a remote shell or rsyncd connection,
|
||||
* and call the appropriate connection function, then run_client.
|
||||
@@ -1356,10 +1407,6 @@ static int start_client(int argc, char *argv[])
|
||||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
/* Don't clobber argv[] so that ps(1) can still show the right
|
||||
* command line. */
|
||||
dup_argv(argv);
|
||||
|
||||
if (!read_batch) { /* for read_batch, NO source is specified */
|
||||
char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
|
||||
if (path) { /* source is remote */
|
||||
@@ -1386,12 +1433,14 @@ static int start_client(int argc, char *argv[])
|
||||
}
|
||||
am_sender = 0;
|
||||
if (rsync_port)
|
||||
daemon_over_rsh = shell_cmd ? 1 : -1;
|
||||
daemon_connection = shell_cmd ? 1 : -1;
|
||||
} else { /* source is local, check dest arg */
|
||||
am_sender = 1;
|
||||
|
||||
if (argc > 1) {
|
||||
p = argv[--argc];
|
||||
if (!*p)
|
||||
p = dot_dir_or_error();
|
||||
remote_argv = argv + argc;
|
||||
} else {
|
||||
static char *dotarg[1] = { "." };
|
||||
@@ -1418,7 +1467,7 @@ static int start_client(int argc, char *argv[])
|
||||
} else { /* hostspec was found, so dest is remote */
|
||||
argv[argc] = path;
|
||||
if (rsync_port)
|
||||
daemon_over_rsh = shell_cmd ? 1 : -1;
|
||||
daemon_connection = shell_cmd ? 1 : -1;
|
||||
}
|
||||
}
|
||||
} else { /* read_batch */
|
||||
@@ -1432,6 +1481,12 @@ static int start_client(int argc, char *argv[])
|
||||
rsync_port = 0;
|
||||
}
|
||||
|
||||
/* A local transfer doesn't unbackslash anything, so leave the args alone. */
|
||||
if (local_server) {
|
||||
old_style_args = 2;
|
||||
trust_sender_args = trust_sender_filter = 1;
|
||||
}
|
||||
|
||||
if (!rsync_port && remote_argc && !**remote_argv) /* Turn an empty arg into a dot dir. */
|
||||
*remote_argv = ".";
|
||||
|
||||
@@ -1439,8 +1494,15 @@ static int start_client(int argc, char *argv[])
|
||||
char *dummy_host;
|
||||
int dummy_port = rsync_port;
|
||||
int i;
|
||||
if (!argv[0][0])
|
||||
goto invalid_empty;
|
||||
/* For local source, extra source args must not have hostspec. */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!argv[i][0]) {
|
||||
invalid_empty:
|
||||
rprintf(FERROR, "Empty source arg specified.\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
if (check_for_hostspec(argv[i], &dummy_host, &dummy_port)) {
|
||||
rprintf(FERROR, "Unexpected remote arg: %s\n", argv[i]);
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
@@ -1450,6 +1512,8 @@ static int start_client(int argc, char *argv[])
|
||||
char *dummy_host;
|
||||
int dummy_port = rsync_port;
|
||||
int i;
|
||||
if (filesfrom_fd < 0)
|
||||
add_implied_include(remote_argv[0], daemon_connection);
|
||||
/* For remote source, any extra source args must have either
|
||||
* the same hostname or an empty hostname. */
|
||||
for (i = 1; i < remote_argc; i++) {
|
||||
@@ -1473,6 +1537,7 @@ static int start_client(int argc, char *argv[])
|
||||
if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
|
||||
arg = ".";
|
||||
remote_argv[i] = arg;
|
||||
add_implied_include(arg, daemon_connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,10 +1546,10 @@ static int start_client(int argc, char *argv[])
|
||||
else
|
||||
env_port = rsync_port;
|
||||
|
||||
if (daemon_over_rsh < 0)
|
||||
if (daemon_connection < 0)
|
||||
return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv);
|
||||
|
||||
if (password_file && !daemon_over_rsh) {
|
||||
if (password_file && !daemon_connection) {
|
||||
rprintf(FERROR, "The --password-file option may only be "
|
||||
"used when accessing an rsync daemon.\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
@@ -1503,6 +1568,10 @@ static int start_client(int argc, char *argv[])
|
||||
shell_user = shell_machine;
|
||||
shell_machine = p+1;
|
||||
}
|
||||
if (*shell_machine == '-') {
|
||||
rprintf(FERROR, "Invalid remote host: hostnames may not start with '-'.\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(CMD, 2)) {
|
||||
@@ -1512,15 +1581,17 @@ static int start_client(int argc, char *argv[])
|
||||
}
|
||||
|
||||
#ifdef HAVE_PUTENV
|
||||
if (daemon_over_rsh)
|
||||
if (daemon_connection)
|
||||
set_env_num("RSYNC_PORT", env_port);
|
||||
#else
|
||||
(void)env_port;
|
||||
#endif
|
||||
|
||||
pid = do_cmd(shell_cmd, shell_machine, shell_user, remote_argv, remote_argc, &f_in, &f_out);
|
||||
|
||||
/* if we're running an rsync server on the remote host over a
|
||||
* remote shell command, we need to do the RSYNCD protocol first */
|
||||
if (daemon_over_rsh) {
|
||||
if (daemon_connection) {
|
||||
int tmpret;
|
||||
tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
|
||||
if (tmpret < 0)
|
||||
@@ -1547,16 +1618,23 @@ static void sigusr2_handler(UNUSED(int val))
|
||||
if (!am_server)
|
||||
output_summary();
|
||||
close_all();
|
||||
#ifdef GCOV_COVERAGE
|
||||
/* The receiver child is killed here via SIGUSR2 and exits with _exit(),
|
||||
* bypassing the gcov atexit flush; without this it writes no .gcda. */
|
||||
{ extern void __gcov_dump(void); __gcov_dump(); }
|
||||
#endif
|
||||
if (got_xfer_error)
|
||||
_exit(RERR_PARTIAL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
#if defined SIGINFO || defined SIGVTALRM
|
||||
static void siginfo_handler(UNUSED(int val))
|
||||
{
|
||||
if (!am_server && !INFO_GTE(PROGRESS, 1))
|
||||
want_progress_now = True;
|
||||
}
|
||||
#endif
|
||||
|
||||
void remember_children(UNUSED(int val))
|
||||
{
|
||||
@@ -1584,7 +1662,6 @@ void remember_children(UNUSED(int val))
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This routine catches signals and tries to send them to gdb.
|
||||
*
|
||||
@@ -1608,7 +1685,6 @@ const char *get_panic_action(void)
|
||||
return "xterm -display :0 -T Panic -n Panic -e gdb /proc/%d/exe %d";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
|
||||
*
|
||||
@@ -1632,6 +1708,22 @@ static void rsync_panic_handler(UNUSED(int whatsig))
|
||||
}
|
||||
#endif
|
||||
|
||||
static void unset_env_var(const char *var)
|
||||
{
|
||||
#ifdef HAVE_UNSETENV
|
||||
unsetenv(var);
|
||||
#else
|
||||
#ifdef HAVE_PUTENV
|
||||
char *mem;
|
||||
if (asprintf(&mem, "%s=", var) < 0)
|
||||
out_of_memory("unset_env_var");
|
||||
putenv(mem);
|
||||
#else
|
||||
(void)var;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
@@ -1667,7 +1759,22 @@ int main(int argc,char *argv[])
|
||||
starttime = time(NULL);
|
||||
our_uid = MY_UID();
|
||||
our_gid = MY_GID();
|
||||
am_root = our_uid == 0;
|
||||
am_root = our_uid == ROOT_UID;
|
||||
|
||||
// DISPLAY should not be emptied unconditionally
|
||||
if (!getenv("SSH_ASKPASS"))
|
||||
unset_env_var("DISPLAY");
|
||||
|
||||
#if defined USE_OPENSSL && defined SET_OPENSSL_CONF
|
||||
#define TO_STR2(x) #x
|
||||
#define TO_STR(x) TO_STR2(x)
|
||||
/* ./configure --with-openssl-conf=/etc/ssl/openssl-rsync.cnf
|
||||
* defines SET_OPENSSL_CONF as that unquoted pathname. */
|
||||
if (!getenv("OPENSSL_CONF")) /* Don't override it if it's already set. */
|
||||
set_env_str("OPENSSL_CONF", TO_STR(SET_OPENSSL_CONF));
|
||||
#undef TO_STR
|
||||
#undef TO_STR2
|
||||
#endif
|
||||
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
@@ -1687,6 +1794,7 @@ int main(int argc,char *argv[])
|
||||
|
||||
#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
|
||||
setlocale(LC_CTYPE, "");
|
||||
setlocale(LC_NUMERIC, "");
|
||||
#endif
|
||||
|
||||
if (!parse_arguments(&argc, (const char ***) &argv)) {
|
||||
|
||||
33
match.c
33
match.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2023 Wayne Davison
|
||||
*
|
||||
* 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
|
||||
@@ -24,7 +24,9 @@
|
||||
|
||||
extern int checksum_seed;
|
||||
extern int append_mode;
|
||||
extern int xfersum_type;
|
||||
|
||||
extern struct name_num_item *xfer_sum_nni;
|
||||
extern int xfer_sum_len;
|
||||
|
||||
int updating_basis_file;
|
||||
char sender_file_sum[MAX_DIGEST_LEN];
|
||||
@@ -140,11 +142,14 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
{
|
||||
OFF_T offset, aligned_offset, end;
|
||||
int32 k, want_i, aligned_i, backup;
|
||||
char sum2[SUM_LENGTH];
|
||||
char sum2[MAX_DIGEST_LEN];
|
||||
uint32 s1, s2, sum;
|
||||
int more;
|
||||
schar *map;
|
||||
|
||||
// prevent possible memory leaks
|
||||
memset(sum2, 0, sizeof sum2);
|
||||
|
||||
/* want_i is used to encourage adjacent matches, allowing the RLL
|
||||
* coding of the output to work more efficiently. */
|
||||
want_i = 0;
|
||||
@@ -230,7 +235,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
done_csum2 = 1;
|
||||
}
|
||||
|
||||
if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
|
||||
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0) {
|
||||
false_alarms++;
|
||||
continue;
|
||||
}
|
||||
@@ -250,7 +255,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
if (i != aligned_i) {
|
||||
if (sum != s->sums[aligned_i].sum1
|
||||
|| l != s->sums[aligned_i].len
|
||||
|| memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
|
||||
|| memcmp(sum2, sum2_at(s, aligned_i), s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
i = aligned_i;
|
||||
}
|
||||
@@ -269,7 +274,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
if (sum != s->sums[i].sum1)
|
||||
goto check_want_i;
|
||||
get_checksum2((char *)map, l, sum2);
|
||||
if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
|
||||
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
/* OK, we have a re-alignment match. Bump the offset
|
||||
* forward to the new match point. */
|
||||
@@ -288,7 +293,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
&& (!updating_basis_file || s->sums[want_i].offset >= offset
|
||||
|| s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
|
||||
&& sum == s->sums[want_i].sum1
|
||||
&& memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
|
||||
&& memcmp(sum2, sum2_at(s, want_i), s->s2length) == 0) {
|
||||
/* we've found an adjacent match - the RLL coder
|
||||
* will be happy */
|
||||
i = want_i;
|
||||
@@ -356,15 +361,13 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
**/
|
||||
void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
|
||||
{
|
||||
int sum_len;
|
||||
|
||||
last_match = 0;
|
||||
false_alarms = 0;
|
||||
hash_hits = 0;
|
||||
matches = 0;
|
||||
data_transfer = 0;
|
||||
|
||||
sum_init(xfersum_type, checksum_seed);
|
||||
sum_init(xfer_sum_nni, checksum_seed);
|
||||
|
||||
if (append_mode > 0) {
|
||||
if (append_mode == 2) {
|
||||
@@ -405,22 +408,22 @@ void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
|
||||
matched(f, s, buf, len, -1);
|
||||
}
|
||||
|
||||
sum_len = sum_end(sender_file_sum);
|
||||
sum_end(sender_file_sum);
|
||||
|
||||
/* If we had a read error, send a bad checksum. We use all bits
|
||||
* off as long as the checksum doesn't happen to be that, in
|
||||
* which case we turn the last 0 bit into a 1. */
|
||||
if (buf && buf->status != 0) {
|
||||
int i;
|
||||
for (i = 0; i < sum_len && sender_file_sum[i] == 0; i++) {}
|
||||
memset(sender_file_sum, 0, sum_len);
|
||||
if (i == sum_len)
|
||||
for (i = 0; i < xfer_sum_len && sender_file_sum[i] == 0; i++) {}
|
||||
memset(sender_file_sum, 0, xfer_sum_len);
|
||||
if (i == xfer_sum_len)
|
||||
sender_file_sum[i-1]++;
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(DELTASUM, 2))
|
||||
rprintf(FINFO,"sending file_sum\n");
|
||||
write_buf(f, sender_file_sum, sum_len);
|
||||
write_buf(f, sender_file_sum, xfer_sum_len);
|
||||
|
||||
if (DEBUG_GTE(DELTASUM, 2)) {
|
||||
rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ x"$2" = x ]; then
|
||||
echo "Usage: $0 SRC_DIR NAME.NUM.md" 1>&2
|
||||
if [ $# != 1 ]; then
|
||||
echo "Usage: $0 NAME.NUM.md" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
srcdir="$1"
|
||||
inname="$2"
|
||||
inname="$1"
|
||||
srcdir=`dirname "$0"`
|
||||
flagfile="$srcdir/.md2man-works"
|
||||
|
||||
if [ ! -d "$srcdir" ]; then
|
||||
echo "The specified SRC_DIR is not a directory: $srcdir" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
force_flagfile="$srcdir/.md2man-force"
|
||||
|
||||
if [ ! -f "$flagfile" ]; then
|
||||
# We test our smallest manpage just to see if the python setup works.
|
||||
if "$srcdir/md2man" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
|
||||
if "$srcdir/md-convert" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
|
||||
touch $flagfile
|
||||
else
|
||||
outname=`echo "$inname" | sed 's/\.md$//'`
|
||||
@@ -37,4 +33,10 @@ if [ ! -f "$flagfile" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
"$srcdir/md2man" "$srcdir/$inname"
|
||||
if [ -f "$force_flagfile" ]; then
|
||||
opt='--force-link-text'
|
||||
else
|
||||
opt=''
|
||||
fi
|
||||
|
||||
"$srcdir/md-convert" $opt "$srcdir/$inname"
|
||||
|
||||
667
md-convert
Executable file
667
md-convert
Executable file
@@ -0,0 +1,667 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# This script transforms markdown files into html and (optionally) nroff. The
|
||||
# output files are written into the current directory named for the input file
|
||||
# without the .md suffix and either the .html suffix or no suffix.
|
||||
#
|
||||
# If the input .md file has a section number at the end of the name (e.g.,
|
||||
# rsync.1.md) a nroff file is also output (PROJ.NUM.md -> PROJ.NUM).
|
||||
#
|
||||
# The markdown input format has one extra extension: if a numbered list starts
|
||||
# at 0, it is turned into a description list. The dl's dt tag is taken from the
|
||||
# contents of the first tag inside the li, which is usually a p, code, or
|
||||
# strong tag.
|
||||
#
|
||||
# The cmarkgfm or commonmark lib is used to transforms the input file into
|
||||
# html. Then, the html.parser is used as a state machine that lets us tweak
|
||||
# the html and (optionally) output nroff data based on the html tags.
|
||||
#
|
||||
# If the string @USE_GFM_PARSER@ exists in the file, the string is removed and
|
||||
# a github-flavored-markup parser is used to parse the file.
|
||||
#
|
||||
# The man-page .md files also get the vars @VERSION@, @BINDIR@, and @LIBDIR@
|
||||
# substituted. Some of these values depend on the Makefile $(prefix) (see the
|
||||
# generated Makefile). If the maintainer wants to build files for /usr/local
|
||||
# while creating release-ready man-page files for /usr, use the environment to
|
||||
# set RSYNC_OVERRIDE_PREFIX=/usr.
|
||||
|
||||
# Copyright (C) 2020 - 2021 Wayne Davison
|
||||
#
|
||||
# This program is freely redistributable.
|
||||
|
||||
import os, sys, re, argparse, subprocess, time
|
||||
from html.parser import HTMLParser
|
||||
|
||||
VALID_PAGES = 'README INSTALL COPYING rsync.1 rrsync.1 rsync-ssl.1 rsyncd.conf.5'.split()
|
||||
|
||||
CONSUMES_TXT = set('h1 h2 h3 p li pre'.split())
|
||||
|
||||
HTML_START = """\
|
||||
<html><head>
|
||||
<title>%TITLE%</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
max-width: 50em;
|
||||
margin: auto;
|
||||
}
|
||||
body, b, strong, u {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
|
||||
a.tgt:after { content: '🔗'; }
|
||||
a.tgt:hover { color: #444; background-color: #eaeaea; }
|
||||
h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
|
||||
code {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-weight: bold;
|
||||
white-space: pre;
|
||||
}
|
||||
pre code {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
blockquote pre code {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
dd p:first-of-type {
|
||||
margin-block-start: 0em;
|
||||
}
|
||||
</style>
|
||||
</head><body>
|
||||
"""
|
||||
|
||||
TABLE_STYLE = """\
|
||||
table {
|
||||
border-color: grey;
|
||||
border-spacing: 0;
|
||||
}
|
||||
tr {
|
||||
border-top: 1px solid grey;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #dfe2e5;
|
||||
text-align: center;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
"""
|
||||
|
||||
MAN_HTML_END = """\
|
||||
<div style="float: right"><p><i>%s</i></p></div>
|
||||
"""
|
||||
|
||||
HTML_END = """\
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
MAN_START = r"""
|
||||
.TH "%s" "%s" "%s" "%s" "User Commands"
|
||||
.\" prefix=%s
|
||||
""".lstrip()
|
||||
|
||||
MAN_END = """\
|
||||
"""
|
||||
|
||||
NORM_FONT = ('\1', r"\fP")
|
||||
BOLD_FONT = ('\2', r"\fB")
|
||||
UNDR_FONT = ('\3', r"\fI")
|
||||
NBR_DASH = ('\4', r"\-")
|
||||
NBR_SPACE = ('\xa0', r"\ ")
|
||||
|
||||
FILENAME_RE = re.compile(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+?)(\.(?P<sect>\d+))?)\.md)$')
|
||||
ASSIGNMENT_RE = re.compile(r'^(\w+)=(.+)')
|
||||
VER_RE = re.compile(r'^#define\s+RSYNC_VERSION\s+"(\d.+?)"', re.M)
|
||||
TZ_RE = re.compile(r'^#define\s+MAINTAINER_TZ_OFFSET\s+(-?\d+(\.\d+)?)', re.M)
|
||||
VAR_REF_RE = re.compile(r'\$\{(\w+)\}')
|
||||
VERSION_RE = re.compile(r' (\d[.\d]+)[, ]')
|
||||
BIN_CHARS_RE = re.compile(r'[\1-\7]+')
|
||||
LONG_OPT_DASH_RE = re.compile(r'(--\w[-\w]+)')
|
||||
SPACE_DOUBLE_DASH_RE = re.compile(r'\s--(\s)')
|
||||
NON_SPACE_SINGLE_DASH_RE = re.compile(r'(^|\W)-')
|
||||
WHITESPACE_RE = re.compile(r'\s')
|
||||
CODE_BLOCK_RE = re.compile(r'[%s]([^=%s]+)[=%s]' % (BOLD_FONT[0], NORM_FONT[0], NORM_FONT[0]))
|
||||
NBR_DASH_RE = re.compile(r'[%s]' % NBR_DASH[0])
|
||||
INVALID_TARGET_CHARS_RE = re.compile(r'[^-A-Za-z0-9._]')
|
||||
INVALID_START_CHAR_RE = re.compile(r'^([^A-Za-z0-9])')
|
||||
MANIFY_LINESTART_RE = re.compile(r"^(['.])", flags=re.M)
|
||||
|
||||
md_parser = None
|
||||
env_subs = { }
|
||||
|
||||
warning_count = 0
|
||||
|
||||
def main():
|
||||
for mdfn in args.mdfiles:
|
||||
parse_md_file(mdfn)
|
||||
|
||||
if args.test:
|
||||
print("The test was successful.")
|
||||
|
||||
|
||||
def parse_md_file(mdfn):
|
||||
fi = FILENAME_RE.match(mdfn)
|
||||
if not fi:
|
||||
die('Failed to parse a md input file name:', mdfn)
|
||||
fi = argparse.Namespace(**fi.groupdict())
|
||||
fi.want_manpage = not not fi.sect
|
||||
if fi.want_manpage:
|
||||
fi.title = fi.prog + '(' + fi.sect + ') manpage'
|
||||
else:
|
||||
fi.title = fi.prog + ' for rsync'
|
||||
|
||||
if fi.want_manpage:
|
||||
if not env_subs:
|
||||
find_man_substitutions()
|
||||
prog_ver = 'rsync ' + env_subs['VERSION']
|
||||
if fi.prog != 'rsync':
|
||||
prog_ver = fi.prog + ' from ' + prog_ver
|
||||
fi.man_headings = (fi.prog, fi.sect, env_subs['date'], prog_ver, env_subs['prefix'])
|
||||
|
||||
with open(mdfn, 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
|
||||
use_gfm_parser = '@USE_GFM_PARSER@' in txt
|
||||
if use_gfm_parser:
|
||||
txt = txt.replace('@USE_GFM_PARSER@', '')
|
||||
|
||||
if fi.want_manpage:
|
||||
txt = (txt.replace('@VERSION@', env_subs['VERSION'])
|
||||
.replace('@BINDIR@', env_subs['bindir'])
|
||||
.replace('@LIBDIR@', env_subs['libdir']))
|
||||
|
||||
if use_gfm_parser:
|
||||
if not gfm_parser:
|
||||
die('Input file requires cmarkgfm parser:', mdfn)
|
||||
fi.html_in = gfm_parser(txt)
|
||||
else:
|
||||
fi.html_in = md_parser(txt)
|
||||
txt = None
|
||||
|
||||
TransformHtml(fi)
|
||||
|
||||
if args.test:
|
||||
return
|
||||
|
||||
output_list = [ (fi.name + '.html', fi.html_out) ]
|
||||
if fi.want_manpage:
|
||||
output_list += [ (fi.name, fi.man_out) ]
|
||||
for fn, txt in output_list:
|
||||
if args.dest and args.dest != '.':
|
||||
fn = os.path.join(args.dest, fn)
|
||||
if os.path.lexists(fn):
|
||||
os.unlink(fn)
|
||||
print("Wrote:", fn)
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
|
||||
def find_man_substitutions():
|
||||
srcdir = os.path.dirname(sys.argv[0]) + '/'
|
||||
mtime = 0
|
||||
|
||||
git_dir = srcdir + '.git'
|
||||
if os.path.lexists(git_dir):
|
||||
mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
|
||||
|
||||
# Allow "prefix" to be overridden via the environment:
|
||||
env_subs['prefix'] = os.environ.get('RSYNC_OVERRIDE_PREFIX', None)
|
||||
|
||||
if args.test:
|
||||
env_subs['VERSION'] = '1.0.0'
|
||||
env_subs['bindir'] = '/usr/bin'
|
||||
env_subs['libdir'] = '/usr/lib/rsync'
|
||||
tz_offset = 0
|
||||
else:
|
||||
for fn in (srcdir + 'version.h', 'Makefile'):
|
||||
try:
|
||||
st = os.lstat(fn)
|
||||
except OSError:
|
||||
die('Failed to find', srcdir + fn)
|
||||
if not mtime:
|
||||
mtime = st.st_mtime
|
||||
|
||||
with open(srcdir + 'version.h', 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
m = VER_RE.search(txt)
|
||||
env_subs['VERSION'] = m.group(1)
|
||||
m = TZ_RE.search(txt) # the tzdata lib may not be installed, so we use a simple hour offset
|
||||
tz_offset = float(m.group(1)) * 60 * 60
|
||||
|
||||
with open('Makefile', 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
m = ASSIGNMENT_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
var, val = (m.group(1), m.group(2))
|
||||
if var == 'prefix' and env_subs[var] is not None:
|
||||
continue
|
||||
while VAR_REF_RE.search(val):
|
||||
val = VAR_REF_RE.sub(lambda m: env_subs[m.group(1)], val)
|
||||
env_subs[var] = val
|
||||
if var == 'srcdir':
|
||||
break
|
||||
|
||||
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(mtime + tz_offset)).lstrip('0')
|
||||
|
||||
if 'SOURCE_DATE_EPOCH' in os.environ:
|
||||
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
|
||||
|
||||
|
||||
def html_via_commonmark(txt):
|
||||
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
|
||||
|
||||
|
||||
class TransformHtml(HTMLParser):
|
||||
def __init__(self, fi):
|
||||
HTMLParser.__init__(self, convert_charrefs=True)
|
||||
|
||||
self.fn = fi.fn
|
||||
|
||||
st = self.state = argparse.Namespace(
|
||||
list_state = [ ],
|
||||
p_macro = ".P\n",
|
||||
at_first_tag_in_li = False,
|
||||
at_first_tag_in_dd = False,
|
||||
dt_from = None,
|
||||
in_pre = False,
|
||||
in_code = False,
|
||||
html_out = [ HTML_START.replace('%TITLE%', fi.title) ],
|
||||
man_out = [ ],
|
||||
txt = '',
|
||||
want_manpage = fi.want_manpage,
|
||||
created_hashtags = set(),
|
||||
derived_hashtags = set(),
|
||||
referenced_hashtags = set(),
|
||||
bad_hashtags = set(),
|
||||
latest_targets = [ ],
|
||||
opt_prefix = 'opt',
|
||||
a_href = None,
|
||||
a_href_external = False,
|
||||
a_txt_start = None,
|
||||
after_a_tag = False,
|
||||
target_suf = '',
|
||||
)
|
||||
|
||||
if st.want_manpage:
|
||||
st.man_out.append(MAN_START % fi.man_headings)
|
||||
|
||||
if '</table>' in fi.html_in:
|
||||
st.html_out[0] = st.html_out[0].replace('</style>', TABLE_STYLE + '</style>')
|
||||
|
||||
self.feed(fi.html_in)
|
||||
fi.html_in = None
|
||||
|
||||
if st.want_manpage:
|
||||
st.html_out.append(MAN_HTML_END % env_subs['date'])
|
||||
st.html_out.append(HTML_END)
|
||||
st.man_out.append(MAN_END)
|
||||
|
||||
fi.html_out = ''.join(st.html_out)
|
||||
st.html_out = None
|
||||
|
||||
fi.man_out = ''.join(st.man_out)
|
||||
st.man_out = None
|
||||
|
||||
for tgt, txt in st.derived_hashtags:
|
||||
derived = txt2target(txt, tgt)
|
||||
if derived not in st.created_hashtags:
|
||||
txt = BIN_CHARS_RE.sub('', txt.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' '))
|
||||
warn('Unknown derived hashtag link in', self.fn, 'based on:', (tgt, txt))
|
||||
|
||||
for bad in st.bad_hashtags:
|
||||
if bad in st.created_hashtags:
|
||||
warn('Missing "#" in hashtag link in', self.fn + ':', bad)
|
||||
else:
|
||||
warn('Unknown non-hashtag link in', self.fn + ':', bad)
|
||||
|
||||
for bad in st.referenced_hashtags - st.created_hashtags:
|
||||
warn('Unknown hashtag link in', self.fn + ':', '#' + bad)
|
||||
|
||||
def handle_UE(self):
|
||||
st = self.state
|
||||
if st.txt.startswith(('.', ',', '!', '?', ';', ':')):
|
||||
st.man_out[-1] = ".UE " + st.txt[0] + "\n"
|
||||
st.txt = st.txt[1:]
|
||||
st.after_a_tag = False
|
||||
|
||||
def handle_starttag(self, tag, attrs_list):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('START', (tag, attrs_list))
|
||||
if st.at_first_tag_in_li:
|
||||
if st.list_state[-1] == 'dl':
|
||||
st.dt_from = tag
|
||||
if tag == 'p':
|
||||
tag = 'dt'
|
||||
else:
|
||||
st.html_out.append('<dt>')
|
||||
elif tag == 'p':
|
||||
st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
|
||||
st.at_first_tag_in_li = False
|
||||
if tag == 'p':
|
||||
if not st.at_first_tag_in_dd:
|
||||
st.man_out.append(st.p_macro)
|
||||
elif tag == 'li':
|
||||
st.at_first_tag_in_li = True
|
||||
lstate = st.list_state[-1]
|
||||
if lstate == 'dl':
|
||||
return
|
||||
if lstate == 'o':
|
||||
st.man_out.append(".IP o\n")
|
||||
else:
|
||||
st.man_out.append(".IP " + str(lstate) + ".\n")
|
||||
st.list_state[-1] += 1
|
||||
elif tag == 'blockquote':
|
||||
st.man_out.append(".RS 4\n")
|
||||
elif tag == 'pre':
|
||||
st.in_pre = True
|
||||
st.man_out.append(st.p_macro + ".nf\n")
|
||||
elif tag == 'code' and not st.in_pre:
|
||||
st.in_code = True
|
||||
st.txt += BOLD_FONT[0]
|
||||
elif tag == 'strong' or tag == 'b':
|
||||
st.txt += BOLD_FONT[0]
|
||||
elif tag == 'em' or tag == 'i':
|
||||
if st.want_manpage:
|
||||
tag = 'u' # Change it into underline to be more like the manpage
|
||||
st.txt += UNDR_FONT[0]
|
||||
elif tag == 'ol':
|
||||
start = 1
|
||||
for var, val in attrs_list:
|
||||
if var == 'start':
|
||||
start = int(val) # We only support integers.
|
||||
break
|
||||
if st.list_state:
|
||||
st.man_out.append(".RS\n")
|
||||
if start == 0:
|
||||
tag = 'dl'
|
||||
attrs_list = [ ]
|
||||
st.list_state.append('dl')
|
||||
else:
|
||||
st.list_state.append(start)
|
||||
st.man_out.append(st.p_macro)
|
||||
st.p_macro = ".IP\n"
|
||||
elif tag == 'ul':
|
||||
st.man_out.append(st.p_macro)
|
||||
if st.list_state:
|
||||
st.man_out.append(".RS\n")
|
||||
st.p_macro = ".IP\n"
|
||||
st.list_state.append('o')
|
||||
elif tag == 'hr':
|
||||
st.man_out.append(".l\n")
|
||||
st.html_out.append("<hr />")
|
||||
return
|
||||
elif tag == 'a':
|
||||
st.a_href = None
|
||||
for var, val in attrs_list:
|
||||
if var == 'href':
|
||||
if val.startswith(('https://', 'http://', 'mailto:', 'ftp:')):
|
||||
if st.after_a_tag:
|
||||
self.handle_UE()
|
||||
st.man_out.append(manify(st.txt.strip()) + "\n")
|
||||
st.man_out.append(".UR " + val + "\n")
|
||||
st.txt = ''
|
||||
st.a_href = val
|
||||
st.a_href_external = True
|
||||
elif '#' in val:
|
||||
pg, tgt = val.split('#', 1)
|
||||
if pg and pg not in VALID_PAGES or '#' in tgt:
|
||||
st.bad_hashtags.add(val)
|
||||
elif tgt in ('', 'opt', 'dopt'):
|
||||
st.a_href = val
|
||||
st.a_href_external = False
|
||||
elif pg == '':
|
||||
st.referenced_hashtags.add(tgt)
|
||||
if tgt in st.latest_targets:
|
||||
warn('Found link to the current section in', self.fn + ':', val)
|
||||
elif val not in VALID_PAGES:
|
||||
st.bad_hashtags.add(val)
|
||||
st.a_txt_start = len(st.txt)
|
||||
st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
|
||||
st.at_first_tag_in_dd = False
|
||||
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('END', (tag,))
|
||||
if st.after_a_tag:
|
||||
self.handle_UE()
|
||||
if tag in CONSUMES_TXT or st.dt_from == tag:
|
||||
txt = st.txt.strip()
|
||||
st.txt = ''
|
||||
else:
|
||||
txt = None
|
||||
add_to_txt = None
|
||||
if tag == 'h1':
|
||||
tgt = txt
|
||||
target_suf = ''
|
||||
if tgt.startswith('NEWS for '):
|
||||
m = VERSION_RE.search(tgt)
|
||||
if m:
|
||||
tgt = m.group(1)
|
||||
st.target_suf = '-' + tgt
|
||||
self.add_targets(tag, tgt)
|
||||
elif tag == 'h2':
|
||||
st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
|
||||
self.add_targets(tag, txt, st.target_suf)
|
||||
st.opt_prefix = 'dopt' if txt == 'DAEMON OPTIONS' else 'opt'
|
||||
elif tag == 'h3':
|
||||
st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
|
||||
self.add_targets(tag, txt, st.target_suf)
|
||||
elif tag == 'p':
|
||||
if st.dt_from == 'p':
|
||||
tag = 'dt'
|
||||
st.man_out.append('.IP "' + manify(txt) + '"\n')
|
||||
if txt.startswith(BOLD_FONT[0]):
|
||||
self.add_targets(tag, txt)
|
||||
st.dt_from = None
|
||||
elif txt != '':
|
||||
st.man_out.append(manify(txt) + "\n")
|
||||
elif tag == 'li':
|
||||
if st.list_state[-1] == 'dl':
|
||||
if st.at_first_tag_in_li:
|
||||
die("Invalid 0. -> td translation")
|
||||
tag = 'dd'
|
||||
if txt != '':
|
||||
st.man_out.append(manify(txt) + "\n")
|
||||
st.at_first_tag_in_li = False
|
||||
elif tag == 'blockquote':
|
||||
st.man_out.append(".RE\n")
|
||||
elif tag == 'pre':
|
||||
st.in_pre = False
|
||||
st.man_out.append(manify(txt) + "\n.fi\n")
|
||||
elif (tag == 'code' and not st.in_pre):
|
||||
st.in_code = False
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'strong' or tag == 'b':
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'em' or tag == 'i':
|
||||
if st.want_manpage:
|
||||
tag = 'u' # Change it into underline to be more like the manpage
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'ol' or tag == 'ul':
|
||||
if st.list_state.pop() == 'dl':
|
||||
tag = 'dl'
|
||||
if st.list_state:
|
||||
st.man_out.append(".RE\n")
|
||||
else:
|
||||
st.p_macro = ".P\n"
|
||||
st.at_first_tag_in_dd = False
|
||||
elif tag == 'hr':
|
||||
return
|
||||
elif tag == 'a':
|
||||
if st.a_href_external:
|
||||
st.txt = st.txt.strip()
|
||||
if args.force_link_text or st.a_href != st.txt:
|
||||
st.man_out.append(manify(st.txt) + "\n")
|
||||
st.man_out.append(".UE\n") # This might get replaced with a punctuation version in handle_UE()
|
||||
st.after_a_tag = True
|
||||
st.a_href_external = False
|
||||
st.txt = ''
|
||||
elif st.a_href:
|
||||
atxt = st.txt[st.a_txt_start:]
|
||||
find = 'href="' + st.a_href + '"'
|
||||
for j in range(len(st.html_out)-1, 0, -1):
|
||||
if find in st.html_out[j]:
|
||||
pg, tgt = st.a_href.split('#', 1)
|
||||
derived = txt2target(atxt, tgt)
|
||||
if pg == '':
|
||||
if derived in st.latest_targets:
|
||||
warn('Found link to the current section in', self.fn + ':', st.a_href)
|
||||
st.derived_hashtags.add((tgt, atxt))
|
||||
st.html_out[j] = st.html_out[j].replace(find, 'href="' + pg + '#' + derived + '"')
|
||||
break
|
||||
else:
|
||||
die('INTERNAL ERROR: failed to find href in html data:', find)
|
||||
st.html_out.append('</' + tag + '>')
|
||||
if add_to_txt:
|
||||
if txt is None:
|
||||
st.txt += add_to_txt
|
||||
else:
|
||||
txt += add_to_txt
|
||||
if st.dt_from == tag:
|
||||
st.man_out.append('.IP "' + manify(txt) + '"\n')
|
||||
st.html_out.append('</dt><dd>')
|
||||
st.at_first_tag_in_dd = True
|
||||
st.dt_from = None
|
||||
elif tag == 'dt':
|
||||
st.html_out.append('<dd>')
|
||||
st.at_first_tag_in_dd = True
|
||||
|
||||
|
||||
def handle_data(self, txt):
|
||||
st = self.state
|
||||
if '](' in txt:
|
||||
warn('Malformed link in', self.fn + ':', txt)
|
||||
if args.debug:
|
||||
self.output_debug('DATA', (txt,))
|
||||
if st.in_pre:
|
||||
html = htmlify(txt)
|
||||
else:
|
||||
txt = LONG_OPT_DASH_RE.sub(lambda x: x.group(1).replace('-', NBR_DASH[0]), txt)
|
||||
txt = SPACE_DOUBLE_DASH_RE.sub(NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
|
||||
txt = NON_SPACE_SINGLE_DASH_RE.sub(r'\1' + NBR_DASH[0], txt)
|
||||
html = htmlify(txt)
|
||||
if st.in_code:
|
||||
txt = WHITESPACE_RE.sub(NBR_SPACE[0], txt)
|
||||
html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # <code> is non-breaking in CSS
|
||||
st.html_out.append(html.replace(NBR_SPACE[0], ' ').replace(NBR_DASH[0], '-⁠'))
|
||||
st.txt += txt
|
||||
|
||||
|
||||
def add_targets(self, tag, txt, suf=None):
|
||||
st = self.state
|
||||
tag = '<' + tag + '>'
|
||||
targets = CODE_BLOCK_RE.findall(txt)
|
||||
if not targets:
|
||||
targets = [ txt ]
|
||||
tag_pos = 0
|
||||
for txt in targets:
|
||||
txt = txt2target(txt, st.opt_prefix)
|
||||
if not txt:
|
||||
continue
|
||||
if suf:
|
||||
txt += suf
|
||||
if txt in st.created_hashtags:
|
||||
for j in range(2, 1000):
|
||||
chk = txt + '-' + str(j)
|
||||
if chk not in st.created_hashtags:
|
||||
print('Made link target unique:', chk)
|
||||
txt = chk
|
||||
break
|
||||
if tag_pos == 0:
|
||||
tag_pos -= 1
|
||||
while st.html_out[tag_pos] != tag:
|
||||
tag_pos -= 1
|
||||
st.html_out[tag_pos] = tag[:-1] + ' id="' + txt + '">'
|
||||
st.html_out.append('<a href="#' + txt + '" class="tgt"></a>')
|
||||
tag_pos -= 1 # take into account the append
|
||||
else:
|
||||
st.html_out[tag_pos] = '<span id="' + txt + '"></span>' + st.html_out[tag_pos]
|
||||
st.created_hashtags.add(txt)
|
||||
st.latest_targets = targets
|
||||
|
||||
|
||||
def output_debug(self, event, extra):
|
||||
import pprint
|
||||
st = self.state
|
||||
if args.debug < 2:
|
||||
st = argparse.Namespace(**vars(st))
|
||||
if len(st.html_out) > 2:
|
||||
st.html_out = ['...'] + st.html_out[-2:]
|
||||
if len(st.man_out) > 2:
|
||||
st.man_out = ['...'] + st.man_out[-2:]
|
||||
print(event, extra)
|
||||
pprint.PrettyPrinter(indent=2).pprint(vars(st))
|
||||
|
||||
|
||||
def txt2target(txt, opt_prefix):
|
||||
txt = txt.strip().rstrip(':')
|
||||
m = CODE_BLOCK_RE.search(txt)
|
||||
if m:
|
||||
txt = m.group(1)
|
||||
txt = NBR_DASH_RE.sub('-', txt)
|
||||
txt = BIN_CHARS_RE.sub('', txt)
|
||||
txt = INVALID_TARGET_CHARS_RE.sub('_', txt)
|
||||
if opt_prefix and txt.startswith('-'):
|
||||
txt = opt_prefix + txt
|
||||
else:
|
||||
txt = INVALID_START_CHAR_RE.sub(r't\1', txt)
|
||||
return txt
|
||||
|
||||
|
||||
def manify(txt):
|
||||
return MANIFY_LINESTART_RE.sub(r'\&\1', txt.replace('\\', '\\\\')
|
||||
.replace(NBR_SPACE[0], NBR_SPACE[1])
|
||||
.replace(NBR_DASH[0], NBR_DASH[1])
|
||||
.replace(NORM_FONT[0], NORM_FONT[1])
|
||||
.replace(BOLD_FONT[0], BOLD_FONT[1])
|
||||
.replace(UNDR_FONT[0], UNDR_FONT[1]))
|
||||
|
||||
|
||||
def htmlify(txt):
|
||||
return txt.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|
||||
|
||||
|
||||
def warn(*msg):
|
||||
print(*msg, file=sys.stderr)
|
||||
global warning_count
|
||||
warning_count += 1
|
||||
|
||||
|
||||
def die(*msg):
|
||||
warn(*msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Convert markdown into html and (optionally) nroff. Each input filename must have a .md suffix, which is changed to .html for the output filename. If the input filename ends with .num.md (e.g. foo.1.md) then a nroff file is also output with the input filename's .md suffix removed (e.g. foo.1).", add_help=False)
|
||||
parser.add_argument('--test', action='store_true', help="Just test the parsing without outputting any files.")
|
||||
parser.add_argument('--dest', metavar='DIR', help="Create files in DIR instead of the current directory.")
|
||||
parser.add_argument('--force-link-text', action='store_true', help="Don't remove the link text if it matches the link href. Useful when nroff doesn't understand .UR and .UE.")
|
||||
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
import cmarkgfm
|
||||
md_parser = cmarkgfm.markdown_to_html
|
||||
gfm_parser = cmarkgfm.github_flavored_markdown_to_html
|
||||
except:
|
||||
try:
|
||||
import commonmark
|
||||
md_parser = html_via_commonmark
|
||||
except:
|
||||
die("Failed to find cmarkgfm or commonmark for python3.")
|
||||
gfm_parser = None
|
||||
|
||||
main()
|
||||
if warning_count:
|
||||
sys.exit(1)
|
||||
382
md2man
382
md2man
@@ -1,382 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# This script takes a manpage written in markdown and turns it into an html web
|
||||
# page and a nroff man page. The input file must have the name of the program
|
||||
# and the section in this format: NAME.NUM.md. The output files are written
|
||||
# into the current directory named NAME.NUM.html and NAME.NUM. The input
|
||||
# format has one extra extension: if a numbered list starts at 0, it is turned
|
||||
# into a description list. The dl's dt tag is taken from the contents of the
|
||||
# first tag inside the li, which is usually a p, code, or strong tag. The
|
||||
# cmarkgfm or commonmark lib is used to transforms the input file into html.
|
||||
# The html.parser is used as a state machine that both tweaks the html and
|
||||
# outputs the nroff data based on the html tags.
|
||||
#
|
||||
# Copyright (C) 2020 Wayne Davison
|
||||
#
|
||||
# This program is freely redistributable.
|
||||
|
||||
import sys, os, re, argparse, subprocess, time
|
||||
from html.parser import HTMLParser
|
||||
|
||||
CONSUMES_TXT = set('h1 h2 p li pre'.split())
|
||||
|
||||
HTML_START = """\
|
||||
<html><head>
|
||||
<title>%s</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
max-width: 50em;
|
||||
margin: auto;
|
||||
}
|
||||
body, b, strong, u {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
code {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-weight: bold;
|
||||
white-space: pre;
|
||||
}
|
||||
pre code {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
blockquote pre code {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
dd p:first-of-type {
|
||||
margin-block-start: 0em;
|
||||
}
|
||||
</style>
|
||||
</head><body>
|
||||
"""
|
||||
|
||||
HTML_END = """\
|
||||
<div style="float: right"><p><i>%s</i></p></div>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
MAN_START = r"""
|
||||
.TH "%s" "%s" "%s" "%s" "User Commands"
|
||||
""".lstrip()
|
||||
|
||||
MAN_END = """\
|
||||
"""
|
||||
|
||||
NORM_FONT = ('\1', r"\fP")
|
||||
BOLD_FONT = ('\2', r"\fB")
|
||||
UNDR_FONT = ('\3', r"\fI")
|
||||
NBR_DASH = ('\4', r"\-")
|
||||
NBR_SPACE = ('\xa0', r"\ ")
|
||||
|
||||
md_parser = None
|
||||
|
||||
def main():
|
||||
fi = re.match(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+)\.(?P<sect>\d+))\.md)$', args.mdfile)
|
||||
if not fi:
|
||||
die('Failed to parse NAME.NUM.md out of input file:', args.mdfile)
|
||||
fi = argparse.Namespace(**fi.groupdict())
|
||||
|
||||
if not fi.srcdir:
|
||||
fi.srcdir = './'
|
||||
|
||||
fi.title = fi.prog + '(' + fi.sect + ') man page'
|
||||
fi.mtime = 0
|
||||
|
||||
git_dir = fi.srcdir + '.git'
|
||||
if os.path.lexists(git_dir):
|
||||
fi.mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
|
||||
|
||||
env_subs = { 'prefix': os.environ.get('RSYNC_OVERRIDE_PREFIX', None) }
|
||||
|
||||
if args.test:
|
||||
env_subs['VERSION'] = '1.0.0'
|
||||
env_subs['libdir'] = '/usr'
|
||||
else:
|
||||
for fn in (fi.srcdir + 'NEWS.md', 'Makefile'):
|
||||
try:
|
||||
st = os.lstat(fn)
|
||||
except:
|
||||
die('Failed to find', fi.srcdir + fn)
|
||||
if not fi.mtime:
|
||||
fi.mtime = st.st_mtime
|
||||
|
||||
with open('Makefile', 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
m = re.match(r'^(\w+)=(.+)', line)
|
||||
if not m:
|
||||
continue
|
||||
var, val = (m.group(1), m.group(2))
|
||||
if var == 'prefix' and env_subs[var] is not None:
|
||||
continue
|
||||
while re.search(r'\$\{', val):
|
||||
val = re.sub(r'\$\{(\w+)\}', lambda m: env_subs[m.group(1)], val)
|
||||
env_subs[var] = val
|
||||
if var == 'VERSION':
|
||||
break
|
||||
|
||||
with open(fi.fn, 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
|
||||
txt = re.sub(r'@VERSION@', env_subs['VERSION'], txt)
|
||||
txt = re.sub(r'@LIBDIR@', env_subs['libdir'], txt)
|
||||
|
||||
fi.html_in = md_parser(txt)
|
||||
txt = None
|
||||
|
||||
fi.date = time.strftime('%d %b %Y', time.localtime(fi.mtime))
|
||||
fi.man_headings = (fi.prog, fi.sect, fi.date, fi.prog + ' ' + env_subs['VERSION'])
|
||||
|
||||
HtmlToManPage(fi)
|
||||
|
||||
if args.test:
|
||||
print("The test was successful.")
|
||||
return
|
||||
|
||||
for fn, txt in ((fi.name + '.html', fi.html_out), (fi.name, fi.man_out)):
|
||||
print("Wrote:", fn)
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
|
||||
def html_via_cmarkgfm(txt):
|
||||
return cmarkgfm.markdown_to_html(txt)
|
||||
|
||||
|
||||
def html_via_commonmark(txt):
|
||||
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
|
||||
|
||||
|
||||
class HtmlToManPage(HTMLParser):
|
||||
def __init__(self, fi):
|
||||
HTMLParser.__init__(self, convert_charrefs=True)
|
||||
|
||||
st = self.state = argparse.Namespace(
|
||||
list_state = [ ],
|
||||
p_macro = ".P\n",
|
||||
at_first_tag_in_li = False,
|
||||
at_first_tag_in_dd = False,
|
||||
dt_from = None,
|
||||
in_pre = False,
|
||||
in_code = False,
|
||||
html_out = [ HTML_START % fi.title ],
|
||||
man_out = [ MAN_START % fi.man_headings ],
|
||||
txt = '',
|
||||
)
|
||||
|
||||
self.feed(fi.html_in)
|
||||
fi.html_in = None
|
||||
|
||||
st.html_out.append(HTML_END % fi.date)
|
||||
st.man_out.append(MAN_END)
|
||||
|
||||
fi.html_out = ''.join(st.html_out)
|
||||
st.html_out = None
|
||||
|
||||
fi.man_out = ''.join(st.man_out)
|
||||
st.man_out = None
|
||||
|
||||
|
||||
def handle_starttag(self, tag, attrs_list):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('START', (tag, attrs_list))
|
||||
if st.at_first_tag_in_li:
|
||||
if st.list_state[-1] == 'dl':
|
||||
st.dt_from = tag
|
||||
if tag == 'p':
|
||||
tag = 'dt'
|
||||
else:
|
||||
st.html_out.append('<dt>')
|
||||
elif tag == 'p':
|
||||
st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
|
||||
st.at_first_tag_in_li = False
|
||||
if tag == 'p':
|
||||
if not st.at_first_tag_in_dd:
|
||||
st.man_out.append(st.p_macro)
|
||||
elif tag == 'li':
|
||||
st.at_first_tag_in_li = True
|
||||
lstate = st.list_state[-1]
|
||||
if lstate == 'dl':
|
||||
return
|
||||
if lstate == 'o':
|
||||
st.man_out.append(".IP o\n")
|
||||
else:
|
||||
st.man_out.append(".IP " + str(lstate) + ".\n")
|
||||
st.list_state[-1] += 1
|
||||
elif tag == 'blockquote':
|
||||
st.man_out.append(".RS 4\n")
|
||||
elif tag == 'pre':
|
||||
st.in_pre = True
|
||||
st.man_out.append(st.p_macro + ".nf\n")
|
||||
elif tag == 'code' and not st.in_pre:
|
||||
st.in_code = True
|
||||
st.txt += BOLD_FONT[0]
|
||||
elif tag == 'strong' or tag == 'b':
|
||||
st.txt += BOLD_FONT[0]
|
||||
elif tag == 'em' or tag == 'i':
|
||||
tag = 'u' # Change it into underline to be more like the man page
|
||||
st.txt += UNDR_FONT[0]
|
||||
elif tag == 'ol':
|
||||
start = 1
|
||||
for var, val in attrs_list:
|
||||
if var == 'start':
|
||||
start = int(val) # We only support integers.
|
||||
break
|
||||
if st.list_state:
|
||||
st.man_out.append(".RS\n")
|
||||
if start == 0:
|
||||
tag = 'dl'
|
||||
attrs_list = [ ]
|
||||
st.list_state.append('dl')
|
||||
else:
|
||||
st.list_state.append(start)
|
||||
st.man_out.append(st.p_macro)
|
||||
st.p_macro = ".IP\n"
|
||||
elif tag == 'ul':
|
||||
st.man_out.append(st.p_macro)
|
||||
if st.list_state:
|
||||
st.man_out.append(".RS\n")
|
||||
st.p_macro = ".IP\n"
|
||||
st.list_state.append('o')
|
||||
st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
|
||||
st.at_first_tag_in_dd = False
|
||||
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('END', (tag,))
|
||||
if tag in CONSUMES_TXT or st.dt_from == tag:
|
||||
txt = st.txt.strip()
|
||||
st.txt = ''
|
||||
else:
|
||||
txt = None
|
||||
add_to_txt = None
|
||||
if tag == 'h1':
|
||||
st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
|
||||
elif tag == 'h2':
|
||||
st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
|
||||
elif tag == 'p':
|
||||
if st.dt_from == 'p':
|
||||
tag = 'dt'
|
||||
st.man_out.append('.IP "' + manify(txt) + '"\n')
|
||||
st.dt_from = None
|
||||
elif txt != '':
|
||||
st.man_out.append(manify(txt) + "\n")
|
||||
elif tag == 'li':
|
||||
if st.list_state[-1] == 'dl':
|
||||
if st.at_first_tag_in_li:
|
||||
die("Invalid 0. -> td translation")
|
||||
tag = 'dd'
|
||||
if txt != '':
|
||||
st.man_out.append(manify(txt) + "\n")
|
||||
st.at_first_tag_in_li = False
|
||||
elif tag == 'blockquote':
|
||||
st.man_out.append(".RE\n")
|
||||
elif tag == 'pre':
|
||||
st.in_pre = False
|
||||
st.man_out.append(manify(txt) + "\n.fi\n")
|
||||
elif (tag == 'code' and not st.in_pre):
|
||||
st.in_code = False
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'strong' or tag == 'b':
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'em' or tag == 'i':
|
||||
tag = 'u' # Change it into underline to be more like the man page
|
||||
add_to_txt = NORM_FONT[0]
|
||||
elif tag == 'ol' or tag == 'ul':
|
||||
if st.list_state.pop() == 'dl':
|
||||
tag = 'dl'
|
||||
if st.list_state:
|
||||
st.man_out.append(".RE\n")
|
||||
else:
|
||||
st.p_macro = ".P\n"
|
||||
st.at_first_tag_in_dd = False
|
||||
st.html_out.append('</' + tag + '>')
|
||||
if add_to_txt:
|
||||
if txt is None:
|
||||
st.txt += add_to_txt
|
||||
else:
|
||||
txt += add_to_txt
|
||||
if st.dt_from == tag:
|
||||
st.man_out.append('.IP "' + manify(txt) + '"\n')
|
||||
st.html_out.append('</dt><dd>')
|
||||
st.at_first_tag_in_dd = True
|
||||
st.dt_from = None
|
||||
elif tag == 'dt':
|
||||
st.html_out.append('<dd>')
|
||||
st.at_first_tag_in_dd = True
|
||||
|
||||
|
||||
def handle_data(self, txt):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('DATA', (txt,))
|
||||
if st.in_pre:
|
||||
html = htmlify(txt)
|
||||
else:
|
||||
txt = re.sub(r'\s--(\s)', NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
|
||||
txt = re.sub(r'(^|\W)-', r'\1' + NBR_DASH[0], txt)
|
||||
html = htmlify(txt)
|
||||
if st.in_code:
|
||||
txt = re.sub(r'\s', NBR_SPACE[0], txt)
|
||||
html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # <code> is non-breaking in CSS
|
||||
st.html_out.append(html.replace(NBR_SPACE[0], ' ').replace(NBR_DASH[0], '-⁠'))
|
||||
st.txt += txt
|
||||
|
||||
|
||||
def output_debug(self, event, extra):
|
||||
import pprint
|
||||
st = self.state
|
||||
if args.debug < 2:
|
||||
st = argparse.Namespace(**vars(st))
|
||||
if len(st.html_out) > 2:
|
||||
st.html_out = ['...'] + st.html_out[-2:]
|
||||
if len(st.man_out) > 2:
|
||||
st.man_out = ['...'] + st.man_out[-2:]
|
||||
print(event, extra)
|
||||
pprint.PrettyPrinter(indent=2).pprint(vars(st))
|
||||
|
||||
|
||||
def manify(txt):
|
||||
return re.sub(r"^(['.])", r'\&\1', txt.replace('\\', '\\\\')
|
||||
.replace(NBR_SPACE[0], NBR_SPACE[1])
|
||||
.replace(NBR_DASH[0], NBR_DASH[1])
|
||||
.replace(NORM_FONT[0], NORM_FONT[1])
|
||||
.replace(BOLD_FONT[0], BOLD_FONT[1])
|
||||
.replace(UNDR_FONT[0], UNDR_FONT[1]), flags=re.M)
|
||||
|
||||
|
||||
def htmlify(txt):
|
||||
return txt.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|
||||
|
||||
|
||||
def warn(*msg):
|
||||
print(*msg, file=sys.stderr)
|
||||
|
||||
|
||||
def die(*msg):
|
||||
warn(*msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Transform a NAME.NUM.md markdown file into a NAME.NUM.html web page & a NAME.NUM man page.', add_help=False)
|
||||
parser.add_argument('--test', action='store_true', help='Test if we can parse the input w/o updating any files.')
|
||||
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument('mdfile', help="The NAME.NUM.md file to parse.")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
import cmarkgfm
|
||||
md_parser = html_via_cmarkgfm
|
||||
except:
|
||||
try:
|
||||
import commonmark
|
||||
md_parser = html_via_commonmark
|
||||
except:
|
||||
die("Failed to find cmarkgfm or commonmark for python3.")
|
||||
|
||||
main()
|
||||
22
mkgitver
Executable file
22
mkgitver
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
srcdir=`dirname $0`
|
||||
|
||||
if [ ! -f git-version.h ]; then
|
||||
touch git-version.h
|
||||
fi
|
||||
|
||||
if test -d "$srcdir/.git" || test -f "$srcdir/.git"; then
|
||||
gitver=`git describe --abbrev=8 2>/dev/null`
|
||||
# NOTE: I'm avoiding "|" in sed since I'm not sure if sed -r is portable and "\|" fails on some OSes.
|
||||
verchk=`echo "$gitver-" | sed -n '/^v3\.[0-9][0-9]*\.[0-9][0-9]*\(pre[0-9]*\)*-/p'`
|
||||
if [ -n "$verchk" ]; then
|
||||
echo "#define RSYNC_GITVER \"$gitver\"" >git-version.h.new
|
||||
if ! diff git-version.h.new git-version.h >/dev/null; then
|
||||
echo "Updating git-version.h"
|
||||
mv git-version.h.new git-version.h
|
||||
else
|
||||
rm git-version.h.new
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
BEGIN {
|
||||
while ((getline i < "proto.h") > 0) old_protos = old_protos ? old_protos "\n" i : i
|
||||
close("proto.h")
|
||||
protos = "/* This file is automatically generated with \"make proto\". DO NOT EDIT */\n"
|
||||
}
|
||||
|
||||
@@ -35,5 +36,5 @@ inheader {
|
||||
|
||||
END {
|
||||
if (old_protos != protos) print protos > "proto.h"
|
||||
printf "" > "proto.h-tstamp"
|
||||
system("touch proto.h-tstamp")
|
||||
}
|
||||
|
||||
12
packaging/auto-Makefile
Normal file
12
packaging/auto-Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
TARGETS := all install install-ssl-daemon install-all install-strip conf gen reconfigure restatus \
|
||||
proto man clean cleantests distclean test check check29 check30 installcheck splint \
|
||||
doxygen doxygen-upload finddead rrsync
|
||||
|
||||
.PHONY: $(TARGETS) auto-prep
|
||||
|
||||
$(TARGETS): auto-prep
|
||||
make -C build $@
|
||||
|
||||
auto-prep:
|
||||
@if test x`packaging/prep-auto-dir` = x; then echo "auto-build-save is not setup"; exit 1; fi
|
||||
@echo 'Build branch: '`readlink build/.branch | tr % /`
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script turns one or more diff files in the patches dir (which is
|
||||
# expected to be a checkout of the rsync-patches git repo) into a branch
|
||||
# in the main rsync git checkout. This allows the applied patch to be
|
||||
# merged with the latest rsync changes and tested. To update the diff
|
||||
# with the resulting changes, see the patch-update script.
|
||||
|
||||
import os, sys, re, argparse, glob
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
def main():
|
||||
global created, info, local_branch
|
||||
|
||||
cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
|
||||
|
||||
local_branch = get_patch_branches(args.base_branch)
|
||||
|
||||
if args.delete_local_branches:
|
||||
for name in sorted(local_branch):
|
||||
branch = f"patch/{args.base_branch}/{name}"
|
||||
cmd_chk(['git', 'branch', '-D', branch])
|
||||
local_branch = set()
|
||||
|
||||
if args.add_missing:
|
||||
for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")):
|
||||
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
|
||||
if name not in local_branch and fn not in args.patch_files:
|
||||
args.patch_files.append(fn)
|
||||
|
||||
if not args.patch_files:
|
||||
return
|
||||
|
||||
for fn in args.patch_files:
|
||||
if not fn.endswith('.diff'):
|
||||
die(f"Filename is not a .diff file: {fn}")
|
||||
if not os.path.isfile(fn):
|
||||
die(f"File not found: {fn}")
|
||||
|
||||
scanned = set()
|
||||
info = { }
|
||||
|
||||
patch_list = [ ]
|
||||
for fn in args.patch_files:
|
||||
m = re.match(r'^(?P<dir>.*?)(?P<name>[^/]+)\.diff$', fn)
|
||||
patch = argparse.Namespace(**m.groupdict())
|
||||
if patch.name in scanned:
|
||||
continue
|
||||
patch.fn = fn
|
||||
|
||||
lines = [ ]
|
||||
commit_hash = None
|
||||
with open(patch.fn, 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
m = re.match(r'^based-on: (\S+)', line)
|
||||
if m:
|
||||
commit_hash = m[1]
|
||||
break
|
||||
if (re.match(r'^index .*\.\..* \d', line)
|
||||
or re.match(r'^diff --git ', line)
|
||||
or re.match(r'^--- (old|a)/', line)):
|
||||
break
|
||||
lines.append(re.sub(r'\s*\Z', "\n", line, 1))
|
||||
info_txt = ''.join(lines).strip() + "\n"
|
||||
lines = None
|
||||
|
||||
parent = args.base_branch
|
||||
patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt)
|
||||
if patches:
|
||||
last = patches.pop()
|
||||
if last != patch.name:
|
||||
warn(f"No identity patch line in {patch.fn}")
|
||||
patches.append(last)
|
||||
if patches:
|
||||
parent = patches.pop()
|
||||
if parent not in scanned:
|
||||
diff_fn = patch.dir + parent + '.diff'
|
||||
if not os.path.isfile(diff_fn):
|
||||
die(f"Failed to find parent of {patch.fn}: {parent}")
|
||||
# Add parent to args.patch_files so that we will look for the
|
||||
# parent's parent. Any duplicates will be ignored.
|
||||
args.patch_files.append(diff_fn)
|
||||
else:
|
||||
warn(f"No patch lines found in {patch.fn}")
|
||||
|
||||
info[patch.name] = [ parent, info_txt, commit_hash ]
|
||||
|
||||
patch_list.append(patch)
|
||||
|
||||
created = set()
|
||||
for patch in patch_list:
|
||||
create_branch(patch)
|
||||
|
||||
cmd_chk(['git', 'checkout', args.base_branch])
|
||||
|
||||
|
||||
def create_branch(patch):
|
||||
if patch.name in created:
|
||||
return
|
||||
created.add(patch.name)
|
||||
|
||||
parent, info_txt, commit_hash = info[patch.name]
|
||||
parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff')
|
||||
|
||||
if parent.name == args.base_branch:
|
||||
parent_branch = commit_hash if commit_hash else args.base_branch
|
||||
else:
|
||||
create_branch(parent)
|
||||
parent_branch = '/'.join(['patch', args.base_branch, parent.name])
|
||||
|
||||
branch = '/'.join(['patch', args.base_branch, patch.name])
|
||||
print("\n" + '=' * 64)
|
||||
print(f"Processing {branch} ({parent_branch})")
|
||||
|
||||
if patch.name in local_branch:
|
||||
cmd_chk(['git', 'branch', '-D', branch])
|
||||
|
||||
cmd_chk(['git', 'checkout', '-b', branch, parent_branch])
|
||||
|
||||
info_fn = 'PATCH.' + patch.name
|
||||
with open(info_fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(info_txt)
|
||||
cmd_chk(['git', 'add', info_fn])
|
||||
|
||||
with open(patch.fn, 'r', encoding='utf-8') as fh:
|
||||
patch_txt = fh.read()
|
||||
|
||||
cmd_run('patch -p1'.split(), input=patch_txt)
|
||||
|
||||
for fn in glob.glob('*.orig') + glob.glob('*/*.orig'):
|
||||
os.unlink(fn)
|
||||
|
||||
pos = 0
|
||||
new_file_re = re.compile(r'\nnew file mode (?P<mode>\d+)\s+--- /dev/null\s+\+\+\+ b/(?P<fn>.+)')
|
||||
while True:
|
||||
m = new_file_re.search(patch_txt, pos)
|
||||
if not m:
|
||||
break
|
||||
os.chmod(m['fn'], int(m['mode'], 8))
|
||||
cmd_chk(['git', 'add', m['fn']])
|
||||
pos = m.end()
|
||||
|
||||
while True:
|
||||
cmd_chk('git status'.split())
|
||||
ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ')
|
||||
if ans == '':
|
||||
break
|
||||
cmd_chk("git add " + ans, shell=True)
|
||||
|
||||
while True:
|
||||
s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."])
|
||||
if not s.returncode:
|
||||
break
|
||||
s = cmd_run(['/bin/zsh'])
|
||||
if s.returncode:
|
||||
die('Aborting due to shell error code')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
|
||||
parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.")
|
||||
parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
|
||||
parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.")
|
||||
parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
|
||||
parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
148
packaging/cull-options
Executable file
148
packaging/cull-options
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
# This script outputs either perl or python code that parses all possible options
|
||||
# that the code in options.c might send to the server. The resulting code is then
|
||||
# included in the rrsync script.
|
||||
|
||||
import re, argparse
|
||||
|
||||
short_no_arg = { }
|
||||
short_with_num = { '@': 1 }
|
||||
long_opts = { # These include some extra long-args that BackupPC uses:
|
||||
'block-size': 1,
|
||||
'daemon': -1,
|
||||
'debug': 1,
|
||||
'fake-super': 0,
|
||||
'fuzzy': 0,
|
||||
'group': 0,
|
||||
'hard-links': 0,
|
||||
'ignore-times': 0,
|
||||
'info': 1,
|
||||
'links': 0,
|
||||
'log-file': 3,
|
||||
'munge-links': 0,
|
||||
'no-munge-links': -1,
|
||||
'one-file-system': 0,
|
||||
'owner': 0,
|
||||
'perms': 0,
|
||||
'recursive': 0,
|
||||
'stderr': 1,
|
||||
'times': 0,
|
||||
'copy-devices': -1,
|
||||
'write-devices': -1,
|
||||
}
|
||||
|
||||
def main():
|
||||
last_long_opt = None
|
||||
|
||||
with open('../options.c') as fh:
|
||||
for line in fh:
|
||||
m = re.search(r"argstr\[x\+\+\] = '([^.ie])'", line)
|
||||
if m:
|
||||
short_no_arg[m.group(1)] = 1
|
||||
last_long_opt = None
|
||||
continue
|
||||
|
||||
m = re.search(r'asprintf\([^,]+, "-([a-zA-Z0-9])\%l?[ud]"', line)
|
||||
if m:
|
||||
short_with_num[m.group(1)] = 1
|
||||
last_long_opt = None
|
||||
continue
|
||||
|
||||
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)"', line)
|
||||
if m:
|
||||
last_long_opt = m.group(1)
|
||||
if last_long_opt not in long_opts:
|
||||
long_opts[last_long_opt] = 0
|
||||
else:
|
||||
last_long_opt = None
|
||||
continue
|
||||
|
||||
if last_long_opt:
|
||||
m = re.search(r'args\[ac\+\+\] = safe_arg\("", ([^[("\s]+)\);', line)
|
||||
if m:
|
||||
long_opts[last_long_opt] = 2
|
||||
last_long_opt = None
|
||||
continue
|
||||
if 'args[ac++] = ' in line:
|
||||
last_long_opt = None
|
||||
|
||||
m = re.search(r'return "--([^"]+-dest)";', line)
|
||||
if m:
|
||||
long_opts[m.group(1)] = 2
|
||||
last_long_opt = None
|
||||
continue
|
||||
|
||||
m = re.search(r'asprintf\([^,]+, "--([^"=]+)=', line)
|
||||
if not m:
|
||||
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)=', line)
|
||||
if not m:
|
||||
m = re.search(r'args\[ac\+\+\] = safe_arg\("--([^"=]+)"', line)
|
||||
if not m:
|
||||
m = re.search(r'fmt = .*: "--([^"=]+)=', line)
|
||||
if m:
|
||||
long_opts[m.group(1)] = 1
|
||||
last_long_opt = None
|
||||
|
||||
long_opts['files-from'] = 3
|
||||
|
||||
txt = """\
|
||||
### START of options data produced by the cull-options script. ###
|
||||
|
||||
# To disable a short-named option, add its letter to this string:
|
||||
"""
|
||||
|
||||
txt += str_assign('short_disabled', 's') + "\n"
|
||||
txt += '# These are also disabled when the restricted dir is not "/":\n'
|
||||
txt += str_assign('short_disabled_subdir', 'KLk') + "\n"
|
||||
txt += '# These are all possible short options that we will accept (when not disabled above):\n'
|
||||
txt += str_assign('short_no_arg', ''.join(sorted(short_no_arg)), 'DO NOT REMOVE ANY')
|
||||
txt += str_assign('short_with_num', ''.join(sorted(short_with_num)), 'DO NOT REMOVE ANY')
|
||||
|
||||
txt += """
|
||||
# To disable a long-named option, change its value to a -1. The values mean:
|
||||
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
|
||||
# check the arg when receiving; and 3 = always check the arg.
|
||||
"""
|
||||
|
||||
print(txt, end='')
|
||||
|
||||
if args.python:
|
||||
print("long_opts = {")
|
||||
sep = ':'
|
||||
else:
|
||||
print("our %long_opt = (")
|
||||
sep = ' =>'
|
||||
|
||||
for opt in sorted(long_opts):
|
||||
if opt.startswith(('min-', 'max-')):
|
||||
val = 1
|
||||
else:
|
||||
val = long_opts[opt]
|
||||
print(' ', repr(opt) + sep, str(val) + ',')
|
||||
|
||||
if args.python:
|
||||
print("}")
|
||||
else:
|
||||
print(");")
|
||||
print("\n### END of options data produced by the cull-options script. ###")
|
||||
|
||||
|
||||
def str_assign(name, val, comment=None):
|
||||
comment = ' # ' + comment if comment else ''
|
||||
if args.python:
|
||||
return name + ' = ' + repr(val) + comment + "\n"
|
||||
return 'our $' + name + ' = ' + repr(val) + ';' + comment + "\n"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Output culled rsync options for rrsync.", add_help=False)
|
||||
out_group = parser.add_mutually_exclusive_group()
|
||||
out_group.add_argument('--perl', action='store_true', help="Output perl code.")
|
||||
out_group.add_argument('--python', action='store_true', help="Output python code (the default).")
|
||||
parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
if not args.perl:
|
||||
args.python = True
|
||||
main()
|
||||
|
||||
# vim: sw=4 et
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
# This script outputs some perl code that parses all possible options
|
||||
# that the code in options.c might send to the server. This perl code
|
||||
# is included in the rrsync script.
|
||||
use strict;
|
||||
|
||||
our %short_no_arg;
|
||||
our %short_with_num = ( '@' => 1 );
|
||||
our %long_opt = ( # These include some extra long-args that BackupPC uses:
|
||||
'block-size' => 1,
|
||||
'daemon' => -1,
|
||||
'debug' => 1,
|
||||
'fake-super' => 0,
|
||||
'fuzzy' => 0,
|
||||
'group' => 0,
|
||||
'hard-links' => 0,
|
||||
'ignore-times' => 0,
|
||||
'info' => 1,
|
||||
'links' => 0,
|
||||
'log-file' => 3,
|
||||
'one-file-system' => 0,
|
||||
'owner' => 0,
|
||||
'perms' => 0,
|
||||
'recursive' => 0,
|
||||
'times' => 0,
|
||||
'write-devices' => -1,
|
||||
);
|
||||
our $last_long_opt;
|
||||
|
||||
open(IN, '../options.c') or die "Unable to open ../options.c: $!\n";
|
||||
|
||||
while (<IN>) {
|
||||
if (/\Qargstr[x++]\E = '([^.ie])'/) {
|
||||
$short_no_arg{$1} = 1;
|
||||
undef $last_long_opt;
|
||||
} elsif (/\Qasprintf(\E[^,]+, "-([a-zA-Z0-9])\%l?[ud]"/) {
|
||||
$short_with_num{$1} = 1;
|
||||
undef $last_long_opt;
|
||||
} elsif (/\Qargs[ac++]\E = "--([^"=]+)"/) {
|
||||
$last_long_opt = $1;
|
||||
$long_opt{$1} = 0 unless exists $long_opt{$1};
|
||||
} elsif (defined($last_long_opt)
|
||||
&& /\Qargs[ac++]\E = ([^["\s]+);/) {
|
||||
$long_opt{$last_long_opt} = 2;
|
||||
undef $last_long_opt;
|
||||
} elsif (/return "--([^"]+-dest)";/) {
|
||||
$long_opt{$1} = 2;
|
||||
undef $last_long_opt;
|
||||
} elsif (/\Qasprintf(\E[^,]+, "--([^"=]+)=/ || /\Qargs[ac++]\E = "--([^"=]+)=/ || /fmt = .*: "--([^"=]+)=/) {
|
||||
$long_opt{$1} = 1;
|
||||
undef $last_long_opt;
|
||||
}
|
||||
}
|
||||
close IN;
|
||||
|
||||
my $short_no_arg = join('', sort keys %short_no_arg);
|
||||
my $short_with_num = join('', sort keys %short_with_num);
|
||||
|
||||
print <<EOT;
|
||||
|
||||
# These options are the only options that rsync might send to the server,
|
||||
# and only in the option format that the stock rsync produces.
|
||||
|
||||
# To disable a short-named option, add its letter to this string:
|
||||
our \$short_disabled = 's';
|
||||
|
||||
our \$short_no_arg = '$short_no_arg'; # DO NOT REMOVE ANY
|
||||
our \$short_with_num = '$short_with_num'; # DO NOT REMOVE ANY
|
||||
|
||||
# To disable a long-named option, change its value to a -1. The values mean:
|
||||
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
|
||||
# check the arg when receiving; and 3 = always check the arg.
|
||||
our \%long_opt = (
|
||||
EOT
|
||||
|
||||
foreach my $opt (sort keys %long_opt) {
|
||||
my $val = $long_opt{$opt};
|
||||
$val = 1 if $opt =~ /^(max-|min-)/;
|
||||
$val = 3 if $opt eq 'files-from';
|
||||
$val = q"$only eq 'r' ? -1 : " . $val if $opt =~ /^(remove-|log-file)/;
|
||||
$val = q"$only eq 'w' ? -1 : " . $val if $opt eq 'sender';
|
||||
print " '$opt' => $val,\n";
|
||||
}
|
||||
|
||||
print ");\n\n";
|
||||
2
packaging/ftp.filt
Normal file
2
packaging/ftp.filt
Normal file
@@ -0,0 +1,2 @@
|
||||
- /generated-files/
|
||||
- /binaries/
|
||||
@@ -1,9 +1,9 @@
|
||||
Summary: A fast, versatile, remote (and local) file-copying tool
|
||||
Name: rsync
|
||||
Version: 3.2.2
|
||||
%define fullversion %{version}pre1
|
||||
Release: 0.1.pre1
|
||||
%define srcdir src-previews
|
||||
Version: 3.4.3
|
||||
%define fullversion %{version}
|
||||
Release: 1
|
||||
%define srcdir src
|
||||
Group: Applications/Internet
|
||||
License: GPL
|
||||
Source0: https://rsync.samba.org/ftp/rsync/%{srcdir}/rsync-%{fullversion}.tar.gz
|
||||
@@ -79,9 +79,5 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%dir /etc/rsync-ssl/certs
|
||||
|
||||
%changelog
|
||||
* Sat Jun 27 2020 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.2.2pre1.
|
||||
|
||||
* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
|
||||
Added installation of /etc/xinetd.d/rsync file and some commented-out
|
||||
lines that demonstrate how to use the rsync-patches tar file.
|
||||
* Wed May 20 2026 Rsync Project <rsync.project@gmail.com>
|
||||
Released 3.4.3.
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Wayne Davison
|
||||
#
|
||||
# This program is freely redistributable.
|
||||
|
||||
import os, re, argparse
|
||||
|
||||
HTML_START = """\
|
||||
<html><head>
|
||||
<title>%s</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
max-width: 50em;
|
||||
margin: auto;
|
||||
}
|
||||
body, b, strong, u {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
code {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre code {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
blockquote pre code {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
dd p:first-of-type {
|
||||
margin-block-start: 0em;
|
||||
}
|
||||
</style>
|
||||
</head><body>
|
||||
"""
|
||||
|
||||
HTML_END = """\
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
md_parser = None
|
||||
|
||||
def main():
|
||||
for mdfn in args.mdfiles:
|
||||
if not mdfn.endswith('.md'):
|
||||
print('Ignoring non-md input file:', mdfn)
|
||||
continue
|
||||
title = re.sub(r'.*/', '', mdfn).replace('.md', '')
|
||||
htfn = mdfn.replace('.md', '.html')
|
||||
|
||||
print("Parsing", mdfn, '->', htfn)
|
||||
|
||||
with open(mdfn, 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
|
||||
txt = re.sub(r'\s--\s', '\xa0-- ', txt)
|
||||
|
||||
html = md_parser(txt)
|
||||
|
||||
html = re.sub(r'(<code>)([\s\S]*?)(</code>)', lambda m: m[1] + re.sub(r'\s', '\xa0', m[2]) + m[3], html)
|
||||
html = html.replace('--', '‑‑').replace("\xa0-", ' ‑').replace("\xa0", ' ')
|
||||
html = re.sub(r'(\W)-', r'\1‑', html)
|
||||
|
||||
if os.path.lexists(htfn):
|
||||
os.unlink(htfn)
|
||||
|
||||
with open(htfn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(HTML_START % title)
|
||||
fh.write(html)
|
||||
fh.write(HTML_END)
|
||||
|
||||
|
||||
def html_via_cmarkgfm(txt):
|
||||
return cmarkgfm.markdown_to_html(txt)
|
||||
|
||||
|
||||
def html_via_commonmark(txt):
|
||||
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Output html for md pages.', add_help=False)
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("mdfiles", nargs='+', help="The .md files to turn into .html files.")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
import cmarkgfm
|
||||
md_parser = html_via_cmarkgfm
|
||||
except:
|
||||
try:
|
||||
import commonmark
|
||||
md_parser = html_via_commonmark
|
||||
except:
|
||||
die("Failed to find cmarkgfm or commonmark for python3.")
|
||||
|
||||
main()
|
||||
18
packaging/openssl-rsync.cnf
Normal file
18
packaging/openssl-rsync.cnf
Normal file
@@ -0,0 +1,18 @@
|
||||
# This config file can be used with rsync to enable legacy digests
|
||||
# (such as MD4) by using the OPENSSL_CONF environment variable.
|
||||
# See rsync's configure --with-openssl-conf=/path/name option.
|
||||
|
||||
openssl_conf = openssl_init
|
||||
|
||||
[openssl_init]
|
||||
providers = provider_sect
|
||||
|
||||
[provider_sect]
|
||||
default = default_sect
|
||||
legacy = legacy_sect
|
||||
|
||||
[default_sect]
|
||||
activate = 1
|
||||
|
||||
[legacy_sect]
|
||||
activate = 1
|
||||
@@ -1,248 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script is used to turn one or more of the "patch/BASE/*" branches
|
||||
# into one or more diffs in the "patches" directory. Pass the option
|
||||
# --gen if you want generated files in the diffs. Pass the name of
|
||||
# one or more diffs if you want to just update a subset of all the
|
||||
# diffs.
|
||||
|
||||
import os, sys, re, argparse, time, shutil
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
MAKE_GEN_CMDS = [
|
||||
'make -f prepare-source.mak conf'.split(),
|
||||
'./config.status'.split(),
|
||||
'make gen'.split(),
|
||||
]
|
||||
TMP_DIR = "patches.gen"
|
||||
|
||||
os.environ['GIT_MERGE_AUTOEDIT'] = 'no'
|
||||
|
||||
def main():
|
||||
global master_commit, parent_patch, description, completed, last_touch
|
||||
|
||||
if not os.path.isdir(args.patches_dir):
|
||||
die(f'No "{args.patches_dir}" directory was found.')
|
||||
if not os.path.isdir('.git'):
|
||||
die('No ".git" directory present in the current dir.')
|
||||
|
||||
starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
|
||||
|
||||
gen_stash(starting_branch)
|
||||
if starting_branch == 'master':
|
||||
cmd_run('md5sum configure.ac >.gen-stash/configure.ac.sum')
|
||||
|
||||
master_commit = latest_git_hash(args.base_branch)
|
||||
|
||||
if args.gen:
|
||||
if os.path.lexists(TMP_DIR):
|
||||
die(f'"{TMP_DIR}" must not exist in the current directory.')
|
||||
gen_files = get_gen_files()
|
||||
os.mkdir(TMP_DIR, 0o700)
|
||||
for cmd in MAKE_GEN_CMDS:
|
||||
cmd_chk(cmd)
|
||||
cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/'])
|
||||
|
||||
last_touch = time.time()
|
||||
|
||||
# Start by finding all patches so that we can load all possible parents.
|
||||
patches = sorted(list(get_patch_branches(args.base_branch)))
|
||||
|
||||
parent_patch = { }
|
||||
description = { }
|
||||
|
||||
for patch in patches:
|
||||
branch = f"patch/{args.base_branch}/{patch}"
|
||||
desc = ''
|
||||
proc = cmd_pipe(['git', 'diff', '-U1000', f"{args.base_branch}...{branch}", '--', f"PATCH.{patch}"])
|
||||
in_diff = False
|
||||
for line in proc.stdout:
|
||||
if in_diff:
|
||||
if not re.match(r'^[ +]', line):
|
||||
continue
|
||||
line = line[1:]
|
||||
m = re.search(r'patch -p1 <patches/(\S+)\.diff', line)
|
||||
if m and m[1] != patch:
|
||||
parpat = parent_patch[patch] = m[1]
|
||||
if not parpat in patches:
|
||||
die(f"Parent of {patch} is not a local branch: {parpat}")
|
||||
desc += line
|
||||
elif re.match(r'^@@ ', line):
|
||||
in_diff = True
|
||||
description[patch] = desc
|
||||
proc.communicate()
|
||||
|
||||
if args.patch_files: # Limit the list of patches to actually process
|
||||
valid_patches = patches
|
||||
patches = [ ]
|
||||
for fn in args.patch_files:
|
||||
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
|
||||
if name not in valid_patches:
|
||||
die(f"Local branch not available for patch: {name}")
|
||||
patches.append(name)
|
||||
|
||||
completed = set()
|
||||
|
||||
for patch in patches:
|
||||
if patch in completed:
|
||||
continue
|
||||
if not update_patch(patch):
|
||||
break
|
||||
|
||||
if args.gen:
|
||||
shutil.rmtree(TMP_DIR)
|
||||
|
||||
while last_touch >= time.time():
|
||||
time.sleep(1)
|
||||
cmd_chk(['git', 'checkout', starting_branch])
|
||||
|
||||
gen_unstash(starting_branch)
|
||||
|
||||
|
||||
def update_patch(patch):
|
||||
global last_touch
|
||||
|
||||
completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops.
|
||||
|
||||
parent = parent_patch.get(patch, None)
|
||||
if parent:
|
||||
if parent not in completed:
|
||||
if not update_patch(parent):
|
||||
return 0
|
||||
based_on = parent = f"patch/{args.base_branch}/{parent}"
|
||||
else:
|
||||
parent = args.base_branch
|
||||
based_on = master_commit
|
||||
|
||||
print(f"======== {patch} ========")
|
||||
|
||||
while args.gen and last_touch >= time.time():
|
||||
time.sleep(1)
|
||||
|
||||
branch = f"patch/{args.base_branch}/{patch}"
|
||||
s = cmd_run(['git', 'checkout', branch])
|
||||
if s.returncode != 0:
|
||||
return 0
|
||||
|
||||
s = cmd_run(['git', 'merge', based_on])
|
||||
ok = s.returncode == 0
|
||||
if not ok or args.shell:
|
||||
gen_unstash(branch)
|
||||
m = re.search(r'([^/]+)$', parent)
|
||||
parent_dir = m[1]
|
||||
if not ok:
|
||||
print(f'"git merge {based_on}" incomplete -- please fix.')
|
||||
os.environ['PS1'] = f"[{parent_dir}] {patch}: "
|
||||
while True:
|
||||
s = cmd_run([os.environ.get('SHELL', '/bin/sh')])
|
||||
if s.returncode != 0:
|
||||
ans = input("Abort? [n/y] ")
|
||||
if re.match(r'^y', ans, flags=re.I):
|
||||
return 0
|
||||
continue
|
||||
cur_branch, is_clean, status_txt = check_git_status(0)
|
||||
if is_clean:
|
||||
break
|
||||
print(status_txt, end='')
|
||||
gen_stash(branch)
|
||||
|
||||
with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh:
|
||||
fh.write(description[patch])
|
||||
fh.write(f"\nbased-on: {based_on}\n")
|
||||
|
||||
if args.gen:
|
||||
gen_files = get_gen_files()
|
||||
for cmd in MAKE_GEN_CMDS:
|
||||
cmd_chk(cmd)
|
||||
cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"])
|
||||
else:
|
||||
gen_files = [ ]
|
||||
last_touch = time.time()
|
||||
|
||||
proc = cmd_pipe(['git', 'diff', based_on])
|
||||
skipping = False
|
||||
for line in proc.stdout:
|
||||
if skipping:
|
||||
if not re.match(r'^diff --git a/', line):
|
||||
continue
|
||||
skipping = False
|
||||
elif re.match(r'^diff --git a/PATCH', line):
|
||||
skipping = True
|
||||
continue
|
||||
if not re.match(r'^index ', line):
|
||||
fh.write(line)
|
||||
proc.communicate()
|
||||
|
||||
if args.gen:
|
||||
e_tmp_dir = re.escape(TMP_DIR)
|
||||
diff_re = re.compile(r'^(diff -Nurp) %s/[^/]+/(.*?) %s/[^/]+/(.*)' % (e_tmp_dir, e_tmp_dir))
|
||||
minus_re = re.compile(r'^\-\-\- %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
|
||||
plus_re = re.compile(r'^\+\+\+ %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
|
||||
|
||||
if parent == args.base_branch:
|
||||
parent_dir = 'master'
|
||||
else:
|
||||
m = re.search(r'([^/]+)$', parent)
|
||||
parent_dir = m[1]
|
||||
|
||||
proc = cmd_pipe(['diff', '-Nurp', f"{TMP_DIR}/{parent_dir}", f"{TMP_DIR}/{patch}"])
|
||||
for line in proc.stdout:
|
||||
line = diff_re.sub(r'\1 a/\2 b/\3', line)
|
||||
line = minus_re.sub(r'--- a/\1', line)
|
||||
line = plus_re.sub(r'+++ b/\1', line)
|
||||
fh.write(line)
|
||||
proc.communicate()
|
||||
for fn in gen_files:
|
||||
os.unlink(fn)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
# The autoconf cache dir can totally mess up if 2 different *.ac files have the same mtime!
|
||||
def gen_stash_prep(branch):
|
||||
if os.path.isdir('autom4te.cache'):
|
||||
shutil.rmtree('autom4te.cache')
|
||||
return '.gen-stash/' + branch.replace('/', '%') + '.tar.gz'
|
||||
|
||||
|
||||
def gen_stash(branch):
|
||||
files = 'configure.sh config.h.in Makefile'.split()
|
||||
for fn in files:
|
||||
if not os.path.exists(fn):
|
||||
return
|
||||
if not os.path.isdir('.gen-stash'):
|
||||
os.mkdir('.gen-stash', 0o700)
|
||||
tar_fn = gen_stash_prep(branch)
|
||||
cmd_run(['./prepare-source'])
|
||||
cmd_chk(['tar', 'czf', tar_fn, *files])
|
||||
|
||||
|
||||
def gen_unstash(branch):
|
||||
tar_fn = gen_stash_prep(branch)
|
||||
if os.path.exists(tar_fn):
|
||||
cmd_chk(['tar', 'xf', tar_fn])
|
||||
out, rc = cmd_txt_status('md5sum --status -c .gen-stash/configure.ac.sum'.split())
|
||||
if rc:
|
||||
print('*' * 10, "configure.ac differs from master version", '*' * 10)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Turn a git branch back into a diff files in the patches dir.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
|
||||
parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
|
||||
parser.add_argument('--shell', '-s', action='store_true', help="Launch a shell for every patch/BASE/* branch updated, not just when a conflict occurs.")
|
||||
parser.add_argument('--gen', metavar='DIR', nargs='?', const='', help='Include generated files. Optional DIR value overrides the default of using the "patches" dir.')
|
||||
parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
|
||||
parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
if args.gen == '':
|
||||
args.gen = args.patches_dir
|
||||
elif args.gen is not None:
|
||||
args.patches_dir = args.gen
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
@@ -1,4 +1,4 @@
|
||||
import os, sys, re, subprocess
|
||||
import os, sys, re, subprocess, argparse
|
||||
|
||||
# This python3 library provides a few helpful routines that are
|
||||
# used by the latest packaging scripts.
|
||||
@@ -23,27 +23,32 @@ def set_default_encoding(enc):
|
||||
|
||||
|
||||
# Set shell=True if the cmd is a string; sets a default encoding unless raw=True was specified.
|
||||
def _tweak_opts(cmd, opts, **maybe_set):
|
||||
# This sets any maybe_set value that isn't already set AND creates a copy of opts for us.
|
||||
opts = {**maybe_set, **opts}
|
||||
def _tweak_opts(cmd, opts, **maybe_set_args):
|
||||
def _maybe_set(o, **msa): # Only set a value if the user didn't already set it.
|
||||
for var, val in msa.items():
|
||||
if var not in o:
|
||||
o[var] = val
|
||||
|
||||
if type(cmd) == str:
|
||||
opts = {'shell': True, **opts}
|
||||
opts = opts.copy()
|
||||
_maybe_set(opts, **maybe_set_args)
|
||||
|
||||
if isinstance(cmd, str):
|
||||
_maybe_set(opts, shell=True)
|
||||
|
||||
want_raw = opts.pop('raw', False)
|
||||
if default_encoding and not want_raw:
|
||||
opts = {'encoding': default_encoding, **opts}
|
||||
_maybe_set(opts, encoding=default_encoding)
|
||||
|
||||
capture = opts.pop('capture', None)
|
||||
if capture:
|
||||
if capture == 'stdout':
|
||||
opts = {'stdout': subprocess.PIPE, **opts}
|
||||
_maybe_set(opts, stdout=subprocess.PIPE)
|
||||
elif capture == 'stderr':
|
||||
opts = {'stderr': subprocess.PIPE, **opts}
|
||||
_maybe_set(opts, stderr=subprocess.PIPE)
|
||||
elif capture == 'output':
|
||||
opts = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, **opts}
|
||||
_maybe_set(opts, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
elif capture == 'combined':
|
||||
opts = {'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT, **opts}
|
||||
_maybe_set(opts, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
discard = opts.pop('discard', None)
|
||||
if discard:
|
||||
@@ -66,30 +71,26 @@ def cmd_chk(cmd, **opts):
|
||||
return subprocess.run(cmd, **_tweak_opts(cmd, opts, check=True))
|
||||
|
||||
|
||||
# Capture stdout in a string and return the (output, return_code) tuple.
|
||||
# Use capture='combined' opt to get both stdout and stderr together.
|
||||
def cmd_txt_status(cmd, **opts):
|
||||
# Capture stdout in a string and return an object with out, err, and rc (return code).
|
||||
# It defaults to capture='stdout' (so err is empty) but can be overridden using
|
||||
# capture='combined' or capture='output' (the latter populates the err value).
|
||||
def cmd_txt(cmd, **opts):
|
||||
input = opts.pop('input', None)
|
||||
if input is not None:
|
||||
opts['stdin'] = subprocess.PIPE
|
||||
proc = subprocess.Popen(cmd, **_tweak_opts(cmd, opts, capture='stdout'))
|
||||
out = proc.communicate(input=input)[0]
|
||||
return (out, proc.returncode)
|
||||
out, err = proc.communicate(input=input)
|
||||
return argparse.Namespace(out=out, err=err, rc=proc.returncode)
|
||||
|
||||
|
||||
# Like cmd_txt_status() but just return the output.
|
||||
def cmd_txt(cmd, **opts):
|
||||
return cmd_txt_status(cmd, **opts)[0]
|
||||
|
||||
|
||||
# Capture stdout in a string and return the output if the command has a 0 return code.
|
||||
# Otherwise it throws an exception that indicates the return code and the output.
|
||||
# Just like calling cmd_txt() except that it raises an error if the command has a non-0 return code.
|
||||
# The raised error includes the cmd, the return code, and the captured output.
|
||||
def cmd_txt_chk(cmd, **opts):
|
||||
out, rc = cmd_txt_status(cmd, **opts)
|
||||
if rc != 0:
|
||||
cmd_err = f'Command "{cmd}" returned non-zero exit status "{rc}" and output:\n{out}'
|
||||
ct = cmd_txt(cmd, **opts)
|
||||
if ct.rc != 0:
|
||||
cmd_err = f'Command "{cmd}" returned non-0 exit status "{ct.rc}" and output:\n{ct.out}{ct.err}'
|
||||
raise Exception(cmd_err)
|
||||
return out
|
||||
return ct
|
||||
|
||||
|
||||
# Starts a piped-output command of stdout (by default) and leaves it up to you to read
|
||||
@@ -102,7 +103,7 @@ def cmd_pipe(cmd, **opts):
|
||||
# arg fatal_unless_clean can be used to make that non-fatal. Returns a
|
||||
# tuple of the current branch, the is_clean flag, and the status text.
|
||||
def check_git_status(fatal_unless_clean=True, subdir='.'):
|
||||
status_txt = cmd_txt_chk(f"cd '{subdir}' && git status")
|
||||
status_txt = cmd_txt_chk(f"cd '{subdir}' && git status").out
|
||||
is_clean = re.search(r'\nnothing to commit.+working (directory|tree) clean', status_txt) != None
|
||||
|
||||
if not is_clean and fatal_unless_clean:
|
||||
@@ -149,7 +150,7 @@ def check_git_state(master_branch, fatal_unless_clean=True, check_extra_dir=None
|
||||
|
||||
# Return the git hash of the most recent commit.
|
||||
def latest_git_hash(branch):
|
||||
out = cmd_txt_chk(['git', 'log', '-1', '--no-color', branch])
|
||||
out = cmd_txt_chk(['git', 'log', '-1', '--no-color', branch]).out
|
||||
m = re.search(r'^commit (\S+)', out, flags=re.M)
|
||||
if not m:
|
||||
die(f"Unable to determine commit hash for master branch: {branch}")
|
||||
@@ -169,50 +170,44 @@ def get_patch_branches(base_branch):
|
||||
return branches
|
||||
|
||||
|
||||
def mandate_gensend_hook():
|
||||
hook = '.git/hooks/pre-push'
|
||||
if not os.path.exists(hook):
|
||||
print('Creating hook file:', hook)
|
||||
cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
|
||||
else:
|
||||
out, rc = cmd_txt_status(['fgrep', 'make gensend', hook], discard='output')
|
||||
if rc:
|
||||
die('Please add a "make gensend" into your', hook, 'script.')
|
||||
|
||||
|
||||
# Snag the GENFILES values out of the Makefile.in file and return them as a list.
|
||||
def get_gen_files():
|
||||
# Snag the GENFILES values out of the Makefile file and return them as a list.
|
||||
def get_gen_files(want_dir_plus_list=False):
|
||||
cont_re = re.compile(r'\\\n')
|
||||
|
||||
extras = [ ]
|
||||
gen_files = [ ]
|
||||
|
||||
with open('Makefile.in', 'r', encoding='utf-8') as fh:
|
||||
auto_dir = os.path.join('auto-build-save', cmd_txt('git rev-parse --abbrev-ref HEAD').out.strip().replace('/', '%'))
|
||||
|
||||
with open(auto_dir + '/Makefile', 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
if not extras:
|
||||
if not gen_files:
|
||||
chk = re.sub(r'^GENFILES=', '', line)
|
||||
if line == chk:
|
||||
continue
|
||||
line = chk
|
||||
m = re.search(r'\\$', line)
|
||||
line = re.sub(r'^\s+|\s*\\\n?$|\s+$', '', line)
|
||||
extras += line.split()
|
||||
gen_files += line.split()
|
||||
if not m:
|
||||
break
|
||||
|
||||
return extras
|
||||
if want_dir_plus_list:
|
||||
return (auto_dir, gen_files)
|
||||
|
||||
return [ os.path.join(auto_dir, fn) for fn in gen_files ]
|
||||
|
||||
|
||||
def get_configure_version():
|
||||
with open('configure.ac', 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
m = re.match(r'^AC_INIT\(\[rsync\],\s*\[(\d.+?)\]', line)
|
||||
if m:
|
||||
return m[1]
|
||||
die("Unable to find AC_INIT with version in configure.ac")
|
||||
def get_rsync_version():
|
||||
with open('version.h', 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
m = re.match(r'^#define\s+RSYNC_VERSION\s+"(\d.+?)"', txt)
|
||||
if m:
|
||||
return m[1]
|
||||
die("Unable to find RSYNC_VERSION define in version.h")
|
||||
|
||||
|
||||
def get_NEWS_version_info():
|
||||
rel_re = re.compile(r'^\| \d{2} \w{3} \d{4}\s+\|\s+(?P<ver>\d+\.\d+\.\d+)\s+\|\s+(?P<pdate>\d{2} \w{3} \d{4}\s+)?\|\s+(?P<pver>\d+)\s+\|')
|
||||
rel_re = re.compile(r'^\| \S{2} \w{3} \d{4}\s+\|\s+(?P<ver>\d+\.\d+\.\d+)\s+\|\s+(?P<pdate>\d{2} \w{3} \d{4})?\s+\|\s+(?P<pver>\d+)\s+\|')
|
||||
last_version = last_protocol_version = None
|
||||
pdate = { }
|
||||
|
||||
@@ -228,12 +223,11 @@ def get_NEWS_version_info():
|
||||
pdate[m['ver']] = m['pdate']
|
||||
if m['ver'] == last_version:
|
||||
last_protocol_version = m['pver']
|
||||
break
|
||||
|
||||
if not last_protocol_version:
|
||||
die(f"Unable to determine protocol_version for {last_version}.")
|
||||
|
||||
return last_version, last_protocol_version
|
||||
return last_version, last_protocol_version, pdate
|
||||
|
||||
|
||||
def get_protocol_versions():
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
cat >/dev/null # Just discard stdin data
|
||||
make gensend
|
||||
43
packaging/prep-auto-dir
Executable file
43
packaging/prep-auto-dir
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# This script will setup the build dir based on the current git branch and the
|
||||
# directory auto-build-save/$BRANCH. We don't use a symlink for the build dir
|
||||
# because we want to maximize the ccache reuse, so all builds must happen in
|
||||
# the same real dir. When a dir is moved out of auto-build-save/$BRANCH to the
|
||||
# build dir, it is replaced with a symlink so that it can still be found under
|
||||
# that dir. The build dir also gets a .branch -> $BRANCH symlink so that we
|
||||
# can figure out the current build dir's branch.
|
||||
|
||||
# To get started, just clone the rsync git repo and create the auto-build-save
|
||||
# dir. If you have an existing git checkout and it is not in a pristine state,
|
||||
# run "make distclean" before creating the auto-build-save dir.
|
||||
|
||||
auto_top='auto-build-save'
|
||||
if test -d $auto_top && test -d .git; then
|
||||
desired_branch=`git rev-parse --abbrev-ref HEAD | tr / %`
|
||||
if test "$desired_branch" = HEAD; then
|
||||
echo "ERROR: switch to the right build dir manually when in detached HEAD mode." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
auto_dir="$auto_top/$desired_branch"
|
||||
if test -d build; then
|
||||
cur_branch=`readlink build/.branch`
|
||||
else
|
||||
cur_branch='/'
|
||||
fi
|
||||
if test "$desired_branch" != "$cur_branch"; then
|
||||
if test "$cur_branch" != /; then
|
||||
rm -f "$auto_top/$cur_branch"
|
||||
mv build "$auto_top/$cur_branch"
|
||||
fi
|
||||
test -d "$auto_dir" || mkdir "$auto_dir"
|
||||
test -h "$auto_dir/.branch" || ln -s "$desired_branch" "$auto_dir/.branch"
|
||||
mv "$auto_dir" build
|
||||
ln -s ../build "$auto_dir"
|
||||
fi
|
||||
if test ! -h Makefile; then
|
||||
rm -f Makefile
|
||||
ln -s packaging/auto-Makefile Makefile
|
||||
fi
|
||||
echo $desired_branch
|
||||
fi
|
||||
@@ -1,385 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script expects the directory ~/samba-rsync-ftp to exist and to be a
|
||||
# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done,
|
||||
# the git repository in the current directory will be updated, and the local
|
||||
# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org.
|
||||
|
||||
import os, sys, re, argparse, glob, shutil, signal
|
||||
from datetime import datetime
|
||||
from getpass import getpass
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
os.environ['LESS'] = 'mqeiXR'; # Make sure that -F is turned off and -R is turned on.
|
||||
dest = os.environ['HOME'] + '/samba-rsync-ftp'
|
||||
ORIGINAL_PATH = os.environ['PATH']
|
||||
|
||||
def main():
|
||||
now = datetime.now()
|
||||
cl_today = now.strftime('* %a %b %d %Y')
|
||||
year = now.strftime('%Y')
|
||||
ztoday = now.strftime('%d %b %Y')
|
||||
today = ztoday.lstrip('0')
|
||||
|
||||
mandate_gensend_hook()
|
||||
|
||||
curdir = os.getcwd()
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
gen_files = get_gen_files()
|
||||
|
||||
dash_line = '=' * 74
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
== This will release a new version of rsync onto an unsuspecting world. ==
|
||||
{dash_line}
|
||||
""")
|
||||
|
||||
if not os.path.isdir(dest):
|
||||
die(dest, "dest does not exist")
|
||||
if not os.path.isdir('.git'):
|
||||
die("There is no .git dir in the current directory.")
|
||||
if os.path.lexists('a'):
|
||||
die('"a" must not exist in the current directory.')
|
||||
if os.path.lexists('b'):
|
||||
die('"b" must not exist in the current directory.')
|
||||
if os.path.lexists('patches.gen'):
|
||||
die('"patches.gen" must not exist in the current directory.')
|
||||
|
||||
check_git_state(args.master_branch, True, 'patches')
|
||||
|
||||
confversion = get_configure_version()
|
||||
|
||||
# All version values are strings!
|
||||
lastversion, last_protocol_version = get_NEWS_version_info()
|
||||
protocol_version, subprotocol_version = get_protocol_versions()
|
||||
|
||||
version = confversion
|
||||
m = re.search(r'pre(\d+)', version)
|
||||
if m:
|
||||
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
|
||||
else:
|
||||
version = version.replace('dev', 'pre1')
|
||||
|
||||
ans = input(f"Please enter the version number of this release: [{version}] ")
|
||||
if ans == '.':
|
||||
version = re.sub(r'pre\d+', '', version)
|
||||
elif ans != '':
|
||||
version = ans
|
||||
if not re.match(r'^[\d.]+(pre\d+)?$', version):
|
||||
die(f'Invalid version: "{version}"')
|
||||
|
||||
v_ver = 'v' + version
|
||||
rsync_ver = 'rsync-' + version
|
||||
|
||||
if os.path.lexists(rsync_ver):
|
||||
die(f'"{rsync_ver}" must not exist in the current directory.')
|
||||
|
||||
out = cmd_txt_chk(['git', 'tag', '-l', v_ver])
|
||||
if out != '':
|
||||
print(f"Tag {v_ver} already exists.")
|
||||
ans = input("\nDelete tag or quit? [Q/del] ")
|
||||
if not re.match(r'^del', ans, flags=re.I):
|
||||
die("Aborted")
|
||||
cmd_chk(['git', 'tag', '-d', v_ver])
|
||||
|
||||
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
|
||||
if 'pre' in version and not confversion.endswith('dev'):
|
||||
lastversion = confversion
|
||||
|
||||
ans = input(f"Enter the previous version to produce a patch against: [{lastversion}] ")
|
||||
if ans != '':
|
||||
lastversion = ans
|
||||
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
|
||||
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
if os.path.lexists(rsync_lastver):
|
||||
die(f'"{rsync_lastver}" must not exist in the current directory.')
|
||||
|
||||
m = re.search(r'(pre\d+)', version)
|
||||
pre = m[1] if m else ''
|
||||
|
||||
release = '0.1' if pre else '1'
|
||||
ans = input(f"Please enter the RPM release number of this release: [{release}] ")
|
||||
if ans != '':
|
||||
release = ans
|
||||
if pre:
|
||||
release += '.' + pre
|
||||
|
||||
finalversion = re.sub(r'pre\d+', '', version)
|
||||
if protocol_version == last_protocol_version:
|
||||
proto_changed = 'unchanged'
|
||||
proto_change_date = ' ' * 11
|
||||
else:
|
||||
proto_changed = 'changed'
|
||||
if finalversion in pdate:
|
||||
proto_change_date = pdate[finalversion]
|
||||
else:
|
||||
while True:
|
||||
ans = input("On what date did the protocol change to {protocol_version} get checked in? (dd Mmm yyyy) ")
|
||||
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
|
||||
break
|
||||
proto_change_date = ans
|
||||
|
||||
if 'pre' in lastversion:
|
||||
if not pre:
|
||||
die("You should not diff a release version against a pre-release version.")
|
||||
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
|
||||
skipping = ' ** SKIPPING **'
|
||||
elif pre:
|
||||
srcdir = srcdiffdir = 'src-previews'
|
||||
lastsrcdir = 'src'
|
||||
skipping = ' ** SKIPPING **'
|
||||
else:
|
||||
srcdir = lastsrcdir = 'src'
|
||||
srcdiffdir = 'src-diffs'
|
||||
skipping = ''
|
||||
|
||||
print(f"""
|
||||
{dash_line}
|
||||
version is "{version}"
|
||||
lastversion is "{lastversion}"
|
||||
dest is "{dest}"
|
||||
curdir is "{curdir}"
|
||||
srcdir is "{srcdir}"
|
||||
srcdiffdir is "{srcdiffdir}"
|
||||
lastsrcdir is "{lastsrcdir}"
|
||||
release is "{release}"
|
||||
|
||||
About to:
|
||||
- tweak SUBPROTOCOL_VERSION in rsync.h, if needed
|
||||
- tweak the version in configure.ac and the spec files
|
||||
- tweak NEWS.md to ensure header values are correct
|
||||
- generate configure.sh, config.h.in, and proto.h
|
||||
- page through the differences
|
||||
""")
|
||||
ans = input("<Press Enter to continue> ")
|
||||
|
||||
specvars = {
|
||||
'Version:': finalversion,
|
||||
'Release:': release,
|
||||
'%define fullversion': f'%{{version}}{pre}',
|
||||
'Released': version + '.',
|
||||
'%define srcdir': srcdir,
|
||||
}
|
||||
|
||||
tweak_files = 'configure.ac rsync.h NEWS.md'.split()
|
||||
tweak_files += glob.glob('packaging/*.spec')
|
||||
tweak_files += glob.glob('packaging/*/*.spec')
|
||||
|
||||
for fn in tweak_files:
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
old_txt = txt = fh.read()
|
||||
if 'configure' in fn:
|
||||
x_re = re.compile(r'^(AC_INIT\(\[rsync\],\s*\[)\d.+?(\])', re.M)
|
||||
txt = replace_or_die(x_re, r'\g<1>%s\2' % version, txt, f"Unable to update AC_INIT with version in {fn}")
|
||||
elif '.spec' in fn:
|
||||
for var, val in specvars.items():
|
||||
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
|
||||
txt = replace_or_die(x_re, var + ' ' + val, txt, f"Unable to update {var} in {fn}")
|
||||
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
|
||||
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}")
|
||||
elif fn == 'rsync.h':
|
||||
x_re = re.compile('(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
|
||||
repl = lambda m: m[1] + ' ' + '0' if not pre or proto_changed != 'changed' else 1 if m[2] == '0' else m[2]
|
||||
txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}")
|
||||
elif fn == 'NEWS.md':
|
||||
efv = re.escape(finalversion)
|
||||
x_re = re.compile(r'^<.+>\s+# NEWS for rsync %s \(UNRELEASED\)\s+Protocol: .+\n' % efv)
|
||||
rel_day = 'UNRELEASED' if pre else today
|
||||
repl = (f'<a name="{finalversion}"></a>\n\n# NEWS for rsync {finalversion} ({rel_day})\n\n'
|
||||
+ f"Protocol: {protocol_version} ({proto_changed})\n")
|
||||
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
|
||||
msg = f"The top lines of {fn} are not in the right format. It should be:\n" + good_top
|
||||
txt = replace_or_die(x_re, repl, txt, msg)
|
||||
x_re = re.compile(r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv, re.M)
|
||||
repl = lambda m: m[1] + (m[2] if pre else ztoday) + m[3] + proto_change_date + m[4] + protocol_version + m[5]
|
||||
txt = replace_or_die(x_re, repl, txt, f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
|
||||
else:
|
||||
die(f"Unrecognized file in tweak_files: {fn}")
|
||||
|
||||
if txt != old_txt:
|
||||
print(f"Updating {fn}")
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
cmd_chk(['packaging/year-tweak'])
|
||||
|
||||
print(dash_line)
|
||||
cmd_run("git diff --color | less -p '^diff .*'")
|
||||
|
||||
srctar_name = f"{rsync_ver}.tar.gz"
|
||||
pattar_name = f"rsync-patches-{version}.tar.gz"
|
||||
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
|
||||
srctar_file = f"{dest}/{srcdir}/{srctar_name}"
|
||||
pattar_file = f"{dest}/{srcdir}/{pattar_name}"
|
||||
diff_file = f"{dest}/{srcdiffdir}/{diff_name}"
|
||||
lasttar_file = f"{dest}/{lastsrcdir}/{rsync_lastver}.tar.gz"
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
About to:
|
||||
- git commit all changes
|
||||
- generate the manpages
|
||||
- merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches
|
||||
- update the files in the "patches" dir and OPTIONALLY
|
||||
(if you type 'y') to launch a shell for each patch
|
||||
""")
|
||||
ans = input("<Press Enter OR 'y' to continue> ")
|
||||
|
||||
s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version}'])
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
cmd_chk('make reconfigure ; make gen')
|
||||
cmd_chk(['rsync', '-a', *gen_files, 'SaVeDiR/'])
|
||||
|
||||
print(f'Creating any missing patch branches.')
|
||||
s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
print('Updating files in "patches" dir ...')
|
||||
s = cmd_run(f'packaging/patch-update --branch={args.master_branch}')
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
if re.match(r'^y', ans, re.I):
|
||||
print(f'\nVisiting all "patch/{args.master_branch}/*" branches ...')
|
||||
cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --shell")
|
||||
|
||||
cmd_run("rm -f *.[o15] *.html")
|
||||
cmd_chk('rsync -a SaVeDiR/ .'.split())
|
||||
|
||||
if os.path.isdir('patches/.git'):
|
||||
s = cmd_run(f"cd patches && git commit -a -m 'The patches for {version}.'")
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
About to:
|
||||
- create signed tag for this release: {v_ver}
|
||||
- create release diffs, "{diff_name}"
|
||||
- create release tar, "{srctar_name}"
|
||||
- generate {rsync_ver}/patches/* files
|
||||
- create patches tar, "{pattar_name}"
|
||||
- update top-level README.md, NEWS.md, TODO, and ChangeLog
|
||||
- update top-level rsync*.html manpages
|
||||
- gpg-sign the release files
|
||||
- update hard-linked top-level release files{skipping}
|
||||
""")
|
||||
ans = input("<Press Enter to continue> ")
|
||||
|
||||
# TODO: is there a better way to ensure that our passphrase is in the agent?
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm TeMp*")
|
||||
|
||||
out = cmd_txt(f"git tag -s -m 'Version {version}.' {v_ver}", capture='combined')
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out or 'failed' in out:
|
||||
die('Aborting')
|
||||
|
||||
if os.path.isdir('patches/.git'):
|
||||
out = cmd_txt(f"cd patches && git tag -s -m 'Version {version}.' {v_ver}", capture='combined')
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out or 'failed' in out:
|
||||
die('Aborting')
|
||||
|
||||
os.environ['PATH'] = ORIGINAL_PATH
|
||||
|
||||
# Extract the generated files from the old tar.
|
||||
tweaked_gen_files = [ f"{rsync_lastver}/{x}" for x in gen_files ]
|
||||
cmd_run(['tar', 'xzf', lasttar_file, *tweaked_gen_files])
|
||||
os.rename(rsync_lastver, 'a')
|
||||
|
||||
print(f"Creating {diff_file} ...")
|
||||
cmd_chk(['rsync', '-a', *gen_files, 'b/'])
|
||||
|
||||
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # CAUTION: must not contain any single quotes!
|
||||
cmd_chk(f"(git diff v{lastversion} {v_ver} -- ':!.github'; diff -upN a b | sed -r '{sed_script}') | gzip -9 >{diff_file}")
|
||||
shutil.rmtree('a')
|
||||
os.rename('b', rsync_ver)
|
||||
|
||||
print(f"Creating {srctar_file} ...")
|
||||
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | tar xf -")
|
||||
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver}/")
|
||||
cmd_chk(['fakeroot', 'tar', 'czf', srctar_file, '--exclude=.github', rsync_ver])
|
||||
shutil.rmtree(rsync_ver)
|
||||
|
||||
print(f'Updating files in "{rsync_ver}/patches" dir ...')
|
||||
os.mkdir(rsync_ver, 0o755)
|
||||
os.mkdir(f"{rsync_ver}/patches", 0o755)
|
||||
cmd_chk(f"packaging/patch-update --skip-check --branch={args.master_branch} --gen={rsync_ver}/patches".split())
|
||||
|
||||
cmd_run("rm -f *.[o15] *.html")
|
||||
cmd_chk('rsync -a SaVeDiR/ .'.split())
|
||||
shutil.rmtree('SaVeDiR')
|
||||
cmd_chk('make gen'.split())
|
||||
|
||||
print(f"Creating {pattar_file} ...")
|
||||
cmd_chk(['fakeroot', 'tar', 'chzf', pattar_file, rsync_ver + '/patches'])
|
||||
shutil.rmtree(rsync_ver)
|
||||
|
||||
print(f"Updating the other files in {dest} ...")
|
||||
md_files = 'README.md NEWS.md'.split()
|
||||
html_files = [ fn for fn in gen_files if fn.endswith('.html') ]
|
||||
cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
|
||||
cmd_chk(["packaging/md2html"] + [ dest +'/'+ fn for fn in md_files ])
|
||||
|
||||
cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
|
||||
|
||||
for fn in (srctar_file, pattar_file, diff_file):
|
||||
asc_fn = fn + '.asc'
|
||||
if os.path.lexists(asc_fn):
|
||||
os.unlink(asc_fn)
|
||||
res = cmd_run(['gpg', '--batch', '-ba', fn])
|
||||
if res.returncode != 0 and res.returncode != 2:
|
||||
die("gpg signing failed")
|
||||
|
||||
if not pre:
|
||||
for find in f'{dest}/rsync-*.gz {dest}/rsync-*.asc {dest}/src-previews/rsync-*diffs.gz*'.split():
|
||||
for fn in glob.glob(find):
|
||||
os.unlink(fn)
|
||||
top_link = [
|
||||
srctar_file, f"{srctar_file}.asc",
|
||||
pattar_file, f"{pattar_file}.asc",
|
||||
diff_file, f"{diff_file}.asc",
|
||||
]
|
||||
for fn in top_link:
|
||||
os.link(fn, re.sub(r'/src(-\w+)?/', '/', fn))
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
Local changes are done. When you're satisfied, push the git repository
|
||||
and rsync the release files. Remember to announce the release on *BOTH*
|
||||
rsync-announce@lists.samba.org and rsync@lists.samba.org (and the web)!
|
||||
""")
|
||||
|
||||
|
||||
def replace_or_die(regex, repl, txt, die_msg):
|
||||
m = regex.search(txt)
|
||||
if not m:
|
||||
die(die_msg)
|
||||
return regex.sub(repl, txt, 1)
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
die("\nAborting due to SIGINT.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Prepare a new release of rsync in the git repo & ftp dir.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='master_branch', default='master', help="The branch to release. Default: master.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
705
packaging/release.py
Executable file
705
packaging/release.py
Executable file
@@ -0,0 +1,705 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Step-based release script for rsync. Each step is a separate invocation
|
||||
# selected by a --step-N-XX option, so the maintainer drives the release
|
||||
# manually one piece at a time.
|
||||
#
|
||||
# All persistent state and working files live in ../release/ (a sibling of
|
||||
# the rsync git checkout):
|
||||
#
|
||||
# ../release/rsync-ftp/ mirror of samba.org:/home/ftp/pub/rsync
|
||||
# ../release/rsync-html/ release-time snapshot of the html site
|
||||
# ../release/work/ scratch space for tarball / diff staging
|
||||
# ../release/release-state.json info shared between steps
|
||||
#
|
||||
# The rsync-patches archive is no longer maintained and has been dropped.
|
||||
#
|
||||
# Run "packaging/release.py --list" to see the step list.
|
||||
|
||||
import os, sys, re, argparse, glob, shutil, json, signal, subprocess
|
||||
from datetime import datetime
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import (
|
||||
warn, die, cmd_run, cmd_chk, cmd_txt, cmd_txt_chk, cmd_pipe,
|
||||
check_git_state, get_rsync_version,
|
||||
get_NEWS_version_info, get_protocol_versions,
|
||||
)
|
||||
|
||||
# ---------- Paths ----------
|
||||
|
||||
RELEASE_DIR = os.path.realpath('../release')
|
||||
FTP_DIR = os.path.join(RELEASE_DIR, 'rsync-ftp')
|
||||
HTML_DIR = os.path.join(RELEASE_DIR, 'rsync-html')
|
||||
WORK_DIR = os.path.join(RELEASE_DIR, 'work')
|
||||
STATE_FILE = os.path.join(RELEASE_DIR, 'release-state.json')
|
||||
|
||||
# The rsync-web/ subdirectory in the rsync source tree is the source-of-truth
|
||||
# for the git-tracked html content. step-1-fetch snapshots it into HTML_DIR
|
||||
# for the release flow, where it can be edited or augmented with server-side
|
||||
# content before step-11-push-html sends it to samba.org.
|
||||
HTML_SRC = os.path.realpath('rsync-web')
|
||||
|
||||
FTP_REMOTE_PATH = '/home/ftp/pub/rsync'
|
||||
HTML_REMOTE_PATH = '/home/httpd/html/rsync'
|
||||
|
||||
# Files that ./configure + make produce and that the release tarball / diff
|
||||
# need to bundle alongside the git-tracked source. Mirrors the GENFILES
|
||||
# definition in Makefile.in (with rrsync.1{,.html} since we always configure
|
||||
# --with-rrsync in --step-4-build).
|
||||
GEN_FILES = [
|
||||
'configure.sh',
|
||||
'aclocal.m4',
|
||||
'config.h.in',
|
||||
'rsync.1', 'rsync.1.html',
|
||||
'rsync-ssl.1', 'rsync-ssl.1.html',
|
||||
'rsyncd.conf.5', 'rsyncd.conf.5.html',
|
||||
'rrsync.1', 'rrsync.1.html',
|
||||
]
|
||||
|
||||
# ---------- Step registry ----------
|
||||
|
||||
STEPS = [
|
||||
('step-1-fetch', 'mirror ../release/rsync-ftp from samba.org and snapshot ../release/rsync-html from rsync-web/'),
|
||||
('step-2-prepare', 'gather release info interactively and write release-state.json'),
|
||||
('step-3-tweak', 'update version.h, rsync.h, NEWS.md, and packaging/*.spec'),
|
||||
('step-4-build', 'run smart-make + make gen'),
|
||||
('step-5-commit', 'git commit -a (commit the prepared release changes)'),
|
||||
('step-6-tag', 'create the gpg-signed git tag'),
|
||||
('step-7-tarball', 'build the source tarball and diffs.gz against the previous release'),
|
||||
('step-8-update-ftp', 'refresh README/NEWS/INSTALL/html in the ftp dir, regen ChangeLog.gz, gpg-sign tarballs'),
|
||||
('step-9-toplinks', 'hard-link top-level release files (final releases only)'),
|
||||
('step-10-push-ftp', 'rsync ../release/rsync-ftp/ to samba.org'),
|
||||
('step-11-push-html', 'rsync ../release/rsync-html/ to samba.org (after any manual edits)'),
|
||||
('step-12-push-git', 'print the git push commands for you to run'),
|
||||
]
|
||||
STEP_FLAGS = [s[0] for s in STEPS]
|
||||
|
||||
DASH_LINE = '=' * 74
|
||||
|
||||
# ---------- State helpers ----------
|
||||
|
||||
def load_state():
|
||||
if not os.path.isfile(STATE_FILE):
|
||||
die(f"{STATE_FILE} not found. Run --step-2-prepare first.")
|
||||
with open(STATE_FILE, 'r', encoding='utf-8') as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
def save_state(state):
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
with open(STATE_FILE, 'w', encoding='utf-8') as fh:
|
||||
json.dump(state, fh, indent=2, sort_keys=True)
|
||||
fh.write('\n')
|
||||
|
||||
|
||||
def require_samba_host():
|
||||
host = os.environ.get('RSYNC_SAMBA_HOST', '')
|
||||
if not host.endswith('.samba.org'):
|
||||
die("Set RSYNC_SAMBA_HOST in your environment to the samba hostname (e.g. hr3.samba.org).")
|
||||
return host
|
||||
|
||||
|
||||
def require_top_of_checkout():
|
||||
if not os.path.isfile('packaging/release.py'):
|
||||
die("Run this script from the top of your rsync checkout.")
|
||||
if not os.path.isdir('.git'):
|
||||
die("There is no .git dir in the current directory.")
|
||||
|
||||
|
||||
def replace_or_die(regex, repl, txt, die_msg):
|
||||
m = regex.search(txt)
|
||||
if not m:
|
||||
die(die_msg)
|
||||
return regex.sub(repl, txt, 1)
|
||||
|
||||
|
||||
def section(title):
|
||||
print(f"\n{DASH_LINE}\n== {title}\n{DASH_LINE}")
|
||||
|
||||
|
||||
def confirm(prompt, default_no=True):
|
||||
suffix = '[n] ' if default_no else '[y] '
|
||||
ans = input(f"{prompt} {suffix}").strip().lower()
|
||||
if default_no:
|
||||
return ans.startswith('y')
|
||||
return ans == '' or ans.startswith('y')
|
||||
|
||||
|
||||
# ---------- Step 1: fetch ftp + html ----------
|
||||
|
||||
def step_1_fetch(args):
|
||||
host = require_samba_host()
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
os.makedirs(WORK_DIR, exist_ok=True)
|
||||
|
||||
section(f"Fetching ftp dir into {FTP_DIR}")
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
os.makedirs(FTP_DIR)
|
||||
# packaging/ftp.filt is the authoritative copy of the .filt filter file
|
||||
# that controls which subtrees rsync excludes from the FTP mirror.
|
||||
# Seed FTP_DIR/.filt from it so the bundled version is what step-1's
|
||||
# rsync uses here, and so step-10-push-ftp propagates it back to the
|
||||
# server. --exclude=/.filt below stops the server's copy from
|
||||
# overwriting our bundled one on the way down.
|
||||
filt = os.path.join(FTP_DIR, '.filt')
|
||||
bundled_filt = os.path.realpath('packaging/ftp.filt')
|
||||
if not os.path.isfile(bundled_filt):
|
||||
die(f"{bundled_filt} not found; cannot seed .filt for the FTP pull.")
|
||||
shutil.copyfile(bundled_filt, filt)
|
||||
cmd_chk(['rsync', '-aivOHP', f'-f:_{filt}', '--exclude=/.filt',
|
||||
f'{host}:{FTP_REMOTE_PATH}/', f'{FTP_DIR}/'])
|
||||
|
||||
section(f"Snapshotting html dir from {HTML_SRC} into {HTML_DIR}")
|
||||
if not os.path.isdir(HTML_SRC):
|
||||
die(f"{HTML_SRC} not found. This should be the in-tree rsync-web/ "
|
||||
f"subdirectory; something is wrong with your checkout.")
|
||||
os.makedirs(HTML_DIR, exist_ok=True)
|
||||
cmd_chk(['rsync', '-aiv', f'{HTML_SRC}/', f'{HTML_DIR}/'])
|
||||
|
||||
# Then mirror non-git html content from the server, skipping files that
|
||||
# the html git already provides (driven by the 'filt' file in HTML_DIR).
|
||||
filt = os.path.join(HTML_DIR, 'filt')
|
||||
if os.path.exists(filt):
|
||||
tmp_filt = os.path.join(HTML_DIR, 'tmp-filt')
|
||||
cmd_chk(f"sed -n -e 's/[-P]/H/p' '{filt}' >'{tmp_filt}'")
|
||||
cmd_chk(['rsync', '-aivOHP', f'-f._{tmp_filt}',
|
||||
f'{host}:{HTML_REMOTE_PATH}/', f'{HTML_DIR}/'])
|
||||
os.unlink(tmp_filt)
|
||||
|
||||
print(f"\nFetch complete. Local dirs are now in {RELEASE_DIR}.")
|
||||
|
||||
|
||||
# ---------- Step 2: prepare ----------
|
||||
|
||||
def step_2_prepare(args):
|
||||
require_top_of_checkout()
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
|
||||
|
||||
now = datetime.now().astimezone()
|
||||
cl_today = now.strftime('* %a %b %d %Y')
|
||||
year = now.strftime('%Y')
|
||||
ztoday = now.strftime('%d %b %Y')
|
||||
today = ztoday.lstrip('0')
|
||||
tz_now = now.strftime('%z')
|
||||
tz_num = tz_now[0:1].replace('+', '') + str(float(tz_now[1:3]) + float(tz_now[3:]) / 60)
|
||||
|
||||
curversion = get_rsync_version()
|
||||
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
|
||||
protocol_version, subprotocol_version = get_protocol_versions()
|
||||
|
||||
# Default next version: bump preN, or move dev -> pre1.
|
||||
version = curversion
|
||||
m = re.search(r'pre(\d+)', version)
|
||||
if m:
|
||||
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
|
||||
else:
|
||||
version = version.replace('dev', 'pre1')
|
||||
|
||||
print(f"\nCurrent version (version.h): {curversion}")
|
||||
print(f"Last released version (NEWS.md): {lastversion}")
|
||||
print(f"Current protocol version: {protocol_version} (last released: {last_protocol_version})")
|
||||
|
||||
ans = input(f"\nVersion to release [{version}, '.' to drop the preN suffix]: ").strip()
|
||||
if ans == '.':
|
||||
version = re.sub(r'pre\d+', '', version)
|
||||
elif ans:
|
||||
version = ans
|
||||
if not re.match(r'^[\d.]+(pre\d+)?$', version):
|
||||
die(f'Invalid version: "{version}"')
|
||||
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
|
||||
|
||||
if 'pre' in version and not curversion.endswith('dev'):
|
||||
lastversion = curversion
|
||||
|
||||
ans = input(f"Previous version to diff against [{lastversion}]: ").strip()
|
||||
if ans:
|
||||
lastversion = ans
|
||||
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
|
||||
|
||||
m = re.search(r'(pre\d+)', version)
|
||||
pre = m[1] if m else ''
|
||||
finalversion = re.sub(r'pre\d+', '', version)
|
||||
|
||||
release = '0.1' if pre else '1'
|
||||
ans = input(f"RPM release number [{release}]: ").strip()
|
||||
if ans:
|
||||
release = ans
|
||||
if pre:
|
||||
release += '.' + pre
|
||||
|
||||
proto_changed = protocol_version != last_protocol_version
|
||||
if proto_changed:
|
||||
if finalversion in pdate:
|
||||
proto_change_date = pdate[finalversion]
|
||||
else:
|
||||
while True:
|
||||
ans = input(f"Date the protocol changed to {protocol_version} (dd Mmm yyyy): ").strip()
|
||||
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
|
||||
break
|
||||
proto_change_date = ans
|
||||
else:
|
||||
proto_change_date = ' ' * 11
|
||||
|
||||
if 'pre' in lastversion:
|
||||
if not pre:
|
||||
die("Refusing to diff a release version against a pre-release version.")
|
||||
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
|
||||
elif pre:
|
||||
srcdir = srcdiffdir = 'src-previews'
|
||||
lastsrcdir = 'src'
|
||||
else:
|
||||
srcdir = lastsrcdir = 'src'
|
||||
srcdiffdir = 'src-diffs'
|
||||
|
||||
state = {
|
||||
'version': version,
|
||||
'lastversion': lastversion,
|
||||
'finalversion': finalversion,
|
||||
'pre': pre,
|
||||
'release': release,
|
||||
'protocol_version': protocol_version,
|
||||
'subprotocol_version': subprotocol_version,
|
||||
'proto_changed': proto_changed,
|
||||
'proto_change_date': proto_change_date,
|
||||
'srcdir': srcdir,
|
||||
'srcdiffdir': srcdiffdir,
|
||||
'lastsrcdir': lastsrcdir,
|
||||
'today': today,
|
||||
'ztoday': ztoday,
|
||||
'cl_today': cl_today,
|
||||
'year': year,
|
||||
'tz_num': tz_num,
|
||||
'master_branch': args.master_branch,
|
||||
}
|
||||
save_state(state)
|
||||
|
||||
section("Release info")
|
||||
for k in ('version', 'lastversion', 'release', 'srcdir', 'srcdiffdir', 'lastsrcdir',
|
||||
'protocol_version', 'proto_changed', 'proto_change_date'):
|
||||
print(f" {k}: {state[k]}")
|
||||
print(f"\nWrote {STATE_FILE}. Re-run --step-2-prepare to change anything.")
|
||||
|
||||
|
||||
# ---------- Step 3: tweak version files ----------
|
||||
|
||||
def step_3_tweak(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
finalversion = state['finalversion']
|
||||
pre = state['pre']
|
||||
release = state['release']
|
||||
today = state['today']
|
||||
ztoday = state['ztoday']
|
||||
cl_today = state['cl_today']
|
||||
year = state['year']
|
||||
tz_num = state['tz_num']
|
||||
proto_changed = state['proto_changed']
|
||||
proto_change_date = state['proto_change_date']
|
||||
protocol_version = state['protocol_version']
|
||||
srcdir = state['srcdir']
|
||||
|
||||
specvars = {
|
||||
'Version:': finalversion,
|
||||
'Release:': release,
|
||||
'%define fullversion': f'%{{version}}{pre}',
|
||||
'Released': version + '.',
|
||||
'%define srcdir': srcdir,
|
||||
}
|
||||
|
||||
tweak_files = ['version.h', 'rsync.h', 'NEWS.md']
|
||||
tweak_files += glob.glob('packaging/*.spec')
|
||||
tweak_files += glob.glob('packaging/*/*.spec')
|
||||
|
||||
for fn in tweak_files:
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
old_txt = txt = fh.read()
|
||||
if fn == 'version.h':
|
||||
x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
|
||||
txt = replace_or_die(x_re, r'\1 "%s"' % version, txt,
|
||||
f"Unable to update RSYNC_VERSION in {fn}")
|
||||
x_re = re.compile(r'^(#define MAINTAINER_TZ_OFFSET).*', re.M)
|
||||
txt = replace_or_die(x_re, r'\1 ' + tz_num, txt,
|
||||
f"Unable to update MAINTAINER_TZ_OFFSET in {fn}")
|
||||
elif fn == 'rsync.h':
|
||||
x_re = re.compile(r'(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
|
||||
repl = lambda m: m[1] + ' ' + (
|
||||
'0' if not pre or not proto_changed
|
||||
else '1' if m[2] == '0'
|
||||
else m[2])
|
||||
txt = replace_or_die(x_re, repl, txt,
|
||||
f"Unable to find SUBPROTOCOL_VERSION in {fn}")
|
||||
elif fn == 'NEWS.md':
|
||||
efv = re.escape(finalversion)
|
||||
x_re = re.compile(
|
||||
r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
|
||||
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
|
||||
rel_day = 'UNRELEASED' if pre else today
|
||||
repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
|
||||
+ '## Changes in this version:\n')
|
||||
if proto_changed:
|
||||
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
|
||||
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
|
||||
msg = (f"The top of {fn} is not in the right format. It should be:\n" + good_top)
|
||||
txt = replace_or_die(x_re, repl, txt, msg)
|
||||
x_re = re.compile(
|
||||
r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv,
|
||||
re.M)
|
||||
repl = lambda m: (m[1] + (m[2] if pre else ztoday) + m[3]
|
||||
+ proto_change_date + m[4] + protocol_version + m[5])
|
||||
txt = replace_or_die(x_re, repl, txt,
|
||||
f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
|
||||
elif '.spec' in fn:
|
||||
for var, val in specvars.items():
|
||||
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
|
||||
txt = replace_or_die(x_re, var + ' ' + val, txt,
|
||||
f"Unable to update {var} in {fn}")
|
||||
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
|
||||
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt,
|
||||
f"Unable to update ChangeLog header in {fn}")
|
||||
else:
|
||||
die(f"Unrecognized file in tweak_files: {fn}")
|
||||
|
||||
if txt != old_txt:
|
||||
print(f"Updating {fn}")
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
cmd_chk(['packaging/year-tweak'])
|
||||
|
||||
section("git diff after tweaks")
|
||||
cmd_run(['git', '--no-pager', 'diff'])
|
||||
|
||||
|
||||
# ---------- Step 4: build ----------
|
||||
|
||||
def step_4_build(args):
|
||||
require_top_of_checkout()
|
||||
load_state() # just to ensure we've prepared
|
||||
|
||||
section("Running prepare-source + configure --prefix=/usr --with-rrsync + make + make gen")
|
||||
# Always re-prepare so configure.sh is current; we run configure ourselves
|
||||
# with the release-required flags rather than relying on the cached
|
||||
# config.status (which may have been produced with different options).
|
||||
if os.path.isfile('.fetch'):
|
||||
cmd_chk(['./prepare-source', 'fetch'])
|
||||
else:
|
||||
cmd_chk(['./prepare-source'])
|
||||
|
||||
cmd_chk(['./configure', '--prefix=/usr', '--with-rrsync'])
|
||||
cmd_chk(['make'])
|
||||
cmd_chk(['make', 'gen'])
|
||||
|
||||
|
||||
# ---------- Step 5: commit ----------
|
||||
|
||||
def step_5_commit(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
|
||||
section("git status")
|
||||
cmd_run(['git', 'status'])
|
||||
if not confirm("Commit all current changes with the release message?"):
|
||||
die("Aborted.")
|
||||
cmd_chk(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
|
||||
|
||||
|
||||
# ---------- Step 6: tag ----------
|
||||
|
||||
def step_6_tag(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
v_ver = 'v' + version
|
||||
|
||||
out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
|
||||
if out.strip():
|
||||
if not confirm(f"Tag {v_ver} already exists. Delete and recreate?"):
|
||||
die("Aborted.")
|
||||
cmd_chk(['git', 'tag', '-d', v_ver])
|
||||
|
||||
# Prime the gpg agent so the actual tag signing won't prompt.
|
||||
section("Priming gpg agent")
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
|
||||
|
||||
section(f"Creating signed tag {v_ver}")
|
||||
out = cmd_txt(['git', 'tag', '-s', '-m', f'Version {version}.', v_ver],
|
||||
capture='combined').out
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out.lower() or 'failed' in out.lower():
|
||||
die("Tag creation failed.")
|
||||
|
||||
|
||||
# ---------- Step 7: tarball + diff ----------
|
||||
|
||||
def step_7_tarball(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
pre = state['pre']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
lastsrcdir = state['lastsrcdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
v_ver = 'v' + version
|
||||
|
||||
srctar_name = f"{rsync_ver}.tar.gz"
|
||||
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
|
||||
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, srctar_name)
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir, diff_name)
|
||||
lasttar_file = os.path.join(FTP_DIR, lastsrcdir, rsync_lastver + '.tar.gz')
|
||||
|
||||
for d in (os.path.dirname(srctar_file), os.path.dirname(diff_file)):
|
||||
os.makedirs(d, exist_ok=True)
|
||||
if not os.path.isfile(lasttar_file):
|
||||
die(f"Previous tarball not found: {lasttar_file}")
|
||||
|
||||
# Stage in ../release/work to keep the source checkout clean.
|
||||
if os.path.isdir(WORK_DIR):
|
||||
shutil.rmtree(WORK_DIR)
|
||||
os.makedirs(WORK_DIR)
|
||||
|
||||
a_dir = os.path.join(WORK_DIR, 'a')
|
||||
b_dir = os.path.join(WORK_DIR, 'b')
|
||||
|
||||
# Extract gen files from the previous tarball into work/a/.
|
||||
tweaked_gen_files = [os.path.join(rsync_lastver, fn) for fn in GEN_FILES]
|
||||
cmd_chk(['tar', '-C', WORK_DIR, '-xzf', lasttar_file, *tweaked_gen_files])
|
||||
os.rename(os.path.join(WORK_DIR, rsync_lastver), a_dir)
|
||||
|
||||
# Copy current gen files (built in the top-level checkout) into work/b/.
|
||||
os.makedirs(b_dir)
|
||||
cmd_chk(['rsync', '-a', *GEN_FILES, b_dir + '/'])
|
||||
|
||||
section(f"Creating {diff_file}")
|
||||
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # no single quotes!
|
||||
cmd_chk(
|
||||
f"(git diff v{lastversion} {v_ver} -- ':!.github'; "
|
||||
f"diff -upN {a_dir} {b_dir} | sed -r '{sed_script}') | gzip -9 >{diff_file}")
|
||||
|
||||
section(f"Creating {srctar_file}")
|
||||
# Reuse work/b/ (which already holds the fresh gen files) as the release
|
||||
# staging dir, then let "git archive" overlay the git-tracked source files
|
||||
# on top. That way the tarball ends up with both gen files and source.
|
||||
rsync_ver_dir = os.path.join(WORK_DIR, rsync_ver)
|
||||
shutil.rmtree(a_dir)
|
||||
os.rename(b_dir, rsync_ver_dir)
|
||||
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | "
|
||||
f"tar -C {WORK_DIR} -xf -")
|
||||
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver_dir}/")
|
||||
cmd_chk(['fakeroot', 'tar', '-C', WORK_DIR, '-czf', srctar_file,
|
||||
'--exclude=.github', rsync_ver])
|
||||
|
||||
# Leave staging in place; --step-8-update-ftp does its own thing.
|
||||
print(f"\nCreated:\n {srctar_file}\n {diff_file}")
|
||||
|
||||
|
||||
# ---------- Step 8: update ftp ----------
|
||||
|
||||
def step_8_update_ftp(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir,
|
||||
f"{rsync_lastver}-{version}.diffs.gz")
|
||||
|
||||
section(f"Refreshing top-of-tree files in {FTP_DIR}")
|
||||
md_files = ['README.md', 'NEWS.md', 'INSTALL.md']
|
||||
html_files = [fn for fn in GEN_FILES if fn.endswith('.html')]
|
||||
cmd_chk(['rsync', '-a', *md_files, *html_files, FTP_DIR + '/'])
|
||||
cmd_chk(['./md-convert', '--dest', FTP_DIR, *md_files])
|
||||
|
||||
section(f"Regenerating {FTP_DIR}/ChangeLog.gz")
|
||||
cmd_chk(f"git log --name-status | gzip -9 >{FTP_DIR}/ChangeLog.gz")
|
||||
|
||||
# Prime gpg agent and then sign the tar + diff.
|
||||
section("Priming gpg agent")
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
|
||||
|
||||
for fn in (srctar_file, diff_file):
|
||||
if not os.path.isfile(fn):
|
||||
die(f"Missing file to sign: {fn}. Did --step-7-tarball run successfully?")
|
||||
asc_fn = fn + '.asc'
|
||||
if os.path.lexists(asc_fn):
|
||||
os.unlink(asc_fn)
|
||||
section(f"GPG-signing {fn}")
|
||||
res = cmd_run(['gpg', '--batch', '-ba', fn])
|
||||
if res.returncode not in (0, 2):
|
||||
die("gpg signing failed.")
|
||||
|
||||
|
||||
# ---------- Step 9: top-level hard links ----------
|
||||
|
||||
def step_9_toplinks(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
pre = state['pre']
|
||||
if pre:
|
||||
print("Skipping: pre-releases do not get top-level hard links.")
|
||||
return
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir,
|
||||
f"{rsync_lastver}-{version}.diffs.gz")
|
||||
|
||||
section("Removing stale top-level rsync-* files")
|
||||
for find in [f'{FTP_DIR}/rsync-*.gz',
|
||||
f'{FTP_DIR}/rsync-*.asc',
|
||||
f'{FTP_DIR}/src-previews/rsync-*diffs.gz*']:
|
||||
for fn in glob.glob(find):
|
||||
os.unlink(fn)
|
||||
|
||||
top_link = [
|
||||
srctar_file, srctar_file + '.asc',
|
||||
diff_file, diff_file + '.asc',
|
||||
]
|
||||
for fn in top_link:
|
||||
target = re.sub(r'/src(-\w+)?/', '/', fn)
|
||||
if os.path.lexists(target):
|
||||
os.unlink(target)
|
||||
os.link(fn, target)
|
||||
print(f" linked {target}")
|
||||
|
||||
|
||||
# ---------- Step 10: push ftp ----------
|
||||
|
||||
def step_10_push_ftp(args):
|
||||
host = require_samba_host()
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
|
||||
section(f"rsync ftp dir to {host}")
|
||||
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
|
||||
f'-f._{os.path.join(FTP_DIR, ".filt")}',
|
||||
f'{FTP_DIR}/', f'{host}:{FTP_REMOTE_PATH}/'])
|
||||
|
||||
|
||||
# ---------- Step 11: push html ----------
|
||||
|
||||
def step_11_push_html(args):
|
||||
host = require_samba_host()
|
||||
if not os.path.isdir(HTML_DIR):
|
||||
die(f"{HTML_DIR} does not exist. Run --step-1-fetch first.")
|
||||
section(f"rsync html dir to {host}")
|
||||
filt = os.path.join(HTML_DIR, 'filt')
|
||||
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
|
||||
f'-f._{filt}',
|
||||
f'{HTML_DIR}/', f'{host}:{HTML_REMOTE_PATH}/'])
|
||||
|
||||
|
||||
# ---------- Step 12: print push-git instructions ----------
|
||||
|
||||
def step_12_push_git(args):
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
master_branch = state['master_branch']
|
||||
v_ver = 'v' + version
|
||||
|
||||
print(f"""\
|
||||
{DASH_LINE}
|
||||
Run these from the rsync-git checkout (this script does not push for you):
|
||||
|
||||
git push origin {master_branch}
|
||||
git push origin {v_ver}
|
||||
|
||||
If you have a 'samba' remote configured (git.samba.org:/data/git/rsync.git):
|
||||
|
||||
git push samba {master_branch}
|
||||
git push samba {v_ver}
|
||||
|
||||
Then upload the tarball + .asc to the GitHub release for {v_ver},
|
||||
and announce on rsync-announce@, rsync@, and Discord.
|
||||
""")
|
||||
|
||||
|
||||
# ---------- shared rsync-with-confirm ----------
|
||||
|
||||
def rsync_with_confirm(rsync_args):
|
||||
"""Run an rsync command in dry-run mode, then ask before running for real."""
|
||||
cmd_run(['rsync', '--dry-run', *rsync_args])
|
||||
if confirm("Run without --dry-run?"):
|
||||
cmd_run(['rsync', *rsync_args])
|
||||
|
||||
|
||||
# ---------- dispatch ----------
|
||||
|
||||
STEP_FUNCS = {
|
||||
'step-1-fetch': step_1_fetch,
|
||||
'step-2-prepare': step_2_prepare,
|
||||
'step-3-tweak': step_3_tweak,
|
||||
'step-4-build': step_4_build,
|
||||
'step-5-commit': step_5_commit,
|
||||
'step-6-tag': step_6_tag,
|
||||
'step-7-tarball': step_7_tarball,
|
||||
'step-8-update-ftp': step_8_update_ftp,
|
||||
'step-9-toplinks': step_9_toplinks,
|
||||
'step-10-push-ftp': step_10_push_ftp,
|
||||
'step-11-push-html': step_11_push_html,
|
||||
'step-12-push-git': step_12_push_git,
|
||||
}
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
die("\nAborting due to SIGINT.")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Step-based release script for rsync.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="Run --list to see the steps. Each invocation runs exactly one --step-* option.")
|
||||
parser.add_argument('--branch', '-b', dest='master_branch', default='master',
|
||||
help="The branch to release (default: master).")
|
||||
parser.add_argument('--list', action='store_true',
|
||||
help="List all release steps and exit.")
|
||||
grp = parser.add_mutually_exclusive_group()
|
||||
for flag, descr in STEPS:
|
||||
grp.add_argument('--' + flag, dest='step', action='store_const',
|
||||
const=flag, help=descr)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list:
|
||||
print("Release steps:")
|
||||
for flag, descr in STEPS:
|
||||
print(f" --{flag:18s} {descr}")
|
||||
return
|
||||
|
||||
if not args.step:
|
||||
parser.error("pick one --step-N-XX option (or --list to see them).")
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
os.environ['LESS'] = 'mqeiXR'
|
||||
STEP_FUNCS[args.step](args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
45
packaging/smart-make
Executable file
45
packaging/smart-make
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
export LANG=C
|
||||
|
||||
branch=`packaging/prep-auto-dir`
|
||||
if test x"$branch" = x; then
|
||||
srcdir=.
|
||||
else
|
||||
cd build
|
||||
srcdir=..
|
||||
fi
|
||||
|
||||
if test -f configure.sh; then
|
||||
cp -p configure.sh configure.sh.old
|
||||
else
|
||||
touch configure.sh.old
|
||||
fi
|
||||
|
||||
if test -f .fetch; then
|
||||
$srcdir/prepare-source fetch
|
||||
else
|
||||
$srcdir/prepare-source
|
||||
fi
|
||||
|
||||
if diff configure.sh configure.sh.old >/dev/null 2>&1; then
|
||||
echo "configure.sh is unchanged."
|
||||
rm configure.sh.old
|
||||
else
|
||||
echo "configure.sh has CHANGED."
|
||||
if test -f config.status; then
|
||||
./config.status --recheck
|
||||
else
|
||||
$srcdir/configure
|
||||
fi
|
||||
fi
|
||||
|
||||
./config.status
|
||||
|
||||
make all
|
||||
|
||||
if test x"$1" = x"check"; then
|
||||
make check
|
||||
fi
|
||||
@@ -7,6 +7,7 @@ Documentation=man:rsync(1) man:rsyncd.conf(5)
|
||||
[Service]
|
||||
ExecStart=/usr/bin/rsync --daemon --no-detach
|
||||
RestartSec=1
|
||||
Restart=on-failure
|
||||
|
||||
# Citing README.md:
|
||||
#
|
||||
@@ -16,14 +17,14 @@ RestartSec=1
|
||||
# This is generally used for public file distribution, [...]
|
||||
#
|
||||
# So let's assume some extra security is more than welcome here. We do full
|
||||
# system protection (which makes it read-only) and hide users' homes and
|
||||
# system protection (which makes /usr, /boot, & /etc read-only) and hide
|
||||
# devices. To override these defaults, it's best to do so in the drop-in
|
||||
# directory, often done via `systemctl edit rsync.service`. The file needs
|
||||
# just the bare minimum of the right [heading] and override values.
|
||||
# See systemd.unit(5) and search for "drop-in" for full details.
|
||||
|
||||
ProtectSystem=full
|
||||
ProtectHome=on
|
||||
#ProtectHome=on|off|read-only
|
||||
PrivateDevices=on
|
||||
NoNewPrivileges=on
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ StandardError=journal
|
||||
# This is generally used for public file distribution, [...]
|
||||
#
|
||||
# So let's assume some extra security is more than welcome here. We do full
|
||||
# system protection (which makes it read-only) and hide users' homes and
|
||||
# system protection (which makes /usr, /boot, & /etc read-only) and hide
|
||||
# devices. To override these defaults, it's best to do so in the drop-in
|
||||
# directory, often done via `systemctl edit rsync@.service`. The file needs
|
||||
# just the bare minimum of the right [heading] and override values.
|
||||
# See systemd.unit(5) and search for "drop-in" for full details.
|
||||
|
||||
ProtectSystem=full
|
||||
ProtectHome=on
|
||||
#ProtectHome=on|off|read-only
|
||||
PrivateDevices=on
|
||||
NoNewPrivileges=on
|
||||
|
||||
@@ -4,19 +4,27 @@
|
||||
# for vars that are defined but not used, and for inconsistent array
|
||||
# sizes. Run it from inside the main rsync directory.
|
||||
|
||||
import re, argparse, glob
|
||||
import os, sys, re, argparse, glob
|
||||
|
||||
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
|
||||
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z][^ \n\t:]*\s+.*);', re.M)
|
||||
EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
|
||||
|
||||
types = { }
|
||||
sizes = { }
|
||||
|
||||
def main():
|
||||
add_syscall_c = set('t_stub.c t_unsafe.c tls.c trimslash.c'.split())
|
||||
add_util_c = set('t_stub.c t_unsafe.c'.split())
|
||||
|
||||
if not os.path.exists('syscall.c'):
|
||||
if os.path.exists('var-checker'):
|
||||
os.chdir('..')
|
||||
else:
|
||||
print("Couldn't find the source dir.")
|
||||
sys.exit(1)
|
||||
|
||||
syscall_c = slurp_file('syscall.c', True)
|
||||
util_c = slurp_file('util.c', True)
|
||||
util_c = slurp_file('util1.c', True)
|
||||
|
||||
for fn in sorted(glob.glob('*.c')):
|
||||
txt = slurp_file(fn)
|
||||
@@ -61,19 +69,44 @@ def parse_vars(fn, lines):
|
||||
for line in lines:
|
||||
line = re.sub(r'\s*\{.*\}', '', line)
|
||||
line = re.sub(r'\s*\(.*\)', '', line)
|
||||
for item in re.split(r'\s*,\s*', line):
|
||||
item = re.sub(r'\s*=.*', '', item)
|
||||
m = re.search(r'(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
|
||||
line = re.sub(r'\s*=\s*[^,]*', '', line)
|
||||
m = re.search(r'^(?:(?:static|extern)\s+)?(?P<type>[^\[,]+?)(?P<vars>\w+([\[,].+)?)$', line)
|
||||
if not m:
|
||||
print(f"Bogus match? ({line})")
|
||||
continue
|
||||
items = m['vars']
|
||||
main_type = m['type'].strip()
|
||||
mt_len = len(main_type)
|
||||
main_type = main_type.rstrip('*')
|
||||
first_stars = '*' * (mt_len - len(main_type))
|
||||
if first_stars:
|
||||
main_type = main_type.rstrip()
|
||||
items = first_stars + items
|
||||
for item in re.split(r'\s*,\s*', items):
|
||||
m = re.search(r'(?P<stars>\*+\s*)?(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
|
||||
if not m:
|
||||
print(f"Bogus match? ({item})")
|
||||
continue
|
||||
if m['sz']:
|
||||
if m['var'] in sizes:
|
||||
if sizes[m['var']] != m['sz']:
|
||||
typ = main_type
|
||||
if m['stars']:
|
||||
typ = typ + m['stars'].strip()
|
||||
chk = [
|
||||
'type', typ, types,
|
||||
'size', m['sz'], sizes,
|
||||
]
|
||||
while chk:
|
||||
label = chk.pop(0)
|
||||
new = chk.pop(0)
|
||||
lst = chk.pop(0)
|
||||
if label == 'type':
|
||||
new = ' '.join(new.split()).replace(' *', '*')
|
||||
if m['var'] in lst:
|
||||
old = lst[m['var']]
|
||||
if new != old:
|
||||
var = m['var']
|
||||
print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
|
||||
print(fn, f'has inconsistent {label} for "{var}":', new, 'vs', old)
|
||||
else:
|
||||
sizes[m['var']] = m['sz']
|
||||
lst[m['var']] = new
|
||||
ret.append(m['var'])
|
||||
return ret
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
import sys, os, re, argparse, subprocess
|
||||
from datetime import datetime
|
||||
|
||||
MAINTAINER_NAME = 'Wayne Davison'
|
||||
MAINTAINER_SUF = ' ' + MAINTAINER_NAME + "\n"
|
||||
|
||||
def main():
|
||||
latest_year = '2000'
|
||||
|
||||
@@ -22,10 +19,6 @@ def main():
|
||||
m = argparse.Namespace(**m.groupdict())
|
||||
if m.year > latest_year:
|
||||
latest_year = m.year
|
||||
if m.fn.startswith('zlib/') or m.fn.startswith('popt/'):
|
||||
continue
|
||||
if re.search(r'\.(c|h|sh|test)$', m.fn):
|
||||
maybe_edit_copyright_year(m.fn, m.year)
|
||||
proc.communicate()
|
||||
|
||||
fn = 'latest-year.h'
|
||||
@@ -39,55 +32,8 @@ def main():
|
||||
fh.write(txt)
|
||||
|
||||
|
||||
def maybe_edit_copyright_year(fn, year):
|
||||
opening_lines = [ ]
|
||||
copyright_line = None
|
||||
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
for lineno, line in enumerate(fh):
|
||||
opening_lines.append(line)
|
||||
if lineno > 3 and not re.search(r'\S', line):
|
||||
break
|
||||
m = re.match(r'^(?P<pre>.*Copyright\s+\S+\s+)(?P<year>\d\d\d\d(?:-\d\d\d\d)?(,\s+\d\d\d\d)*)(?P<suf>.+)', line)
|
||||
if not m:
|
||||
continue
|
||||
copyright_line = argparse.Namespace(**m.groupdict())
|
||||
copyright_line.lineno = len(opening_lines)
|
||||
copyright_line.is_maintainer_line = MAINTAINER_NAME in copyright_line.suf
|
||||
copyright_line.txt = line
|
||||
if copyright_line.is_maintainer_line:
|
||||
break
|
||||
|
||||
if not copyright_line:
|
||||
return
|
||||
|
||||
if copyright_line.is_maintainer_line:
|
||||
cyears = copyright_line.year.split('-')
|
||||
if year == cyears[0]:
|
||||
cyears = [ year ]
|
||||
else:
|
||||
cyears = [ cyears[0], year ]
|
||||
txt = copyright_line.pre + '-'.join(cyears) + MAINTAINER_SUF
|
||||
if txt == copyright_line.txt:
|
||||
return
|
||||
opening_lines[copyright_line.lineno - 1] = txt
|
||||
else:
|
||||
if fn.startswith('lib/') or fn.startswith('testsuite/'):
|
||||
return
|
||||
txt = copyright_line.pre + year + MAINTAINER_SUF
|
||||
opening_lines[copyright_line.lineno - 1] += txt
|
||||
|
||||
remaining_txt = fh.read()
|
||||
|
||||
print(f"Updating {fn} with year {year}")
|
||||
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(''.join(opening_lines))
|
||||
fh.write(remaining_txt)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Grab the year of last mod for our c & h files and make sure the Copyright comment is up-to-date.")
|
||||
parser = argparse.ArgumentParser(description="Grab the year of the last mod for our c & h files and make sure the LATEST_YEAR value is accurate.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/findme.c
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
file accompanying popt source distributions, available from
|
||||
ftp://ftp.rpm.org/pub/rpm/dist. */
|
||||
|
||||
#include "system.h"
|
||||
#include "findme.h"
|
||||
|
||||
const char * findProgramPath(const char * argv0)
|
||||
{
|
||||
char * path = getenv("PATH");
|
||||
char * pathbuf;
|
||||
char * start, * chptr;
|
||||
char * buf;
|
||||
size_t bufsize;
|
||||
|
||||
if (argv0 == NULL) return NULL; /* XXX can't happen */
|
||||
/* If there is a / in the argv[0], it has to be an absolute path */
|
||||
if (strchr(argv0, '/'))
|
||||
return xstrdup(argv0);
|
||||
|
||||
if (path == NULL) return NULL;
|
||||
|
||||
bufsize = strlen(path) + 1;
|
||||
start = pathbuf = alloca(bufsize);
|
||||
if (pathbuf == NULL) return NULL; /* XXX can't happen */
|
||||
strlcpy(pathbuf, path, bufsize);
|
||||
bufsize += sizeof "/" - 1 + strlen(argv0);
|
||||
buf = malloc(bufsize);
|
||||
if (buf == NULL) return NULL; /* XXX can't happen */
|
||||
|
||||
chptr = NULL;
|
||||
/*@-branchstate@*/
|
||||
do {
|
||||
if ((chptr = strchr(start, ':')))
|
||||
*chptr = '\0';
|
||||
snprintf(buf, bufsize, "%s/%s", start, argv0);
|
||||
|
||||
if (!access(buf, X_OK))
|
||||
return buf;
|
||||
|
||||
if (chptr)
|
||||
start = chptr + 1;
|
||||
else
|
||||
start = NULL;
|
||||
} while (start && *start);
|
||||
/*@=branchstate@*/
|
||||
|
||||
free(buf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/findme.h
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
file accompanying popt source distributions, available from
|
||||
ftp://ftp.rpm.org/pub/rpm/dist. */
|
||||
|
||||
#ifndef H_FINDME
|
||||
#define H_FINDME
|
||||
|
||||
/**
|
||||
* Return absolute path to executable by searching PATH.
|
||||
* @param argv0 name of executable
|
||||
* @return (malloc'd) absolute path to executable (or NULL)
|
||||
*/
|
||||
/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
|
||||
/*@*/;
|
||||
|
||||
#endif
|
||||
959
popt/lookup3.c
Normal file
959
popt/lookup3.c
Normal file
@@ -0,0 +1,959 @@
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*
|
||||
* These are functions for producing 32-bit hashes for hash table lookup.
|
||||
* jlu32w(), jlu32l(), jlu32lpair(), jlu32b(), _JLU3_MIX(), and _JLU3_FINAL()
|
||||
* are externally useful functions. Routines to test the hash are included
|
||||
* if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
* the public domain. It has no warranty.
|
||||
*
|
||||
* You probably want to use jlu32l(). jlu32l() and jlu32b()
|
||||
* hash byte arrays. jlu32l() is is faster than jlu32b() on
|
||||
* little-endian machines. Intel and AMD are little-endian machines.
|
||||
* On second thought, you probably want jlu32lpair(), which is identical to
|
||||
* jlu32l() except it returns two 32-bit hashes for the price of one.
|
||||
* You could implement jlu32bpair() if you wanted but I haven't bothered here.
|
||||
*
|
||||
* If you want to find a hash of, say, exactly 7 integers, do
|
||||
* a = i1; b = i2; c = i3;
|
||||
* _JLU3_MIX(a,b,c);
|
||||
* a += i4; b += i5; c += i6;
|
||||
* _JLU3_MIX(a,b,c);
|
||||
* a += i7;
|
||||
* _JLU3_FINAL(a,b,c);
|
||||
* then use c as the hash value. If you have a variable size array of
|
||||
* 4-byte integers to hash, use jlu32w(). If you have a byte array (like
|
||||
* a character string), use jlu32l(). If you have several byte arrays, or
|
||||
* a mix of things, see the comments above jlu32l().
|
||||
*
|
||||
* Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
* then mix those integers. This is fast (you can do a lot more thorough
|
||||
* mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
* on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_JLU3_SELFTEST)
|
||||
# define _JLU3_jlu32w 1
|
||||
# define _JLU3_jlu32l 1
|
||||
# define _JLU3_jlu32lpair 1
|
||||
# define _JLU3_jlu32b 1
|
||||
#endif
|
||||
|
||||
static const union _dbswap {
|
||||
const uint32_t ui;
|
||||
const unsigned char uc[4];
|
||||
} endian = { .ui = 0x11223344 };
|
||||
# define HASH_LITTLE_ENDIAN (endian.uc[0] == (unsigned char) 0x44)
|
||||
# define HASH_BIG_ENDIAN (endian.uc[0] == (unsigned char) 0x11)
|
||||
|
||||
#ifndef ROTL32
|
||||
# define ROTL32(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
|
||||
#endif
|
||||
|
||||
/* NOTE: The _size parameter should be in bytes. */
|
||||
#define _JLU3_INIT(_h, _size) (0xdeadbeef + ((uint32_t)(_size)) + (_h))
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* _JLU3_MIX -- mix 3 32-bit values reversibly.
|
||||
*
|
||||
* This is reversible, so any information in (a,b,c) before _JLU3_MIX() is
|
||||
* still in (a,b,c) after _JLU3_MIX().
|
||||
*
|
||||
* If four pairs of (a,b,c) inputs are run through _JLU3_MIX(), or through
|
||||
* _JLU3_MIX() in reverse, there are at least 32 bits of the output that
|
||||
* are sometimes the same for one pair and different for another pair.
|
||||
* This was tested for:
|
||||
* * pairs that differed by one bit, by two bits, in any combination
|
||||
* of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
* (a,b,c).
|
||||
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
* is commonly produced by subtraction) look like a single 1-bit
|
||||
* difference.
|
||||
* * the base values were pseudorandom, all zero but one bit set, or
|
||||
* all zero plus a counter that starts at zero.
|
||||
*
|
||||
* Some k values for my "a-=c; a^=ROTL32(c,k); c+=b;" arrangement that
|
||||
* satisfy this are
|
||||
* 4 6 8 16 19 4
|
||||
* 9 15 3 18 27 15
|
||||
* 14 9 3 7 17 3
|
||||
* Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
* for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
* used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
* the operations, constants, and arrangements of the variables.
|
||||
*
|
||||
* This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
* that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
* most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
* avalanche in c.
|
||||
*
|
||||
* This allows some parallelism. Read-after-writes are good at doubling
|
||||
* the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
* direction as the goal of parallelism. I did what I could. Rotates
|
||||
* seem to cost as much as shifts on every machine I could lay my hands
|
||||
* on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
* rotates.
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
#define _JLU3_MIX(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= ROTL32(c, 4); c += b; \
|
||||
b -= a; b ^= ROTL32(a, 6); a += c; \
|
||||
c -= b; c ^= ROTL32(b, 8); b += a; \
|
||||
a -= c; a ^= ROTL32(c,16); c += b; \
|
||||
b -= a; b ^= ROTL32(a,19); a += c; \
|
||||
c -= b; c ^= ROTL32(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* _JLU3_FINAL -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
*
|
||||
* Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
* produce values of c that look totally different. This was tested for
|
||||
* * pairs that differed by one bit, by two bits, in any combination
|
||||
* of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
* (a,b,c).
|
||||
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
* is commonly produced by subtraction) look like a single 1-bit
|
||||
* difference.
|
||||
* * the base values were pseudorandom, all zero but one bit set, or
|
||||
* all zero plus a counter that starts at zero.
|
||||
*
|
||||
* These constants passed:
|
||||
* 14 11 25 16 4 14 24
|
||||
* 12 14 25 16 4 14 24
|
||||
* and these came close:
|
||||
* 4 8 15 26 3 22 24
|
||||
* 10 8 15 26 3 22 24
|
||||
* 11 8 15 26 3 22 24
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
#define _JLU3_FINAL(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= ROTL32(b,14); \
|
||||
a ^= c; a -= ROTL32(c,11); \
|
||||
b ^= a; b -= ROTL32(a,25); \
|
||||
c ^= b; c -= ROTL32(b,16); \
|
||||
a ^= c; a -= ROTL32(c,4); \
|
||||
b ^= a; b -= ROTL32(a,14); \
|
||||
c ^= b; c -= ROTL32(b,24); \
|
||||
}
|
||||
|
||||
#if defined(_JLU3_jlu32w)
|
||||
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* This works on all machines. To be useful, it requires
|
||||
* -- that the key be an array of uint32_t's, and
|
||||
* -- that the size be the number of uint32_t's in the key
|
||||
*
|
||||
* The function jlu32w() is identical to jlu32l() on little-endian
|
||||
* machines, and identical to jlu32b() on big-endian machines,
|
||||
* except that the size has to be measured in uint32_ts rather than in
|
||||
* bytes. jlu32l() is more complicated than jlu32w() only because
|
||||
* jlu32l() has to dance around fitting the key bytes into registers.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint32_t values
|
||||
* @param size the size of the key, in uint32_ts
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size)
|
||||
{
|
||||
uint32_t a = _JLU3_INIT(h, (size * sizeof(*k)));
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (k == NULL)
|
||||
goto exit;
|
||||
|
||||
/*----------------------------------------------- handle most of the key */
|
||||
while (size > 3) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 3;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------------------- handle the last 3 uint32_t's */
|
||||
switch (size) {
|
||||
case 3 : c+=k[2];
|
||||
case 2 : b+=k[1];
|
||||
case 1 : a+=k[0];
|
||||
_JLU3_FINAL(a,b,c);
|
||||
/* fallthrough */
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
/*---------------------------------------------------- report the result */
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32w) */
|
||||
|
||||
#if defined(_JLU3_jlu32l)
|
||||
uint32_t jlu32l(uint32_t h, const void *key, size_t size);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* jlu32l() -- hash a variable-length key into a 32-bit value
|
||||
* h : can be any 4-byte value
|
||||
* k : the key (the unaligned variable-length array of bytes)
|
||||
* size : the size of the key, counting by bytes
|
||||
* Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
* the return value. Two keys differing by one or two bits will have
|
||||
* totally different hash values.
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* If you are hashing n strings (uint8_t **)k, do it like this:
|
||||
* for (i=0, h=0; i<n; ++i) h = jlu32l(h, k[i], len[i]);
|
||||
*
|
||||
* By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||
* code any way you wish, private, educational, or commercial. It's free.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
* acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint8_t values
|
||||
* @param size the size of the key
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
uint32_t jlu32l(uint32_t h, const void *key, size_t size)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(h, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
goto exit;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff; break;
|
||||
case 2: a += k[0]&0xffff; break;
|
||||
case 1: a += k[0]&0xff; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0] break;
|
||||
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
|
||||
case 9: c += k8[8]; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
|
||||
case 5: b += k8[4]; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
|
||||
case 1: a += k8[0]; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*----------- all but last block: aligned reads and different mixing */
|
||||
while (size > 12) {
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12:
|
||||
c += k[4]+(((uint32_t)k[5])<<16);
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11:
|
||||
c += ((uint32_t)k8[10])<<16;
|
||||
/* fallthrough */
|
||||
case 10:
|
||||
c += (uint32_t)k[4];
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9:
|
||||
c += (uint32_t)k8[8];
|
||||
/* fallthrough */
|
||||
case 8:
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7:
|
||||
b += ((uint32_t)k8[6])<<16;
|
||||
/* fallthrough */
|
||||
case 6:
|
||||
b += (uint32_t)k[2];
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5:
|
||||
b += (uint32_t)k8[4];
|
||||
/* fallthrough */
|
||||
case 4:
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3:
|
||||
a += ((uint32_t)k8[2])<<16;
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
a += (uint32_t)k[0];
|
||||
break;
|
||||
case 1:
|
||||
a += (uint32_t)k8[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += (uint32_t)k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += (uint32_t)k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += (uint32_t)k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) {
|
||||
case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
|
||||
case 9: c += (uint32_t)k[8]; /* fallthrough */
|
||||
case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
|
||||
case 5: b += (uint32_t)k[4]; /* fallthrough */
|
||||
case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
|
||||
case 1: a += (uint32_t)k[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32l) */
|
||||
|
||||
#if defined(_JLU3_jlu32lpair)
|
||||
/**
|
||||
* jlu32lpair: return 2 32-bit hash values.
|
||||
*
|
||||
* This is identical to jlu32l(), except it returns two 32-bit hash
|
||||
* values instead of just one. This is good enough for hash table
|
||||
* lookup with 2^^64 buckets, or if you want a second hash if you're not
|
||||
* happy with the first, or if you want a probably-unique 64-bit ID for
|
||||
* the key. *pc is better mixed than *pb, so use *pc first. If you want
|
||||
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *key the key, an array of uint8_t values
|
||||
* @param size the size of the key in bytes
|
||||
* @retval *pc, IN: primary initval, OUT: primary hash
|
||||
* *retval *pb IN: secondary initval, OUT: secondary hash
|
||||
*/
|
||||
void jlu32lpair(const void *key, size_t size, uint32_t *pc, uint32_t *pb)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(*pc, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
goto exit;
|
||||
|
||||
c += *pb; /* Add the secondary hash. */
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff; break;
|
||||
case 2: a += k[0]&0xffff; break;
|
||||
case 1: a += k[0]&0xff; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
|
||||
case 9: c += k8[8]; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
|
||||
case 5: b += k8[4]; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
|
||||
case 1: a += k8[0]; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*----------- all but last block: aligned reads and different mixing */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12:
|
||||
c += k[4]+(((uint32_t)k[5])<<16);
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11:
|
||||
c += ((uint32_t)k8[10])<<16;
|
||||
/* fallthrough */
|
||||
case 10:
|
||||
c += k[4];
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9:
|
||||
c += k8[8];
|
||||
/* fallthrough */
|
||||
case 8:
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7:
|
||||
b += ((uint32_t)k8[6])<<16;
|
||||
/* fallthrough */
|
||||
case 6:
|
||||
b += k[2];
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5:
|
||||
b += k8[4];
|
||||
/* fallthrough */
|
||||
case 4:
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3:
|
||||
a += ((uint32_t)k8[2])<<16;
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
a += k[0];
|
||||
break;
|
||||
case 1:
|
||||
a += k8[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) {
|
||||
case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
|
||||
case 9: c += k[8]; /* fallthrough */
|
||||
case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
|
||||
case 5: b += k[4]; /* fallthrough */
|
||||
case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
|
||||
case 1: a += k[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
*pc = c;
|
||||
*pb = b;
|
||||
return;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32lpair) */
|
||||
|
||||
#if defined(_JLU3_jlu32b)
|
||||
uint32_t jlu32b(uint32_t h, const void *key, size_t size);
|
||||
/*
|
||||
* jlu32b():
|
||||
* This is the same as jlu32w() on big-endian machines. It is different
|
||||
* from jlu32l() on all machines. jlu32b() takes advantage of
|
||||
* big-endian byte ordering.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint8_t values
|
||||
* @param size the size of the key
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
uint32_t jlu32b(uint32_t h, const void *key, size_t size)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(h, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
return h;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]<<8" actually reads beyond the end of the string, but
|
||||
* then shifts out the part it's not allowed to read. Because the
|
||||
* string is aligned, the illegal read is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff00; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff0000; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff000000; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff00; break;
|
||||
case 2: a += k[0]&0xffff0000; break;
|
||||
case 1: a += k[0]&0xff000000; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) { /* all the case statements fall through */
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += ((uint32_t)k8[10])<<8; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<16; /* fallthrough */
|
||||
case 9: c += ((uint32_t)k8[8])<<24; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<8; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<16; /* fallthrough */
|
||||
case 5: b += ((uint32_t)k8[4])<<24; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<8; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<16; /* fallthrough */
|
||||
case 1: a += ((uint32_t)k8[0])<<24; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !VALGRIND */
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += ((uint32_t)k[0])<<24;
|
||||
a += ((uint32_t)k[1])<<16;
|
||||
a += ((uint32_t)k[2])<<8;
|
||||
a += ((uint32_t)k[3]);
|
||||
b += ((uint32_t)k[4])<<24;
|
||||
b += ((uint32_t)k[5])<<16;
|
||||
b += ((uint32_t)k[6])<<8;
|
||||
b += ((uint32_t)k[7]);
|
||||
c += ((uint32_t)k[8])<<24;
|
||||
c += ((uint32_t)k[9])<<16;
|
||||
c += ((uint32_t)k[10])<<8;
|
||||
c += ((uint32_t)k[11]);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) { /* all the case statements fall through */
|
||||
case 12: c += k[11]; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<8; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<16; /* fallthrough */
|
||||
case 9: c += ((uint32_t)k[8])<<24; /* fallthrough */
|
||||
case 8: b += k[7]; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<8; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<16; /* fallthrough */
|
||||
case 5: b += ((uint32_t)k[4])<<24; /* fallthrough */
|
||||
case 4: a += k[3]; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<8; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<16; /* fallthrough */
|
||||
case 1: a += ((uint32_t)k[0])<<24; /* fallthrough */
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32b) */
|
||||
|
||||
#if defined(_JLU3_SELFTEST)
|
||||
|
||||
/* used for timings */
|
||||
static void driver1(void)
|
||||
{
|
||||
uint8_t buf[256];
|
||||
uint32_t i;
|
||||
uint32_t h=0;
|
||||
time_t a,z;
|
||||
|
||||
time(&a);
|
||||
for (i=0; i<256; ++i) buf[i] = 'x';
|
||||
for (i=0; i<1; ++i) {
|
||||
h = jlu32l(h, &buf[0], sizeof(buf[0]));
|
||||
}
|
||||
time(&z);
|
||||
if (z-a > 0) printf("time %d %.8x\n", (int)(z-a), h);
|
||||
}
|
||||
|
||||
/* check that every input bit changes every output bit half the time */
|
||||
#define HASHSTATE 1
|
||||
#define HASHLEN 1
|
||||
#define MAXPAIR 60
|
||||
#define MAXLEN 70
|
||||
static void driver2(void)
|
||||
{
|
||||
uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
|
||||
uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
|
||||
uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
|
||||
uint32_t x[HASHSTATE],y[HASHSTATE];
|
||||
uint32_t hlen;
|
||||
|
||||
printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
|
||||
for (hlen=0; hlen < MAXLEN; ++hlen) {
|
||||
z=0;
|
||||
for (i=0; i<hlen; ++i) { /*-------------- for each input byte, */
|
||||
for (j=0; j<8; ++j) { /*--------------- for each input bit, */
|
||||
for (m=1; m<8; ++m) { /*---- for several possible initvals, */
|
||||
for (l=0; l<HASHSTATE; ++l)
|
||||
e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
|
||||
|
||||
/* check that every output bit is affected by that input bit */
|
||||
for (k=0; k<MAXPAIR; k+=2) {
|
||||
uint32_t finished=1;
|
||||
/* keys have one bit different */
|
||||
for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
|
||||
/* have a and b be two keys differing in only one bit */
|
||||
a[i] ^= (k<<j);
|
||||
a[i] ^= (k>>(8-j));
|
||||
c[0] = jlu32l(m, a, hlen);
|
||||
b[i] ^= ((k+1)<<j);
|
||||
b[i] ^= ((k+1)>>(8-j));
|
||||
d[0] = jlu32l(m, b, hlen);
|
||||
/* check every bit is 1, 0, set, and not set at least once */
|
||||
for (l=0; l<HASHSTATE; ++l) {
|
||||
e[l] &= (c[l]^d[l]);
|
||||
f[l] &= ~(c[l]^d[l]);
|
||||
g[l] &= c[l];
|
||||
h[l] &= ~c[l];
|
||||
x[l] &= d[l];
|
||||
y[l] &= ~d[l];
|
||||
if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
|
||||
}
|
||||
if (finished) break;
|
||||
}
|
||||
if (k>z) z=k;
|
||||
if (k == MAXPAIR) {
|
||||
printf("Some bit didn't change: ");
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
|
||||
e[0],f[0],g[0],h[0],x[0],y[0]);
|
||||
printf("i %u j %u m %u len %u\n", i, j, m, hlen);
|
||||
}
|
||||
if (z == MAXPAIR) goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (z < MAXPAIR) {
|
||||
printf("Mix success %2u bytes %2u initvals ",i,m);
|
||||
printf("required %u trials\n", z/2);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Check for reading beyond the end of the buffer and alignment problems */
|
||||
static void driver3(void)
|
||||
{
|
||||
uint8_t buf[MAXLEN+20], *b;
|
||||
uint32_t len;
|
||||
uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
|
||||
uint32_t h;
|
||||
uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t i;
|
||||
uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t j;
|
||||
uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t ref,x,y;
|
||||
uint8_t *p;
|
||||
uint32_t m = 13;
|
||||
|
||||
printf("Endianness. These lines should all be the same (for values filled in):\n");
|
||||
printf("%.8x %.8x %.8x\n",
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-1)/4),
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-5)/4),
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-9)/4));
|
||||
p = q;
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qq[1];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qqq[2];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qqqq[3];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
printf("\n");
|
||||
for (h=0, b=buf+1; h<8; ++h, ++b) {
|
||||
for (i=0; i<MAXLEN; ++i) {
|
||||
len = i;
|
||||
for (j=0; j<i; ++j)
|
||||
*(b+j)=0;
|
||||
|
||||
/* these should all be equal */
|
||||
m = 1;
|
||||
ref = jlu32l(m, b, len);
|
||||
*(b+i)=(uint8_t)~0;
|
||||
*(b-1)=(uint8_t)~0;
|
||||
x = jlu32l(m, b, len);
|
||||
y = jlu32l(m, b, len);
|
||||
if ((ref != x) || (ref != y))
|
||||
printf("alignment error: %.8x %.8x %.8x %u %u\n",ref,x,y, h, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for problems with nulls */
|
||||
static void driver4(void)
|
||||
{
|
||||
uint8_t buf[1];
|
||||
uint32_t h;
|
||||
uint32_t i;
|
||||
uint32_t state[HASHSTATE];
|
||||
|
||||
buf[0] = ~0;
|
||||
for (i=0; i<HASHSTATE; ++i)
|
||||
state[i] = 1;
|
||||
printf("These should all be different\n");
|
||||
h = 0;
|
||||
for (i=0; i<8; ++i) {
|
||||
h = jlu32l(h, buf, 0);
|
||||
printf("%2ld 0-byte strings, hash is %.8x\n", (long)i, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
driver1(); /* test that the key is hashed: used for timings */
|
||||
driver2(); /* test that whole key is hashed thoroughly */
|
||||
driver3(); /* test that nothing but the key is hashed */
|
||||
driver4(); /* test hashing multiple buffers (all buffers are null) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* _JLU3_SELFTEST */
|
||||
1483
popt/popt.c
1483
popt/popt.c
File diff suppressed because it is too large
Load Diff
473
popt/popt.h
473
popt/popt.h
@@ -1,5 +1,4 @@
|
||||
/** \file popt/popt.h
|
||||
* \ingroup popt
|
||||
/** @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -13,45 +12,49 @@
|
||||
|
||||
#define POPT_OPTION_DEPTH 10
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Arg type identifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ARG_NONE 0 /*!< no arg */
|
||||
#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
|
||||
#define POPT_ARG_INT 2 /*!< arg will be converted to int */
|
||||
#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
|
||||
#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
|
||||
#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
|
||||
#define POPT_ARG_NONE 0U /*!< no arg */
|
||||
#define POPT_ARG_STRING 1U /*!< arg will be saved as string */
|
||||
#define POPT_ARG_INT 2U /*!< arg ==> int */
|
||||
#define POPT_ARG_LONG 3U /*!< arg ==> long */
|
||||
#define POPT_ARG_INCLUDE_TABLE 4U /*!< arg points to table */
|
||||
#define POPT_ARG_CALLBACK 5U /*!< table-wide callback... must be
|
||||
set first in table; arg points
|
||||
to callback, descrip points to
|
||||
callback data to pass */
|
||||
#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
|
||||
#define POPT_ARG_INTL_DOMAIN 6U /*!< set the translation domain
|
||||
for this table and any
|
||||
included tables; arg points
|
||||
to the domain string */
|
||||
#define POPT_ARG_VAL 7 /*!< arg should take value val */
|
||||
#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
|
||||
#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
|
||||
#define POPT_ARG_VAL 7U /*!< arg should take value val */
|
||||
#define POPT_ARG_FLOAT 8U /*!< arg ==> float */
|
||||
#define POPT_ARG_DOUBLE 9U /*!< arg ==> double */
|
||||
#define POPT_ARG_LONGLONG 10U /*!< arg ==> long long */
|
||||
|
||||
#define POPT_ARG_MASK 0x0000FFFF
|
||||
/*@}*/
|
||||
#define POPT_ARG_MAINCALL (16U+11U) /*!< EXPERIMENTAL: return (*arg) (argc, argv) */
|
||||
#define POPT_ARG_ARGV 12U /*!< dupe'd arg appended to realloc'd argv array. */
|
||||
#define POPT_ARG_SHORT 13U /*!< arg ==> short */
|
||||
#define POPT_ARG_BITSET (16U+14U) /*!< arg ==> bit set */
|
||||
|
||||
/** \ingroup popt
|
||||
#define POPT_ARG_MASK 0x000000FFU
|
||||
#define POPT_GROUP_MASK 0x0000FF00U
|
||||
|
||||
/**
|
||||
* \name Arg modifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
|
||||
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
|
||||
#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
|
||||
#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
|
||||
#define POPT_ARGFLAG_ONEDASH 0x80000000U /*!< allow -longoption */
|
||||
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000U /*!< don't show in help/usage */
|
||||
#define POPT_ARGFLAG_STRIP 0x20000000U /*!< strip this arg from argv(only applies to long args) */
|
||||
#define POPT_ARGFLAG_OPTIONAL 0x10000000U /*!< arg may be missing */
|
||||
|
||||
#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
|
||||
#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
|
||||
#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
|
||||
#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
|
||||
#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
|
||||
#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
|
||||
#define POPT_ARGFLAG_OR 0x08000000U /*!< arg will be or'ed */
|
||||
#define POPT_ARGFLAG_NOR 0x09000000U /*!< arg will be nor'ed */
|
||||
#define POPT_ARGFLAG_AND 0x04000000U /*!< arg will be and'ed */
|
||||
#define POPT_ARGFLAG_NAND 0x05000000U /*!< arg will be nand'ed */
|
||||
#define POPT_ARGFLAG_XOR 0x02000000U /*!< arg will be xor'ed */
|
||||
#define POPT_ARGFLAG_NOT 0x01000000U /*!< arg will be negated */
|
||||
#define POPT_ARGFLAG_LOGICALOPS \
|
||||
(POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
|
||||
|
||||
@@ -60,158 +63,126 @@
|
||||
#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
|
||||
/*!< clear arg bit(s) */
|
||||
|
||||
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
|
||||
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000U /*!< show default value in --help */
|
||||
#define POPT_ARGFLAG_RANDOM 0x00400000U /*!< random value in [1,arg] */
|
||||
#define POPT_ARGFLAG_TOGGLE 0x00200000U /*!< permit --[no]opt prefix toggle */
|
||||
|
||||
/*@}*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Callback modifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
|
||||
#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
|
||||
#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
|
||||
#define POPT_CBFLAG_PRE 0x80000000U /*!< call the callback before parse */
|
||||
#define POPT_CBFLAG_POST 0x40000000U /*!< call the callback after parse */
|
||||
#define POPT_CBFLAG_INC_DATA 0x20000000U /*!< use data from the include line,
|
||||
not the subtable */
|
||||
#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
|
||||
#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
|
||||
/*@}*/
|
||||
#define POPT_CBFLAG_SKIPOPTION 0x10000000U /*!< don't callback with option */
|
||||
#define POPT_CBFLAG_CONTINUE 0x08000000U /*!< continue callbacks with option */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Error return values
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ERROR_NOARG -10 /*!< missing argument */
|
||||
#define POPT_ERROR_BADOPT -11 /*!< unknown option */
|
||||
#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
|
||||
#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
|
||||
#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
|
||||
#define POPT_ERROR_BADQUOTE -15 /*!< error in parameter quoting */
|
||||
#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
|
||||
#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
|
||||
#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
|
||||
#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
|
||||
#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
|
||||
#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
|
||||
/*@}*/
|
||||
#define POPT_ERROR_BADCONFIG -22 /*!< config file failed sanity test */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name poptBadOption() flags
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
|
||||
/*@}*/
|
||||
#define POPT_BADOPTION_NOALIAS (1U << 0) /*!< don't go into an alias */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name poptGetContext() flags
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
|
||||
#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
|
||||
#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
|
||||
#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
|
||||
/*@}*/
|
||||
#define POPT_CONTEXT_NO_EXEC (1U << 0) /*!< ignore exec expansions */
|
||||
#define POPT_CONTEXT_KEEP_FIRST (1U << 1) /*!< pay attention to argv[0] */
|
||||
#define POPT_CONTEXT_POSIXMEHARDER (1U << 2) /*!< options can't follow args */
|
||||
#define POPT_CONTEXT_ARG_OPTS (1U << 4) /*!< return args as options with value 0 */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
struct poptOption {
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char * longName; /*!< may be NULL */
|
||||
char shortName; /*!< may be NUL */
|
||||
int argInfo;
|
||||
/*@shared@*/ /*@null@*/
|
||||
char shortName; /*!< may be '\0' */
|
||||
unsigned int argInfo; /*!< type of argument expected after the option */
|
||||
void * arg; /*!< depends on argInfo */
|
||||
int val; /*!< 0 means don't return, just update flag */
|
||||
/*@observer@*/ /*@null@*/
|
||||
int val; /*!< 0 means don't return, just update arg */
|
||||
const char * descrip; /*!< description for autohelp -- may be NULL */
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char * argDescrip; /*!< argument description for autohelp */
|
||||
const char * argDescrip; /*!< argument description for autohelp -- may be NULL */
|
||||
};
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* A popt alias argument for poptAddAlias().
|
||||
*/
|
||||
struct poptAlias {
|
||||
/*@owned@*/ /*@null@*/
|
||||
const char * longName; /*!< may be NULL */
|
||||
char shortName; /*!< may be NUL */
|
||||
int argc;
|
||||
/*@owned@*/
|
||||
const char ** argv; /*!< must be free()able */
|
||||
};
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* A popt alias or exec argument for poptAddItem().
|
||||
*/
|
||||
/*@-exporttype@*/
|
||||
typedef struct poptItem_s {
|
||||
struct poptOption option; /*!< alias/exec name(s) and description. */
|
||||
int argc; /*!< (alias) no. of args. */
|
||||
/*@owned@*/
|
||||
const char ** argv; /*!< (alias) args, must be free()able. */
|
||||
} * poptItem;
|
||||
/*@=exporttype@*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Auto-generated help/usage
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Empty table marker to enable displaying popt alias/exec options.
|
||||
*/
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption poptAliasOptions[];
|
||||
/*@=exportvar@*/
|
||||
#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
|
||||
0, "Options implemented via popt alias/exec:", NULL },
|
||||
|
||||
/**
|
||||
* Auto help table options.
|
||||
*/
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption poptHelpOptions[];
|
||||
/*@=exportvar@*/
|
||||
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption * poptHelpOptionsI18N;
|
||||
/*@=exportvar@*/
|
||||
|
||||
#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
|
||||
0, "Help options:", NULL },
|
||||
|
||||
#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
|
||||
/*@}*/
|
||||
#define POPT_TABLEEND { NULL, '\0', 0, NULL, 0, NULL, NULL }
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
/*@-exporttype@*/
|
||||
typedef /*@abstract@*/ struct poptContext_s * poptContext;
|
||||
/*@=exporttype@*/
|
||||
typedef struct poptContext_s * poptContext;
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
/*@-exporttype -typeuse@*/
|
||||
typedef struct poptOption * poptOption;
|
||||
/*@=exporttype =typeuse@*/
|
||||
#endif
|
||||
|
||||
/*@-exportconst@*/
|
||||
/**
|
||||
*/
|
||||
enum poptCallbackReason {
|
||||
POPT_CALLBACK_REASON_PRE = 0,
|
||||
POPT_CALLBACK_REASON_POST = 1,
|
||||
POPT_CALLBACK_REASON_OPTION = 2
|
||||
};
|
||||
/*@=exportconst@*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*@-type@*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Table callback prototype.
|
||||
* @param con context
|
||||
* @param reason reason for callback
|
||||
@@ -221,13 +192,18 @@ extern "C" {
|
||||
*/
|
||||
typedef void (*poptCallbackType) (poptContext con,
|
||||
enum poptCallbackReason reason,
|
||||
/*@null@*/ const struct poptOption * opt,
|
||||
/*@null@*/ const char * arg,
|
||||
/*@null@*/ const void * data)
|
||||
/*@globals internalState @*/
|
||||
/*@modifies internalState @*/;
|
||||
const struct poptOption * opt,
|
||||
const char * arg,
|
||||
const void * data);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Destroy context.
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
poptContext poptFreeContext( poptContext con);
|
||||
|
||||
/**
|
||||
* Initialize popt context.
|
||||
* @param name context name (usually argv[0] program name)
|
||||
* @param argc no. of arguments
|
||||
@@ -236,97 +212,90 @@ typedef void (*poptCallbackType) (poptContext con,
|
||||
* @param flags or'd POPT_CONTEXT_* bits
|
||||
* @return initialized popt context
|
||||
*/
|
||||
/*@only@*/ /*@null@*/
|
||||
poptContext poptGetContext(
|
||||
/*@dependent@*/ /*@keep@*/ const char * name,
|
||||
int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
|
||||
/*@dependent@*/ /*@keep@*/ const struct poptOption * options,
|
||||
int flags)
|
||||
/*@*/;
|
||||
const char * name,
|
||||
int argc, const char ** argv,
|
||||
const struct poptOption * options,
|
||||
unsigned int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Destroy context (alternative implementation).
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
poptContext poptFini( poptContext con);
|
||||
|
||||
/**
|
||||
* Initialize popt context (alternative implementation).
|
||||
* This routine does poptGetContext() and then poptReadConfigFiles().
|
||||
* @param argc no. of arguments
|
||||
* @param argv argument array
|
||||
* @param options address of popt option table
|
||||
* @param configPaths colon separated file path(s) to read.
|
||||
* @return initialized popt context (NULL on error).
|
||||
*/
|
||||
poptContext poptInit(int argc, const char ** argv,
|
||||
const struct poptOption * options,
|
||||
const char * configPaths);
|
||||
|
||||
/**
|
||||
* Reinitialize popt context.
|
||||
* @param con context
|
||||
*/
|
||||
/*@unused@*/
|
||||
void poptResetContext(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
void poptResetContext(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return value of next option found.
|
||||
* @param con context
|
||||
* @return next option val, -1 on last item, POPT_ERROR_* on error
|
||||
*/
|
||||
int poptGetNextOpt(/*@null@*/poptContext con)
|
||||
/*@globals fileSystem, internalState @*/
|
||||
/*@modifies con, fileSystem, internalState @*/;
|
||||
int poptGetNextOpt(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return next option argument (if any).
|
||||
* @param con context
|
||||
* @return option argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptGetOptArg(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
char * poptGetOptArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return next argument.
|
||||
* @param con context
|
||||
* @return next argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptGetArg(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
const char * poptGetArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Peek at current argument.
|
||||
* @param con context
|
||||
* @return current argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptPeekArg(/*@null@*/poptContext con)
|
||||
/*@*/;
|
||||
const char * poptPeekArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return remaining arguments.
|
||||
* @param con context
|
||||
* @return argument array, NULL terminated
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char ** poptGetArgs(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
const char ** poptGetArgs(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return the option which caused the most recent error.
|
||||
* @param con context
|
||||
* @param flags
|
||||
* @return offending option
|
||||
*/
|
||||
/*@observer@*/
|
||||
const char * poptBadOption(/*@null@*/poptContext con, int flags)
|
||||
/*@*/;
|
||||
const char * poptBadOption(poptContext con, unsigned int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
* Destroy context.
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
/*@null@*/
|
||||
poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
|
||||
/*@modifies con @*/;
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add arguments to context.
|
||||
* @param con context
|
||||
* @param argv argument array, NULL terminated
|
||||
* @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
|
||||
/*@modifies con @*/;
|
||||
int poptStuffArgs(poptContext con, const char ** argv);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add alias to context.
|
||||
* @todo Pass alias by reference, not value.
|
||||
* @deprecated Use poptAddItem instead.
|
||||
@@ -335,44 +304,64 @@ int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
|
||||
* @param flags (unused)
|
||||
* @return 0 on success
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
|
||||
/*@modifies con @*/;
|
||||
int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add alias/exec item to context.
|
||||
* @param con context
|
||||
* @param newItem alias/exec item to add
|
||||
* @param flags 0 for alias, 1 for exec
|
||||
* @return 0 on success
|
||||
*/
|
||||
int poptAddItem(poptContext con, poptItem newItem, int flags)
|
||||
/*@modifies con @*/;
|
||||
int poptAddItem(poptContext con, poptItem newItem, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Test path/file for config file sanity (regular file, permissions etc)
|
||||
* @param fn file name
|
||||
* @return 1 on OK, 0 on NOTOK.
|
||||
*/
|
||||
int poptSaneFile(const char * fn);
|
||||
|
||||
/**
|
||||
* Read a file into a buffer.
|
||||
* @param fn file name
|
||||
* @retval *bp buffer (malloc'd) (or NULL)
|
||||
* @retval *nbp no. of bytes in buffer (including final NUL) (or NULL)
|
||||
* @param flags 1 to trim escaped newlines
|
||||
* return 0 on success
|
||||
*/
|
||||
int poptReadFile(const char * fn, char ** bp,
|
||||
size_t * nbp, int flags);
|
||||
#define POPT_READFILE_TRIMNEWLINES 1
|
||||
|
||||
/**
|
||||
* Read configuration file.
|
||||
* @param con context
|
||||
* @param fn file name to read
|
||||
* @return 0 on success, POPT_ERROR_ERRNO on failure
|
||||
*/
|
||||
int poptReadConfigFile(poptContext con, const char * fn)
|
||||
/*@globals errno, fileSystem, internalState @*/
|
||||
/*@modifies con->execs, con->numExecs,
|
||||
errno, fileSystem, internalState @*/;
|
||||
int poptReadConfigFile(poptContext con, const char * fn);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Read configuration file(s).
|
||||
* Colon separated files to read, looping over poptReadConfigFile().
|
||||
* Note that an '@' character preceding a path in the list will
|
||||
* also perform additional sanity checks on the file before reading.
|
||||
* @param con context
|
||||
* @param paths colon separated file name(s) to read
|
||||
* @return 0 on success, POPT_ERROR_BADCONFIG on failure
|
||||
*/
|
||||
int poptReadConfigFiles(poptContext con, const char * paths);
|
||||
|
||||
/**
|
||||
* Read default configuration from /etc/popt and $HOME/.popt.
|
||||
* @param con context
|
||||
* @param useEnv (unused)
|
||||
* @return 0 on success, POPT_ERROR_ERRNO on failure
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
|
||||
/*@globals fileSystem, internalState @*/
|
||||
/*@modifies con->execs, con->numExecs,
|
||||
fileSystem, internalState @*/;
|
||||
int poptReadDefaultConfig(poptContext con, int useEnv);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Duplicate an argument array.
|
||||
* @note: The argument array is malloc'd as a single area, so only argv must
|
||||
* be free'd.
|
||||
@@ -383,12 +372,11 @@ int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
|
||||
* @retval argvPtr address of returned argument array
|
||||
* @return 0 on success, POPT_ERROR_NOARG on failure
|
||||
*/
|
||||
int poptDupArgv(int argc, /*@null@*/ const char **argv,
|
||||
/*@null@*/ /*@out@*/ int * argcPtr,
|
||||
/*@null@*/ /*@out@*/ const char *** argvPtr)
|
||||
/*@modifies *argcPtr, *argvPtr @*/;
|
||||
int poptDupArgv(int argc, const char **argv,
|
||||
int * argcPtr,
|
||||
const char *** argvPtr);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Parse a string into an argument array.
|
||||
* The parse allows ', ", and \ quoting, but ' is treated the same as " and
|
||||
* both may include \ quotes.
|
||||
@@ -400,10 +388,9 @@ int poptDupArgv(int argc, /*@null@*/ const char **argv,
|
||||
* @retval argvPtr address of returned argument array
|
||||
*/
|
||||
int poptParseArgvString(const char * s,
|
||||
/*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
|
||||
/*@modifies *argcPtr, *argvPtr @*/;
|
||||
int * argcPtr, const char *** argvPtr);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Parses an input configuration file and returns an string that is a
|
||||
* command line. For use with popt. You must free the return value when done.
|
||||
*
|
||||
@@ -418,8 +405,8 @@ bla=bla
|
||||
|
||||
this_is = fdsafdas
|
||||
bad_line=
|
||||
reall bad line
|
||||
reall bad line = again
|
||||
really bad line
|
||||
really bad line = again
|
||||
5555= 55555
|
||||
test = with lots of spaces
|
||||
\endverbatim
|
||||
@@ -449,83 +436,82 @@ this_is = fdsafdas
|
||||
* @return 0 on success
|
||||
* @see poptParseArgvString
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, *argstrp, fileSystem @*/;
|
||||
/*@=fcnuse@*/
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return formatted error string for popt failure.
|
||||
* @param error popt error
|
||||
* @return error string
|
||||
*/
|
||||
/*@observer@*/
|
||||
const char * poptStrerror(const int error)
|
||||
/*@*/;
|
||||
const char * poptStrerror(const int error);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Limit search for executables.
|
||||
* @param con context
|
||||
* @param path single path to search for executables
|
||||
* @param allowAbsolute absolute paths only?
|
||||
*/
|
||||
/*@unused@*/
|
||||
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
|
||||
/*@modifies con @*/;
|
||||
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Print detailed description of options.
|
||||
* @param con context
|
||||
* @param fp ouput file handle
|
||||
* @param fp output file handle
|
||||
* @param flags (unused)
|
||||
*/
|
||||
void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, fileSystem @*/;
|
||||
void poptPrintHelp(poptContext con, FILE * fp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Print terse description of options.
|
||||
* @param con context
|
||||
* @param fp ouput file handle
|
||||
* @param fp output file handle
|
||||
* @param flags (unused)
|
||||
*/
|
||||
void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, fileSystem @*/;
|
||||
void poptPrintUsage(poptContext con, FILE * fp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Provide text to replace default "[OPTION...]" in help/usage output.
|
||||
* @param con context
|
||||
* @param text replacement text
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
void poptSetOtherOptionHelp(poptContext con, const char * text)
|
||||
/*@modifies con @*/;
|
||||
/*@=fcnuse@*/
|
||||
void poptSetOtherOptionHelp(poptContext con, const char * text);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return argv[0] from context.
|
||||
* @param con context
|
||||
* @return argv[0]
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
/*@observer@*/
|
||||
const char * poptGetInvocationName(poptContext con)
|
||||
/*@*/;
|
||||
/*@=fcnuse@*/
|
||||
const char * poptGetInvocationName(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Shuffle argv pointers to remove stripped args, returns new argc.
|
||||
* @param con context
|
||||
* @param argc no. of args
|
||||
* @param argv arg vector
|
||||
* @return new argc
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
int poptStrippedArgv(poptContext con, int argc, char ** argv)
|
||||
/*@modifies *argv @*/;
|
||||
/*@=fcnuse@*/
|
||||
int poptStrippedArgv(poptContext con, int argc, char ** argv);
|
||||
|
||||
/**
|
||||
* Add a string to an argv array.
|
||||
* @retval *argvp argv array
|
||||
* @param argInfo (unused)
|
||||
* @param val string arg to add (using strdup)
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveString(const char *** argvp, unsigned int argInfo,
|
||||
const char * val);
|
||||
|
||||
/**
|
||||
* Save a long long, performing logical operation with value.
|
||||
* @warning Alignment check may be too strict on certain platorms.
|
||||
* @param arg integer pointer, aligned on int boundary.
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param aLongLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveLongLong(long long * arg, unsigned int argInfo,
|
||||
long long aLongLong);
|
||||
|
||||
/**
|
||||
* Save a long, performing logical operation with value.
|
||||
@@ -535,12 +521,17 @@ int poptStrippedArgv(poptContext con, int argc, char ** argv)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
/*@-incondefs@*/
|
||||
/*@unused@*/
|
||||
int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
|
||||
/*@modifies *arg @*/
|
||||
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
|
||||
/*@=incondefs@*/
|
||||
int poptSaveLong(long * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/**
|
||||
* Save a short integer, performing logical operation with value.
|
||||
* @warning Alignment check may be too strict on certain platorms.
|
||||
* @param arg short pointer, aligned on short boundary.
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveShort(short * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/**
|
||||
* Save an integer, performing logical operation with value.
|
||||
@@ -550,14 +541,40 @@ int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
/*@-incondefs@*/
|
||||
/*@unused@*/
|
||||
int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
|
||||
/*@modifies *arg @*/
|
||||
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
|
||||
/*@=incondefs@*/
|
||||
int poptSaveInt(int * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/* The bit set typedef. */
|
||||
typedef struct poptBits_s {
|
||||
unsigned int bits[1];
|
||||
} * poptBits;
|
||||
|
||||
#define _POPT_BITS_N 1024U /*!< estimated population */
|
||||
#define _POPT_BITS_M ((3U * _POPT_BITS_N) / 2U)
|
||||
#define _POPT_BITS_K 16U /*!< no. of linear hash combinations */
|
||||
|
||||
extern unsigned int _poptBitsN;
|
||||
extern unsigned int _poptBitsM;
|
||||
extern unsigned int _poptBitsK;
|
||||
|
||||
int poptBitsAdd(poptBits bits, const char * s);
|
||||
int poptBitsChk(poptBits bits, const char * s);
|
||||
int poptBitsClr(poptBits bits);
|
||||
int poptBitsDel(poptBits bits, const char * s);
|
||||
int poptBitsIntersect(poptBits * ap, const poptBits b);
|
||||
int poptBitsUnion(poptBits * ap, const poptBits b);
|
||||
int poptBitsArgs(poptContext con, poptBits * ap);
|
||||
|
||||
/**
|
||||
* Save a string into a bit set (experimental).
|
||||
* @retval *bits bit set (lazily malloc'd if NULL)
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param s string to add to bit set
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveBits(poptBits * bitsp, unsigned int argInfo,
|
||||
const char * s);
|
||||
|
||||
|
||||
/*@=type@*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptconfig.c
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -8,54 +8,300 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "poptint.h"
|
||||
/*@access poptContext @*/
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
|
||||
static void configLine(poptContext con, char * line)
|
||||
/*@modifies con @*/
|
||||
#if defined(HAVE_FNMATCH_H)
|
||||
#include <fnmatch.h>
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
#include <glob.h>
|
||||
|
||||
#if !defined(HAVE_GLOB_PATTERN_P)
|
||||
/* Return nonzero if PATTERN contains any metacharacters.
|
||||
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
|
||||
static int
|
||||
glob_pattern_p (const char * pattern, int quote)
|
||||
{
|
||||
size_t nameLength;
|
||||
const char * p;
|
||||
int open = 0;
|
||||
|
||||
for (p = pattern; *p != '\0'; ++p)
|
||||
switch (*p) {
|
||||
case '?':
|
||||
case '*':
|
||||
return 1;
|
||||
break;
|
||||
case '\\':
|
||||
if (quote && p[1] != '\0')
|
||||
++p;
|
||||
break;
|
||||
case '[':
|
||||
open = 1;
|
||||
break;
|
||||
case ']':
|
||||
if (open)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(__GLIBC__) */
|
||||
|
||||
static int poptGlobFlags = 0;
|
||||
|
||||
static int poptGlob_error(UNUSED(const char * epath),
|
||||
UNUSED(int eerrno))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* HAVE_GLOB_H */
|
||||
|
||||
/**
|
||||
* Return path(s) from a glob pattern.
|
||||
* @param con context
|
||||
* @param pattern glob pattern
|
||||
* @retval *acp no. of paths
|
||||
* @retval *avp array of paths
|
||||
* @return 0 on success
|
||||
*/
|
||||
static int poptGlob(UNUSED(poptContext con), const char * pattern,
|
||||
int * acp, const char *** avp)
|
||||
{
|
||||
const char * pat = pattern;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
if (glob_pattern_p(pat, 0)) {
|
||||
glob_t _g, *pglob = &_g;
|
||||
|
||||
if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
|
||||
if (acp) {
|
||||
*acp = (int) pglob->gl_pathc;
|
||||
pglob->gl_pathc = 0;
|
||||
}
|
||||
if (avp) {
|
||||
*avp = (const char **) pglob->gl_pathv;
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
globfree(pglob);
|
||||
} else if (rc == GLOB_NOMATCH) {
|
||||
*avp = NULL;
|
||||
*acp = 0;
|
||||
rc = 0;
|
||||
} else
|
||||
rc = POPT_ERROR_ERRNO;
|
||||
} else
|
||||
#endif /* HAVE_GLOB_H */
|
||||
{
|
||||
if (acp)
|
||||
*acp = 1;
|
||||
if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
|
||||
(*avp)[0] = xstrdup(pat);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int poptSaneFile(const char * fn)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
|
||||
return 0;
|
||||
if (stat(fn, &sb) == -1)
|
||||
return 0;
|
||||
if (!S_ISREG(sb.st_mode))
|
||||
return 0;
|
||||
if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
|
||||
{
|
||||
int fdno;
|
||||
char * b = NULL;
|
||||
off_t nb = 0;
|
||||
char * s, * t, * se;
|
||||
int rc = POPT_ERROR_ERRNO; /* assume failure */
|
||||
|
||||
fdno = open(fn, O_RDONLY);
|
||||
if (fdno < 0)
|
||||
goto exit;
|
||||
|
||||
if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
|
||||
|| (uintmax_t)nb >= SIZE_MAX
|
||||
|| lseek(fdno, 0, SEEK_SET) == (off_t)-1
|
||||
|| (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
|
||||
|| read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
|
||||
{
|
||||
int oerrno = errno;
|
||||
(void) close(fdno);
|
||||
if (nb != (off_t)-1 && (uintmax_t)nb >= SIZE_MAX)
|
||||
errno = -EOVERFLOW;
|
||||
else
|
||||
errno = oerrno;
|
||||
goto exit;
|
||||
}
|
||||
if (close(fdno) == -1)
|
||||
goto exit;
|
||||
if (b == NULL) {
|
||||
rc = POPT_ERROR_MALLOC;
|
||||
goto exit;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
/* Trim out escaped newlines. */
|
||||
if (flags & POPT_READFILE_TRIMNEWLINES)
|
||||
{
|
||||
for (t = b, s = b, se = b + nb; *s && s < se; s++) {
|
||||
switch (*s) {
|
||||
case '\\':
|
||||
if (s[1] == '\n') {
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
*t++ = *s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*t++ = '\0';
|
||||
nb = (off_t)(t - b);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (rc != 0) {
|
||||
if (b)
|
||||
free(b);
|
||||
b = NULL;
|
||||
nb = 0;
|
||||
}
|
||||
if (bp)
|
||||
*bp = b;
|
||||
else if (b)
|
||||
free(b);
|
||||
if (nbp)
|
||||
*nbp = (size_t)nb;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for application match.
|
||||
* @param con context
|
||||
* @param s config application name
|
||||
* return 0 if config application matches
|
||||
*/
|
||||
static int configAppMatch(poptContext con, const char * s)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
if (con->appName == NULL) /* XXX can't happen. */
|
||||
return rc;
|
||||
|
||||
#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
|
||||
if (glob_pattern_p(s, 1)) {
|
||||
static int flags = FNM_PATHNAME | FNM_PERIOD;
|
||||
#ifdef FNM_EXTMATCH
|
||||
flags |= FNM_EXTMATCH;
|
||||
#endif
|
||||
rc = fnmatch(s, con->appName, flags);
|
||||
} else
|
||||
#endif
|
||||
rc = strcmp(s, con->appName);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int poptConfigLine(poptContext con, char * line)
|
||||
{
|
||||
char *b = NULL;
|
||||
size_t nb = 0;
|
||||
char * se = line;
|
||||
const char * appName;
|
||||
const char * entryType;
|
||||
const char * opt;
|
||||
poptItem item = (poptItem) alloca(sizeof(*item));
|
||||
struct poptItem_s item_buf;
|
||||
poptItem item = &item_buf;
|
||||
int i, j;
|
||||
int rc = POPT_ERROR_BADCONFIG;
|
||||
|
||||
if (con->appName == NULL)
|
||||
return;
|
||||
nameLength = strlen(con->appName);
|
||||
goto exit;
|
||||
|
||||
/*@-boundswrite@*/
|
||||
memset(item, 0, sizeof(*item));
|
||||
|
||||
if (strncmp(line, con->appName, nameLength)) return;
|
||||
appName = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (*se == '\0')
|
||||
goto exit;
|
||||
else
|
||||
*se++ = '\0';
|
||||
|
||||
line += nameLength;
|
||||
if (*line == '\0' || !isSpace(line)) return;
|
||||
if (configAppMatch(con, appName)) goto exit;
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
entryType = line;
|
||||
while (*line == '\0' || !isSpace(line)) line++;
|
||||
*line++ = '\0';
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
entryType = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (*se != '\0') *se++ = '\0';
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
if (*line == '\0') return;
|
||||
opt = line;
|
||||
while (*line == '\0' || !isSpace(line)) line++;
|
||||
*line++ = '\0';
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
if (*se == '\0') goto exit;
|
||||
opt = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (opt[0] == '-' && *se == '\0') goto exit;
|
||||
if (*se != '\0') *se++ = '\0';
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
if (*line == '\0') return;
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
if (opt[0] == '-' && *se == '\0') goto exit;
|
||||
|
||||
/*@-temptrans@*/ /* FIX: line alias is saved */
|
||||
if (opt[0] == '-' && opt[1] == '-')
|
||||
item->option.longName = opt + 2;
|
||||
else if (opt[0] == '-' && opt[2] == '\0')
|
||||
item->option.shortName = opt[1];
|
||||
/*@=temptrans@*/
|
||||
else {
|
||||
const char * fn = opt;
|
||||
|
||||
if (poptParseArgvString(line, &item->argc, &item->argv)) return;
|
||||
/* XXX handle globs and directories in fn? */
|
||||
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
||||
goto exit;
|
||||
if (b == NULL || nb == 0)
|
||||
goto exit;
|
||||
|
||||
/* Append remaining text to the interpolated file option text. */
|
||||
if (*se != '\0') {
|
||||
size_t nse = strlen(se) + 1;
|
||||
if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
|
||||
goto exit;
|
||||
(void) stpcpy( stpcpy(&b[nb-1], " "), se);
|
||||
nb += nse;
|
||||
}
|
||||
se = b;
|
||||
|
||||
/* Use the basename of the path as the long option name. */
|
||||
{ const char * longName = strrchr(fn, '/');
|
||||
if (longName != NULL)
|
||||
longName++;
|
||||
else
|
||||
longName = fn;
|
||||
if (longName == NULL) /* XXX can't happen. */
|
||||
goto exit;
|
||||
/* Single character basenames are treated as short options. */
|
||||
if (longName[1] != '\0')
|
||||
item->option.longName = longName;
|
||||
else
|
||||
item->option.shortName = longName[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
|
||||
|
||||
/*@-modobserver@*/
|
||||
item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
|
||||
for (i = 0, j = 0; i < item->argc; i++, j++) {
|
||||
const char * f;
|
||||
@@ -81,103 +327,183 @@ static void configLine(poptContext con, char * line)
|
||||
item->argv[j] = NULL;
|
||||
item->argc = j;
|
||||
}
|
||||
/*@=modobserver@*/
|
||||
/*@=boundswrite@*/
|
||||
|
||||
/*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
|
||||
if (!strcmp(entryType, "alias"))
|
||||
(void) poptAddItem(con, item, 0);
|
||||
rc = poptAddItem(con, item, 0);
|
||||
else if (!strcmp(entryType, "exec"))
|
||||
(void) poptAddItem(con, item, 1);
|
||||
/*@=nullstate@*/
|
||||
rc = poptAddItem(con, item, 1);
|
||||
exit:
|
||||
rc = 0; /* XXX for now, always return success */
|
||||
if (b)
|
||||
free(b);
|
||||
return rc;
|
||||
}
|
||||
/*@=compmempass@*/
|
||||
|
||||
int poptReadConfigFile(poptContext con, const char * fn)
|
||||
{
|
||||
const char * file, * chptr, * end;
|
||||
char * buf;
|
||||
/*@dependent@*/ char * dst;
|
||||
int fd, rc;
|
||||
off_t fileLength;
|
||||
|
||||
fd = open(fn, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
|
||||
|
||||
fileLength = lseek(fd, 0, SEEK_END);
|
||||
if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
|
||||
rc = errno;
|
||||
(void) close(fd);
|
||||
errno = rc;
|
||||
return POPT_ERROR_ERRNO;
|
||||
}
|
||||
|
||||
file = alloca(fileLength + 1);
|
||||
if (read(fd, (char *)file, fileLength) != fileLength) {
|
||||
rc = errno;
|
||||
(void) close(fd);
|
||||
errno = rc;
|
||||
return POPT_ERROR_ERRNO;
|
||||
}
|
||||
if (close(fd) == -1)
|
||||
return POPT_ERROR_ERRNO;
|
||||
|
||||
/*@-boundswrite@*/
|
||||
dst = buf = alloca(fileLength + 1);
|
||||
|
||||
chptr = file;
|
||||
end = (file + fileLength);
|
||||
/*@-infloops@*/ /* LCL: can't detect chptr++ */
|
||||
while (chptr < end) {
|
||||
switch (*chptr) {
|
||||
case '\n':
|
||||
*dst = '\0';
|
||||
dst = buf;
|
||||
while (*dst && isSpace(dst)) dst++;
|
||||
if (*dst && *dst != '#')
|
||||
configLine(con, dst);
|
||||
chptr++;
|
||||
/*@switchbreak@*/ break;
|
||||
case '\\':
|
||||
*dst++ = *chptr++;
|
||||
if (chptr < end) {
|
||||
if (*chptr == '\n')
|
||||
dst--, chptr++;
|
||||
/* \ at the end of a line does not insert a \n */
|
||||
else
|
||||
*dst++ = *chptr++;
|
||||
}
|
||||
/*@switchbreak@*/ break;
|
||||
default:
|
||||
*dst++ = *chptr++;
|
||||
/*@switchbreak@*/ break;
|
||||
}
|
||||
}
|
||||
/*@=infloops@*/
|
||||
/*@=boundswrite@*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
|
||||
{
|
||||
char * fn, * home;
|
||||
char * b = NULL, *be;
|
||||
size_t nb = 0;
|
||||
const char *se;
|
||||
char *t = NULL, *te;
|
||||
int rc;
|
||||
|
||||
if (con->appName == NULL) return 0;
|
||||
|
||||
rc = poptReadConfigFile(con, "/etc/popt");
|
||||
if (rc) return rc;
|
||||
|
||||
if ((home = getenv("HOME"))) {
|
||||
size_t bufsize = strlen(home) + 20;
|
||||
fn = alloca(bufsize);
|
||||
if (fn == NULL) return 0;
|
||||
snprintf(fn, bufsize, "%s/.popt", home);
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
if (rc) return rc;
|
||||
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
||||
return (errno == ENOENT ? 0 : rc);
|
||||
if (b == NULL || nb == 0) {
|
||||
rc = POPT_ERROR_BADCONFIG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if ((t = malloc(nb + 1)) == NULL)
|
||||
goto exit;
|
||||
te = t;
|
||||
|
||||
be = (b + nb);
|
||||
for (se = b; se < be; se++) {
|
||||
switch (*se) {
|
||||
case '\n':
|
||||
*te = '\0';
|
||||
te = t;
|
||||
while (*te && _isspaceptr(te)) te++;
|
||||
if (*te && *te != '#')
|
||||
if ((rc = poptConfigLine(con, te)) != 0)
|
||||
goto exit;
|
||||
break;
|
||||
case '\\':
|
||||
*te = *se++;
|
||||
/* \ at the end of a line does not insert a \n */
|
||||
if (se < be && *se != '\n') {
|
||||
te++;
|
||||
*te++ = *se;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*te++ = *se;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
exit:
|
||||
free(t);
|
||||
if (b)
|
||||
free(b);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int poptReadConfigFiles(poptContext con, const char * paths)
|
||||
{
|
||||
char * buf = (paths ? xstrdup(paths) : NULL);
|
||||
const char * p;
|
||||
char * pe;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
for (p = buf; p != NULL && *p != '\0'; p = pe) {
|
||||
const char ** av = NULL;
|
||||
int ac = 0;
|
||||
int i;
|
||||
int xx;
|
||||
|
||||
/* locate start of next path element */
|
||||
pe = strchr(p, ':');
|
||||
if (pe != NULL && *pe == ':')
|
||||
*pe++ = '\0';
|
||||
else
|
||||
pe = (char *) (p + strlen(p));
|
||||
|
||||
xx = poptGlob(con, p, &ac, &av);
|
||||
|
||||
/* work-off each resulting file from the path element */
|
||||
for (i = 0; i < ac; i++) {
|
||||
const char * fn = av[i];
|
||||
if (!poptSaneFile(fn))
|
||||
continue;
|
||||
xx = poptReadConfigFile(con, fn);
|
||||
if (xx && rc == 0)
|
||||
rc = xx;
|
||||
free((void *)av[i]);
|
||||
av[i] = NULL;
|
||||
}
|
||||
free(av);
|
||||
av = NULL;
|
||||
}
|
||||
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
|
||||
{
|
||||
char * home;
|
||||
struct stat sb;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
if (con->appName == NULL) goto exit;
|
||||
|
||||
rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
|
||||
if (rc) goto exit;
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
|
||||
const char ** av = NULL;
|
||||
int ac = 0;
|
||||
int i;
|
||||
|
||||
if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
|
||||
for (i = 0; rc == 0 && i < ac; i++) {
|
||||
const char * fn = av[i];
|
||||
if (!poptSaneFile(fn))
|
||||
continue;
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
free((void *)av[i]);
|
||||
av[i] = NULL;
|
||||
}
|
||||
free(av);
|
||||
av = NULL;
|
||||
}
|
||||
}
|
||||
if (rc) goto exit;
|
||||
#endif
|
||||
|
||||
if ((home = getenv("HOME"))) {
|
||||
char * fn = malloc(strlen(home) + 20);
|
||||
if (fn != NULL) {
|
||||
(void) stpcpy(stpcpy(fn, home), "/.popt");
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
free(fn);
|
||||
} else
|
||||
rc = POPT_ERROR_ERRNO;
|
||||
if (rc) goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
poptContext
|
||||
poptFini(poptContext con)
|
||||
{
|
||||
return poptFreeContext(con);
|
||||
}
|
||||
|
||||
poptContext
|
||||
poptInit(int argc, const char ** argv,
|
||||
const struct poptOption * options, const char * configPaths)
|
||||
{
|
||||
poptContext con = NULL;
|
||||
const char * argv0;
|
||||
|
||||
if (argv == NULL || argv[0] == NULL || options == NULL)
|
||||
return con;
|
||||
|
||||
if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
|
||||
else argv0 = argv[0];
|
||||
|
||||
con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
|
||||
if (con != NULL&& poptReadConfigFiles(con, configPaths))
|
||||
con = poptFini(con);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user